def __init__(self, fh):
        """
        Arguments:
            fh {str} -- absolute path to .nd2 file
            fh {IO} -- input buffer handler (opened with "rb" mode)
        """
        super(ND2Reader, self).__init__()

        self.filename = ""

        if isinstance(fh, str):
            if not fh.endswith(".nd2"):
                raise InvalidFileType(
                    ("The file %s you want to read with nd2reader" % fh)
                    + " does not have extension .nd2."
                )
            self.filename = fh
            fh = open(fh, "rb")

        self._fh = fh

        self._parser = Parser(self._fh)

        # Setup metadata
        self.metadata = self._parser.metadata

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None
Exemplo n.º 2
0
    def __init__(self, filename, memmap=False):
        super(self.__class__, self).__init__()
        self.filename = filename

        # first use the parser to parse the file
        self._fh = MemmappableFile(self.filename, memmap=memmap)
        self._parser = Parser(self._fh)

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()
Exemplo n.º 3
0
 def test_get_image(self):
     stitched_path = "test_data/test_stitched.nd2"
     if not path.isfile(stitched_path):
         file_name, header = urllib.request.urlretrieve(
             "https://downloads.openmicroscopy.org/images/ND2/karl/sample_image.nd2",
             stitched_path,
         )
     with open(stitched_path, "rb") as fh:
         parser = Parser(fh)
         parser.get_image(0)
Exemplo n.º 4
0
    def __init__(self, filename, memmap=False):
        super(self.__class__, self).__init__()
        self.filename = filename

        # first use the parser to parse the file
        self._fh = MemmappableFile(self.filename, memmap=memmap)
        self._parser = Parser(self._fh)

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None
Exemplo n.º 5
0
    def __init__(self, filename):
        super(ND2Reader, self).__init__()
        self.filename = filename

        # first use the parser to parse the file
        self._fh = open(filename, "rb")
        self._parser = Parser(self._fh)

        # Setup metadata
        self.metadata = self._parser.metadata

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None
Exemplo n.º 6
0
    def __init__(self, filename):
        super(ND2Reader, self).__init__()

        if not filename.endswith(".nd2"):
            raise InvalidFileType("The file %s you want to read with nd2reader does not have extension .nd2." % filename)

        self.filename = filename

        # first use the parser to parse the file
        self._fh = open(filename, "rb")
        self._parser = Parser(self._fh)

        # Setup metadata
        self.metadata = self._parser.metadata

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None
Exemplo n.º 7
0
class ND2ReaderForMetadata(ND2Reader):

    def __init__(self, filename):
        super(ND2Reader, self).__init__()
        self.filename = filename

        # first use the parser to parse the file
        self._fh = open(filename, "rb")
        self._parser = Parser(self._fh)

        # Setup metadata
        self.metadata = self._parser.metadata

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None

    def get_metadata(self):
        metadata_dict = self.metadata

        metadata_dict.pop('rois')
        metadata_dict.pop('z_levels')
        metadata_dict.pop('frames')
        metadata_dict.pop('date')

        metadata_dict['pfs_status'] = self._parser._raw_metadata.pfs_status
        metadata_dict['pfs_offset'] = self._parser._raw_metadata.pfs_offset

        metadata_dict['timesteps'] = self.timesteps
        metadata_dict['frame_rate'] = self.frame_rate

        return metadata_dict
