From 5f559da8bc16f9320749c281279e0f972de73d13 Mon Sep 17 00:00:00 2001 From: Pierre Pouchin Date: Thu, 11 Apr 2024 18:30:49 +0200 Subject: [PATCH] Refactor getPosition methods and add offset properties to images (for crops) --- .../fr/igred/omero/meta/PlaneInfoWrapper.java | 23 ++++++++ .../igred/omero/repository/ImageWrapper.java | 20 +++++-- .../igred/omero/repository/PixelsWrapper.java | 54 +++++++++++++------ .../omero/meta/PlaneInfoWrapperTest.java | 8 ++- .../fr/igred/omero/repository/ImageTest.java | 14 +++-- 5 files changed, 90 insertions(+), 29 deletions(-) diff --git a/src/main/java/fr/igred/omero/meta/PlaneInfoWrapper.java b/src/main/java/fr/igred/omero/meta/PlaneInfoWrapper.java index e9cfd5b8..30c7521f 100644 --- a/src/main/java/fr/igred/omero/meta/PlaneInfoWrapper.java +++ b/src/main/java/fr/igred/omero/meta/PlaneInfoWrapper.java @@ -35,6 +35,7 @@ import static java.lang.Double.NaN; import static ome.formats.model.UnitsFactory.convertTime; +import static ome.units.UNITS.MICROMETER; import static ome.units.UNITS.SECOND; @@ -152,6 +153,28 @@ public static Length getMinPosition(Collection plane } + /** + * Retrieves the min value for the specified getter in a PlaneInfoWrapper collection. + * + * @param planesInfo A collection of PlaneInfoWrappers. + * @param getter The getter function to use. + * + * @return See above. + */ + public static Length getMinPosition(Collection planesInfo, + Function getter) { + Unit unit; + unit = planesInfo.stream() + .map(getter) + .map(UnitsFactory::convertLength) + .filter(Objects::nonNull) + .map(ome.units.quantity.Length::unit) + .findFirst() + .orElse(MICROMETER); + return getMinPosition(planesInfo, getter, unit); + } + + /** * @return See above. * diff --git a/src/main/java/fr/igred/omero/repository/ImageWrapper.java b/src/main/java/fr/igred/omero/repository/ImageWrapper.java index 1ee96502..08e58e60 100644 --- a/src/main/java/fr/igred/omero/repository/ImageWrapper.java +++ b/src/main/java/fr/igred/omero/repository/ImageWrapper.java @@ -121,17 +121,23 @@ private static void setCalibration(PixelsWrapper pixels, Calibration calibration calibration.setXUnit(positionX.getSymbol()); calibration.setYUnit(positionY.getSymbol()); calibration.setZUnit(positionZ.getSymbol()); - calibration.xOrigin = positionX.getValue(); - calibration.yOrigin = positionY.getValue(); - calibration.zOrigin = positionZ.getValue(); if (spacingX != null) { + calibration.setXUnit(spacingX.getSymbol()); calibration.pixelWidth = spacingX.getValue(); + // positionX and spacingX should use the same unit + calibration.xOrigin = -positionX.getValue() / calibration.pixelWidth; } if (spacingY != null) { + calibration.setYUnit(spacingY.getSymbol()); calibration.pixelHeight = spacingY.getValue(); + // positionY and spacingY should use the same unit + calibration.yOrigin = -positionY.getValue() / calibration.pixelHeight; } if (spacingZ != null) { + calibration.setZUnit(spacingZ.getSymbol()); calibration.pixelDepth = spacingZ.getValue(); + // positionZ and spacingZ should use the same unit + calibration.zOrigin = -positionZ.getValue() / calibration.pixelDepth; } if (!Double.isNaN(stepT.getValue())) { calibration.setTimeUnit(stepT.getSymbol()); @@ -737,6 +743,9 @@ public ImagePlus toImagePlus(Client client, Calibration calibration = imp.getCalibration(); setCalibration(pixels, calibration); + calibration.xOrigin -= startX; + calibration.yOrigin -= startY; + calibration.zOrigin -= startZ; imp.setCalibration(calibration); boolean isFloat = FormatTools.isFloatingPoint(pixelType); @@ -794,6 +803,11 @@ public ImagePlus toImagePlus(Client client, imp.setPosition(1); if (IJ.getVersion().compareTo("1.53a") >= 0) { imp.setProp(IJ_ID_PROPERTY, getId()); + imp.setProp("OFFSET_X", startX); + imp.setProp("OFFSET_Y", startY); + imp.setProp("OFFSET_C", startC); + imp.setProp("OFFSET_Z", startZ); + imp.setProp("OFFSET_T", startT); } return imp; } diff --git a/src/main/java/fr/igred/omero/repository/PixelsWrapper.java b/src/main/java/fr/igred/omero/repository/PixelsWrapper.java index 63a1acfb..82f8d9e5 100644 --- a/src/main/java/fr/igred/omero/repository/PixelsWrapper.java +++ b/src/main/java/fr/igred/omero/repository/PixelsWrapper.java @@ -31,6 +31,7 @@ import omero.gateway.model.PlaneInfoData; import omero.gateway.rnd.Plane2D; import omero.model.Length; +import omero.model.LengthI; import omero.model.Time; import java.util.ArrayList; @@ -39,8 +40,8 @@ import java.util.concurrent.ExecutionException; import static fr.igred.omero.exception.ExceptionHandler.call; +import static fr.igred.omero.meta.PlaneInfoWrapper.getMinPosition; import static ome.formats.model.UnitsFactory.convertLength; -import static ome.units.UNITS.MICROMETER; /** @@ -238,50 +239,71 @@ public Time getMeanExposureTime(int channel) { /** - * Retrieves the X stage position. + * Retrieves the X stage position, using the same unit as {@link #getPixelSizeX()} if possible. *

Planes information needs to be {@link #loadPlanesInfo(Client) loaded} first.

* * @return See above. */ public Length getPositionX() { + Length x = getMinPosition(planesInfo, PlaneInfoWrapper::getPositionX); + ome.units.quantity.Length pixSizeX = convertLength(getPixelSizeX()); + ome.units.quantity.Length posX = convertLength(x); + + if (pixSizeX != null) { + Unit unit = pixSizeX.unit(); + if (posX.value(unit) != null) { + x = new LengthI(posX.value(unit).doubleValue(), unit); + } + } - Unit unit = pixSizeX == null ? MICROMETER : pixSizeX.unit(); - return PlaneInfoWrapper.getMinPosition(planesInfo, - PlaneInfoWrapper::getPositionX, - unit); + return x; } /** - * Retrieves the Y stage position. + * Retrieves the Y stage position, using the same unit as {@link #getPixelSizeY()} if possible. *

Planes information needs to be {@link #loadPlanesInfo(Client) loaded} first.

* * @return See above. */ public Length getPositionY() { + Length y = getMinPosition(planesInfo, PlaneInfoWrapper::getPositionY); + ome.units.quantity.Length pixSizeY = convertLength(getPixelSizeY()); + ome.units.quantity.Length posY = convertLength(y); + + if (pixSizeY != null) { + Unit unit = pixSizeY.unit(); + if (posY.value(unit) != null) { + y = new LengthI(posY.value(unit).doubleValue(), unit); + } + } - Unit unit = pixSizeY == null ? MICROMETER : pixSizeY.unit(); - return PlaneInfoWrapper.getMinPosition(planesInfo, - PlaneInfoWrapper::getPositionY, - unit); + return y; } /** - * Retrieves the Z stage position. + * Retrieves the Z stage position, using the same unit as {@link #getPixelSizeZ()} if possible. *

Planes information needs to be {@link #loadPlanesInfo(Client) loaded} first.

* * @return See above. */ public Length getPositionZ() { + Length z = getMinPosition(planesInfo, PlaneInfoWrapper::getPositionZ); + ome.units.quantity.Length pixSizeZ = convertLength(getPixelSizeZ()); + ome.units.quantity.Length posZ = convertLength(z); + + if (pixSizeZ != null) { + Unit unit = pixSizeZ.unit(); + if (posZ.value(unit) != null) { + z = new LengthI(posZ.value(unit).doubleValue(), unit); + } + } - Unit unit = pixSizeZ == null ? MICROMETER : pixSizeZ.unit(); - return PlaneInfoWrapper.getMinPosition(planesInfo, - PlaneInfoWrapper::getPositionZ, - unit); + return z; } diff --git a/src/test/java/fr/igred/omero/meta/PlaneInfoWrapperTest.java b/src/test/java/fr/igred/omero/meta/PlaneInfoWrapperTest.java index efa4c8af..cd760f9b 100644 --- a/src/test/java/fr/igred/omero/meta/PlaneInfoWrapperTest.java +++ b/src/test/java/fr/igred/omero/meta/PlaneInfoWrapperTest.java @@ -67,11 +67,9 @@ void testGetMinPosition() throws Exception { pixels.loadPlanesInfo(client); List planes = pixels.getPlanesInfo(); - Length positionX = getMinPosition(planes, - PlaneInfoWrapper::getPositionX, - UNITS.NANOMETER); - assertEquals(100000, positionX.getValue()); - assertEquals("nm", positionX.getSymbol()); + Length posX = getMinPosition(planes, PlaneInfoWrapper::getPositionX); + assertEquals(0.1, posX.getValue()); + assertEquals("mm", posX.getSymbol()); } } \ No newline at end of file diff --git a/src/test/java/fr/igred/omero/repository/ImageTest.java b/src/test/java/fr/igred/omero/repository/ImageTest.java index 4d0211d7..7045bda2 100644 --- a/src/test/java/fr/igred/omero/repository/ImageTest.java +++ b/src/test/java/fr/igred/omero/repository/ImageTest.java @@ -52,6 +52,7 @@ import java.util.Map; import java.util.NoSuchElementException; +import static java.lang.Math.abs; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -321,8 +322,8 @@ void testToImagePlusBound() throws Exception { final double pixSize = 0.5; final double pixDepth = 1.5; final double deltaT = 150; - final double xyOrigin = 100; - final double zOrigin = 20; + final double xyOrigin = -100.0d / pixSize; + final double zOrigin = -20.0d / pixDepth; int[] xBounds = {0, 2}; int[] yBounds = {0, 2}; @@ -339,6 +340,9 @@ void testToImagePlusBound() throws Exception { cBounds[1] = SECURE_RANDOM.nextInt(3 - cBounds[0]) + cBounds[0] + 2; tBounds[1] = SECURE_RANDOM.nextInt(5 - tBounds[0]) + tBounds[0] + 2; + double xOrigin = xyOrigin - xBounds[0]; + double yOrigin = xyOrigin - yBounds[0]; + String fake = "8bit-unsigned&pixelType=uint8&sizeZ=3&sizeC=5&sizeT=7&sizeX=512&sizeY=512.fake"; File fakeFile = createFile(fake); @@ -365,9 +369,9 @@ void testToImagePlusBound() throws Exception { assertEquals(pixDepth, imp.getCalibration().pixelDepth, Double.MIN_VALUE); // Round numbers because rounding errors happen when converting units assertEquals(deltaT, imp.getCalibration().frameInterval, DOUBLE_PRECISION * deltaT); - assertEquals(xyOrigin, imp.getCalibration().xOrigin, DOUBLE_PRECISION * xyOrigin); - assertEquals(xyOrigin, imp.getCalibration().yOrigin, DOUBLE_PRECISION * xyOrigin); - assertEquals(zOrigin, imp.getCalibration().zOrigin, DOUBLE_PRECISION * zOrigin); + assertEquals(xOrigin, imp.getCalibration().xOrigin, DOUBLE_PRECISION * abs(xOrigin)); + assertEquals(yOrigin, imp.getCalibration().yOrigin, DOUBLE_PRECISION * abs(yOrigin)); + assertEquals(zOrigin, imp.getCalibration().zOrigin, DOUBLE_PRECISION * -zOrigin); assertEquals("µm", imp.getCalibration().getUnit()); assertEquals("µm", imp.getCalibration().getZUnit()); assertEquals("ms", imp.getCalibration().getTimeUnit());