From 32ee7b8a28fd18e77cf57baebb45faa699b87d60 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Thu, 3 Apr 2025 14:16:17 +0200 Subject: [PATCH 01/15] Add ImageMetadata class to store metadata --- src/imcflibs/imagej/bioformats.py | 147 ++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/src/imcflibs/imagej/bioformats.py b/src/imcflibs/imagej/bioformats.py index 758cc072..8b381e42 100644 --- a/src/imcflibs/imagej/bioformats.py +++ b/src/imcflibs/imagej/bioformats.py @@ -27,6 +27,153 @@ ) +class ImageMetadata(object): + """A class to store metadata information from an image. + + This class stores metadata information extracted from an image file, such as image dimensions, + pixel dimensions, and calibration units. It provides a method to convert the attributes to a + dictionary and a string representation of the object. + + Attributes + ---------- + unit_width : float or None + Physical width of a pixel in the given unit. + unit_height : float or None + Physical height of a pixel in the given unit. + unit_depth : float or None + Physical depth of a voxel in the given unit. + pixel_width : int or None + Width of the image in pixels. + pixel_height : int or None + Height of the image in pixels. + slice_count : int or None + Number of Z-slices in the image. + channel_count : int or None + Number of channels in the image. + timepoints_count : int or None + Number of timepoints in the image. + dimension_order : str or None + Order of dimensions (e.g., "XYZCT"). + pixel_type : str or None + Data type of the pixel values (e.g., "uint16"). + + Examples + -------- + >>> metadata = ImageMetadata( + ... unit_width=0.1, + ... unit_height=0.1 + ... ) + >>> print(metadata) + + """ + + def __init__( + self, + unit_width=None, + unit_height=None, + unit_depth=None, + pixel_width=None, + pixel_height=None, + slice_count=None, + channel_count=None, + timepoints_count=None, + dimension_order=None, + pixel_type=None, + ): + self.unit_width = unit_width + self.unit_height = unit_height + self.unit_depth = unit_depth + self.pixel_width = pixel_width + self.pixel_height = pixel_height + self.slice_count = slice_count + self.channel_count = channel_count + self.timepoints_count = timepoints_count + self.dimension_order = dimension_order + self.pixel_type = pixel_type + + def to_dict(self): + """Convert the object attributes to a dictionary. + + Returns + ------- + dict + A dictionary representation of the object attributes. + """ + return self.__dict__ + + def __repr__(self): + """Return a string representation of the object.""" + return "".format( + ", ".join("{}={}".format(k, v) for k, v in self.__dict__.items()) + ) + + +class StageMetadata: + """A class to store stage coordinates and calibration metadata for a set of images. + + Attributes + ---------- + dimensions : int + Number of dimensions (2D or 3D). + stage_coordinates_x : list of float + Absolute stage x-coordinates. + stage_coordinates_y : list of float + Absolute stage y-coordinates. + stage_coordinates_z : list of float + Absolute stage z-coordinates. + relative_coordinates_x : list of float + Relative stage x-coordinates in pixels. + relative_coordinates_y : list of float + Relative stage y-coordinates in pixels. + relative_coordinates_z : list of float + Relative stage z-coordinates in pixels. + image_calibration : list of float + Calibration values for x, y, and z in unit/px. + calibration_unit : str + Unit used for image calibration. + image_dimensions_czt : list of int + Number of images in dimensions (channels, z-slices, timepoints). + series_names : list of str + Names of all series in the image files. + max_size : list of float + Maximum physical size (x/y/z) across all files. + """ + + def __init__( + self, + dimensions=2, + stage_coordinates_x=None, + stage_coordinates_y=None, + stage_coordinates_z=None, + relative_coordinates_x=None, + relative_coordinates_y=None, + relative_coordinates_z=None, + image_calibration=None, + calibration_unit="unknown", + image_dimensions_czt=None, + series_names=None, + max_size=None, + ): + self.dimensions = dimensions + self.stage_coordinates_x = stage_coordinates_x or [] + self.stage_coordinates_y = stage_coordinates_y or [] + self.stage_coordinates_z = stage_coordinates_z or [] + self.relative_coordinates_x = relative_coordinates_x or [] + self.relative_coordinates_y = relative_coordinates_y or [] + self.relative_coordinates_z = relative_coordinates_z or [] + self.image_calibration = image_calibration or [1.0, 1.0, 1.0] + self.calibration_unit = calibration_unit or "unknown" + self.image_dimensions_czt = image_dimensions_czt or [1, 1, 1] + self.series_names = series_names or [] + self.max_size = max_size or [1.0, 1.0, 1.0] + + def __repr__(self): + """Return a string representation of the object.""" + return "".format( + self.dimensions, self.calibration_unit + ) + + def import_image( filename, color_mode="color", From a13dc5cbae4b2d58399909fe60b911f8a99a56b5 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Thu, 3 Apr 2025 14:20:54 +0200 Subject: [PATCH 02/15] Replace dict returns with ImageMetadata class --- src/imcflibs/imagej/bioformats.py | 56 +++++++++---------------------- 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/src/imcflibs/imagej/bioformats.py b/src/imcflibs/imagej/bioformats.py index 8b381e42..0111be4a 100644 --- a/src/imcflibs/imagej/bioformats.py +++ b/src/imcflibs/imagej/bioformats.py @@ -452,54 +452,30 @@ def get_metadata_from_file(path_to_image): Returns ------- - dict - A dictionary containing the following metadata: - - { - unit_width : float, # physical width of a pixel - unit_height : float, # physical height of a pixel - unit_depth : float, # physical depth of a voxel - pixel_width : int, # width of the image in pixels - pixel_height : int, # height of the image in pixels - slice_count : int, # number of Z-slices - channel_count : int, # number of channels - timepoints_count : int, # number of timepoints - dimension_order : str, # order of dimensions, e.g. "XYZCT" - pixel_type : str, # data type of the pixel values - } + ImageMetadata + An instance of `imcflibs.imagej.bioformats.ImageMetadata` containing the extracted metadata. """ + reader = ImageReader() ome_meta = MetadataTools.createOMEXMLMetadata() reader.setMetadataStore(ome_meta) reader.setId(str(path_to_image)) - phys_size_x = ome_meta.getPixelsPhysicalSizeX(0) - phys_size_y = ome_meta.getPixelsPhysicalSizeY(0) - phys_size_z = ome_meta.getPixelsPhysicalSizeZ(0) - pixel_size_x = ome_meta.getPixelsSizeX(0) - pixel_size_y = ome_meta.getPixelsSizeY(0) - pixel_size_z = ome_meta.getPixelsSizeZ(0) - channel_count = ome_meta.getPixelsSizeC(0) - timepoints_count = ome_meta.getPixelsSizeT(0) - dimension_order = ome_meta.getPixelsDimensionOrder(0) - pixel_type = ome_meta.getPixelsType(0) - - image_calibration = { - "unit_width": phys_size_x.value(), - "unit_height": phys_size_y.value(), - "unit_depth": phys_size_z.value(), - "pixel_width": pixel_size_x.getNumberValue(), - "pixel_height": pixel_size_y.getNumberValue(), - "slice_count": pixel_size_z.getNumberValue(), - "channel_count": channel_count.getNumberValue(), - "timepoints_count": timepoints_count.getNumberValue(), - "dimension_order": dimension_order, - "pixel_type": pixel_type, - } - + metadata = ImageMetadata( + unit_width=ome_meta.getPixelsPhysicalSizeX(0), + unit_height=ome_meta.getPixelsPhysicalSizeY(0), + unit_depth=ome_meta.getPixelsPhysicalSizeZ(0), + pixel_width=ome_meta.getPixelsSizeX(0), + pixel_height=ome_meta.getPixelsSizeY(0), + slice_count=ome_meta.getPixelsSizeZ(0), + channel_count=ome_meta.getPixelsSizeC(0), + timepoints_count=ome_meta.getPixelsSizeT(0), + dimension_order=ome_meta.getPixelsDimensionOrder(0), + pixel_type=ome_meta.getPixelsType(0), + ) reader.close() - return image_calibration + return metadata def get_stage_coords(source, filenames): From daa23c3ce2296aa5b4113eed594e2ec022ae3a93 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Thu, 3 Apr 2025 14:21:35 +0200 Subject: [PATCH 03/15] Replace dict return with StageMetadata class --- src/imcflibs/imagej/bioformats.py | 220 ++++++++++++------------------ 1 file changed, 86 insertions(+), 134 deletions(-) diff --git a/src/imcflibs/imagej/bioformats.py b/src/imcflibs/imagej/bioformats.py index 0111be4a..1a31298a 100644 --- a/src/imcflibs/imagej/bioformats.py +++ b/src/imcflibs/imagej/bioformats.py @@ -486,44 +486,40 @@ def get_stage_coords(source, filenames): source : str Path to the images. filenames : list of str - List of images filenames. + List of image filenames. Returns ------- - dict - - { - dimensions : int, # number of dimensions (2D or 3D) - stage_coordinates_x : list, # absolute stage x-coordinated - stage_coordinates_y : list, # absolute stage y-coordinated - stage_coordinates_z : list, # absolute stage z-coordinated - relative_coordinates_x : list, # relative stage x-coordinates in px - relative_coordinates_y : list, # relative stage y-coordinates in px - relative_coordinates_z : list, # relative stage z-coordinates in px - image_calibration : list, # x,y,z image calibration in unit/px - calibration_unit : str, # image calibration unit - image_dimensions_czt : list, # number of images in dimensions c,z,t - series_names : list of str, # names of all series in the files - max_size : list of int, # max size (x/y/z) across all files - } + StageMetadata + An object containing extracted stage metadata. """ - - # open an array to store the abosolute stage coordinates from metadata + # Initialize lists to store stage coordinates and series names stage_coordinates_x = [] stage_coordinates_y = [] stage_coordinates_z = [] series_names = [] + # Intiialize default values + dimensions = 2 + image_calibration = [] + calibration_unit = "unknown" + image_dimensions_czt = [] + max_size = [] + + # Initialize max_size variables to track the maximums + max_phys_size_x = 0.0 + max_phys_size_y = 0.0 + max_phys_size_z = 0.0 + for counter, image in enumerate(filenames): - # parse metadata reader = ImageReader() reader.setFlattenedResolutions(False) - omeMeta = MetadataTools.createOMEXMLMetadata() - reader.setMetadataStore(omeMeta) + ome_meta = MetadataTools.createOMEXMLMetadata() + reader.setMetadataStore(ome_meta) reader.setId(source + str(image)) series_count = reader.getSeriesCount() - # get hyperstack dimensions from the first image + # Process only the first image to get values not dependent on series if counter == 0: frame_size_x = reader.getSizeX() frame_size_y = reader.getSizeY() @@ -531,124 +527,80 @@ def get_stage_coords(source, filenames): frame_size_c = reader.getSizeC() frame_size_t = reader.getSizeT() - # note the dimensions - if frame_size_z == 1: - dimensions = 2 - if frame_size_z > 1: - dimensions = 3 - - # get the physical calibration for the first image series - physSizeX = omeMeta.getPixelsPhysicalSizeX(0) - physSizeY = omeMeta.getPixelsPhysicalSizeY(0) - physSizeZ = omeMeta.getPixelsPhysicalSizeZ(0) - - # workaround to get the z-interval if physSizeZ.value() returns None. - z_interval = 1 - if physSizeZ is not None: - z_interval = physSizeZ.value() - - if frame_size_z > 1 and physSizeZ is None: - log.debug("no z calibration found, trying to recover") - first_plane = omeMeta.getPlanePositionZ(0, 0) - next_plane_imagenumber = frame_size_c + frame_size_t - 1 - second_plane = omeMeta.getPlanePositionZ(0, next_plane_imagenumber) - z_interval = abs(abs(first_plane.value()) - abs(second_plane.value())) - log.debug("z-interval seems to be: " + str(z_interval)) - - # create an image calibration - image_calibration = [ - physSizeX.value(), - physSizeY.value(), - z_interval, - ] - calibration_unit = physSizeX.unit().getSymbol() - image_dimensions_czt = [ - frame_size_c, - frame_size_z, - frame_size_t, - ] + dimensions = 2 if frame_size_z == 1 else 3 + + # Retrieve physical size coordinates safely + phys_size_x = getattr(ome_meta.getPixelsPhysicalSizeX(0), "value", lambda: 1.0)() + phys_size_y = getattr(ome_meta.getPixelsPhysicalSizeY(0), "value", lambda: 1.0)() + phys_size_z = getattr(ome_meta.getPixelsPhysicalSizeZ(0), "value", lambda: None)() + + z_interval = phys_size_z if phys_size_z is not None else 1.0 + + # Handle missing Z calibration + if phys_size_z is None and frame_size_z > 1: + first_plane = getattr(ome_meta.getPlanePositionZ(0, 0), "value", lambda: 0)() + next_plane_index = frame_size_c + frame_size_t - 1 + second_plane = getattr(ome_meta.getPlanePositionZ(0, next_plane_index), "value", lambda: 0)() + z_interval = abs(first_plane - second_plane) + + image_calibration = [phys_size_x, phys_size_y, z_interval] + calibration_unit = ( + getattr(ome_meta.getPixelsPhysicalSizeX(0).unit(), "getSymbol", lambda: "unknown")() + if phys_size_x + else "unknown" + ) + image_dimensions_czt = [frame_size_c, frame_size_z, frame_size_t] reader.close() for series in range(series_count): - if omeMeta.getImageName(series) == "macro image": + if ome_meta.getImageName(series) == "macro image": continue if series_count > 1 and not str(image).endswith(".vsi"): - series_names.append(omeMeta.getImageName(series)) + series_names.append(ome_meta.getImageName(series)) else: series_names.append(str(image)) - # get the plane position in calibrated units - current_position_x = omeMeta.getPlanePositionX(series, 0) - current_position_y = omeMeta.getPlanePositionY(series, 0) - current_position_z = omeMeta.getPlanePositionZ(series, 0) - - physSizeX_max = ( - physSizeX.value() - if physSizeX.value() >= omeMeta.getPixelsPhysicalSizeX(series).value() - else omeMeta.getPixelsPhysicalSizeX(series).value() - ) - physSizeY_max = ( - physSizeY.value() - if physSizeY.value() >= omeMeta.getPixelsPhysicalSizeY(series).value() - else omeMeta.getPixelsPhysicalSizeY(series).value() - ) - if omeMeta.getPixelsPhysicalSizeZ(series): - physSizeZ_max = ( - physSizeZ.value() - if physSizeZ.value() - >= omeMeta.getPixelsPhysicalSizeZ(series).value() - else omeMeta.getPixelsPhysicalSizeZ(series).value() - ) - - else: - physSizeZ_max = 1.0 - # get the absolute stage positions and store them - pos_x = current_position_x.value() - pos_y = current_position_y.value() - - if current_position_z is None: - log.debug("the z-position is missing in the ome-xml metadata.") - pos_z = 1.0 - else: - pos_z = current_position_z.value() - - stage_coordinates_x.append(pos_x) - stage_coordinates_y.append(pos_y) - stage_coordinates_z.append(pos_z) - - max_size = [physSizeX_max, physSizeY_max, physSizeZ_max] - - # calculate the store the relative stage movements in px (for the grid/collection stitcher) - relative_coordinates_x_px = [] - relative_coordinates_y_px = [] - relative_coordinates_z_px = [] - - for i in range(len(stage_coordinates_x)): - rel_pos_x = ( - stage_coordinates_x[i] - stage_coordinates_x[0] - ) / physSizeX.value() - rel_pos_y = ( - stage_coordinates_y[i] - stage_coordinates_y[0] - ) / physSizeY.value() - rel_pos_z = (stage_coordinates_z[i] - stage_coordinates_z[0]) / z_interval - - relative_coordinates_x_px.append(rel_pos_x) - relative_coordinates_y_px.append(rel_pos_y) - relative_coordinates_z_px.append(rel_pos_z) - - return { - "dimensions": dimensions, - "stage_coordinates_x": stage_coordinates_x, - "stage_coordinates_y": stage_coordinates_y, - "stage_coordinates_z": stage_coordinates_z, - "relative_coordinates_x": relative_coordinates_x_px, - "relative_coordinates_y": relative_coordinates_y_px, - "relative_coordinates_z": relative_coordinates_z_px, - "image_calibration": image_calibration, - "calibration_unit": calibration_unit, - "image_dimensions_czt": image_dimensions_czt, - "series_names": series_names, - "max_size": max_size, - } + current_position_x = getattr(ome_meta.getPlanePositionX(series, 0), "value", lambda: 0)() + current_position_y = getattr(ome_meta.getPlanePositionY(series, 0), "value", lambda: 0)() + current_position_z = getattr(ome_meta.getPlanePositionZ(series, 0), "value", lambda: 1.0)() + + max_phys_size_x = max(max_phys_size_x, ome_meta.getPixelsPhysicalSizeX(series).value()) + max_phys_size_y = max(max_phys_size_y, ome_meta.getPixelsPhysicalSizeY(series).value()) + max_phys_size_z = max(max_phys_size_z, ome_meta.getPixelsPhysicalSizeZ(series).value() + if phys_size_z else z_interval) + + stage_coordinates_x.append(current_position_x) + stage_coordinates_y.append(current_position_y) + stage_coordinates_z.append(current_position_z) + + max_size = [max_phys_size_x, max_phys_size_y, max_phys_size_z] + + relative_coordinates_x_px = [ + (stage_coordinates_x[i] - stage_coordinates_x[0]) / (phys_size_x or 1.0) + for i in range(len(stage_coordinates_x)) + ] + relative_coordinates_y_px = [ + (stage_coordinates_y[i] - stage_coordinates_y[0]) / (phys_size_y or 1.0) + for i in range(len(stage_coordinates_y)) + ] + relative_coordinates_z_px = [ + (stage_coordinates_z[i] - stage_coordinates_z[0]) / (z_interval or 1.0) + for i in range(len(stage_coordinates_z)) + ] + + return StageMetadata( + dimensions=dimensions, + stage_coordinates_x=stage_coordinates_x, + stage_coordinates_y=stage_coordinates_y, + stage_coordinates_z=stage_coordinates_z, + relative_coordinates_x=relative_coordinates_x_px, + relative_coordinates_y=relative_coordinates_y_px, + relative_coordinates_z=relative_coordinates_z_px, + image_calibration=image_calibration, + calibration_unit=calibration_unit, + image_dimensions_czt=image_dimensions_czt, + series_names=series_names, + max_size=max_size, + ) From f2908fe78c322111d847dad6924e6e35c1b24258 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Thu, 3 Apr 2025 14:21:57 +0200 Subject: [PATCH 04/15] Add additions to imagej.bioformats --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 816a70ff..6df28b5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,11 @@ As this is a major release, not all changes and functions are listed below. For * `imcflibs.pathtools.create_directory` to create a new directory at the specified path. +* Additions to `imcflibs.imagej.bioformats`: + * `imcflibs.imagej.bioformats.export` to export an image to a given file. + * `imcflibs.imagej.bioformats.get_metadata_from_file` to extract various metadata from a given file using BioFormats. + * `imcflibs.imagej.bioformats.get_stage_coords`to get stage coordinates and calibration for one or more given images. + ## 1.4.0 ### Added From 14934a09ffc60b464e71b220ad1cbec46617a676 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Thu, 3 Apr 2025 16:44:35 +0200 Subject: [PATCH 05/15] Add unit as attribute --- src/imcflibs/imagej/bioformats.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/imcflibs/imagej/bioformats.py b/src/imcflibs/imagej/bioformats.py index 1a31298a..77d952b0 100644 --- a/src/imcflibs/imagej/bioformats.py +++ b/src/imcflibs/imagej/bioformats.py @@ -72,6 +72,7 @@ def __init__( unit_width=None, unit_height=None, unit_depth=None, + unit=None, pixel_width=None, pixel_height=None, slice_count=None, @@ -83,6 +84,7 @@ def __init__( self.unit_width = unit_width self.unit_height = unit_height self.unit_depth = unit_depth + self.unit = unit self.pixel_width = pixel_width self.pixel_height = pixel_height self.slice_count = slice_count From ed87e582fa3ca4a31d1f17c6f861b62e5787d67d Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Thu, 3 Apr 2025 16:45:32 +0200 Subject: [PATCH 06/15] Remove redundant source folder parameter --- src/imcflibs/imagej/bioformats.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/imcflibs/imagej/bioformats.py b/src/imcflibs/imagej/bioformats.py index 77d952b0..8cdecce0 100644 --- a/src/imcflibs/imagej/bioformats.py +++ b/src/imcflibs/imagej/bioformats.py @@ -480,15 +480,13 @@ def get_metadata_from_file(path_to_image): return metadata -def get_stage_coords(source, filenames): +def get_stage_coords(filenames): """Get stage coordinates and calibration for a given list of images. Parameters ---------- - source : str - Path to the images. filenames : list of str - List of image filenames. + List of image filepaths. Returns ------- @@ -518,7 +516,7 @@ def get_stage_coords(source, filenames): reader.setFlattenedResolutions(False) ome_meta = MetadataTools.createOMEXMLMetadata() reader.setMetadataStore(ome_meta) - reader.setId(source + str(image)) + reader.setId(str(image)) series_count = reader.getSeriesCount() # Process only the first image to get values not dependent on series From e8f1a01ff79a1f02b36ef6f479f8c3054204c437 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Thu, 3 Apr 2025 16:46:08 +0200 Subject: [PATCH 07/15] Fix method for string representation --- src/imcflibs/imagej/bioformats.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/imcflibs/imagej/bioformats.py b/src/imcflibs/imagej/bioformats.py index 8cdecce0..11dbb57c 100644 --- a/src/imcflibs/imagej/bioformats.py +++ b/src/imcflibs/imagej/bioformats.py @@ -110,7 +110,7 @@ def __repr__(self): ) -class StageMetadata: +class StageMetadata(object): """A class to store stage coordinates and calibration metadata for a set of images. Attributes @@ -171,11 +171,10 @@ def __init__( def __repr__(self): """Return a string representation of the object.""" - return "".format( - self.dimensions, self.calibration_unit + return "".format( + ", ".join("{}={}".format(k, v) for k, v in self.__dict__.items()) ) - def import_image( filename, color_mode="color", @@ -467,6 +466,7 @@ def get_metadata_from_file(path_to_image): unit_width=ome_meta.getPixelsPhysicalSizeX(0), unit_height=ome_meta.getPixelsPhysicalSizeY(0), unit_depth=ome_meta.getPixelsPhysicalSizeZ(0), + unit=ome_meta.getPixelsPhysicalSizeX(0).unit().symbol, pixel_width=ome_meta.getPixelsSizeX(0), pixel_height=ome_meta.getPixelsSizeY(0), slice_count=ome_meta.getPixelsSizeZ(0), @@ -562,9 +562,12 @@ def get_stage_coords(filenames): else: series_names.append(str(image)) - current_position_x = getattr(ome_meta.getPlanePositionX(series, 0), "value", lambda: 0)() - current_position_y = getattr(ome_meta.getPlanePositionY(series, 0), "value", lambda: 0)() - current_position_z = getattr(ome_meta.getPlanePositionZ(series, 0), "value", lambda: 1.0)() + current_position_x = getattr(ome_meta.getPlanePositionX(series, 0), "value", + lambda: 0)() + current_position_y = getattr(ome_meta.getPlanePositionY(series, 0), "value", + lambda: 0)() + current_position_z = getattr(ome_meta.getPlanePositionZ(series, 0), "value", + lambda: 1.0)() max_phys_size_x = max(max_phys_size_x, ome_meta.getPixelsPhysicalSizeX(series).value()) max_phys_size_y = max(max_phys_size_y, ome_meta.getPixelsPhysicalSizeY(series).value()) From a33b2b1735cb121feb3a113547c983e8770fee37 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Thu, 3 Apr 2025 16:48:50 +0200 Subject: [PATCH 08/15] Run black formatting --- src/imcflibs/imagej/bioformats.py | 60 ++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/src/imcflibs/imagej/bioformats.py b/src/imcflibs/imagej/bioformats.py index 11dbb57c..24281e54 100644 --- a/src/imcflibs/imagej/bioformats.py +++ b/src/imcflibs/imagej/bioformats.py @@ -175,6 +175,7 @@ def __repr__(self): ", ".join("{}={}".format(k, v) for k, v in self.__dict__.items()) ) + def import_image( filename, color_mode="color", @@ -530,22 +531,36 @@ def get_stage_coords(filenames): dimensions = 2 if frame_size_z == 1 else 3 # Retrieve physical size coordinates safely - phys_size_x = getattr(ome_meta.getPixelsPhysicalSizeX(0), "value", lambda: 1.0)() - phys_size_y = getattr(ome_meta.getPixelsPhysicalSizeY(0), "value", lambda: 1.0)() - phys_size_z = getattr(ome_meta.getPixelsPhysicalSizeZ(0), "value", lambda: None)() + phys_size_x = getattr( + ome_meta.getPixelsPhysicalSizeX(0), "value", lambda: 1.0 + )() + phys_size_y = getattr( + ome_meta.getPixelsPhysicalSizeY(0), "value", lambda: 1.0 + )() + phys_size_z = getattr( + ome_meta.getPixelsPhysicalSizeZ(0), "value", lambda: None + )() z_interval = phys_size_z if phys_size_z is not None else 1.0 # Handle missing Z calibration if phys_size_z is None and frame_size_z > 1: - first_plane = getattr(ome_meta.getPlanePositionZ(0, 0), "value", lambda: 0)() + first_plane = getattr( + ome_meta.getPlanePositionZ(0, 0), "value", lambda: 0 + )() next_plane_index = frame_size_c + frame_size_t - 1 - second_plane = getattr(ome_meta.getPlanePositionZ(0, next_plane_index), "value", lambda: 0)() + second_plane = getattr( + ome_meta.getPlanePositionZ(0, next_plane_index), "value", lambda: 0 + )() z_interval = abs(first_plane - second_plane) image_calibration = [phys_size_x, phys_size_y, z_interval] calibration_unit = ( - getattr(ome_meta.getPixelsPhysicalSizeX(0).unit(), "getSymbol", lambda: "unknown")() + getattr( + ome_meta.getPixelsPhysicalSizeX(0).unit(), + "getSymbol", + lambda: "unknown", + )() if phys_size_x else "unknown" ) @@ -562,17 +577,28 @@ def get_stage_coords(filenames): else: series_names.append(str(image)) - current_position_x = getattr(ome_meta.getPlanePositionX(series, 0), "value", - lambda: 0)() - current_position_y = getattr(ome_meta.getPlanePositionY(series, 0), "value", - lambda: 0)() - current_position_z = getattr(ome_meta.getPlanePositionZ(series, 0), "value", - lambda: 1.0)() - - max_phys_size_x = max(max_phys_size_x, ome_meta.getPixelsPhysicalSizeX(series).value()) - max_phys_size_y = max(max_phys_size_y, ome_meta.getPixelsPhysicalSizeY(series).value()) - max_phys_size_z = max(max_phys_size_z, ome_meta.getPixelsPhysicalSizeZ(series).value() - if phys_size_z else z_interval) + current_position_x = getattr( + ome_meta.getPlanePositionX(series, 0), "value", lambda: 0 + )() + current_position_y = getattr( + ome_meta.getPlanePositionY(series, 0), "value", lambda: 0 + )() + current_position_z = getattr( + ome_meta.getPlanePositionZ(series, 0), "value", lambda: 1.0 + )() + + max_phys_size_x = max( + max_phys_size_x, ome_meta.getPixelsPhysicalSizeX(series).value() + ) + max_phys_size_y = max( + max_phys_size_y, ome_meta.getPixelsPhysicalSizeY(series).value() + ) + max_phys_size_z = max( + max_phys_size_z, + ome_meta.getPixelsPhysicalSizeZ(series).value() + if phys_size_z + else z_interval, + ) stage_coordinates_x.append(current_position_x) stage_coordinates_y.append(current_position_y) From ab0e72087ca893b5ee495e51b62ef2bdf3eccfd9 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Thu, 3 Apr 2025 17:29:54 +0200 Subject: [PATCH 09/15] Fix unit retrieval, caused formatting issues --- src/imcflibs/imagej/bioformats.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/imcflibs/imagej/bioformats.py b/src/imcflibs/imagej/bioformats.py index 24281e54..c766f331 100644 --- a/src/imcflibs/imagej/bioformats.py +++ b/src/imcflibs/imagej/bioformats.py @@ -464,9 +464,9 @@ def get_metadata_from_file(path_to_image): reader.setId(str(path_to_image)) metadata = ImageMetadata( - unit_width=ome_meta.getPixelsPhysicalSizeX(0), - unit_height=ome_meta.getPixelsPhysicalSizeY(0), - unit_depth=ome_meta.getPixelsPhysicalSizeZ(0), + unit_width=ome_meta.getPixelsPhysicalSizeX(0).value(), + unit_height=ome_meta.getPixelsPhysicalSizeY(0).value(), + unit_depth=ome_meta.getPixelsPhysicalSizeZ(0).value(), unit=ome_meta.getPixelsPhysicalSizeX(0).unit().symbol, pixel_width=ome_meta.getPixelsSizeX(0), pixel_height=ome_meta.getPixelsSizeY(0), From c76b740478019e74af88be5e717de55941beeccb Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Tue, 8 Apr 2025 11:12:56 +0200 Subject: [PATCH 10/15] Set pad option to ticked to prevent edge erosion --- src/imcflibs/imagej/prefs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/imcflibs/imagej/prefs.py b/src/imcflibs/imagej/prefs.py index 154eff56..5162e89d 100644 --- a/src/imcflibs/imagej/prefs.py +++ b/src/imcflibs/imagej/prefs.py @@ -37,8 +37,8 @@ def set_default_ij_options(): # Set foreground color to be white and background black IJ.run("Colors...", "foreground=white background=black selection=red") - # Set black background for binary images and set pad edges to false to prevent eroding from image edge - IJ.run("Options...", "black ") + # Set black background for binary images and set pad edges to true to prevent eroding from image edge + IJ.run("Options...", "iterations=1 count=1 black pad") # Set default saving format to .txt files IJ.run("Input/Output...", "file=.txt save_column save_row") From e79d452cf4161223af1ecc8851f0240a2da81a72 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Tue, 8 Apr 2025 13:27:53 +0200 Subject: [PATCH 11/15] Remove string representation method --- src/imcflibs/imagej/bioformats.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/imcflibs/imagej/bioformats.py b/src/imcflibs/imagej/bioformats.py index c766f331..5ef19a6b 100644 --- a/src/imcflibs/imagej/bioformats.py +++ b/src/imcflibs/imagej/bioformats.py @@ -103,12 +103,6 @@ def to_dict(self): """ return self.__dict__ - def __repr__(self): - """Return a string representation of the object.""" - return "".format( - ", ".join("{}={}".format(k, v) for k, v in self.__dict__.items()) - ) - class StageMetadata(object): """A class to store stage coordinates and calibration metadata for a set of images. From 7048e168ed18c37805bd0e6ebb2028b0b099f0bd Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Tue, 8 Apr 2025 13:46:49 +0200 Subject: [PATCH 12/15] Add test file for bioformats metadata retrieval --- .../bioformats/metadata/test_metadata.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/interactive-imagej/bioformats/metadata/test_metadata.md diff --git a/tests/interactive-imagej/bioformats/metadata/test_metadata.md b/tests/interactive-imagej/bioformats/metadata/test_metadata.md new file mode 100644 index 00000000..e0a55d87 --- /dev/null +++ b/tests/interactive-imagej/bioformats/metadata/test_metadata.md @@ -0,0 +1,34 @@ +Following is a testing script for the retrieval of metadata methods in imcflibs.imagej.bioformats. + +Copy the following code to a Fiji that has the release `python-imcflibs-1.5.0.jar` in the /jars directory. + +Add the source folder and the names of the files under the corresponding lines, and run the script. If the metadata is printed in Fiji output, the methods are working as intended + +``` +import os +from imcflibs.imagej import bioformats +from ij import IJ + +# Testing for the metadata retrieval through Bioformats + +# Add directory path here that contains the files you wish to test for +path = r"U:\\VAMP\\rohang0000\\stage_test" +file_path_1 = os.path.join(path, "DON_25922_20250201_25922_2_01.vsi") +file_path_2 = os.path.join(path, "DON_25922_20250201_25922_2_02.vsi") +file_path_3 = os.path.join(path, "DON_25922_20250201_25922_2_03.vsi") + +metadata = bioformats.get_metadata_from_file(file_path_1) +print(metadata.unit_width) +print(metadata.unit) +print(metadata.channel_count) + +# Stage metadata and coordinates test for a list of vsi files +fnames = [file_path_1, file_path_2, file_path_3] + +metadata_stage = bioformats.get_stage_coords(fnames) + +print(metadata_stage.image_calibration) +print(metadata_stage.stage_coordinates_x) +print(metadata_stage.stage_coordinates_y) +print(metadata_stage.stage_coordinates_z) +``` \ No newline at end of file From af3de72dd4090a256205961bc316dcd301a5a4e4 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Tue, 8 Apr 2025 16:05:29 +0200 Subject: [PATCH 13/15] Update testing file to use test data repository --- .../bioformats/metadata/test_metadata.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/interactive-imagej/bioformats/metadata/test_metadata.md b/tests/interactive-imagej/bioformats/metadata/test_metadata.md index e0a55d87..ec1f72de 100644 --- a/tests/interactive-imagej/bioformats/metadata/test_metadata.md +++ b/tests/interactive-imagej/bioformats/metadata/test_metadata.md @@ -5,17 +5,20 @@ Copy the following code to a Fiji that has the release `python-imcflibs-1.5.0.ja Add the source folder and the names of the files under the corresponding lines, and run the script. If the metadata is printed in Fiji output, the methods are working as intended ``` +# @ File (label="IMCF testdata location", style="directory") IMCF_TESTDATA + import os +from imcflibs.pathtools import join2 from imcflibs.imagej import bioformats from ij import IJ # Testing for the metadata retrieval through Bioformats # Add directory path here that contains the files you wish to test for -path = r"U:\\VAMP\\rohang0000\\stage_test" -file_path_1 = os.path.join(path, "DON_25922_20250201_25922_2_01.vsi") -file_path_2 = os.path.join(path, "DON_25922_20250201_25922_2_02.vsi") -file_path_3 = os.path.join(path, "DON_25922_20250201_25922_2_03.vsi") + +file_path_1 = join2(IMCF_TESTDATA, "bioformats/DON_25922_20250201_25922_2_01.vsi") +file_path_2 = join2(IMCF_TESTDATA, "bioformats/DON_25922_20250201_25922_2_02.vsi") +file_path_3 = join2(IMCF_TESTDATA, "bioformats/DON_25922_20250201_25922_2_03.vsi") metadata = bioformats.get_metadata_from_file(file_path_1) print(metadata.unit_width) From a41ff42f14386469e709fb1d98b46d7d15cb2c41 Mon Sep 17 00:00:00 2001 From: Rohan Girish Date: Tue, 8 Apr 2025 16:11:15 +0200 Subject: [PATCH 14/15] Fix path to test data --- .../interactive-imagej/bioformats/metadata/test_metadata.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/interactive-imagej/bioformats/metadata/test_metadata.md b/tests/interactive-imagej/bioformats/metadata/test_metadata.md index ec1f72de..a6328955 100644 --- a/tests/interactive-imagej/bioformats/metadata/test_metadata.md +++ b/tests/interactive-imagej/bioformats/metadata/test_metadata.md @@ -16,9 +16,9 @@ from ij import IJ # Add directory path here that contains the files you wish to test for -file_path_1 = join2(IMCF_TESTDATA, "bioformats/DON_25922_20250201_25922_2_01.vsi") -file_path_2 = join2(IMCF_TESTDATA, "bioformats/DON_25922_20250201_25922_2_02.vsi") -file_path_3 = join2(IMCF_TESTDATA, "bioformats/DON_25922_20250201_25922_2_03.vsi") +file_path_1 = join2(IMCF_TESTDATA, "bioformats-multiposition/DON_25922_20250201_25922_2_01.vsi") +file_path_2 = join2(IMCF_TESTDATA, "bioformats-multiposition/DON_25922_20250201_25922_2_02.vsi") +file_path_3 = join2(IMCF_TESTDATA, "bioformats-multiposition/DON_25922_20250201_25922_2_03.vsi") metadata = bioformats.get_metadata_from_file(file_path_1) print(metadata.unit_width) From 64a1a09a8690009caf66f8889b9f4591a82369ab Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Tue, 8 Apr 2025 16:33:03 +0200 Subject: [PATCH 15/15] Line length fixes --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6df28b5d..36b42e5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,8 +79,10 @@ As this is a major release, not all changes and functions are listed below. For * Additions to `imcflibs.imagej.bioformats`: * `imcflibs.imagej.bioformats.export` to export an image to a given file. - * `imcflibs.imagej.bioformats.get_metadata_from_file` to extract various metadata from a given file using BioFormats. - * `imcflibs.imagej.bioformats.get_stage_coords`to get stage coordinates and calibration for one or more given images. + * `imcflibs.imagej.bioformats.get_metadata_from_file` to extract various + metadata from a given file using BioFormats. + * `imcflibs.imagej.bioformats.get_stage_coords`to get stage coordinates and + calibration for one or more given images. ## 1.4.0