Exemplo n.º 8
0
class ND2Reader(FramesSequenceND):
    """PIMS wrapper for the ND2 parser.
    This is the main class: use this to process your .nd2 files.
    """

    class_priority = 12

    def __init__(self, filename):
        super(ND2Reader, self).__init__()

        if not filename.endswith(".nd2"):
            raise InvalidFileType("The file %s you want to read with nd2reader does not have extension .nd2." % filename)

        self.filename = filename

        # first use the parser to parse the file
        self._fh = open(filename, "rb")
        self._parser = Parser(self._fh)

        # Setup metadata
        self.metadata = self._parser.metadata

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None

    @classmethod
    def class_exts(cls):
        """Let PIMS open function use this reader for opening .nd2 files

        """
        return {'nd2'} | super(ND2Reader, cls).class_exts()

    def close(self):
        """Correctly close the file handle

        """
        if self._fh is not None:
            self._fh.close()

    def _get_default(self, coord):
        try:
            return self.default_coords[coord]
        except KeyError:
            return 0

    def get_frame_2D(self, c=0, t=0, z=0, x=0, y=0, v=0):
        """Gets a given frame using the parser
        Args:
            x: The x-index (pims expects this)
            y: The y-index (pims expects this)
            c: The color channel number
            t: The frame number
            z: The z stack number
            v: The field of view index
        Returns:
            pims.Frame: The requested frame
        """
        # This needs to be set to width/height to return an image
        x = self.metadata["width"]
        y = self.metadata["height"]

        return self._parser.get_image_by_attributes(t, v, c, z, y, x)

    @property
    def parser(self):
        """
        Returns the parser object.
        Returns:
            Parser: the parser object
        """
        return self._parser

    @property
    def pixel_type(self):
        """Return the pixel data type

        Returns:
            dtype: the pixel data type

        """
        return self._dtype

    @property
    def timesteps(self):
        """Get the timesteps of the experiment

        Returns:
            np.ndarray: an array of times in milliseconds.

        """
        if self._timesteps is None:
            return self.get_timesteps()
        return self._timesteps

    @property
    def events(self):
        """Get the events of the experiment

        Returns:
            iterator of events as dict
        """

        return self._get_metadata_property("events")

    @property
    def frame_rate(self):
        """The (average) frame rate
        
        Returns:
            float: the (average) frame rate in frames per second
        """
        total_duration = 0.0

        for loop in self.metadata['experiment']['loops']:
            total_duration += loop['duration']

        if total_duration == 0:
            total_duration = self.timesteps[-1]

            if total_duration == 0:
                raise ValueError('Total measurement duration could not be determined from loops')

        return self.metadata['num_frames'] / (total_duration/1000.0)

    def _get_metadata_property(self, key, default=None):
        if self.metadata is None:
            return default

        if key not in self.metadata:
            return default

        if self.metadata[key] is None:
            return default

        return self.metadata[key]

    def _setup_axes(self):
        """Setup the xyctz axes, iterate over t axis by default

        """
        self._init_axis_if_exists('x', self._get_metadata_property("width", default=0))
        self._init_axis_if_exists('y', self._get_metadata_property("height", default=0))
        self._init_axis_if_exists('c', len(self._get_metadata_property("channels", default=[])), min_size=2)
        self._init_axis_if_exists('t', len(self._get_metadata_property("frames", default=[])))
        self._init_axis_if_exists('z', len(self._get_metadata_property("z_levels", default=[])), min_size=2)
        self._init_axis_if_exists('v', len(self._get_metadata_property("fields_of_view", default=[])), min_size=2)

        if len(self.sizes) == 0:
            raise EmptyFileError("No axes were found for this .nd2 file.")

        # provide the default
        self.iter_axes = self._guess_default_iter_axis()

        self._register_get_frame(self.get_frame_2D, 'yx')

    def _init_axis_if_exists(self, axis, size, min_size=1):
        if size >= min_size:
            self._init_axis(axis, size)

    def _guess_default_iter_axis(self):
        """
        Guesses the default axis to iterate over based on axis sizes.
        Returns:
            the axis to iterate over
        """
        priority = ['t', 'z', 'c', 'v']
        found_axes = []
        for axis in priority:
            try:
                current_size = self.sizes[axis]
            except KeyError:
                continue

            if current_size > 1:
                return axis

            found_axes.append(axis)

        return found_axes[0]

    def get_timesteps(self):
        """Get the timesteps of the experiment

        Returns:
            np.ndarray: an array of times in milliseconds.

        """
        if self._timesteps is not None and len(self._timesteps) > 0:
            return self._timesteps

        self._timesteps = np.array(list(self._parser._raw_metadata.acquisition_times), dtype=np.float) * 1000.0

        return self._timesteps
