def map_name_to_coordinate(name): '''Maps identifier string representation to coordinate. Parameters ---------- name: str well name Returns ------- Tuple[int] zero-based row, column position of a given well within the plate Examples -------- >>> Well.map_name_to_coordinate("A02") (0, 1) ''' row_name, col_name = re.match(r'([A-Z])(\d+)', name).group(1, 2) row_index = utils.map_letter_to_number(row_name) - 1 col_index = int(col_name) - 1 return (row_index, col_index)
def read(self, microscope_metadata_files, microscope_image_filenames): '''Reads metadata from "mlf" and "mrf" metadata files in case they are provided. Parameters ---------- microscope_metadata_files: List[str] absolute path to microscope metadata files microscope_image_filenames: List[str] names of the corresponding microscope image files Returns ------- bioformats.omexml.OMEXML OMEXML image metadata ''' microscope_image_filenames = natsorted(microscope_image_filenames) metadata = bioformats.OMEXML(XML_DECLARATION) if len(microscope_metadata_files) == 0: logger.warn('no microscope metadata files found') return None elif len(microscope_metadata_files) != 2: logger.warn('expected two microscope metadata files') return None for f in microscope_metadata_files: if f.endswith('mlf'): mlf_filename = f elif f.endswith('mrf'): mrf_filename = f mrf_tree = etree.parse(mrf_filename) mrf_root = mrf_tree.getroot() mrf_ns = mrf_root.nsmap['bts'] # Obtain the positional information for each image acquisition site # from the ".mlf" file: mlf_tree = etree.parse(mlf_filename) mlf_root = mlf_tree.getroot() record_elements = mlf_root.xpath('.//bts:MeasurementRecord', namespaces=mlf_root.nsmap) mlf_ns = mlf_root.nsmap['bts'] metadata.image_count = len([ e for e in record_elements if e.attrib['{%s}Type' % mlf_ns] != 'ERR' ]) lookup = defaultdict(list) for e in record_elements: # Translate positional information into well identifier string well_row = utils.map_number_to_letter( int(e.attrib['{%s}Row' % mlf_ns])) well_col = int(e.attrib['{%s}Column' % mlf_ns]) well_id = '%s%.2d' % (well_row, well_col) if e.attrib['{%s}Type' % mlf_ns] == 'ERR': field_index = int(e.attrib['{%s}FieldIndex' % mlf_ns]) logger.error( 'erroneous acquisition - no channel and z-position ' 'information available at well %s field %d' % (well_id, field_index)) continue # This microscope stores each plane in a separate file. Therefore, # we can use the filename to match images. name = e.text index = microscope_image_filenames.index(name) img = metadata.image(index) img.AcquisitionDate = e.attrib['{%s}Time' % mlf_ns] # Image files always contain only a single plane img.Pixels.SizeT = 1 img.Pixels.SizeC = 1 img.Pixels.SizeZ = 1 img.Pixels.plane_count = 1 # A name has to be set as a flag for the handler to update # the metadata img.Name = name # Make channel name consistent with how it is encoded in the image # file name to ensure that the result is the same, independent of # whether it was obtained from the metadata or the image file name. img.Pixels.Channel(0).Name = e.attrib['{%s}Ch' % mlf_ns] img.Pixels.Plane(0).PositionX = float(e.attrib['{%s}X' % mlf_ns]) img.Pixels.Plane(0).PositionY = float(e.attrib['{%s}Y' % mlf_ns]) img.Pixels.Plane(0).PositionZ = float(e.attrib['{%s}Z' % mlf_ns]) img.Pixels.Plane(0).TheZ = int(e.attrib['{%s}ZIndex' % mlf_ns]) img.Pixels.Plane(0).TheT = int(e.attrib['{%s}TimelineIndex' % mlf_ns]) idx = microscope_image_filenames.index(img.Name) lookup[well_id].append(idx) # Obtain the general experiment information and well plate format # specifications from the ".mrf" file: name = mrf_root.attrib['{%s}Title' % mrf_ns] plate = metadata.PlatesDucktype(metadata.root_node).newPlate(name=name) plate.RowNamingConvention = 'letter' plate.ColumnNamingConvention = 'number' plate.Rows = mrf_root.attrib['{%s}RowCount' % mrf_ns] plate.Columns = mrf_root.attrib['{%s}ColumnCount' % mrf_ns] wells = lookup.keys() for w in set(wells): # Create a *Well* element for each imaged well in the plate row = utils.map_letter_to_number(w[0]) - 1 col = int(w[1:]) - 1 well = metadata.WellsDucktype(plate).new(row=row, column=col) well_samples = metadata.WellSampleDucktype(well.node) for i, ref in enumerate(lookup[w]): # Create a *WellSample* element for each acquisition site well_samples.new(index=i) well_samples[i].ImageRef = ref return metadata
def read(self, microscope_metadata_files, microscope_image_filenames): '''Reads metadata from "mlf" and "mrf" metadata files in case they are provided. Parameters ---------- microscope_metadata_files: List[str] absolute path to microscope metadata files microscope_image_filenames: List[str] names of the corresponding microscope image files Returns ------- bioformats.omexml.OMEXML OMEXML image metadata ''' microscope_image_filenames = natsorted(microscope_image_filenames) metadata = bioformats.OMEXML(XML_DECLARATION) if len(microscope_metadata_files) == 0: logger.warn('no microscope metadata files found') return None elif len(microscope_metadata_files) != 2: logger.warn('expected two microscope metadata files') return None for f in microscope_metadata_files: if f.endswith('mlf'): mlf_filename = f elif f.endswith('mrf'): mrf_filename = f mrf_tree = etree.parse(mrf_filename) mrf_root = mrf_tree.getroot() mrf_ns = mrf_root.nsmap['bts'] # Obtain the positional information for each image acquisition site # from the ".mlf" file: mlf_tree = etree.parse(mlf_filename) mlf_root = mlf_tree.getroot() record_elements = mlf_root.xpath( './/bts:MeasurementRecord', namespaces=mlf_root.nsmap ) mlf_ns = mlf_root.nsmap['bts'] metadata.image_count = len([ e for e in record_elements if e.attrib['{%s}Type' % mlf_ns] != 'ERR' ]) lookup = defaultdict(list) for e in record_elements: # Translate positional information into well identifier string well_row = utils.map_number_to_letter( int(e.attrib['{%s}Row' % mlf_ns])) well_col = int(e.attrib['{%s}Column' % mlf_ns]) well_id = '%s%.2d' % (well_row, well_col) if e.attrib['{%s}Type' % mlf_ns] == 'ERR': field_index = int(e.attrib['{%s}FieldIndex' % mlf_ns]) logger.error( 'erroneous acquisition - no channel and z-position ' 'information available at well %s field %d' % (well_id, field_index) ) continue # This microscope stores each plane in a separate file. Therefore, # we can use the filename to match images. name = e.text # Check for .tif to .png/.jpg conversion if microscope_image_filenames[0].endswith('.png'): name = name.replace('.tif','.png') elif microscope_image_filenames[0].endswith('.jpg'): name = name.replace('.tif','.jpg') index = microscope_image_filenames.index(name) img = metadata.image(index) img.AcquisitionDate = e.attrib['{%s}Time' % mlf_ns] # Image files always contain only a single plane img.Pixels.SizeT = 1 img.Pixels.SizeC = 1 img.Pixels.SizeZ = 1 img.Pixels.plane_count = 1 # A name has to be set as a flag for the handler to update # the metadata img.Name = name # Make channel name consistent with how it is encoded in the image # file name to ensure that the result is the same, independent of # whether it was obtained from the metadata or the image file name. img.Pixels.Channel(0).Name = e.attrib['{%s}Ch' % mlf_ns] img.Pixels.Plane(0).PositionX = float(e.attrib['{%s}X' % mlf_ns]) img.Pixels.Plane(0).PositionY = float(e.attrib['{%s}Y' % mlf_ns]) img.Pixels.Plane(0).PositionZ = float(e.attrib['{%s}Z' % mlf_ns]) img.Pixels.Plane(0).TheZ = int(e.attrib['{%s}ZIndex' % mlf_ns]) img.Pixels.Plane(0).TheT = int(e.attrib['{%s}TimelineIndex' % mlf_ns]) idx = microscope_image_filenames.index(img.Name) lookup[well_id].append(idx) # Obtain the general experiment information and well plate format # specifications from the ".mrf" file: name = mrf_root.attrib['{%s}Title' % mrf_ns] plate = metadata.PlatesDucktype(metadata.root_node).newPlate(name=name) plate.RowNamingConvention = 'letter' plate.ColumnNamingConvention = 'number' plate.Rows = mrf_root.attrib['{%s}RowCount' % mrf_ns] plate.Columns = mrf_root.attrib['{%s}ColumnCount' % mrf_ns] wells = lookup.keys() for w in set(wells): # Create a *Well* element for each imaged well in the plate row = utils.map_letter_to_number(w[0]) - 1 col = int(w[1:]) - 1 well = metadata.WellsDucktype(plate).new(row=row, column=col) well_samples = metadata.WellSampleDucktype(well.node) for i, ref in enumerate(lookup[w]): # Create a *WellSample* element for each acquisition site well_samples.new(index=i) well_samples[i].ImageRef = ref return metadata
def read(self, microscope_metadata_files, microscope_image_filenames): '''Read metadata from "nd" metadata file. Parameters ---------- microscope_metadata_files: List[str] absolute path to the microscope metadata files microscope_image_filenames: List[str] names of the corresponding microscope image files Returns ------- bioformats.omexml.OMEXML OMEXML image metadata Raises ------ ValueError when `microscope_metadata_files` doesn't have length one ''' microscope_image_filenames = natsorted(microscope_image_filenames) if len(microscope_metadata_files) != 1: raise ValueError('Expected one microscope metadata file.') nd_filename = microscope_metadata_files[0] metadata = bioformats.OMEXML(XML_DECLARATION) # 1) Obtain the general experiment information and well plate format # specifications from the ".nd" file: nd = read_nd_file(nd_filename) metadata.image_count = nd['NStagePositions'] if nd['DoWave']: metadata.image_count *= nd['NWavelengths'] if nd['DoTimelapse']: metadata.image_count *= nd['NTimePoints'] for i in xrange(metadata.image_count): img = metadata.image(i) img.Name = '' # Images files may contain a variable number of z-stacks # (SizeZ >= 1), but only one time point (SizeT == 1) # and one channel (SizeC == 1) img.Pixels.SizeT = 1 img.Pixels.SizeC = 1 img.Pixels.SizeZ = nd['NZSteps'] img.Pixels.plane_count = nd['NZSteps'] # TODO: case when no stage positions are available (manual acquisition) name = os.path.splitext(os.path.basename(nd_filename))[0] plate = metadata.PlatesDucktype(metadata.root_node).newPlate(name=name) plate.RowNamingConvention = 'letter' plate.ColumnNamingConvention = 'number' rows = [ nd['Stage%d' % (i+1)]['row'] for i in xrange(nd['NStagePositions']) ] plate.Rows = len(set(rows)) columns = [ nd['Stage%d' % (i+1)]['column'] for i in xrange(nd['NStagePositions']) ] plate.Columns = len(set(columns)) # Create a lut table to get all images planes per well sites = [ nd['Stage%d' % (i+1)]['site'] for i in xrange(nd['NStagePositions']) ] wells = [ '%s%.2d' % (rows[i], columns[i]) for i in xrange(len(sites)) ] lut = defaultdict(list) for i, filename in enumerate(natsorted(microscope_image_filenames)): fields = MetadataHandler.extract_fields_from_filename( IMAGE_FILE_REGEX_PATTERN, filename, defaults=False ) # NOTE: We assume that the "site" id is global per plate field_index = sites.index(int(fields.s)) lut[wells[field_index]].append(i) for w in set(wells): # Create a "Well" instance for each imaged well in the plate row_index = utils.map_letter_to_number(w[0]) - 1 col_index = int(w[1:]) - 1 well = metadata.WellsDucktype(plate).new( row=row_index, column=col_index ) well_samples = metadata.WellSampleDucktype(well.node) for i, ref in enumerate(lut[w]): # Create a *WellSample* element for each acquisition site well_samples.new(index=i) well_samples[i].ImageRef = ref return metadata