Skip to content

Commit

Permalink
Merge pull request #22 from GReD-Clermont/dev
Browse files Browse the repository at this point in the history
Refactor code after PR#19
Update documentation
  • Loading branch information
ppouchin authored Apr 4, 2024
2 parents 4a3b71e + 7672f15 commit 4ce9491
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 69 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ imageName = Ext.getName("image", imageIds[0]);
print(imageName);
```


### Creating projects, datasets and tags

Projects can be created with *Ext.createProject*:
Expand Down Expand Up @@ -273,6 +272,14 @@ 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".
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:

```
imageplusID = Ext.getImage(imageIds[0], "x:0:100,y::100,z:50:,t:3");
```

ROIs from OMERO can also be added to the ROI manager or to the Overlay of the current image (boolean toOverlay). ROIs
composed of multiple shapes (eg 3D/4D) will share the same values in the "ROI" and "ROI_ID" properties in ImageJ. These
can be optionally changed with the "property" parameter: local indices will be in "property" while OMERO IDs will be
Expand Down
139 changes: 78 additions & 61 deletions src/main/java/fr/igred/ij/plugin/OMEROMacroExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import fr.igred.omero.repository.DatasetWrapper;
import fr.igred.omero.repository.GenericRepositoryObjectWrapper;
import fr.igred.omero.repository.ImageWrapper;
import fr.igred.omero.repository.PixelsWrapper.Bounds;
import fr.igred.omero.repository.PixelsWrapper.Coordinates;
import fr.igred.omero.repository.PlateWrapper;
import fr.igred.omero.repository.ProjectWrapper;
import fr.igred.omero.repository.ScreenWrapper;
Expand Down Expand Up @@ -61,13 +63,14 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.NoSuchElementException;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static ij.macro.ExtensionDescriptor.newDescriptor;
import static java.lang.Integer.parseInt;


public class OMEROMacroExtension implements PlugIn, MacroExtension {
Expand Down Expand Up @@ -185,6 +188,69 @@ private static ResultsTable getTable(String resultsName) {
}


/**
* Extracts the coordinates from different strings.
*
* @param start The start coordinate that was parsed.
* @param sep The optional separator.
* @param end The end coordinate that was parsed.
*
* @return See above.
*/
private static int[] extractCoordinates(String start, String sep, String end) {
int[] coordinates = new int[2];
boolean startEmpty = start == null || start.isEmpty();
boolean sepEmpty = sep == null || sep.isEmpty();
boolean endEmpty = end == null || end.isEmpty();

coordinates[0] = startEmpty ? 0 : parseInt(start);
if (sepEmpty && !startEmpty) {
coordinates[1] = coordinates[0]; // Input is like z:5 it's a single slice
} else {
coordinates[1] = endEmpty ? -1 : (parseInt(end) - 1);
}
return coordinates;
}


/**
* Extracts the bounds from a string.
*
* @param bounds The bounds string.
*
* @return See above.
*/
private static Bounds extractBounds(CharSequence bounds) {
Map<String, Integer> s = new HashMap<>(5);
Map<String, Integer> e = new HashMap<>(5);

// Regex captures in any order XYCZT coordinates of the form x:: x:0: x::100 x:0:100 c:0
String regexPattern = "([xyczt]):(\\d*)(:?)(\\d*)";
Pattern pattern = Pattern.compile(regexPattern, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(bounds);
while (matcher.find()) {
for (int i = 1; i <= matcher.groupCount(); i += 4) {
String axis = matcher.group(i).toLowerCase();
String start = matcher.group(i + 1);
String sep = matcher.group(i + 2);
String end = matcher.group(i + 3);
int[] coordinates = extractCoordinates(start, sep, end);
s.putIfAbsent(axis, coordinates[0]);
e.putIfAbsent(axis, coordinates[1]);
}
}
int[] x = {s.getOrDefault("x", 0), e.getOrDefault("x", -1)}; // Defaults to the whole dimension
int[] y = {s.getOrDefault("y", 0), e.getOrDefault("y", -1)};
int[] c = {s.getOrDefault("c", 0), e.getOrDefault("c", -1)};
int[] z = {s.getOrDefault("z", 0), e.getOrDefault("z", -1)};
int[] t = {s.getOrDefault("t", 0), e.getOrDefault("t", -1)};

Coordinates start = new Coordinates(x[0], y[0], c[0], z[0], t[0]);
Coordinates end = new Coordinates(x[1], y[1], c[1], z[1], t[1]);
return new Bounds(start, end);
}


/**
* Filters the objects list to only keep objects from the set user.
*
Expand Down Expand Up @@ -1091,73 +1157,23 @@ 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::"
* 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 id The image ID.
* @param bounds The XYCZT bounds
*
* @return The image, as an {@link ImagePlus}.
*/
public ImagePlus getImage(long id, String bounds) {
public ImagePlus getImage(long id, CharSequence bounds) {
ImagePlus imp = null;
try {
ImageWrapper image = client.getImage(id);
if (bounds == null)
if (bounds == null) {
imp = image.toImagePlus(client);
else {
// Regex captures in any order XYCZT coordinates of the form x:: x:0: x::100 x:0:100 c:0
String regexPattern = "(?:([xyczt]):(\\d*)(:?)(\\d*))";
Pattern pattern = Pattern.compile(regexPattern, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(bounds);

int[] xBounds = {0, -1}; // Defaults to the whole dimension
int[] yBounds = {0, -1};
int[] cBounds = {0, -1};
int[] zBounds = {0, -1};
int[] tBounds = {0, -1};
int start, end;
boolean startEmpty, endEmpty, sepEmpty;
while (matcher.find()) {
for (int i = 1; i <= matcher.groupCount(); i += 4) {
String coordinateType = matcher.group(i);
startEmpty = matcher.group(i + 1) == null || matcher.group(i + 1).isEmpty();
sepEmpty = matcher.group(i + 2) == null || matcher.group(i + 2).isEmpty();
endEmpty = matcher.group(i + 3) == null || matcher.group(i + 3).isEmpty();
if(startEmpty && endEmpty) // in the form z: or z::
continue;

start = startEmpty ? 0 : Integer.parseInt(matcher.group(i + 1));
if(sepEmpty)
end = start; // Input is like z:5 it's a single slice
else
end = endEmpty ? -1 : (Integer.parseInt(matcher.group(i + 3)) - 1);
switch(coordinateType.toLowerCase()) {
case "x":
xBounds[0] = start;
xBounds[1] = end;
break;
case "y":
yBounds[0] = start;
yBounds[1] = end;
break;
case "c":
cBounds[0] = start;
cBounds[1] = end;
break;
case "z":
zBounds[0] = start;
zBounds[1] = end;
break;
case "t":
tBounds[0] = start;
tBounds[1] = end;
break;
}
}
}
imp = image.toImagePlus(client, xBounds, yBounds, cBounds, zBounds, tBounds);
} else {
Bounds b = extractBounds(bounds);
imp = image.toImagePlus(client, b);
}
} catch (ServiceException | AccessException | ExecutionException | NoSuchElementException e) {
IJ.error("Could not retrieve image: " + e.getMessage());
Expand Down Expand Up @@ -1479,7 +1495,8 @@ public String handleExtension(String name, Object[] args) {
break;

case "getImage":
ImagePlus imp = getImage(((Double) args[0]).longValue(), (String) args[1]);
id = ((Double) args[0]).longValue();
ImagePlus imp = getImage(id, (CharSequence) args[1]);
if (imp != null) {
imp.show();
results = String.valueOf(imp.getID());
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/helper.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ Ext.getImage(id)
> Opens the image with the given `id`.
> Returns the image ID in ImageJ.
Ext.getImage(id, region)
> Opens a subregion of the image with the given `id`.
> The region is specified as a string in the format "x:start:end,y:start:end,...".
> Returns the image ID in ImageJ.
Ext.getROIs(imageId, toOverlay, property)
> Retrieves the ROIs for the image with the given `imageId`.
> These are added to the ROI manager by default.
Expand Down
14 changes: 7 additions & 7 deletions src/test/java/fr/igred/ij/plugin/OMEROExtensionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ 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(String bounds) {
void testGetImageTwoBounds(CharSequence bounds) {
final int partSize = 100;
final int fullSize = 510;
ImagePlus imp = ext.getImage(1L, bounds);
Expand All @@ -337,7 +337,7 @@ void testGetImageTwoBounds(String bounds) {

@ParameterizedTest
@ValueSource(strings = {"x:100: y::412", "X:100: Y::412", "x::412 y:100:"})
void testGetImageOneBound(String bounds) {
void testGetImageOneBound(CharSequence bounds) {
final int size = 412;
ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
Expand All @@ -347,7 +347,7 @@ void testGetImageOneBound(String 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(String bounds) {
void testGetImageAllBounds(CharSequence bounds) {
final int sizeX = 180;
final int sizeY = 12;
final int sizeZ = 2;
Expand All @@ -364,7 +364,7 @@ void testGetImageAllBounds(String bounds) {

@ParameterizedTest
@ValueSource(strings = {"", " ", "x: y:: ", "x::9999 y:0:9999 z:50:99 c::99 t::99", "^#azerty*$"})
void testGetImageNoBounds(String bounds) {
void testGetImageNoBounds(CharSequence bounds) {
final int size = 512;
final int sizeZ = 3;
final int sizeC = 5;
Expand All @@ -380,7 +380,7 @@ void testGetImageNoBounds(String bounds) {

@ParameterizedTest
@ValueSource(strings = {"z:0", "z:2", "Z:0", "z:0:1"})
void testGetImageZ(String bounds) {
void testGetImageZ(CharSequence bounds) {
final int size = 512;
final int sizeZ = 1;
final int sizeC = 5;
Expand All @@ -396,7 +396,7 @@ void testGetImageZ(String bounds) {

@ParameterizedTest
@ValueSource(strings = {"c:0", "c:2", "C:0", "c:0:1"})
void testGetImageC(String bounds) {
void testGetImageC(CharSequence bounds) {
final int size = 512;
final int sizeZ = 3;
final int sizeC = 1;
Expand All @@ -412,7 +412,7 @@ void testGetImageC(String bounds) {

@ParameterizedTest
@ValueSource(strings = {"t:0", "t:2", "T:0", "t:0:1"})
void testGetImageT(String bounds) {
void testGetImageT(CharSequence bounds) {
final int size = 512;
final int sizeZ = 3;
final int sizeC = 5;
Expand Down

0 comments on commit 4ce9491

Please sign in to comment.