Exemplo n.º 9
0
class ND2ReaderSelfV3(ND2Reader):

    def __init__(self, filename):
        super(ND2Reader, self).__init__()
        self.filename = filename

        # first use the parser to parse the file
        self._fh = open(filename, "rb")
        self._parser = Parser(self._fh)

        # Setup metadata
        self.metadata = self._parser.metadata

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None

    def get_metadata(self):
        metadata_dict = self.metadata

        metadata_dict.pop('rois')
        try:
            metadata_dict['z_levels'] = list(metadata_dict.pop('z_levels'))
            metadata_dict['z_coordinates'] = metadata_dict.pop('z_coordinates')
        except Exception:
            pass
        metadata_dict.pop('frames')
        metadata_dict.pop('date')

        metadata_dict['pfs_status'] = self._parser._raw_metadata.pfs_status
        metadata_dict['pfs_offset'] = self._parser._raw_metadata.pfs_offset

        metadata_dict['timesteps'] = self.timesteps
        metadata_dict['frame_rate'] = self.frame_rate

        info_to_parse = self.parser._raw_metadata.image_text_info
        metadata_text_dict = self.parse_text_info(info_to_parse)

        metadata_dict = {**metadata_dict, **metadata_text_dict}

        info_to_parse = self.parser._raw_metadata.image_metadata_sequence
        metadata_dict_sequence = self.parse_sequence_info(info_to_parse)
        try:
            metadata_dict['EnableGainMultiplier'] = metadata_dict_sequence.pop('EnableGainMultiplier')
            metadata_dict['GainMultiplier'] = metadata_dict.pop('Multiplier')
            metadata_dict['Conversion_Gain'] = metadata_dict.pop('Conversion_Gain')
        except Exception:
            pass
        metadata_dict['Others'] = metadata_dict_sequence

        return metadata_dict

    def recursive_add_to_dict(self, dictionary):
        metadata_text_dict = {}
        for key_decoded, value_decoded in dictionary.items():
            if type(key_decoded) is bytes:
                key_decoded = key_decoded.decode("utf-8")
            if type(value_decoded) is bytes:
                value_decoded = value_decoded.decode("utf-8")

            if type(value_decoded) == dict:
                return_dict = self.recursive_add_to_dict(value_decoded)
                metadata_text_dict = {**metadata_text_dict, **return_dict}
            elif type(value_decoded) != str:
                metadata_text_dict[key_decoded] = value_decoded
            else:
                pass

        return metadata_text_dict

    def parse_sequence_info(self, info_to_parse):
        main_part = info_to_parse[b'SLxPictureMetadata']

        metadata_text_dict = self.recursive_add_to_dict(main_part)

        return metadata_text_dict

    @staticmethod
    def parse_text_info(info_to_parse):
        main_part = info_to_parse[b'SLxImageTextInfo']
        metadata_text_dict = {}

        for key, value in main_part.items():
            value_string = value.decode("utf-8")
            if value_string != '':
                split_string = value_string.split('\r\n')
                for line_number, line in enumerate(split_string):
                    if line == '':
                        continue
                    if line_number == 0:  # these are the headers, they do not have a value, only key
                        key = key.decode("utf-8")
                        if '5' in key or '6' in key:
                            continue
                        elif '9' in key:
                            value = value_string
                            key = 'date'
                        elif '13' in key:
                            value = value_string
                            key = 'Objective'
                        metadata_text_dict[key] = value
                        continue
                    try:
                        key, value = line.split(':')  # try to split, works for most
                    except Exception as e:
                        if "too many" in str(e):
                            split_line = line.split(':')  # microscope name has a : in it
                            key = split_line[0]
                            value = ":".join(split_line[1:])
                        elif "not enough" in str(e):
                            continue
                    if key == 'Metadata:' or key == '':  # remove emtpy stuff and the metadata header key
                        continue
                    key = key.lstrip()  # remove the spaces at the start of some keys
                    if type(value) is str:
                        value = value.lstrip()  # remove the spaces at the start of some value that are strings
                    key = key.replace(", ", "_")
                    key = key.replace(" ", "_")  # prevent spaces in key name, matlab does not like that
                    if ',' in value:
                        value = float(value.replace(',', '.'))
                        metadata_text_dict[key] = value
                    else:
                        try:
                            metadata_text_dict[key] = int(value)
                        except Exception:
                            metadata_text_dict[key] = value

        return metadata_text_dict
