def __init__(self, filename, process_func=None, dtype=None,
                 as_grey=False, char_encoding=None):
        self._filename = filename
        self._file = open(filename, "rb")
        self._char_encoding = (char_encoding if char_encoding is not None
                               else self.default_char_encoding)

        ### Read metadata ###
        self.metadata = {}
        #Decode each string from the numpy array read by np.fromfile
        decode = np.vectorize(lambda x: x.decode(self._char_encoding))

        for name, sp in Spec.metadata.items():
            self._file.seek(sp[0])
            cnt = (1 if len(sp) < 3 else sp[2])
            v = np.fromfile(self._file, dtype=sp[1], count=cnt)
            if v.dtype.kind == "S" and name not in Spec.no_decode:
                #silently ignore string decoding failures
                try:
                    v = decode(v)
                except:
                    pass
            if cnt == 1:
                #for convenience, if the array contains only one single entry,
                #return this entry itself.
                v = np.asscalar(v)
            self.metadata[name] = v

        ### Some metadata is "special", deal with it
        #Determine data type
        self._file_dtype = Spec.dtypes[self.metadata.pop("datatype")]
        if dtype is None:
            self._dtype = self._file_dtype
        else:
            self._dtype = dtype

        #movie dimensions
        self._width = self.metadata.pop("xdim")
        self._height = self.metadata.pop("ydim")
        self._len = self.metadata.pop("NumFrames")

        #The number of ROIs is given in the SPE file. Only return as many
        #ROIs as given
        num_rois = self.metadata.pop("NumROI", None)
        num_rois = (1 if num_rois < 1 else num_rois)
        self.metadata["ROIs"] = self.metadata["ROIs"][:num_rois]

        #chip sizes
        self.metadata["ChipSize"] = (self.metadata.pop("xDimDet", None),
                                     self.metadata.pop("yDimDet", None))
        self.metadata["VirtChipSize"] = (self.metadata.pop("VChipXdim", None),
                                         self.metadata.pop("VChipYdim", None))

        #geometric operations
        g = []
        f = self.metadata.pop("geometric", 0)
        if f & 1:
            g.append("rotate")
        if f & 2:
            g.append("reverse")
        if f & 4:
            g.append("flip")
        self.metadata["geometric"] = g

        #Make some additional information more human-readable
        t = self.metadata["type"]
        if 1 <= t <= len(Spec.controllers):
            self.metadata["type"] = Spec.controllers[t - 1]
        else:
            self.metadata.pop("type", None)
        m = self.metadata["readoutMode"]
        if 1 <= m <= len(Spec.readout_modes):
            self.metadata["readoutMode"] = Spec.readout_modes[m - 1]
        else:
            self.metadata.pop("readoutMode", None)

        ### pims-specific stuff
        self._validate_process_func(process_func)
        self._as_grey(as_grey, process_func)
Diagonals are connected, so there will be 1 feature for all pixels plus
dataset/ETHPy150Open soft-matter/pims/pims/spe_stack.py/SpeStack.__init__--crossover with--dataset/ETHPy150Open mapbox/rasterio/tests/test_features.py/test_shapes_connectivity_queen