Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor code after PR#19 #22

Merged
merged 4 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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