Exemplo n.º 10
0
class ND2Reader(FramesSequenceND):
    """PIMS wrapper for the ND2 parser.
    This is the main class: use this to process your .nd2 files.
    """

    class_priority = 12

    def __init__(self, filename):
        super(self.__class__, self).__init__()
        self.filename = filename

        # first use the parser to parse the file
        self._fh = open(filename, "rb")
        self._parser = Parser(self._fh)

        # Setup metadata
        self.metadata = self._parser.metadata

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None

    @classmethod
    def class_exts(cls):
        """Let PIMS open function use this reader for opening .nd2 files

        """
        return {'nd2'} | super(ND2Reader, cls).class_exts()

    def close(self):
        """Correctly close the file handle

        """
        if self._fh is not None:
            self._fh.close()

    def _get_default(self, coord):
        try:
            return self.default_coords[coord]
        except KeyError:
            return 0

    def get_frame_vczyx(self, v=None, c=None, t=None, z=None, x=None, y=None):
        x = self.metadata["width"] if x <= 0 else x
        y = self.metadata["height"] if y <= 0 else y

        result = []
        for v in self._get_possible_coords('v', v):
            for c in self._get_possible_coords('c', c):
                for z in self._get_possible_coords('z', z):
                    result.append(
                        self._parser.get_image_by_attributes(t, v, c, z, y, x))

        return np.squeeze(np.array(result, dtype=self._dtype))

    def _get_possible_coords(self, dim, default):
        if dim in self.sizes:
            return [default] if default is not None else range(self.sizes[dim])
        return [None]

    @property
    def parser(self):
        """
        Returns the parser object.
        Returns:
            Parser: the parser object
        """
        return self._parser

    @property
    def pixel_type(self):
        """Return the pixel data type

        Returns:
            dtype: the pixel data type

        """
        return self._dtype

    @property
    def timesteps(self):
        """Get the timesteps of the experiment

        Returns:
            np.ndarray: an array of times in milliseconds.

        """
        if self._timesteps is None:
            return self.get_timesteps()
        return self._timesteps

    @property
    def events(self):
        """Get the events of the experiment

        Returns:
            iterator of events as dict
        """

        return self._get_metadata_property("events")

    @property
    def frame_rate(self):
        """The (average) frame rate
        
        Returns:
            float: the (average) frame rate in frames per second
        """
        total_duration = 0.0

        for loop in self.metadata['experiment']['loops']:
            total_duration += loop['duration']

        if total_duration == 0:
            raise ValueError(
                'Total measurement duration could not be determined from loops'
            )

        return self.metadata['num_frames'] / (total_duration / 1000.0)

    def _get_metadata_property(self, key, default=None):
        if self.metadata is None:
            return default

        if key not in self.metadata:
            return default

        if self.metadata[key] is None:
            return default

        return self.metadata[key]

    def _setup_axes(self):
        """Setup the xyctz axes, iterate over t axis by default

        """
        self._init_axis_if_exists(
            'x', self._get_metadata_property("width", default=0))
        self._init_axis_if_exists(
            'y', self._get_metadata_property("height", default=0))
        self._init_axis_if_exists('c',
                                  len(
                                      self._get_metadata_property("channels",
                                                                  default=[])),
                                  min_size=2)
        self._init_axis_if_exists(
            't', len(self._get_metadata_property("frames", default=[])))
        self._init_axis_if_exists('z',
                                  len(
                                      self._get_metadata_property("z_levels",
                                                                  default=[])),
                                  min_size=2)
        self._init_axis_if_exists(
            'v',
            len(self._get_metadata_property("fields_of_view", default=[])),
            min_size=2)

        if len(self.sizes) == 0:
            raise EmptyFileError("No axes were found for this .nd2 file.")

        # provide the default
        self.iter_axes = self._guess_default_iter_axis()

        self._register_get_frame(self.get_frame_vczyx, 'vczyx')
        self._register_get_frame(self.get_frame_vczyx, 'vzyx')
        self._register_get_frame(self.get_frame_vczyx, 'vcyx')
        self._register_get_frame(self.get_frame_vczyx, 'vyx')

        self._register_get_frame(self.get_frame_vczyx, 'czyx')
        self._register_get_frame(self.get_frame_vczyx, 'cyx')

        self._register_get_frame(self.get_frame_vczyx, 'zyx')
        self._register_get_frame(self.get_frame_vczyx, 'yx')

    def _init_axis_if_exists(self, axis, size, min_size=1):
        if size >= min_size:
            self._init_axis(axis, size)

    def _guess_default_iter_axis(self):
        """
        Guesses the default axis to iterate over based on axis sizes.
        Returns:
            the axis to iterate over
        """
        priority = ['t', 'z', 'c', 'v']
        found_axes = []
        for axis in priority:
            try:
                current_size = self.sizes[axis]
            except KeyError:
                continue

            if current_size > 1:
                return axis

            found_axes.append(axis)

        return found_axes[0]

    def get_timesteps(self):
        """Get the timesteps of the experiment

        Returns:
            np.ndarray: an array of times in milliseconds.

        """
        if self._timesteps is not None and len(self._timesteps) > 0:
            return self._timesteps

        self._timesteps = np.array(list(
            self._parser._raw_metadata.acquisition_times),
                                   dtype=np.float) * 1000.0

        return self._timesteps
