Skip to content

Commit

Permalink
Add option to crop image from ROI ID
Browse files Browse the repository at this point in the history
  • Loading branch information
ppouchin committed Oct 3, 2024
1 parent e7e085b commit f41d467
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 39 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,13 @@ Pixel intensities can be retrieved from images:
imageplusID = Ext.getImage(imageIds[0]);
```

Images can also be cropped on import by specifying the ROI as a String of the form:
"x:xstart:xend,y:ystart:yend,c:cstart:cend,z:zstart:zend,t:tstart:tend".
Images can also be cropped on import by specifying the ROI in one of two ways:
1. using the ROI ID (as a String) from OMERO, for example:
```
imageplusID = Ext.getImage(imageIds[0], Roi.getProperty("ROI_ID"));
```

2. as a String of the form: "x:xstart:xend,y:ystart:yend,c:cstart:cend,z:zstart:zend,t:tstart:tend".
For example, cropping a 512x512x3x100x5 image starting at (0,0,0,50,3) and ending at (100,100,2,99,3) can be done with:

```
Expand Down
66 changes: 49 additions & 17 deletions src/main/java/fr/igred/ij/plugin/OMEROMacroExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public class OMEROMacroExtension implements PlugIn, MacroExtension {
newDescriptor("delete", this, ARG_STRING, ARG_NUMBER),
newDescriptor("getName", this, ARG_STRING, ARG_NUMBER),
newDescriptor("getImage", this, ARG_NUMBER, ARG_STRING + ARG_OPTIONAL),
newDescriptor("getImageFromROI", this, ARG_NUMBER, ARG_NUMBER),
newDescriptor("getROIs", this, ARG_NUMBER, ARG_NUMBER + ARG_OPTIONAL, ARG_STRING + ARG_OPTIONAL),
newDescriptor("saveROIs", this, ARG_NUMBER, ARG_STRING + ARG_OPTIONAL),
newDescriptor("removeROIs", this, ARG_NUMBER, ARG_STRING + ARG_OPTIONAL),
Expand All @@ -128,6 +129,26 @@ public class OMEROMacroExtension implements PlugIn, MacroExtension {
private ExperimenterWrapper user = null;


/**
* Safely converts a String to a Long, returning null if it fails.
*
* @param s The string.
*
* @return The integer value represented by s, null if not applicable.
*/
private static Long safeParseLong(String s) {
Long l = null;
if (s != null) {
try {
l = Long.parseLong(s);
} catch (NumberFormatException ignored) {
// DO NOTHING
}
}
return l;
}


/**
* Converts a list of GenericObjectWrappers to a comma-delimited list of IDs.
*
Expand Down Expand Up @@ -1166,20 +1187,30 @@ public String getName(String type, long id) {
* Opens an image with optional bounds. The bounds are in the form "x:min:max" with max included. Each of XYCZT is
* optional, min and max are also optional: "x:0:100 y::200 z:5: t::"
*
* @param id The image ID.
* @param bounds The XYCZT bounds
* @param id The image ID.
* @param roi The ROI ID or XYCZT bounds
*
* @return The image, as an {@link ImagePlus}.
*/
public ImagePlus getImage(long id, CharSequence bounds) {
public ImagePlus getImage(long id, String roi) {
ImagePlus imp = null;
try {
ImageWrapper image = client.getImage(id);
if (bounds == null) {
if (roi == null) {
imp = image.toImagePlus(client);
} else {
Bounds b = extractBounds(bounds);
imp = image.toImagePlus(client, b);
final Long roiId = safeParseLong(roi);
if (roiId != null) {
ROIWrapper oRoi = image.getROIs(client)
.stream()
.filter(r -> r.getId() == roiId)
.findFirst()
.orElseThrow(() -> new NoSuchElementException("ROI not found: " + roi));
imp = image.toImagePlus(client, oRoi);
} else {
Bounds b = extractBounds(roi);
imp = image.toImagePlus(client, b);
}
}
} catch (ServiceException | AccessException | ExecutionException | NoSuchElementException e) {
IJ.error("Could not retrieve image: " + e.getMessage());
Expand Down Expand Up @@ -1426,16 +1457,17 @@ public ExtensionDescriptor[] getExtensionFunctions() {

@Override
public String handleExtension(String name, Object[] args) {
long id;
long id1;
long id2;
String type;
String type1;
String type2;
String property;
String tableName;
String path;
String results = null;
long id;
long id1;
long id2;
String type;
String type1;
String type2;
String property;
String tableName;
String path;
String results = null;
ImagePlus imp;
switch (name) {
case "connectToOMERO":
String host = ((String) args[0]);
Expand Down Expand Up @@ -1573,7 +1605,7 @@ public String handleExtension(String name, Object[] args) {

case "getImage":
id = ((Double) args[0]).longValue();
ImagePlus imp = getImage(id, (CharSequence) args[1]);
imp = getImage(id, (String) args[1]);
if (imp != null) {
imp.show();
results = String.valueOf(imp.getID());
Expand Down
9 changes: 9 additions & 0 deletions src/test/java/fr/igred/ij/plugin/OMEROExtensionErrorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ void testGetImageError() {
}


@Test
void testGetImageFromROIError() {
Object[] args = {1.0d, "-1"};
ext.handleExtension("getImage", args);
String expected = "Could not retrieve image: ROI not found: -1";
assertEquals(expected, outContent.toString().trim());
}


@Test
void testListForUserError() {
Object[] args = {"hello"};
Expand Down
70 changes: 50 additions & 20 deletions src/test/java/fr/igred/ij/plugin/OMEROExtensionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ void testCreateProject() {

@ParameterizedTest
@NullSource
@ValueSource(doubles = {1.0})
@ValueSource(doubles = 1.0)
void testCreateDataset(Double projectId) {
Object[] args = {"toDelete", "toBeDeleted", projectId};
String result = ext.handleExtension("createDataset", args);
Expand Down Expand Up @@ -326,18 +326,18 @@ void testGetImage() {

@ParameterizedTest
@ValueSource(strings = {"x:100:200 y:1:511", "x:50:150 y:2:512", "x:50:150 y:2:513"})
void testGetImageTwoBounds(CharSequence bounds) {
void testGetImageTwoBounds(String bounds) {
final int partSize = 100;
final int fullSize = 510;
ImagePlus imp = ext.getImage(1L, bounds);
ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(partSize, imp.getWidth());
assertEquals(fullSize, imp.getHeight());
}


@ParameterizedTest
@ValueSource(strings = {"x:100: y::412", "X:100: Y::412", "x::412 y:100:"})
void testGetImageOneBound(CharSequence bounds) {
void testGetImageOneBound(String bounds) {
final int size = 412;
ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
Expand All @@ -347,13 +347,13 @@ void testGetImageOneBound(CharSequence bounds) {

@ParameterizedTest
@ValueSource(strings = {"x:300:480 y:24:36 z:1:3 c:0:4 t:3:6", "X:300:480 Y:24:36 Z:1:3 C:0:4 T:3:6"})
void testGetImageAllBounds(CharSequence bounds) {
void testGetImageAllBounds(String bounds) {
final int sizeX = 180;
final int sizeY = 12;
final int sizeZ = 2;
final int sizeC = 4;
final int sizeT = 3;
ImagePlus imp = ext.getImage(1L, bounds);
ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(sizeX, imp.getWidth());
assertEquals(sizeY, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
Expand All @@ -364,12 +364,12 @@ void testGetImageAllBounds(CharSequence bounds) {

@ParameterizedTest
@ValueSource(strings = {"", " ", "x: y:: ", "x::9999 y:0:9999 z:50:99 c::99 t::99", "^#azerty*$"})
void testGetImageNoBounds(CharSequence bounds) {
final int size = 512;
void testGetImageNoBounds(String bounds) {
final int size = 512;
final int sizeZ = 3;
final int sizeC = 5;
final int sizeT = 7;
ImagePlus imp = ext.getImage(1L, bounds);
ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
assertEquals(size, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
Expand All @@ -380,12 +380,12 @@ void testGetImageNoBounds(CharSequence bounds) {

@ParameterizedTest
@ValueSource(strings = {"z:0", "z:2", "Z:0", "z:0:1"})
void testGetImageZ(CharSequence bounds) {
final int size = 512;
void testGetImageZ(String bounds) {
final int size = 512;
final int sizeZ = 1;
final int sizeC = 5;
final int sizeT = 7;
ImagePlus imp = ext.getImage(1L, bounds);
ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
assertEquals(size, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
Expand All @@ -396,12 +396,12 @@ void testGetImageZ(CharSequence bounds) {

@ParameterizedTest
@ValueSource(strings = {"c:0", "c:2", "C:0", "c:0:1"})
void testGetImageC(CharSequence bounds) {
final int size = 512;
void testGetImageC(String bounds) {
final int size = 512;
final int sizeZ = 3;
final int sizeC = 1;
final int sizeT = 7;
ImagePlus imp = ext.getImage(1L, bounds);
ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
assertEquals(size, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
Expand All @@ -412,12 +412,12 @@ void testGetImageC(CharSequence bounds) {

@ParameterizedTest
@ValueSource(strings = {"t:0", "t:2", "T:0", "t:0:1"})
void testGetImageT(CharSequence bounds) {
final int size = 512;
void testGetImageT(String bounds) {
final int size = 512;
final int sizeZ = 3;
final int sizeC = 5;
final int sizeT = 1;
ImagePlus imp = ext.getImage(1L, bounds);
ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
assertEquals(size, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
Expand All @@ -426,6 +426,36 @@ void testGetImageT(CharSequence bounds) {
}


@Test
void testGetImageFromROI() {
ImagePlus imp = ext.getImage(1L, null);
Overlay overlay = new Overlay();
Roi roi = new Roi(25, 30, 70, 50);
roi.setImage(imp);
overlay.add(roi);
imp.setOverlay(overlay);
int savedROIs = ext.saveROIs(imp, 1L, "");
overlay.clear();
imp.setOverlay(null);
int loadedROIs = ext.getROIs(imp, 1L, true, "");

Roi retrievedRoi = imp.getOverlay().get(0);
String roiId = retrievedRoi.getProperty("ROI_ID");
ImagePlus cropped = ext.getImage(1L, roiId);

ext.removeROIs(1L);
int clearedROIs = ext.getROIs(imp, 1L, true, "");

assertEquals(1, savedROIs);
assertEquals(1, loadedROIs);
assertEquals(0, clearedROIs);
assertEquals(25, Integer.parseInt(cropped.getProp("IMAGE_POS_X")));
assertEquals(30, Integer.parseInt(cropped.getProp("IMAGE_POS_Y")));
assertEquals(70, cropped.getWidth());
assertEquals(50, cropped.getHeight());
}


@Test
void testSaveAndGetROIs() {
ImagePlus imp = ext.getImage(1L, null);
Expand Down Expand Up @@ -455,7 +485,7 @@ void testSaveAndGetROIs() {
@CsvSource(delimiter = ';', value = {"image;1;null;testKey1\ttestValue1\ttestKey2\t20",
"image;3;' ';testKey1 testValue1 testKey2 20",
"image;2;&&;testKey1&&testValue2&&testKey2&&30",
"image;4;'';''"}, nullValues = {"null"})
"image;4;'';''"}, nullValues = "null")
void testGetKeyValuePairs(String type, Double id, String separator, String output) {
Object[] args = {type, id, separator};
String result = ext.handleExtension("getKeyValuePairs", args);
Expand All @@ -467,7 +497,7 @@ void testGetKeyValuePairs(String type, Double id, String separator, String outpu
@CsvSource(delimiter = ';', value = {"image;1;testKey1;null;testValue1",
"image;3;testKey2;null;20",
"image;2;testKey2;null;30",
"image;2;notExist;default;default"}, nullValues = {"null"})
"image;2;notExist;default;default"}, nullValues = "null")
void testGetValue(String type, Double id, String key, String defaultValue, String output) {
Object[] args = {type, id, key, defaultValue};
String result = ext.handleExtension("getValue", args);
Expand Down

0 comments on commit f41d467

Please sign in to comment.