import json class DacMetadata(object): """Represents a display_and_comments.txt metadata file""" def __init__(self, dac_id, dac_file_path): """Contructor. :param str dac_id: eg "res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1" :param str dac_file_path: eg "/Users/graffy/ownCloud/ipr/lipase/raw-images/res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/display_and_comments.txt" """ self.dac_id = dac_id self.dac_file_path = dac_file_path with open(dac_file_path, "r") as dac_file: self.dac = json.load(dac_file, encoding='latin-1') # note : the micromanager metadata files are encoded in latin-1, not utf8 (see accents in comments) def get_channel_name(self, channel_index): channels = self.dac['Channels'] return channels[channel_index]["Name"] class Sequence(object): def __init__(self, catalog, sequence_id, micro_manager_metadata_file_path): self.catalog = catalog self.sequence_id = sequence_id self.micro_manager_metadata_file_path = micro_manager_metadata_file_path print(micro_manager_metadata_file_path) print('reading micro manager metatdata from %s' % micro_manager_metadata_file_path) # note : the micromanager metadata files are encoded in latin-1, not utf8 (see accents in comments) with open(micro_manager_metadata_file_path, "r") as mmm_file: self.mmm = json.load(mmm_file, encoding='latin-1') # note : the micromanager metadata files are encoded in latin-1, not utf8 (see accents in comments) micro_manager_metadata_file_parent_path = '/'.join(micro_manager_metadata_file_path.split('/')[0:-2]) print('micro_manager_metadata_file_parent_path = %s' % micro_manager_metadata_file_parent_path) dac_file_path = micro_manager_metadata_file_parent_path + '/display_and_comments.txt' dac_id = '/'.join(self.sequence_id.split('/')[0:-1]) self.dac = DacMetadata(dac_id, dac_file_path) for channel_index in range(self.num_channels): assert self.get_channel_name(channel_index) == self.dac.get_channel_name(channel_index), 'mismatched channel names for channel index %d : %s in %s, %s in %s' % (channel_index, self.get_channel_name(channel_index), self.micro_manager_metadata_file_path, self.dac.get_channel_name(channel_index), self.dac.dac_file_path) @property def num_frames(self): summary = self.mmm['Summary'] return int(summary['Frames']) @property def width(self): summary = self.mmm['Summary'] return int(summary['Width']) @property def height(self): summary = self.mmm['Summary'] return int(summary['Height']) @property def num_channels(self): summary = self.mmm['Summary'] return int(summary['Channels']) @property def num_slices(self): summary = self.mmm['Summary'] return int(summary['Slices']) @property def num_bits_per_pixels(self): summary = self.mmm['Summary'] return int(summary['BitDepth']) def get_root_path(self): return '/'.join(self.micro_manager_metadata_file_path.split('/')[:-1]) def get_image_file_path(self, channel_index, frame_index, z_index=0): ''' :param int channel_index: :param int frame_index: :param int z_index: ''' assert frame_index < self.num_frames assert channel_index < self.num_channels frame_info = self.mmm['FrameKey-%d-%d-%d' % (frame_index, channel_index, z_index)] rel_file_path = frame_info['FileName'] return self.get_root_path() + '/' + rel_file_path def get_channel_index(self, channel_id): ''' :param str channel_id: ''' summary = self.mmm['Summary'] channel_index = summary['ChNames'].index(channel_id) return channel_index def get_channel_name(self, channel_index): summary = self.mmm['Summary'] return summary['ChNames'][channel_index] def get_black(self): ''' returns the black sequence related to the the sequence self :return Sequence: ''' seqid_to_black = { 'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0': 'res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0', 'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2': 'res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0', } white_sequence = seqid_to_black[self.sequence_id] return self.catalog.sequences[white_sequence] def get_white(self): ''' returns the white sequence related to the the sequence self :return Sequence: ''' # assert fixme : res_soleil2018/white/white_24112018_2/Pos0 is visible seqid_to_white = { 'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0': 'res_soleil2018/white/white_24112018_2/Pos0', 'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2': 'res_soleil2018/white/white_24112018_2/Pos0', } white_sequence = seqid_to_white[self.sequence_id] return self.catalog.sequences[white_sequence] def as_hyperstack(self): hyperstack = IJ.createHyperStack(self.sequence_id, self.width, self.height, self.num_channels, self.num_slices, self.num_frames, self.num_bits_per_pixels) for channel_index in range(self.num_channels): for frame_index in range(self.num_frames): slice_index = 0 src_image_file_path = self.get_image_file_path(channel_index=channel_index, frame_index=frame_index) # print(src_image_file_path) src_image = IJ.openImage(src_image_file_path) # print(src_image.getProperties()) hyperstack.setPositionWithoutUpdate(channel_index + 1, slice_index + 1, frame_index + 1) hyperstack.setProcessor(src_image.getProcessor()) return hyperstack def as_stack(self, channel_id): ''' :param str channel_id: eg 'DM300_327-353_fluo' ''' channel_index = self.get_channel_index(channel_id) hyperstack = IJ.createHyperStack(self.sequence_id, self.width, self.height, 1, self.num_slices, self.num_frames, self.num_bits_per_pixels) for frame_index in range(self.num_frames): slice_index = 0 src_image_file_path = self.get_image_file_path(channel_index=channel_index, frame_index=frame_index) # print(src_image_file_path) src_image = IJ.openImage(src_image_file_path) # print(src_image.getProperties()) hyperstack.setPositionWithoutUpdate(channel_index + 1, slice_index + 1, frame_index + 1) hyperstack.setProcessor(src_image.getProcessor()) return hyperstack def open_in_imagej(self): # ip = IJ.createHyperStack(title=self.sequence_id, width=self.width, height= self.height, channels=1, slices=1, frames=self.get_num_frames(), bitdepth=16) hyperstack = self.as_hyperstack() hyperstack.show() for channel_index in range(self.num_channels): hyperstack.setPositionWithoutUpdate(channel_index + 1, 1, 1) IJ.run("Enhance Contrast", "saturated=0.35") return hyperstack class ImageCatalog(object): def __init__(self, raw_images_root): self.raw_images_root = raw_images_root self.sequences = {} # nb : we use the path as sequence id because the "Comment" field in the summary section of the metadata file is not guaranteed to be unique (eg they are the same in res_soleil2018/white/white_24112018_1/Pos0 and in res_soleil2018/white/white_24112018_2/Pos0) sequence_ids = [] sequence_ids.append('res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0') sequence_ids.append('res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2') sequence_ids.append('res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0') sequence_ids.append('res_soleil2018/DARK/DARK_40X_zstack_vis_327-353_1/Pos0') # sequence_ids.append('res_soleil2018/white/white_24112018_1/Pos0') # this sequence seems broken (only 5 images while there's supposed to be 201 frames) sequence_ids.append('res_soleil2018/white/white_24112018_2/Pos0') for sequence_id in sequence_ids: micro_manager_metadata_file_path = raw_images_root + '/' + sequence_id + '/metadata.txt' # micro_manager_metadata_file_path = '/tmp/toto.json' self.sequences[sequence_id] = Sequence(self, sequence_id, micro_manager_metadata_file_path) def __str__(self): for sequence_id, sequence in self.sequences.iteritems(): return str(sequence_id) + ':' + str(sequence) # self.add_micromanager_metadata(raw_images_root + '/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/metadata.txt') # def add_micromanager_metadata(self, micro_manager_metadata_file_path): # self.sequences[ micro_manager_metadata_file_path ] = Sequence(self, micro_manager_metadata_file_path)