Exemplo n.º 11
0
class ND2Reader(FramesSequenceND):
    """PIMS wrapper for the ND2 parser.
    This is the main class: use this to process your .nd2 files.
    """

    class_priority = 12

    def __init__(self, filename):
        super(self.__class__, self).__init__()
        self.filename = filename

        # first use the parser to parse the file
        self._fh = open(filename, "rb")
        self._parser = Parser(self._fh)

        # Setup metadata
        self.metadata = self._parser.metadata

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None

    @classmethod
    def class_exts(cls):
        """Let PIMS open function use this reader for opening .nd2 files

        """
        return {'nd2'} | super(ND2Reader, cls).class_exts()

    def close(self):
        """Correctly close the file handle

        """
        if self._fh is not None:
            self._fh.close()

    def _get_default(self, coord):
        try:
            return self.default_coords[coord]
        except KeyError:
            return 0

    def get_frame_2D(self, c=0, t=0, z=0, x=0, y=0, v=0):
        """Gets a given frame using the parser

        Args:
            x: The x-index (pims expects this)
            y: The y-index (pims expects this)
            c: The color channel number
            t: The frame number
            z: The z stack number
            v: The field of view index

        Returns:
            numpy.ndarray: The requested frame

        """
        try:
            c_name = self.metadata["channels"][c]
        except KeyError:
            c_name = self.metadata["channels"][0]

        x = self.metadata["width"] if x <= 0 else x
        y = self.metadata["height"] if y <= 0 else y
        return self._parser.get_image_by_attributes(t, v, c_name, z, y, x)

    @property
    def parser(self):
        """
        Returns the parser object.
        Returns:
            Parser: the parser object
        """
        return self._parser

    @property
    def pixel_type(self):
        """Return the pixel data type

        Returns:
            dtype: the pixel data type

        """
        return self._dtype

    @property
    def timesteps(self):
        """Get the timesteps of the experiment

        Returns:
            np.ndarray: an array of times in milliseconds.

        """
        if self._timesteps is None:
            return self.get_timesteps()
        return self._timesteps

    @property
    def frame_rate(self):
        """The (average) frame rate
        
        Returns:
            float: the (average) frame rate in frames per second
        """
        return 1000. / np.mean(np.diff(self.timesteps))

    def _get_metadata_property(self, key, default=None):
        if self.metadata is None:
            return default

        if key not in self.metadata:
            return default

        if self.metadata[key] is None:
            return default

        return self.metadata[key]

    def _setup_axes(self):
        """Setup the xyctz axes, iterate over t axis by default

        """
        self._init_axis_if_exists(
            'x', self._get_metadata_property("width", default=0))
        self._init_axis_if_exists(
            'y', self._get_metadata_property("height", default=0))
        self._init_axis_if_exists('c',
                                  len(
                                      self._get_metadata_property("channels",
                                                                  default=[])),
                                  min_size=2)
        self._init_axis_if_exists(
            't', len(self._get_metadata_property("frames", default=[])))
        self._init_axis_if_exists('z',
                                  len(
                                      self._get_metadata_property("z_levels",
                                                                  default=[])),
                                  min_size=2)
        self._init_axis_if_exists(
            'v',
            len(self._get_metadata_property("fields_of_view", default=[])),
            min_size=2)

        if len(self.sizes) == 0:
            raise EmptyFileError("No axes were found for this .nd2 file.")

        # provide the default
        self.iter_axes = self._guess_default_iter_axis()

    def _init_axis_if_exists(self, axis, size, min_size=1):
        if size >= min_size:
            self._init_axis(axis, size)

    def _guess_default_iter_axis(self):
        """
        Guesses the default axis to iterate over based on axis sizes.
        Returns:
            the axis to iterate over
        """
        priority = ['t', 'z', 'c', 'v']
        found_axes = []
        for axis in priority:
            try:
                current_size = self.sizes[axis]
            except KeyError:
                continue

            if current_size > 1:
                return axis

            found_axes.append(axis)

        return found_axes[0]

    def get_timesteps(self):
        """Get the timesteps of the experiment

        Returns:
            np.ndarray: an array of times in milliseconds.

        """
        if self._timesteps is not None and len(timesteps) > 0:
            return self._timesteps

        timesteps = np.array([])
        current_time = 0.0

        for loop in self.metadata['experiment']['loops']:
            if loop['stimulation']:
                continue

            if loop['sampling_interval'] == 0:
                # This is a loop were no data is acquired
                current_time += loop['duration']
                continue

            timesteps = np.concatenate(
                (timesteps,
                 np.arange(current_time, current_time + loop['duration'],
                           loop['sampling_interval'])))
            current_time += loop['duration']

        # if experiment did not finish, number of timesteps is wrong. Take correct amount of leading timesteps.
        self._timesteps = timesteps[:self.metadata['num_frames']]

        return self._timesteps
Exemplo n.º 12
0
class ND2Reader:
    """PIMS wrapper for the ND2 parser.
    This is the main class: use this to process your .nd2 files.
    """

    class_priority = 12

    def __init__(self, filename, memmap=False):
        super(self.__class__, self).__init__()
        self.filename = filename

        # first use the parser to parse the file
        self._fh = MemmappableFile(self.filename, memmap=memmap)
        self._parser = Parser(self._fh)

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        # self._setup_axes()

        # Other properties
        # self._timesteps = None

    def reopen(self):
        # TODO: this is incredibly clunky
        fh = MemmappableFile(self.filename, memmap=self._fh.is_memmap)
        reader = copy(self)
        reader._fh = fh
        parser = copy(self._parser)
        parser._fh = fh
        raw_metadata = copy(parser._raw_metadata)
        raw_metadata._fh = fh
        parser._raw_metadata = raw_metadata
        reader._parser = parser
        return reader

    # @classmethod
    # def class_exts(cls):
    #     """Let PIMS open function use this reader for opening .nd2 files

    #     """
    #     return {"nd2"} | super(ND2Reader, cls).class_exts()

    def close(self):
        """Correctly close the file handle"""
        if self._fh is not None:
            self._fh.close()

    def _get_default(self, coord):
        try:
            return self.default_coords[coord]
        except KeyError:
            return 0

    def get_frame_2D(self, c=0, t=0, z=0, x=0, y=0, v=0, memmap=False):
        """Gets a given frame using the parser

        Args:
            x: The x-index (pims expects this)
            y: The y-index (pims expects this)
            c: The color channel number
            t: The frame number
            z: The z stack number
            v: The field of view index

        Returns:
            numpy.ndarray: The requested frame

        """
        try:
            c_name = self.metadata["channels"][c]
        except KeyError:
            c_name = self.metadata["channels"][0]

        x = self.metadata["width"] if x <= 0 else x
        y = self.metadata["height"] if y <= 0 else y
        return self._parser.get_image_by_attributes(
            t, v, c_name, z, y, x, memmap=memmap
        )

    @property
    def parser(self):
        """
        Returns the parser object.
        Returns:
            Parser: the parser object
        """
        return self._parser

    @property
    def metadata(self):
        return self._parser.metadata

    # @property
    # def pixel_type(self):
    #     """Return the pixel data type

    #     Returns:
    #         dtype: the pixel data type

    #     """
    #     return self._dtype

    # @property
    # def timesteps(self):
    #     """Get the timesteps of the experiment

    #     Returns:
    #         np.ndarray: an array of times in milliseconds.

    #     """
    #     if self._timesteps is None:
    #         return self.get_timesteps()
    #     return self._timesteps

    # @property
    # def frame_rate(self):
    #     """The (average) frame rate

    #     Returns:
    #         float: the (average) frame rate in frames per second
    #     """
    #     total_duration = 0.0

    #     for loop in self.metadata["experiment"]["loops"]:
    #         total_duration += loop["duration"]

    #     if total_duration == 0:
    #         raise ValueError(
    #             "Total measurement duration could not be determined from loops"
    #         )

    #     return self.metadata["num_frames"] / (total_duration / 1000.0)

    @property
    def sizes(self):
        return {
            "x": self.metadata["height"],
            "y": self.metadata["width"],
            "c": len(self.metadata["channels"]),
            "v": len(self.metadata["fields_of_view"]),
            "t": len(self.metadata["frames"]),
        }

    def _get_metadata_property(self, key, default=None):
        if self.metadata is None:
            return default

        if key not in self.metadata:
            return default

        if self.metadata[key] is None:
            return default

        return self.metadata[key]

    # def _setup_axes(self):
    #     """Setup the xyctz axes, iterate over t axis by default

    #     """
    #     self._init_axis_if_exists("x", self._get_metadata_property("width", default=0))
    #     self._init_axis_if_exists("y", self._get_metadata_property("height", default=0))
    #     self._init_axis_if_exists(
    #         "c", len(self._get_metadata_property("channels", default=[])), min_size=2
    #     )
    #     self._init_axis_if_exists(
    #         "t", len(self._get_metadata_property("frames", default=[]))
    #     )
    #     self._init_axis_if_exists(
    #         "z", len(self._get_metadata_property("z_levels", default=[])), min_size=2
    #     )
    #     self._init_axis_if_exists(
    #         "v",
    #         len(self._get_metadata_property("fields_of_view", default=[])),
    #         min_size=2,
    #     )

    #     if len(self.sizes) == 0:
    #         raise EmptyFileError("No axes were found for this .nd2 file.")

    #     # provide the default
    #     self.iter_axes = self._guess_default_iter_axis()

    # def _init_axis_if_exists(self, axis, size, min_size=1):
    #     if size >= min_size:
    #         self._init_axis(axis, size)

    # def _guess_default_iter_axis(self):
    #     """
    #     Guesses the default axis to iterate over based on axis sizes.
    #     Returns:
    #         the axis to iterate over
    #     """
    #     priority = ["t", "z", "c", "v"]
    #     found_axes = []
    #     for axis in priority:
    #         try:
    #             current_size = self.sizes[axis]
    #         except KeyError:
    #             continue

    #         if current_size > 1:
    #             return axis

    #         found_axes.append(axis)

    #     return found_axes[0]

    def get_timesteps(self):
        """Get the timesteps of the experiment

        Returns:
            np.ndarray: an array of times in milliseconds.

        """
        if self._timesteps is not None and len(self._timesteps) > 0:
            return self._timesteps

        self._timesteps = (
            np.array(list(self._parser._raw_metadata.acquisition_times), dtype=np.float)
            * 1000.0
        )

        return self._timesteps
Exemplo n.º 13
0
    def test_can_open_test_file(self):
        self.create_test_nd2()

        with open(self.test_file, "rb") as fh:
            parser = Parser(fh)
            self.assertTrue(parser.supported)
Exemplo n.º 14
0
class ND2ReaderForMetadata(ND2Reader):
    """
    The ND2Reader based reader purely for metadata
    """
    def __init__(self, filename):
        super(ND2Reader, self).__init__()
        self.filename = filename

        # first use the parser to parse the file
        self._fh = open(filename, "rb")
        self._parser = Parser(self._fh)

        # Setup metadata
        self.metadata = self._parser.metadata

        # Set data type
        self._dtype = self._parser.get_dtype_from_metadata()

        # Setup the axes
        self._setup_axes()

        # Other properties
        self._timesteps = None

    def get_metadata(self, verbose=True):
        """
        Get metadata. Reads out nd2 and returns the metadata
        """
        # get base metadata
        metadata_dict = self.metadata

        # remove ROIs
        metadata_dict.pop('rois', None)

        # try to find z levels
        try:
            metadata_dict['z_levels'] = list(metadata_dict.pop('z_levels'))
            metadata_dict['z_coordinates'] = metadata_dict.pop('z_coordinates')
        except Exception:
            if verbose:
                logger.warning("ND2: Z-levels missing from metadata")

        # remove frames and date (for now)
        metadata_dict.pop('frames', None)
        metadata_dict.pop('date', None)

        # check pfs status
        try:
            metadata_dict['pfs_status'] = self._parser._raw_metadata.pfs_status
            metadata_dict['pfs_offset'] = self._parser._raw_metadata.pfs_offset
        except Exception:
            if verbose:
                logger.warning("ND2: PFS data missing from metadata")

        # check timesteps and frame rate
        try:
            metadata_dict['timesteps'] = self.timesteps / 1000  # divide by 1000 to convert to seconds
            metadata_dict['frame_rate'] = self.frame_rate
        except Exception:
            if verbose:
                logger.warning("ND2: Timestep data missing from metadata")

        # add more info
        try:
            info_to_parse = self.parser._raw_metadata.image_text_info
            metadata_text_dict = self.parse_text_info(info_to_parse)
            metadata_dict = {**metadata_dict, **metadata_text_dict}
        except Exception:
            if verbose:
                logger.warning("ND2: Detailed metadata missing")

        # add raw metadata
        try:
            info_to_parse = self.parser._raw_metadata.image_metadata_sequence
            metadata_dict_sequence = self.parse_sequence_info(info_to_parse)
            try:
                # move some important stuff to main metadata
                metadata_dict['EnableGainMultiplier'] = metadata_dict_sequence.pop('EnableGainMultiplier')
                metadata_dict['GainMultiplier'] = metadata_dict.pop('Multiplier')
                metadata_dict['Conversion_Gain'] = metadata_dict.pop('Conversion_Gain')
            except Exception:
                pass
            # move rest to others
            metadata_dict['Others'] = metadata_dict_sequence
        except Exception:
            if verbose:
                logger.warning("ND2: Raw metadata missing")

        # prevent None values by making None string
        for key, value in metadata_dict.items():
            if value is None:
                metadata_dict[key] = str(value)

        return metadata_dict

    def recursive_add_to_dict(self, dictionary):
        """
        Function to parse dictionaries in metadata since many include dictionaries in dictionaries
        ----------------------------------------------
        :param dictionary: the dictionary to be parsed
        :return: the result of the dictionary
        """
        metadata_text_dict = {}
        for key_decoded, value_decoded in dictionary.items():
            # decode keys and values
            if type(key_decoded) is bytes:
                key_decoded = key_decoded.decode("utf-8")
            if type(value_decoded) is bytes:
                value_decoded = value_decoded.decode("utf-8")

            # if dict, add to restart function. Otherwise to add
            if type(value_decoded) == dict:
                return_dict = self.recursive_add_to_dict(value_decoded)
                metadata_text_dict = {**metadata_text_dict, **return_dict}
            elif type(value_decoded) != str:
                metadata_text_dict[key_decoded] = value_decoded
            else:
                pass

        return metadata_text_dict

    def parse_sequence_info(self, info_to_parse):
        """
        Parses the metadata info of the sequence
        --------------------------------------------
        :param info_to_parse: the info to parse from nd2 file
        :return: the parsed metadata
        """
        main_part = info_to_parse[b'SLxPictureMetadata']

        metadata_text_dict = self.recursive_add_to_dict(main_part)

        return metadata_text_dict

    @staticmethod
    def parse_text_info(info_to_parse):
        """
        Parses the metadata info of the image
        --------------------------------------------
        :param info_to_parse: the info to parse from nd2 file
        :return: the parsed metadata
        """
        main_part = info_to_parse[b'SLxImageTextInfo']
        metadata_text_dict = {}

        for key, value in main_part.items():
            # decode
            value_string = value.decode("utf-8")
            if value_string != '':
                # if not empty, split
                split_string = value_string.split('\r\n')
                for line_number, line in enumerate(split_string):
                    if line == '':
                        continue
                    if line_number == 0:  # these are the headers, they do not have a value, only key
                        key = key.decode("utf-8")
                        # we do not want those
                        if '5' in key or '6' in key:
                            continue
                        elif '9' in key:
                            # this is the date
                            value = value_string
                            key = 'date'
                        elif '13' in key:
                            # this is the objective
                            value = value_string
                            key = 'Objective'
                        # otherwise just add
                        metadata_text_dict[key] = value
                        continue
                    try:
                        key, value = line.split(':')  # try to split, works for most
                    except Exception as e:
                        if "too many" in str(e):
                            split_line = line.split(':')  # microscope name has a : in it
                            key = split_line[0]
                            value = ":".join(split_line[1:])
                        elif "not enough" in str(e):
                            continue
                    if key == 'Metadata:' or key == '' or key.count(' ') == len(key):
                        # remove emtpy stuff and the metadata header key. Last check is to see if only spaces
                        continue
                    key = key.lstrip()  # remove the spaces at the start of some keys
                    if type(value) is str:
                        value = value.lstrip()  # remove the spaces at the start of some value that are strings
                    key = key.replace(", ", "_")
                    key = key.replace(" ", "_")  # prevent spaces in key name, matlab does not like that
                    if ',' in value:
                        value = float(value.replace(',', '.'))
                        metadata_text_dict[key] = value
                    else:
                        try:
                            metadata_text_dict[key] = int(value)
                        except Exception:
                            metadata_text_dict[key] = value

        return metadata_text_dict