diff --git a/.gitignore b/.gitignore index 5abffee43749afc5d07d2735617b71e4130a8b93..b2f15ce895696fd311b35bd9ebb831c094d75ac0 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ bin/ *.jar .classpath .project -export.jardesc \ No newline at end of file +export.jardesc +**/.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1ebb6b972a9833c577a89ce11befb0423ffaaf7c..07534cb08f3c819c6ce972a35d7a8b3c738d2bcb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,17 +1,17 @@ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> - <artifactId>pom-icy</artifactId> <groupId>org.bioimageanalysis.icy</groupId> - <version>2.0.0</version> + <artifactId>pom-icy</artifactId> + <version>2.2.0</version> </parent> <artifactId>connected-components</artifactId> - <version>4.8.8</version> + <version>4.8.9</version> <packaging>jar</packaging> @@ -22,55 +22,36 @@ Output can be pushed to the swimming pool for other plug-ins to further exploit the extracted objects. </description> - <profiles> - <profile> - <id>icy-plugin</id> - <activation> - <activeByDefault>true</activeByDefault> - </activation> - </profile> - </profiles> - <dependencies> - <dependency> - <groupId>org.bioimageanalysis.icy</groupId> - <artifactId>icy-kernel</artifactId> - <version>${icy-kernel.version}</version> - </dependency> <dependency> <groupId>org.bioimageanalysis.icy</groupId> - <artifactId>blocks</artifactId> - <version>${blocks.version}</version> + <artifactId>ezplug</artifactId> </dependency> - <dependency> + <dependency> <groupId>org.bioimageanalysis.icy</groupId> - <artifactId>icy-bioformats</artifactId> - <version>${icy-bioformats.version}</version> + <artifactId>protocols</artifactId> </dependency> - <dependency> + + <dependency> <groupId>org.bioimageanalysis.icy</groupId> - <artifactId>jama</artifactId> - <version>${jama.version}</version> - </dependency> - <dependency> - <groupId>net.sourceforge.jexcelapi</groupId> - <artifactId>jxl</artifactId> - <version>${jxl.version}</version> + <artifactId>vecmath</artifactId> </dependency> <dependency> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>spot-detection-utilities</artifactId> - <version>${spot-detection-utilities.version}</version> </dependency> <dependency> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>quickhull</artifactId> - <version>${quickhull.version}</version> </dependency> <dependency> <groupId>org.bioimageanalysis.icy</groupId> - <artifactId>vecmath</artifactId> - <version>${vecmath.version}</version> + <artifactId>jama</artifactId> + </dependency> + + <dependency> + <groupId>net.sourceforge.jexcelapi</groupId> + <artifactId>jxl</artifactId> </dependency> </dependencies> diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponent.java b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponent.java index 0b20a5b6193be92c44f18ce28445723cfd224836..fe242f983ab038d1eb57080429822b3ac956a2ef 100644 --- a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponent.java +++ b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponent.java @@ -19,8 +19,7 @@ import plugins.kernel.roi.roi2d.ROI2DArea; import plugins.kernel.roi.roi3d.ROI3DArea; import plugins.nchenouard.spot.Detection; -public class ConnectedComponent extends Detection implements Iterable<Point3i> -{ +public class ConnectedComponent extends Detection implements Iterable<Point3i> { private int c = -1; /** @@ -60,18 +59,16 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Creates a new connected component with given initial capacity - * + * * @param initialCapacity int */ - public ConnectedComponent(int initialCapacity) - { + public ConnectedComponent(int initialCapacity) { super(0, 0, 0, 0); - this.points = new ArrayList<Point3i>(initialCapacity); + this.points = new ArrayList<>(initialCapacity); coordsSum = new Point3d(); } - void addPointInternal(Point3i point) - { + void addPointInternal(Point3i point) { points.add(point); // accumulate coordinates to compute the mass center @@ -84,12 +81,10 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Adds a point to this component and updates the mass center - * - * @param point - * the point to add + * + * @param point the point to add */ - public void addPoint(Point3i point) - { + public void addPoint(Point3i point) { addPointInternal(point); updateDetectionCoords(); } @@ -97,12 +92,10 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Adds a point to this component. This method also accumulates coordinates to optimize the mass * center computation - * - * @param point - * the point to add + * + * @param point the point to add */ - void removePointInternal(Point3i point) - { + void removePointInternal(Point3i point) { if (!points.remove(point)) return; @@ -114,8 +107,7 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> coordsDirty = true; } - public void removeAllPoints() - { + public void removeAllPoints() { points.clear(); coordsSum.set(0, 0, 0); x = 0; @@ -126,69 +118,60 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * @return true is the object touches the image edge along X */ - public boolean isOnEdgeX() - { + public boolean isOnEdgeX() { return onEdgeX; } /** * @return true is the object touches the image edge along Y */ - public boolean isOnEdgeY() - { + public boolean isOnEdgeY() { return onEdgeY; } /** * @return true is the object touches the image edge along Z (always true for 2D images) */ - public boolean isOnEdgeZ() - { + public boolean isOnEdgeZ() { return onEdgeZ; } /** * Computes the bounding box of this component, and stores the result into the given arguments - * - * @param start - * the first corner of the bounding box in X-Y-Z order (Upper-Left hand-Top) - * @param end - * the second corner of the bounding box in X-Y-Z order (Lower-Right hand-Bottom) - * @deprecated Use - * {@link ConnectedComponentDescriptor#computeBoundingBox(ConnectedComponent, Point3i, Point3i)} - * instead - */ - public void computeBoundingBox(Point3i start, Point3i end) - { + * + * @param start the first corner of the bounding box in X-Y-Z order (Upper-Left hand-Top) + * @param end the second corner of the bounding box in X-Y-Z order (Lower-Right hand-Bottom) + * @deprecated Use {@link ConnectedComponentDescriptor#computeBoundingBox(ConnectedComponent, Point3i, Point3i)} + * instead + */ + @Deprecated + public void computeBoundingBox(Point3i start, Point3i end) { shapeDescriptor.computeBoundingBox(this, start, end); } /** * Computes the average intensity of the component on each channel of the given sequence, at the * time point where this component was found - * + * * @param sequence Sequence * @return an array containing the average intensity of the component in each band */ - public double[] computeMeanIntensity(Sequence sequence) - { + public double[] computeMeanIntensity(Sequence sequence) { return computeMeanIntensity(sequence, t); } /** * Computes the average intensity of the component on each channel of the given sequence and the * specified time point - * + * * @param sequence Sequence - * @param t int + * @param t int * @return an array containing the average intensity of the component in each band */ - public double[] computeMeanIntensity(Sequence sequence, int t) - { + public double[] computeMeanIntensity(Sequence sequence, int t) { double[] intensitySum = new double[sequence.getSizeC()]; - for (Point3i point : points) - { + for (Point3i point : points) { int offsetXY = point.x + point.y * sequence.getSizeX(); Object dataCXY = sequence.getDataXYC(t, point.z); @@ -206,36 +189,32 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Computes the minimum intensity of the component on each channel of the given sequence, at the * time point where this component was found - * + * * @param sequence Sequence * @return an array containing the average intensity of the component in each band */ - public double[] computeMinIntensity(Sequence sequence) - { + public double[] computeMinIntensity(Sequence sequence) { return computeMinIntensity(sequence, t); } /** * Computes the minimum intensity of the component on each channel of the given sequence and the * specified time point - * + * * @param sequence Sequence - * @param t int + * @param t int * @return an array containing the average intensity of the component in each band */ - public double[] computeMinIntensity(Sequence sequence, int t) - { + public double[] computeMinIntensity(Sequence sequence, int t) { double[] minIntensity = new double[sequence.getSizeC()]; Arrays.fill(minIntensity, Double.MAX_VALUE); - for (Point3i point : points) - { + for (Point3i point : points) { int offsetXY = point.x + point.y * sequence.getSizeX(); Object dataCXY = sequence.getImage(t, point.z).getDataXYC(); - for (int c = 0; c < minIntensity.length; c++) - { + for (int c = 0; c < minIntensity.length; c++) { double val = Array1DUtil.getValue(((Object[]) dataCXY)[c], offsetXY, sequence.getDataType_().isSigned()); if (val < minIntensity[c]) @@ -248,12 +227,11 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Computes the maximum intensity of the component on each channel of the given sequence, at the * time point where this component was found - * + * * @param sequence Sequence * @return an array containing the average intensity of the component in each band */ - public double[] computeMaxIntensity(Sequence sequence) - { + public double[] computeMaxIntensity(Sequence sequence) { return computeMaxIntensity(sequence, t); } @@ -262,21 +240,18 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> * and the specified time point * * @param sequence Sequence - * @param t int + * @param t int * @return an array containing the average intensity of the component in each band */ - public double[] computeMaxIntensity(Sequence sequence, int t) - { + public double[] computeMaxIntensity(Sequence sequence, int t) { double[] maxIntensity = new double[sequence.getSizeC()]; - for (Point3i point : points) - { + for (Point3i point : points) { int offsetXY = point.x + point.y * sequence.getSizeX(); Object dataCXY = sequence.getImage(t, point.z).getDataXYC(); - for (int c = 0; c < maxIntensity.length; c++) - { + for (int c = 0; c < maxIntensity.length; c++) { double val = Array1DUtil.getValue(((Object[]) dataCXY)[c], offsetXY, sequence.getDataType_().isSigned()); if (val > maxIntensity[c]) @@ -289,19 +264,15 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Computes the closest euclidean distance between any two points of this component and the * given component - * - * @param component - * the component to compute the distance to + * + * @param component the component to compute the distance to * @return double */ - public double distanceTo(ConnectedComponent component) - { + public double distanceTo(ConnectedComponent component) { double distance = Double.MAX_VALUE; - for (Point3i srcPt : this) - { + for (Point3i srcPt : this) { Vector3d srcV = new Vector3d(srcPt.x, srcPt.y, srcPt.z); - for (Point3i dstPt : component) - { + for (Point3i dstPt : component) { Vector3d dstV = new Vector3d(dstPt.x, dstPt.y, dstPt.z); dstV.sub(srcV); double length = dstV.length(); @@ -315,11 +286,10 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Returns an iterator over the internal points array. Useful to browse all points without * duplicating the array - * + * * @return Point3i Iterator */ - public Iterable<Point3i> getIterablePoints() - { + public Iterable<Point3i> getIterablePoints() { return points; } @@ -328,32 +298,28 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> * points of this component. Note that the result vector orientation is meaningless if the * component is spherical.<br> * NOTE: this is an potentially expensive operation: O(N log N) - * + * * @return 3D Vector */ - public Vector3d getMajorAxis() - { + public Vector3d getMajorAxis() { double maxDist = 0; Vector3d vector = new Vector3d(); Point3d pid = new Point3d(), pjd = new Point3d(); int n = points.size(); - for (int i = 0; i < n - 1; i++) - { + for (int i = 0; i < n - 1; i++) { Point3i pi = points.get(i); pid.set(pi.x, pi.y, pi.z); - for (int j = i + 1; j < n; j++) - { + for (int j = i + 1; j < n; j++) { Point3i pj = points.get(j); pjd.set(pj.x, pj.y, pj.z); // compare the squared distance to save the square root operation double dist = pid.distanceSquared(pjd); - if (dist > maxDist) - { + if (dist > maxDist) { maxDist = dist; vector.sub(pjd, pid); } @@ -363,8 +329,7 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> return vector; } - void updateDetectionCoords() - { + void updateDetectionCoords() { double factor = 1.0 / getSize(); x = coordsSum.x * factor; y = coordsSum.y * factor; @@ -372,30 +337,26 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> coordsDirty = false; } - public int getC() - { + public int getC() { return c; } @Override - public double getX() - { + public double getX() { if (coordsDirty) updateDetectionCoords(); return x; } @Override - public double getY() - { + public double getY() { if (coordsDirty) updateDetectionCoords(); return y; } @Override - public double getZ() - { + public double getZ() { if (coordsDirty) updateDetectionCoords(); return z; @@ -403,11 +364,10 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Computes the mass center of this component - * + * * @return 3D Point */ - public Point3d getMassCenter() - { + public Point3d getMassCenter() { if (coordsDirty) updateDetectionCoords(); return new Point3d(x, y, z); @@ -418,16 +378,14 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Returns the maximum distance between any point of this component and the specified point - * + * * @param point 3D Point * @return double */ - public double getMaxDistanceTo(Point3d point) - { + public double getMaxDistanceTo(Point3d point) { double maxDist = 0; - for (Point3i p : this) - { + for (Point3i p : this) { double dist = point.distance(new Point3d(p.x, p.y, p.z)); if (dist > maxDist) maxDist = dist; @@ -438,32 +396,27 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * The size in pixels (or voxels) of this component - * + * * @return The size in pixels (or voxels) of this component */ - public int getSize() - { + public int getSize() { return points.size(); } /** * @return An array containing all the pixels of this component */ - public Point3i[] getPoints() - { + public Point3i[] getPoints() { return points.toArray(new Point3i[getSize()]); } /** - * @param outputSequence - * (set to null if not wanted) an output sequence to receive the extracted contour + * @param outputSequence (set to null if not wanted) an output sequence to receive the extracted contour * @return An array containing all the contour pixels of this component */ - public Point3i[] getContourPoints(Sequence outputSequence) - { - if (contourPoints == null) - { - ArrayList<Point3i> list = new ArrayList<Point3i>(getSize() / 2); + public Point3i[] getContourPoints(Sequence outputSequence) { + if (contourPoints == null) { + ArrayList<Point3i> list = new ArrayList<>(getSize() / 2); Point3i min = new Point3i(), max = new Point3i(); shapeDescriptor.computeBoundingBox(this, min, max); @@ -476,8 +429,7 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> byte[][] outputMask = null; - if (outputSequence != null) - { + if (outputSequence != null) { outputSequence.removeAllImages(); for (int i = 0; i < d; i++) outputSequence.setImage(0, i, new IcyBufferedImage(w, h, 1, DataType.UBYTE)); @@ -495,28 +447,25 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> Point3i localP = new Point3i(); - if (min.z != max.z) - { - mainLoop: for (Point3i p : points) - { + if (min.z != max.z) { + mainLoop: + for (Point3i p : points) { localP.sub(p, min); int xy = localP.y * w + localP.x; if (localP.x == 0 || localP.y == 0 || localP.x == w - 1 || localP.y == h - 1 || localP.z == 0 - || localP.z == d - 1) - { + || localP.z == d - 1) { list.add(p); if (outputMask != null) outputMask[localP.z][xy] = (byte) 1; continue; } - for (byte[] z : new byte[][] {mask_z_xy[localP.z - 1], mask_z_xy[localP.z], + for (byte[] z : new byte[][]{mask_z_xy[localP.z - 1], mask_z_xy[localP.z], mask_z_xy[localP.z + 1]}) if (z[xy - w] == 0 || z[xy - 1] == 0 || z[xy + 1] == 0 || z[xy + w] == 0 || z[xy - w - 1] == 0 - || z[xy - w + 1] == 0 || z[xy + w - 1] == 0 || z[xy + w + 1] == 0) - { + || z[xy - w + 1] == 0 || z[xy + w - 1] == 0 || z[xy + w + 1] == 0) { list.add(p); if (outputMask != null) outputMask[localP.z][xy] = (byte) 1; @@ -524,22 +473,18 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> } // the top and bottom neighbors were forgotten in the previous loop - if (mask_z_xy[localP.z - 1][xy] == 0 || mask_z_xy[localP.z + 1][xy] == 0) - { + if (mask_z_xy[localP.z - 1][xy] == 0 || mask_z_xy[localP.z + 1][xy] == 0) { list.add(p); } } } - else - { - for (Point3i p : points) - { + else { + for (Point3i p : points) { localP.sub(p, min); int xy = localP.y * w + localP.x; - if (localP.x == 0 || localP.y == 0 || localP.x == w - 1 || localP.y == h - 1) - { + if (localP.x == 0 || localP.y == 0 || localP.x == w - 1 || localP.y == h - 1) { list.add(p); if (outputMask != null) outputMask[localP.z][xy] = (byte) 1; @@ -574,21 +519,18 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * @param component ConnectedComponent * @return true if this component intersects (e.g. has at least one voxel overlapping with) the - * specified component + * specified component */ - public boolean intersects(ConnectedComponent component) - { + public boolean intersects(ConnectedComponent component) { int thisSize = getSize(); int componentSize = component.getSize(); Point3i thisPt = new Point3i(); Point3i componentPt = new Point3i(); - for (int i = 0; i < thisSize; i++) - { + for (int i = 0; i < thisSize; i++) { thisPt.set(points.get(i)); - for (int j = 0; j < componentSize; j++) - { + for (int j = 0; j < componentSize; j++) { componentPt.set(component.points.get(j)); if (thisPt.equals(componentPt)) return true; @@ -600,20 +542,17 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Creates a new component with the intersection between the current and specified components - * + * * @param component ConnectedComponent * @return ConnectedComponent */ - public ConnectedComponent intersection(ConnectedComponent component) - { + public ConnectedComponent intersection(ConnectedComponent component) { ConnectedComponent intersection = new ConnectedComponent(0); // construct the intersection for (Point3i srcPt : this) - for (Point3i dstPt : component) - { - if (srcPt.equals(dstPt)) - { + for (Point3i dstPt : component) { + if (srcPt.equals(dstPt)) { intersection.addPointInternal(srcPt); break; } @@ -623,45 +562,35 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> } @Override - public Iterator<Point3i> iterator() - { + public Iterator<Point3i> iterator() { return points.iterator(); } /** * Paints this component onto the given sequence with the specified value - * - * @param s - * Sequence - * @param t - * int - * @param c - * int - * @param value - * double - * @throws InterruptedException - */ - public void paintOnSequence(Sequence s, int t, int c, double value) throws InterruptedException - { + * + * @param s Sequence + * @param t int + * @param c int + * @param value double + */ + public void paintOnSequence(Sequence s, int t, int c, double value) throws InterruptedException { DataIteratorUtil.set(new SequenceDataIterator(s, toROI(), true, -1, t, c), value); } - public void setC(int c) - { + public void setC(int c) { this.c = c; } /** * @return a region of interest of type area representing this connected component */ - public ROI toROI() - { + public ROI toROI() { int z = 0; ROI3DArea area3D = new ROI3DArea(); area3D.beginUpdate(); - for (Point3i pt : this) - { + for (Point3i pt : this) { z = pt.z; area3D.addPoint(pt.x, pt.y, pt.z); } @@ -681,11 +610,10 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> /** * Creates a new byte sequence filled with this component. The sequence has a unique time point, * and the image size is equal to the bounding box of this component. - * + * * @return Sequence */ - public Sequence toSequence() - { + public Sequence toSequence() { Sequence seq = new Sequence(); // get the bounding box first @@ -706,16 +634,13 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> // force non volatile boolean v = seq.isVolatile(); seq.setVolatile(false); - try - { + try { // fill up the created sequence byte[][] z_xy = seq.getDataXYZAsByte(0, 0); for (Point3i point : points) z_xy[point.z - start.z][(point.y - start.y) * width + (point.x - start.x)] = (byte) 1; - } - finally - { + } finally { // restore volatile state seq.setVolatile(v); } @@ -724,8 +649,7 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> } @Override - public String toString() - { + public String toString() { return "[" + getX() + ", " + getY() + ", " + getZ() + "], " + getSize() + " points"; } } diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponentDescriptor.java b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponentDescriptor.java index dd037cbb5efe3ed313f1973a3979d7d4c68de1ef..21b6d2aaa1f58f944c366b5b3acf02743a432e35 100644 --- a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponentDescriptor.java +++ b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponentDescriptor.java @@ -25,9 +25,8 @@ import plugins.adufour.quickhull.QuickHull3D; import plugins.adufour.vars.lang.Var; import plugins.adufour.vars.lang.VarDouble; -public class ConnectedComponentDescriptor extends Plugin implements PluginBundled, Block -{ - Var<ConnectedComponent> varCC = new Var<ConnectedComponent>("Connected component", ConnectedComponent.class); +public class ConnectedComponentDescriptor extends Plugin implements PluginBundled, Block { + Var<ConnectedComponent> varCC = new Var<>("Connected component", ConnectedComponent.class); VarDouble perimeter = new VarDouble("perimeter", 0.0); @@ -42,20 +41,17 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle VarDouble shortAxisZ = new VarDouble("short diameter (Z)", 0.0); @Override - public String getMainPluginClassName() - { + public String getMainPluginClassName() { return ConnectedComponents.class.getName(); } @Override - public void declareInput(VarList inputMap) - { + public void declareInput(VarList inputMap) { inputMap.add("component", varCC); } @Override - public void declareOutput(VarList outputMap) - { + public void declareOutput(VarList outputMap) { outputMap.add("perimeter", perimeter); outputMap.add("long diameter", longAxis); outputMap.add("short diameter", shortAxis); @@ -65,8 +61,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle } @Override - public void run() - { + public void run() { ConnectedComponent cc = varCC.getValue(); if (cc == null) @@ -85,32 +80,26 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle /** * @param cc ConnectedComponent - * @return a triplet representing the radiuses of the best fitting ellipse (the third value is 0 - * for 2D objects) + * @return a triplet representing the radius of the best fitting ellipse (the third value is 0 + * for 2D objects) */ - public double[] computeEllipseDimensions(ConnectedComponent cc) - { + public double[] computeEllipseDimensions(ConnectedComponent cc) { double[] axes = new double[3]; - try - { - if (is2D(cc)) - { + try { + if (is2D(cc)) { Point2d radii = new Point2d(); computeEllipse(cc, null, radii, null, null); - if (radii.x > radii.y) - { + if (radii.x > radii.y) { axes[0] = radii.x; axes[1] = radii.y; } - else - { + else { axes[0] = radii.y; axes[1] = radii.x; } } - else - { + else { Point3d radii = new Point3d(); computeEllipse(cc, null, radii, null, null); axes[0] = radii.x; @@ -118,8 +107,8 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle axes[2] = radii.z; } } - catch (Exception e) - { + catch (Exception e) { + // Do nothing } return axes; @@ -127,40 +116,31 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle /** * Computes the bounding box of this component, and stores the result into the given arguments - * - * @param cc - * the input component - * @param minBounds - * the first corner of the bounding box in X-Y-Z order (Upper-Left hand-Top) - * @param maxBounds - * the second corner of the bounding box in X-Y-Z order (Lower-Right hand-Bottom) + * + * @param cc the input component + * @param minBounds the first corner of the bounding box in X-Y-Z order (Upper-Left hand-Top) + * @param maxBounds the second corner of the bounding box in X-Y-Z order (Lower-Right hand-Bottom) */ - public void computeBoundingBox(ConnectedComponent cc, Point3i minBounds, Point3i maxBounds) - { - if (minBounds != null) - { + public void computeBoundingBox(ConnectedComponent cc, Point3i minBounds, Point3i maxBounds) { + if (minBounds != null) { minBounds.x = Integer.MAX_VALUE; minBounds.y = Integer.MAX_VALUE; minBounds.z = Integer.MAX_VALUE; } - if (maxBounds != null) - { + if (maxBounds != null) { maxBounds.x = 0; maxBounds.y = 0; maxBounds.z = 0; } - for (Point3i point : cc) - { - if (minBounds != null) - { + for (Point3i point : cc) { + if (minBounds != null) { minBounds.x = Math.min(minBounds.x, point.x); minBounds.y = Math.min(minBounds.y, point.y); minBounds.z = Math.min(minBounds.z, point.z); } - if (maxBounds != null) - { + if (maxBounds != null) { maxBounds.x = Math.max(maxBounds.x, point.x); maxBounds.y = Math.max(maxBounds.y, point.y); maxBounds.z = Math.max(maxBounds.z, point.z); @@ -170,32 +150,25 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle /** * Computes the bounding box of this component, and stores the result into the given arguments - * - * @param cc - * the input component - * @param bsCenter - * the computed center of the bounding sphere - * @param bsRadius - * the computed radius of the bounding sphere + * + * @param cc the input component + * @param bsCenter the computed center of the bounding sphere + * @param bsRadius the computed radius of the bounding sphere */ - public void computeBoundingSphere(ConnectedComponent cc, Point3d bsCenter, VarDouble bsRadius) - { + public void computeBoundingSphere(ConnectedComponent cc, Point3d bsCenter, VarDouble bsRadius) { bsCenter.set(cc.getMassCenter()); bsRadius.setValue(cc.getMaxDistanceTo(bsCenter)); } /** - * @param cc ConnectedComponent - * @param contourPoints - * (set to <code>null</code> if not wanted) an output list of extracted contour - * points - * @param outputSequence - * (set to <code>null</code> if not wanted) an output sequence to receive the - * extracted contour + * @param cc ConnectedComponent + * @param contourPoints (set to <code>null</code> if not wanted) an output list of extracted contour + * points + * @param outputSequence (set to <code>null</code> if not wanted) an output sequence to receive the + * extracted contour * @return The 3D perimeter (or 3D surface) of this component */ - public double computePerimeter(ConnectedComponent cc, ArrayList<Point3i> contourPoints, Sequence outputSequence) - { + public double computePerimeter(ConnectedComponent cc, ArrayList<Point3i> contourPoints, Sequence outputSequence) { double perimeter = 0; if (contourPoints != null) @@ -212,8 +185,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle byte[][] outputMask = null; - if (outputSequence != null) - { + if (outputSequence != null) { outputSequence.removeAllImages(); for (int i = 0; i < d; i++) outputSequence.setImage(0, i, new IcyBufferedImage(w, h, 1, DataType.UBYTE)); @@ -234,8 +206,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle // count the edges and corners in 2D/3D double a = 0, b = 0; - for (Point3i p : cc) - { + for (Point3i p : cc) { localP.sub(p, min); int xy = localP.y * w + localP.x; @@ -252,16 +223,14 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle nbEdges++; if (localP.y == h - 1 || z[xy + w] == 0) nbEdges++; - if (min.z != max.z) - { // 3D + if (min.z != max.z) { // 3D if (localP.z == 0 || mask_z_xy[localP.z - 1][xy] == 0) nbEdges++; if (localP.z == d - 1 || mask_z_xy[localP.z + 1][xy] == 0) nbEdges++; } - switch (nbEdges) - { + switch (nbEdges) { case 0: break; case 1: @@ -280,8 +249,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle perimeter += Math.sqrt(3); } - if (nbEdges > 0) - { + if (nbEdges > 0) { if (contourPoints != null) contourPoints.add(p); if (outputMask != null) @@ -305,13 +273,11 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle * NOTE: the circularity index is adjusted to work with digitized contours, and partially * corrects for digitization artifacts (see the * {@link #computePerimeter(ConnectedComponent, ArrayList, Sequence)} method) - * - * @param cc - * the input component + * + * @param cc the input component * @return 1 for a perfect circle (or sphere), and lower than 1 otherwise */ - public double computeSphericity(ConnectedComponent cc) - { + public double computeSphericity(ConnectedComponent cc) { double dim = is2D(cc) ? 2.0 : 3.0; double area = cc.getSize(); @@ -342,38 +308,30 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle * Computes the eccentricity of the given component. This method fits an ellipse with radii a * and b (in 2D) or an ellipsoid with radii a, b and c (in 3D) and returns in both cases the * ratio b/a - * - * @param cc - * the input component + * + * @param cc the input component * @return the ratio b/a, where a and b are the two first largest ellipse radii (there are only - * two in 2D) + * two in 2D) */ - public double computeEccentricity(ConnectedComponent cc) - { - if (is2D(cc)) - { - try - { + public double computeEccentricity(ConnectedComponent cc) { + if (is2D(cc)) { + try { Point2d radii = new Point2d(); computeEllipse(cc, null, radii, null, null); return radii.x / radii.y; } - catch (RuntimeException e) - { + catch (RuntimeException e) { // error during the ellipse computation return Double.NaN; } } - else - { + else { Point3d radii = new Point3d(); - try - { + try { computeEllipse(cc, null, radii, null, null); return radii.x / radii.y; } - catch (Exception e) - { + catch (Exception e) { return Double.NaN; } } @@ -382,10 +340,9 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle /** * @param cc ConnectedComponent * @return The hull ratio, measured as the ratio between the object volume and its convex hull - * (envelope) + * (envelope) */ - public double computeHullRatio(ConnectedComponent cc) - { + public double computeHullRatio(ConnectedComponent cc) { double hull = computeConvexAreaAndVolume(cc)[1]; return hull == 0.0 ? 0.0 : Math.min(1.0, cc.getSize() / hull); @@ -394,21 +351,19 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle /** * @param cc ConnectedComponent * @return An array containing [contour, area] of the smallest convex envelope surrounding the - * object. The 2 values are returned together because their computation is simultaneous - * (in the 3D case only) + * object. The 2 values are returned together because their computation is simultaneous + * (in the 3D case only) */ - public double[] computeConvexAreaAndVolume(ConnectedComponent cc) - { + public double[] computeConvexAreaAndVolume(ConnectedComponent cc) { int i = 0, n = cc.getSize(); if (n == 1) - return new double[] {0.0, 1.0}; + return new double[]{0.0, 1.0}; double contour = 0.0; double area = 0.0; - if (is2D(cc)) - { - List<Point2D> points = new ArrayList<Point2D>(); + if (is2D(cc)) { + List<Point2D> points = new ArrayList<>(); for (Point3i p : cc) points.add(new Point2D.Double(p.x, p.y)); @@ -419,10 +374,9 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle // volume = sum( sqrt[ (x[i] - x[i-1])^2 + (y[i] - y[i-1])^2 ] ) // area = 0.5 * sum( (x[i-1] * y[i]) - (y[i-1] * x[i]) ) - Point2D p1 = points.get(points.size() - 1), p2 = null; + Point2D p1 = points.get(points.size() - 1), p2; - for (i = 0; i < points.size(); i++) - { + for (i = 0; i < points.size(); i++) { p2 = points.get(i); contour += p1.distance(p2); area += (p2.getX() * p1.getY()) - (p2.getY() * p1.getX()); @@ -432,8 +386,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle area *= 0.5; } else - try - { + try { Point3d[] points = new Point3d[n]; for (Point3i p : cc) @@ -447,8 +400,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle Vector3d v13 = new Vector3d(); Vector3d cross = new Vector3d(); - for (int[] face : hullFaces) - { + for (int[] face : hullFaces) { Point3d p1 = hullPoints[face[0]]; Point3d p2 = hullPoints[face[1]]; Point3d p3 = hullPoints[face[2]]; @@ -463,41 +415,33 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle area += contour * cross.x * (p1.x + p2.x + p3.x); } } - catch (IllegalArgumentException e) - { + catch (IllegalArgumentException e) { // less than 4 points, or coplanarity detected - return new double[] {n, n}; + return new double[]{n, n}; } - return new double[] {contour, area}; + return new double[]{contour, area}; } /** * Computes the geometric moment of the given component - * - * @param cc - * the input component - * @param p - * the moment order along X - * @param q - * the moment order along Y - * @param r - * the moment order along Z (set to 0 if the object is 2D) + * + * @param cc the input component + * @param p the moment order along X + * @param q the moment order along Y + * @param r the moment order along Z (set to 0 if the object is 2D) * @return the geometric moment */ - public double computeGeometricMoment(ConnectedComponent cc, int p, int q, int r) - { + public double computeGeometricMoment(ConnectedComponent cc, int p, int q, int r) { double moment = 0; Point3d center = cc.getMassCenter(); - if (is2D(cc)) - { + if (is2D(cc)) { for (Point3i point : cc) moment += Math.pow(point.x - center.x, p) * Math.pow(point.y - center.y, q); } - else - { + else { for (Point3i point : cc) moment += Math.pow(point.x - center.x, p) * Math.pow(point.y - center.y, q) * Math.pow(point.z - center.z, r); @@ -508,38 +452,28 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle /** * Compute the best fitting ellipsoid for the given component.<br> * This method is adapted from Yury Petrov's Matlab code and ported to Java by the BoneJ project - * - * @param cc - * the component to fit - * @param center - * (set to null if not wanted) the calculated ellipsoid center - * @param radii - * (set to null if not wanted) the calculated ellipsoid radius in each - * eigen-direction - * @param eigenVectors - * (set to null if not wanted) the calculated ellipsoid eigen-vectors - * @param equation - * (set to null if not wanted) an array of size 9 containing the calculated ellipsoid - * equation - * @throws IllegalArgumentException - * if the number of points in the component is too low (minimum is 9) - * @throws SingularMatrixException - * if the component is flat (i.e. lies in a 2D plane) + * + * @param cc the component to fit + * @param center (set to null if not wanted) the calculated ellipsoid center + * @param radii (set to null if not wanted) the calculated ellipsoid radius in each + * eigen-direction + * @param eigenVectors (set to null if not wanted) the calculated ellipsoid eigen-vectors + * @param equation (set to null if not wanted) an array of size 9 containing the calculated ellipsoid + * equation + * @throws IllegalArgumentException if the number of points in the component is too low (minimum is 9) + * @throws SingularMatrixException if the component is flat (i.e. lies in a 2D plane) */ public void computeEllipse(ConnectedComponent cc, Point3d center, Point3d radii, Vector3d[] eigenVectors, - double[] equation) throws IllegalArgumentException - { + double[] equation) throws IllegalArgumentException { int nPoints = cc.getSize(); - if (nPoints < 9) - { + if (nPoints < 9) { throw new IllegalArgumentException("Too few points; need at least 9 to calculate a unique ellipsoid"); } Point3i[] points = cc.getPoints(); double[][] d = new double[nPoints][9]; - for (int i = 0; i < nPoints; i++) - { + for (int i = 0; i < nPoints; i++) { double x = points[i].x; double y = points[i].y; double z = points[i].z; @@ -558,12 +492,10 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle Matrix ones = ones(nPoints, 1); Matrix V; - try - { + try { V = D.transpose().times(D).inverse().times(D.transpose().times(ones)); } - catch (RuntimeException e) - { + catch (RuntimeException e) { throw new SingularMatrixException("The component is most probably flat (i.e. lies in a 2D plane)"); } @@ -590,8 +522,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle if (center != null) center.set(C.get(0, 0), C.get(1, 0), C.get(2, 0)); - if (eigenVectors != null && eigenVectors.length == 3) - { + if (eigenVectors != null && eigenVectors.length == 3) { eigenVectors[0] = new Vector3d(eVec.get(0, 0), eVec.get(0, 1), eVec.get(0, 2)); eigenVectors[1] = new Vector3d(eVec.get(1, 0), eVec.get(1, 1), eVec.get(1, 2)); eigenVectors[2] = new Vector3d(eVec.get(2, 0), eVec.get(2, 1), eVec.get(2, 2)); @@ -603,34 +534,26 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle /** * 2D direct ellipse fitting.<br> * (Java port of Chernov's MATLAB implementation of the direct ellipse fit) - * - * @param cc - * the component to fit - * @param center - * (set to null if not wanted) the calculated ellipse center - * @param radii - * (set to null if not wanted) the calculated ellipse radius in each eigen-direction - * @param angle - * (set to null if not wanted) the calculated ellipse orientation - * @param equation - * (set to null if not wanted) a 6-element array, {a b c d f g}, which are the - * calculated algebraic parameters of the fitting ellipse: <i>ax</i><sup>2</sup> + 2 - * <i>bxy</i> + <i>cy</i><sup>2</sup> +2<i>dx</i> + 2<i>fy</i> + <i>g</i> = 0. The - * vector <b>A</b> represented in the array is normed, so that ||<b>A</b>||=1. - * @throws RuntimeException - * if the ellipse calculation fails (e.g. if a singular matrix is detected) + * + * @param cc the component to fit + * @param center (set to null if not wanted) the calculated ellipse center + * @param radii (set to null if not wanted) the calculated ellipse radius in each eigen-direction + * @param angle (set to null if not wanted) the calculated ellipse orientation + * @param equation (set to null if not wanted) a 6-element array, {a b c d f g}, which are the + * calculated algebraic parameters of the fitting ellipse: <i>ax</i><sup>2</sup> + 2 + * <i>bxy</i> + <i>cy</i><sup>2</sup> +2<i>dx</i> + 2<i>fy</i> + <i>g</i> = 0. The + * vector <b>A</b> represented in the array is normed, so that ||<b>A</b>||=1. + * @throws RuntimeException if the ellipse calculation fails (e.g. if a singular matrix is detected) */ public void computeEllipse(ConnectedComponent cc, Point2d center, Point2d radii, VarDouble angle, double[] equation) - throws RuntimeException - { + throws RuntimeException { Point3i[] points = cc.getPoints(); Point3d ccenter = cc.getMassCenter(); double[][] d1 = new double[cc.getSize()][3]; double[][] d2 = new double[cc.getSize()][3]; - for (int i = 0; i < d1.length; i++) - { + for (int i = 0; i < d1.length; i++) { final double xixC = points[i].x - ccenter.x; final double yiyC = points[i].y - ccenter.y; @@ -672,10 +595,8 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle Matrix cond = (R1.times(4)).arrayTimes(R3).minus(R2.arrayTimes(R2)); int _f = 0; - for (int i = 0; i < 3; i++) - { - if (cond.get(0, i) > 0) - { + for (int i = 0; i < 3; i++) { + if (cond.get(0, i) > 0) { _f = i; break; } @@ -717,23 +638,27 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle // semiaxis length final double af = 2 * (a * f * f + c * d * d + g * b * b - 2 * b * d * f - a * c * g); - final double aL = Math.sqrt((af) / ((b * b - a * c) * (Math.sqrt((a - c) * (a - c) + 4 * b * b) - (a + c)))); + final double sqrt = Math.sqrt((a - c) * (a - c) + 4 * b * b); + //final double aL = Math.sqrt((af) / ((b * b - a * c) * (Math.sqrt((a - c) * (a - c) + 4 * b * b) - (a + c)))); + final double aL = Math.sqrt((af) / ((b * b - a * c) * (sqrt - (a + c)))); + //final double bL = Math.sqrt((af) / ((b * b - a * c) * (-Math.sqrt((a - c) * (a - c) + 4 * b * b) - (a + c)))); + final double bL = Math.sqrt((af) / ((b * b - a * c) * (-sqrt - (a + c)))); - final double bL = Math.sqrt((af) / ((b * b - a * c) * (-Math.sqrt((a - c) * (a - c) + 4 * b * b) - (a + c)))); double phi = 0; - if (b == 0) - { + if (b == 0) { if (a <= c) phi = 0; - else if (a > c) + else phi = Math.PI / 2; } - else - { + else { + final double atan = Math.atan(2 * b / (a - c)) / 2; if (a < c) - phi = Math.atan(2 * b / (a - c)) / 2; + //phi = Math.atan(2 * b / (a - c)) / 2; + phi = atan; else if (a > c) - phi = Math.atan(2 * b / (a - c)) / 2 + Math.PI / 2; + //phi = Math.atan(2 * b / (a - c)) / 2 + Math.PI / 2; + phi = atan + Math.PI / 2; } if (center != null) @@ -744,8 +669,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle angle.setValue(phi); } - public boolean is2D(ConnectedComponent cc) - { + public boolean is2D(ConnectedComponent cc) { Point3i minBB = new Point3i(); Point3i maxBB = new Point3i(); computeBoundingBox(cc, minBB, maxBB); @@ -753,19 +677,16 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle return minBB.z == maxBB.z; } - private Matrix diag(Matrix matrix) - { + private Matrix diag(Matrix matrix) { int min = Math.min(matrix.getRowDimension(), matrix.getColumnDimension()); double[][] diag = new double[min][1]; - for (int i = 0; i < min; i++) - { + for (int i = 0; i < min; i++) { diag[i][0] = matrix.get(i, i); } return new Matrix(diag); } - private Matrix ones(int m, int n) - { + private Matrix ones(int m, int n) { double[][] array = new double[m][n]; for (double[] row : array) Arrays.fill(row, 1.0); diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$1.class b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$1.class deleted file mode 100644 index 3a65f5464ac163f7fc4b922f0614bd1e0e738542..0000000000000000000000000000000000000000 Binary files a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$1.class and /dev/null differ diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$2.class b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$2.class deleted file mode 100644 index bdcd201539ae2e983a556fbb1f8bd35ec81a9df8..0000000000000000000000000000000000000000 Binary files a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$2.class and /dev/null differ diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$ExtractionType.class b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$ExtractionType.class deleted file mode 100644 index 5bade0851a76b5ec7660fe31ba70fbed7a6c53ba..0000000000000000000000000000000000000000 Binary files a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$ExtractionType.class and /dev/null differ diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Label.class b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Label.class deleted file mode 100644 index 286aafc760e2fffd02cef831392ef551ec31e150..0000000000000000000000000000000000000000 Binary files a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Label.class and /dev/null differ diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Sorting$1.class b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Sorting$1.class deleted file mode 100644 index a575676183f4f2f945a6e9278aebdc4471ff5296..0000000000000000000000000000000000000000 Binary files a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Sorting$1.class and /dev/null differ diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Sorting$2.class b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Sorting$2.class deleted file mode 100644 index 8d3491cc8d4cad2b56a233c56aba2e354cd4ae55..0000000000000000000000000000000000000000 Binary files a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Sorting$2.class and /dev/null differ diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Sorting.class b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Sorting.class deleted file mode 100644 index 67cb4c595fb10b50662c392e6673f9714dd67db0..0000000000000000000000000000000000000000 Binary files a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents$Sorting.class and /dev/null differ diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents.java b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents.java index 1ae45da533e9e27e1bb418f903a58afb8e53e128..cc010e02f83e48fd49ad57dd26a145412bad063a 100644 --- a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents.java +++ b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents.java @@ -36,12 +36,10 @@ import plugins.adufour.blocks.util.VarList; import plugins.adufour.ezplug.EzGroup; import plugins.adufour.ezplug.EzLabel; import plugins.adufour.ezplug.EzPlug; -import plugins.adufour.ezplug.EzVar; import plugins.adufour.ezplug.EzVarBoolean; import plugins.adufour.ezplug.EzVarEnum; import plugins.adufour.ezplug.EzVarFile; import plugins.adufour.ezplug.EzVarInteger; -import plugins.adufour.ezplug.EzVarListener; import plugins.adufour.ezplug.EzVarSequence; import plugins.adufour.vars.lang.VarGenericArray; import plugins.adufour.vars.lang.VarROIArray; @@ -52,15 +50,13 @@ import plugins.kernel.roi.roi3d.ROI3DArea; import plugins.nchenouard.spot.DetectionResult; import plugins.nchenouard.spot.Spot; -public class ConnectedComponents extends EzPlug implements Block -{ +public class ConnectedComponents extends EzPlug implements Block { /** * List of extraction methods suitable for the Connected Components plugin - * + * * @author Alexandre Dufour */ - public enum ExtractionType - { + public enum ExtractionType { /** * The user defined value is considered as the background, and all components with a * different intensity should be extracted (regardless of intensity variations) @@ -82,8 +78,7 @@ public class ConnectedComponents extends EzPlug implements Block ROI } - public enum Sorting - { + public enum Sorting { /** * Components are not sorted */ @@ -91,41 +86,25 @@ public class ConnectedComponents extends EzPlug implements Block /** * Components are sorted by ascending depth value */ - DEPTH_ASC(new Comparator<ConnectedComponent>() - { - @Override - public int compare(ConnectedComponent o1, ConnectedComponent o2) - { - return (int) Math.signum(o1.getZ() - o2.getZ()); - } - }), + DEPTH_ASC((o1, o2) -> (int) Math.signum(o1.getZ() - o2.getZ())), /** * Components are sorted by descending depth value */ - DEPTH_DESC(new Comparator<ConnectedComponent>() - { - @Override - public int compare(ConnectedComponent o1, ConnectedComponent o2) - { - return (int) Math.signum(o2.getZ() - o1.getZ()); - } - }); + DEPTH_DESC((o1, o2) -> (int) Math.signum(o2.getZ() - o1.getZ())); /** * The comparator which can be used to sort an array of connected components */ public final Comparator<ConnectedComponent> comparator; - Sorting(Comparator<ConnectedComponent> comparator) - { + Sorting(Comparator<ConnectedComponent> comparator) { this.comparator = comparator; } } protected EzVarSequence input = new EzVarSequence("Input"); - protected EzVarEnum<ExtractionType> extractionMethod = new EzVarEnum<ExtractionType>("Extraction mode", - ExtractionType.values()); + protected EzVarEnum<ExtractionType> extractionMethod = new EzVarEnum<>("Extraction mode", ExtractionType.values()); protected EzLabel extractionMethodDetail = new EzLabel("Description"); @@ -146,8 +125,7 @@ public class ConnectedComponents extends EzPlug implements Block protected EzVarBoolean exportSequence = new EzVarBoolean("Labeled sequence", true); - protected EzVarEnum<Sorting> labelSorting = new EzVarEnum<ConnectedComponents.Sorting>("Sort components", - Sorting.values(), Sorting.ARBITRARY); + protected EzVarEnum<Sorting> labelSorting = new EzVarEnum<>("Sort components", Sorting.values(), Sorting.ARBITRARY); protected EzVarBoolean exportSwPool = new EzVarBoolean("Swimming pool", false); protected EzVarBoolean exportROI = new EzVarBoolean("ROI", false); @@ -155,52 +133,38 @@ public class ConnectedComponents extends EzPlug implements Block protected EzVarFile exportExcelFile = new EzVarFile("Excel file", ""); protected VarSequence outputSequence = new VarSequence("output", null); - protected VarGenericArray<ConnectedComponent[]> outputCCs = new VarGenericArray<ConnectedComponent[]>("components", - ConnectedComponent[].class, null); + protected VarGenericArray<ConnectedComponent[]> outputCCs = new VarGenericArray<>("components", ConnectedComponent[].class, null); protected VarROIArray outputROIs = new VarROIArray("list of ROI"); @Override - protected void initialize() - { + protected void initialize() { addEzComponent(input); - input.addVarChangeListener(new EzVarListener<Sequence>() - { - @Override - public void variableChanged(EzVar<Sequence> source, Sequence newValue) - { - if (newValue != null) - { - boolean doZ = newValue.getSizeZ() > 1; - discardEdgesZ.setValue(doZ); - discardEdgesZ.setVisible(doZ); - } + input.addVarChangeListener((source, newValue) -> { + if (newValue != null) { + boolean doZ = newValue.getSizeZ() > 1; + discardEdgesZ.setValue(doZ); + discardEdgesZ.setVisible(doZ); } }); addEzComponent(extractionMethod); - extractionMethod.addVarChangeListener(new EzVarListener<ExtractionType>() - { - @Override - public void variableChanged(EzVar<ExtractionType> source, ExtractionType newValue) - { - switch (newValue) - { - case BACKGROUND: - extractionMethodDetail.setText( - "Standard mode: extracts all pixels different than the given background value, regardless of their intensity"); - break; - case BACKGROUND_LABELED: - extractionMethodDetail.setText( - "Standard labeled mode: extracts all pixels different than the background, however different intensities are seen as different objects"); - break; - case VALUE: - extractionMethodDetail - .setText("Value-extraction mode: extracts all pixels with the specified value"); - break; - case ROI: - extractionMethodDetail.setText("ROI mode: extracts all ROI in the input sequence"); - break; - } + extractionMethod.addVarChangeListener((source, newValue) -> { + switch (newValue) { + case BACKGROUND: + extractionMethodDetail.setText( + "Standard mode: extracts all pixels different than the given background value, regardless of their intensity"); + break; + case BACKGROUND_LABELED: + extractionMethodDetail.setText( + "Standard labeled mode: extracts all pixels different than the background, however different intensities are seen as different objects"); + break; + case VALUE: + extractionMethodDetail + .setText("Value-extraction mode: extracts all pixels with the specified value"); + break; + case ROI: + extractionMethodDetail.setText("ROI mode: extracts all ROI in the input sequence"); + break; } }); @@ -228,8 +192,7 @@ public class ConnectedComponents extends EzPlug implements Block } @Override - public void declareInput(VarList inputMap) - { + public void declareInput(VarList inputMap) { inputMap.add("input", input.getVariable()); inputMap.add("method", extractionMethod.getVariable()); inputMap.add("value", background.getVariable()); @@ -244,18 +207,16 @@ public class ConnectedComponents extends EzPlug implements Block } @Override - public void declareOutput(VarList outputMap) - { + public void declareOutput(VarList outputMap) { outputMap.add("labeled sequence", outputSequence); outputMap.add("objects", outputCCs); outputMap.add("list of extracted ROI", outputROIs); } @Override - protected void execute() - { - ArrayList<ConnectedComponent> componentsList = new ArrayList<ConnectedComponent>(); - Map<Integer, List<ConnectedComponent>> componentsMap = new TreeMap<Integer, List<ConnectedComponent>>(); + protected void execute() { + ArrayList<ConnectedComponent> componentsList = new ArrayList<>(); + Map<Integer, List<ConnectedComponent>> componentsMap; int min = boundSize.getValue() ? minSize.getValue() : 0; int max = boundSize.getValue() ? maxSize.getValue() : Integer.MAX_VALUE; @@ -269,14 +230,13 @@ public class ConnectedComponents extends EzPlug implements Block // never remove objects touching the Z edge in 2D (that would remove... everything!) boolean discardEdgesAlongZ = (inputSequence.getSizeZ() > 1 && discardEdgesZ.getValue()); - try - { - if (extractionMethod.getValue() == ExtractionType.ROI) - { + try { + if (extractionMethod.getValue() == ExtractionType.ROI) { int width = inputSequence.getWidth(); int height = inputSequence.getHeight(); - Sequence labeledSequence = new Sequence(inputSequence.getMetadata()); + //Sequence labeledSequence = new Sequence(inputSequence.getMetadata()); + Sequence labeledSequence = new Sequence(inputSequence.getOMEXMLMetadata()); for (int t = 0; t < inputSequence.getSizeT(); t++) for (int z = 0; z < inputSequence.getSizeZ(); z++) labeledSequence.setImage(t, z, new IcyBufferedImage(width, height, 1, DataType.USHORT)); @@ -288,15 +248,13 @@ public class ConnectedComponents extends EzPlug implements Block componentsMap = extractConnectedComponents(labeledSequence, 0, ExtractionType.BACKGROUND_LABELED, discardEdgesX.getValue(), discardEdgesY.getValue(), discardEdgesAlongZ, min, max, output); } - else - { + else { componentsMap = extractConnectedComponents(inputSequence, background.getValue(), extractionMethod.getValue(), discardEdgesX.getValue(), discardEdgesY.getValue(), discardEdgesAlongZ, min, max, output); } } - catch (InterruptedException e) - { + catch (InterruptedException e) { new FailedAnnounceFrame(e.getMessage()); return; } @@ -304,44 +262,37 @@ public class ConnectedComponents extends EzPlug implements Block outputSequence.setValue(output); int nbObjects = 0; - for (List<ConnectedComponent> ccs : componentsMap.values()) - { + for (List<ConnectedComponent> ccs : componentsMap.values()) { nbObjects += ccs.size(); componentsList.addAll(ccs); } outputCCs.setValue(componentsList.toArray(new ConnectedComponent[nbObjects])); - if (getUI() != null) - { + if (getUI() != null) { objectCount.setText("Total: " + nbObjects + " components"); - if (exportSequence.getValue()) - { + if (exportSequence.getValue()) { createLabeledSequence(output, componentsMap, labelSorting.getValue().comparator); addSequence(output); } } - if (exportSwPool.getValue()) - { + if (exportSwPool.getValue()) { DetectionResult result = convertToDetectionResult(componentsMap, input.getValue()); SwimmingObject object = new SwimmingObject(result, "Set of " + nbObjects + " connected components"); Icy.getMainInterface().getSwimmingPool().add(object); } - if (exportROI.getValue() || outputROIs.isReferenced()) - { - if (output.getSizeZ() > 1) - { - ArrayList<ROI3DArea> rois = new ArrayList<ROI3DArea>(componentsMap.size()); + if (exportROI.getValue() || outputROIs.isReferenced()) { + if (output.getSizeZ() > 1) { + ArrayList<ROI3DArea> rois = new ArrayList<>(componentsMap.size()); int ccID = 1; for (List<ConnectedComponent> ccs : componentsMap.values()) - for (ConnectedComponent cc : ccs) - { + for (ConnectedComponent cc : ccs) { ROI3DArea area = new ROI3DArea(); area.setName("Object #" + ccID++); area.beginUpdate(); @@ -354,13 +305,11 @@ public class ConnectedComponents extends EzPlug implements Block outputROIs.setValue(rois.toArray(new ROI3DArea[rois.size()])); } - else - { - ArrayList<ROI2DArea> rois = new ArrayList<ROI2DArea>(componentsMap.size()); + else { + ArrayList<ROI2DArea> rois = new ArrayList<>(componentsMap.size()); for (List<ConnectedComponent> ccs : componentsMap.values()) - for (ConnectedComponent cc : ccs) - { + for (ConnectedComponent cc : ccs) { ROI2DArea area = new ROI2DArea(); area.beginUpdate(); for (Point3i pt : cc) @@ -373,8 +322,7 @@ public class ConnectedComponents extends EzPlug implements Block outputROIs.setValue(rois.toArray(new ROI2DArea[rois.size()])); } - if (exportROI.getValue()) - { + if (exportROI.getValue()) { // replace all ROI on the input by the new ones Sequence in = input.getValue(); @@ -390,23 +338,20 @@ public class ConnectedComponents extends EzPlug implements Block } } - if (exportExcel.getValue()) - { + if (exportExcel.getValue()) { int page = 1; - WritableSheet sheet = null; - WritableWorkbook workbook = null; + WritableSheet sheet; + WritableWorkbook workbook; - try - { + try { File f = exportExcelFile.getValue(true); if (!FileUtil.getFileExtension(f.getPath(), false).equalsIgnoreCase("xls")) f = new File(f.getPath() + ".xls"); workbook = XLSUtil.createWorkbook(f); sheet = XLSUtil.createNewPage(workbook, "Page " + page); } - catch (Exception e) - { + catch (Exception e) { throw new IcyHandledException(e.getMessage()); } @@ -455,8 +400,7 @@ public class ConnectedComponents extends EzPlug implements Block ConnectedComponentDescriptor shapeDescriptor = new ConnectedComponentDescriptor(); int cpt = 2; for (Integer time : componentsMap.keySet()) - for (ConnectedComponent cc : componentsMap.get(time)) - { + for (ConnectedComponent cc : componentsMap.get(time)) { boolean is2D = shapeDescriptor.is2D(cc); Point3d center = cc.getMassCenter(); center.x *= resolution.x; @@ -477,59 +421,65 @@ public class ConnectedComponents extends EzPlug implements Block XLSUtil.setCellNumber(sheet, 11, cpt, shapeDescriptor.computeEccentricity(cc)); double[] contour_area = shapeDescriptor.computeConvexAreaAndVolume(cc); - XLSUtil.setCellNumber(sheet, 12, cpt, - contour_area[1] == 0.0 ? 0.0 : Math.min(1.0, cc.getSize() / contour_area[1])); + XLSUtil.setCellNumber(sheet, 12, cpt, contour_area[1] == 0.0 ? 0.0 : Math.min(1.0, cc.getSize() / contour_area[1])); XLSUtil.setCellNumber(sheet, 13, cpt, shapeDescriptor.computeGeometricMoment(cc, 1, 0, 0)); XLSUtil.setCellNumber(sheet, 14, cpt, shapeDescriptor.computeGeometricMoment(cc, 0, 1, 0)); + if (!is2D) XLSUtil.setCellNumber(sheet, 15, cpt, shapeDescriptor.computeGeometricMoment(cc, 0, 0, 1)); + XLSUtil.setCellNumber(sheet, 16, cpt, shapeDescriptor.computeGeometricMoment(cc, 1, 1, 0)); + if (!is2D) XLSUtil.setCellNumber(sheet, 17, cpt, shapeDescriptor.computeGeometricMoment(cc, 1, 0, 1)); + if (!is2D) XLSUtil.setCellNumber(sheet, 18, cpt, shapeDescriptor.computeGeometricMoment(cc, 0, 1, 1)); + if (!is2D) XLSUtil.setCellNumber(sheet, 19, cpt, shapeDescriptor.computeGeometricMoment(cc, 1, 1, 1)); + XLSUtil.setCellNumber(sheet, 20, cpt, shapeDescriptor.computeGeometricMoment(cc, 2, 0, 0)); XLSUtil.setCellNumber(sheet, 21, cpt, shapeDescriptor.computeGeometricMoment(cc, 0, 2, 0)); + if (!is2D) XLSUtil.setCellNumber(sheet, 22, cpt, shapeDescriptor.computeGeometricMoment(cc, 0, 0, 2)); + XLSUtil.setCellNumber(sheet, 23, cpt, shapeDescriptor.computeGeometricMoment(cc, 2, 2, 0)); + if (!is2D) XLSUtil.setCellNumber(sheet, 24, cpt, shapeDescriptor.computeGeometricMoment(cc, 2, 0, 2)); + if (!is2D) XLSUtil.setCellNumber(sheet, 25, cpt, shapeDescriptor.computeGeometricMoment(cc, 0, 2, 2)); + if (!is2D) XLSUtil.setCellNumber(sheet, 26, cpt, shapeDescriptor.computeGeometricMoment(cc, 2, 2, 2)); + XLSUtil.setCellNumber(sheet, 27, cpt, contour_area[0]); XLSUtil.setCellNumber(sheet, 28, cpt, contour_area[1]); cpt++; - if (cpt == Short.MAX_VALUE) - { + if (cpt == Short.MAX_VALUE) { page++; sheet = XLSUtil.createNewPage(workbook, "Page " + page); cpt = 1; } } - try - { + try { XLSUtil.saveAndClose(workbook); } - catch (Exception e) - { + catch (Exception e) { throw new IcyHandledException(e.getMessage()); } } } @Override - public void clean() - { + public void clean() { } - private static class Label - { + private static class Label { final double imageValue; /** @@ -553,31 +503,19 @@ public class ConnectedComponents extends EzPlug implements Block /** * Creates a new label with the given value. If no parent is set to this label, the given * value will be the final one - * - * @param value - * the pixel value - * @param label - * the label value - * @param onEdgeX - * true if the pixel is on the image edge along X - * @param onEdgeY - * true if the pixel is on the image edge along Y - * @param onEdgeZ - * true if the pixel is on the image edge along Z + * + * @param value the pixel value + * @param label the label value */ - Label(double value, int label) - { + Label(double value, int label) { this.imageValue = value; this.targetLabelValue = label; } /** * Retrieves the final object label (recursively) - * - * @return */ - int getFinalLabelValue() - { + int getFinalLabelValue() { return targetLabel == null ? targetLabelValue : targetLabel.getFinalLabelValue(); } } @@ -586,19 +524,15 @@ public class ConnectedComponents extends EzPlug implements Block * Extracts the connected components in the given sequence with specified size bounds. Note that * the method works on both binary gray-scale images. If the input is not binary, any value * other than 0 will be extracted as a component. - * - * @param inputSequence - * A binary or gray-scale sequence. If the sequence is gray-scale, any value other - * than 0 will be extracted as a component - * @param labeledSequence - * a single-band integer sequence that will be filled with the labeled components + * + * @param inputSequence A binary or gray-scale sequence. If the sequence is gray-scale, any value other + * than 0 will be extracted as a component + * @param labeledSequence a single-band integer sequence that will be filled with the labeled components * @return a map with extracted components, indexed by the sequence time points * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(Sequence inputSequence, - Sequence labeledSequence) - { + public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(Sequence inputSequence, Sequence labeledSequence) { return extractConnectedComponents(inputSequence, false, 0, Integer.MAX_VALUE, labeledSequence); } @@ -606,23 +540,17 @@ public class ConnectedComponents extends EzPlug implements Block * Extracts the connected components in the given sequence with specified size bounds. Note that * the method works on both binary gray-scale images. If the input is not binary, any value * other than 0 will be extracted as a component. - * - * @param inputSequence - * A binary or gray-scale sequence. If the sequence is gray-scale, any value other - * than 0 will be extracted as a component - * @param minSize - * the minimum size of the objects to extract - * @param maxSize - * the maximum size of the objects to extract - * @param labeledSequence - * a single-band integer sequence that will be filled with the labeled components + * + * @param inputSequence A binary or gray-scale sequence. If the sequence is gray-scale, any value other + * than 0 will be extracted as a component + * @param minSize the minimum size of the objects to extract + * @param maxSize the maximum size of the objects to extract + * @param labeledSequence a single-band integer sequence that will be filled with the labeled components * @return a map with extracted components, indexed by the sequence time points * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(Sequence inputSequence, int minSize, - int maxSize, Sequence labeledSequence) - { + public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(Sequence inputSequence, int minSize, int maxSize, Sequence labeledSequence) { return extractConnectedComponents(inputSequence, false, minSize, maxSize, labeledSequence); } @@ -631,24 +559,17 @@ public class ConnectedComponents extends EzPlug implements Block * the method works on both binary gray-scale images. If the input is not binary, an option can * be set to distinguish touching objects which have a different intensity value, or to extract * a specific intensity value - * - * @param inputSequence - * A binary or gray-scale sequence. - * @param isInputLabeled - * if true, touching components with different pixel values are extracted separately - * @param minSize - * the minimum size of the objects to extract - * @param maxSize - * the maximum size of the objects to extract - * @param labeledSequence - * a single-band integer sequence that will be filled with the labeled components + * + * @param inputSequence A binary or gray-scale sequence. + * @param isInputLabeled if true, touching components with different pixel values are extracted separately + * @param minSize the minimum size of the objects to extract + * @param maxSize the maximum size of the objects to extract + * @param labeledSequence a single-band integer sequence that will be filled with the labeled components * @return a map with extracted components, indexed by the sequence time points * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(Sequence inputSequence, - boolean isInputLabeled, int minSize, int maxSize, Sequence labeledSequence) - { + public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(Sequence inputSequence, boolean isInputLabeled, int minSize, int maxSize, Sequence labeledSequence) { return extractConnectedComponents(inputSequence, 0, isInputLabeled ? ExtractionType.BACKGROUND_LABELED : ExtractionType.BACKGROUND, minSize, maxSize, labeledSequence); @@ -659,28 +580,19 @@ public class ConnectedComponents extends EzPlug implements Block * the method works on both binary gray-scale images. If the input is not binary, an option can * be set to distinguish touching objects which have a different intensity value, or to extract * a specific intensity value - * - * @param inputSequence - * A binary or gray-scale sequence. - * @param value - * the user value to be interpreted depending on the type parameter - * @param type - * the extraction method (or how to interpret the value parameter) - * @param minSize - * the minimum size of the objects to extract - * @param maxSize - * the maximum size of the objects to extract - * @param labeledSequence - * a single-band integer sequence that will be filled with the labeled components + * + * @param inputSequence A binary or gray-scale sequence. + * @param value the user value to be interpreted depending on the type parameter + * @param type the extraction method (or how to interpret the value parameter) + * @param minSize the minimum size of the objects to extract + * @param maxSize the maximum size of the objects to extract + * @param labeledSequence a single-band integer sequence that will be filled with the labeled components * @return a map with extracted components, indexed by the sequence time points * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(Sequence inputSequence, - double value, ExtractionType type, int minSize, int maxSize, Sequence labeledSequence) - { - return extractConnectedComponents(inputSequence, value, type, false, false, false, minSize, maxSize, - labeledSequence); + public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(Sequence inputSequence, double value, ExtractionType type, int minSize, int maxSize, Sequence labeledSequence) { + return extractConnectedComponents(inputSequence, value, type, false, false, false, minSize, maxSize, labeledSequence); } /** @@ -688,38 +600,28 @@ public class ConnectedComponents extends EzPlug implements Block * the method works on both binary gray-scale images. If the input is not binary, an option can * be set to distinguish touching objects which have a different intensity value, or to extract * a specific intensity value - * - * @param inputSequence - * A binary or gray-scale sequence. - * @param value - * the user value to be interpreted depending on the type parameter - * @param type - * the extraction type (or how to interpret the value parameter) - * @param noEdgeX - * set to true if components touching the image edge along X should be discarded - * during the extraction - * @param noEdgeY - * set to true if components touching the image edge along Y should be discarded - * during the extraction - * @param noEdgeZ - * set to true if components touching the image edge along Z should be discarded - * during the extraction - * @param minSize - * the minimum size of the objects to extract - * @param maxSize - * the maximum size of the objects to extract - * @param labeledSequence - * a single-band integer sequence that will be filled with the labeled components + * + * @param inputSequence A binary or gray-scale sequence. + * @param value the user value to be interpreted depending on the type parameter + * @param type the extraction type (or how to interpret the value parameter) + * @param noEdgeX set to true if components touching the image edge along X should be discarded + * during the extraction + * @param noEdgeY set to true if components touching the image edge along Y should be discarded + * during the extraction + * @param noEdgeZ set to true if components touching the image edge along Z should be discarded + * during the extraction + * @param minSize the minimum size of the objects to extract + * @param maxSize the maximum size of the objects to extract + * @param labeledSequence a single-band integer sequence that will be filled with the labeled components * @return a map with extracted components, indexed by the sequence time points * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(Sequence inputSequence, - double value, ExtractionType type, boolean noEdgeX, boolean noEdgeY, boolean noEdgeZ, int minSize, - int maxSize, Sequence labeledSequence) - { + public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents( + Sequence inputSequence, double value, ExtractionType type, boolean noEdgeX, boolean noEdgeY, boolean noEdgeZ, int minSize, int maxSize, Sequence labeledSequence + ) { if (inputSequence == null || inputSequence.getSizeT() == 0) - throw new IllegalArgumentException("Cannot extract connected components from an emtpy sequence !"); + throw new IllegalArgumentException("Cannot extract connected components from an empty sequence !"); int width = inputSequence.getSizeX(); int height = inputSequence.getSizeY(); @@ -727,10 +629,9 @@ public class ConnectedComponents extends EzPlug implements Block if (labeledSequence == null) labeledSequence = new Sequence(); - Map<Integer, List<ConnectedComponent>> componentsMap = new TreeMap<Integer, List<ConnectedComponent>>(); + Map<Integer, List<ConnectedComponent>> componentsMap = new TreeMap<>(); - for (int t = 0; t < inputSequence.getSizeT(); t++) - { + for (int t = 0; t < inputSequence.getSizeT(); t++) { for (int z = 0; z < inputSequence.getSizeZ(); z++) labeledSequence.setImage(t, z, new IcyBufferedImage(width, height, 1, DataType.UINT)); @@ -741,8 +642,7 @@ public class ConnectedComponents extends EzPlug implements Block noEdgeZ, minSize, maxSize, volOUT); // int cpt=1; - for (ConnectedComponent cc : components) - { + for (ConnectedComponent cc : components) { cc.setT(t); // System.out.println(t + "\t" + cpt++ + "\t" + (int)cc.getMassCenter().x + "\t" + // (int)cc.getMassCenter().y); @@ -759,38 +659,29 @@ public class ConnectedComponents extends EzPlug implements Block /** * Extracts the connected components in the given volumetric image with specified size bounds. - * The the method works on both binary gray-scale images. If the input is not binary, an option + * The method works on both binary gray-scale images. If the input is not binary, an option * can be set to distinguish touching objects which have a different intensity value, or to * extract a specific intensity value - * - * @param stack - * A binary or gray-scale volumetric image. - * @param value - * the user value to be interpreted depending on the type parameter - * @param type - * the extraction type (or how to interpret the value parameter) - * @param noEdgeX - * set to true if components touching the image edge along X should be discarded - * during the extraction - * @param noEdgeY - * set to true if components touching the image edge along Y should be discarded - * during the extraction - * @param noEdgeZ - * set to true if components touching the image edge along Z should be discarded - * during the extraction - * @param minSize - * the minimum size of the objects to extract - * @param maxSize - * the maximum size of the objects to extract + * + * @param stack A binary or gray-scale volumetric image. + * @param value the user value to be interpreted depending on the type parameter + * @param type the extraction type (or how to interpret the value parameter) + * @param noEdgeX set to true if components touching the image edge along X should be discarded + * during the extraction + * @param noEdgeY set to true if components touching the image edge along Y should be discarded + * during the extraction + * @param noEdgeZ set to true if components touching the image edge along Z should be discarded + * during the extraction + * @param minSize the minimum size of the objects to extract + * @param maxSize the maximum size of the objects to extract * @return a collection containing all components extracted. * @see ExtractionType * @see ConnectedComponent */ - public static List<ConnectedComponent> extractConnectedComponents(VolumetricImage stack, double value, - ExtractionType type, boolean noEdgeX, boolean noEdgeY, boolean noEdgeZ, int minSize, int maxSize) - { - return extractConnectedComponents(stack, value, type, noEdgeX, noEdgeY, noEdgeZ, minSize, maxSize, - new VolumetricImage()); + public static List<ConnectedComponent> extractConnectedComponents( + VolumetricImage stack, double value, ExtractionType type, boolean noEdgeX, boolean noEdgeY, boolean noEdgeZ, int minSize, int maxSize + ) { + return extractConnectedComponents(stack, value, type, noEdgeX, noEdgeY, noEdgeZ, minSize, maxSize, new VolumetricImage()); } /** @@ -798,40 +689,29 @@ public class ConnectedComponents extends EzPlug implements Block * The the method works on both binary gray-scale images. If the input is not binary, an option * can be set to distinguish touching objects which have a different intensity value, or to * extract a specific intensity value - * - * @param stack - * A binary or gray-scale volumetric image. - * @param value - * the user value to be interpreted depending on the type parameter - * @param type - * the extraction type (or how to interpret the value parameter) - * @param noEdgeX - * set to true if components touching the image edge along X should be discarded - * during the extraction - * @param noEdgeY - * set to true if components touching the image edge along Y should be discarded - * during the extraction - * @param noEdgeZ - * set to true if components touching the image edge along Z should be discarded - * during the extraction - * @param minSize - * the minimum size of the objects to extract - * @param maxSize - * the maximum size of the objects to extract - * @param labeledStack - * a volumetric image where labeled information will be stored during the extraction. - * Note that this volumetric image should be non-null and filled with zero-values (no - * cleaning is performed to optimize recursive processes) + * + * @param stack A binary or gray-scale volumetric image. + * @param value the user value to be interpreted depending on the type parameter + * @param type the extraction type (or how to interpret the value parameter) + * @param noEdgeX set to true if components touching the image edge along X should be discarded + * during the extraction + * @param noEdgeY set to true if components touching the image edge along Y should be discarded + * during the extraction + * @param noEdgeZ set to true if components touching the image edge along Z should be discarded + * during the extraction + * @param minSize the minimum size of the objects to extract + * @param maxSize the maximum size of the objects to extract + * @param labeledStack a volumetric image where labeled information will be stored during the extraction. + * Note that this volumetric image should be non-null and filled with zero-values (no + * cleaning is performed to optimize recursive processes) * @return a collection containing all components extracted. - * @throws NullPointerException - * is labeledStack is null + * @throws NullPointerException is labeledStack is null * @see ExtractionType * @see ConnectedComponent */ - public static List<ConnectedComponent> extractConnectedComponents(VolumetricImage stack, double value, - ExtractionType type, boolean noEdgeX, boolean noEdgeY, boolean noEdgeZ, int minSize, int maxSize, - final VolumetricImage labeledStack) throws NullPointerException - { + public static List<ConnectedComponent> extractConnectedComponents( + VolumetricImage stack, double value, ExtractionType type, boolean noEdgeX, boolean noEdgeY, boolean noEdgeZ, int minSize, int maxSize, final VolumetricImage labeledStack + ) throws NullPointerException { int width = stack.getFirstImage().getSizeX(); int height = stack.getFirstImage().getSizeY(); int depth = stack.getSize(); @@ -847,12 +727,11 @@ public class ConnectedComponents extends EzPlug implements Block int highestKnownLabel = 0; - boolean onEdgeX = false; - boolean onEdgeY = false; - boolean onEdgeZ = false; + boolean onEdgeX; + boolean onEdgeY; + boolean onEdgeZ; - for (int z = 0; z < depth; z++) - { + for (int z = 0; z < depth; z++) { onEdgeZ = (z == 0 || z == depth - 1); final IcyBufferedImage currentSliceImage = labeledStack.getImage(z); @@ -866,12 +745,10 @@ public class ConnectedComponents extends EzPlug implements Block Object inputData = stack.getImage(z).getDataXY(0); DataType dataType = stack.getImage(z).getDataType_(); - for (int y = 0; y < height; y++) - { + for (int y = 0; y < height; y++) { onEdgeY = (y == 0 || y == height - 1); - for (int x = 0; x < width; x++, voxelOffset++) - { + for (int x = 0; x < width; x++, voxelOffset++) { onEdgeX = (x == 0 || x == width - 1); double pixelValue = Array1DUtil.getValue(inputData, voxelOffset, dataType); @@ -898,20 +775,16 @@ public class ConnectedComponents extends EzPlug implements Block // n = valid neighbor // . = other neighbor - if (z == 0) - { - if (y == 0) - { - if (x == 0) - { + if (z == 0) { + if (y == 0) { + if (x == 0) { // e e e // e x . // e . . // do nothing } - else - { + else { // e e e // n x . // . . . @@ -920,12 +793,10 @@ public class ConnectedComponents extends EzPlug implements Block neighborhoodSize = 1; } } - else - { + else { int north = voxelOffset - width; - if (x == 0) - { + if (x == 0) { // e n n // e x . // e . . @@ -934,8 +805,7 @@ public class ConnectedComponents extends EzPlug implements Block neighborLabelValues[1] = _labelsInCurrentSlice[north + 1]; neighborhoodSize = 2; } - else if (x == width - 1) - { + else if (x == width - 1) { // n n e // n x e // . . e @@ -945,8 +815,7 @@ public class ConnectedComponents extends EzPlug implements Block neighborLabelValues[2] = _labelsInCurrentSlice[voxelOffset - 1]; neighborhoodSize = 3; } - else - { + else { // n n n // n x . // . . . @@ -959,14 +828,11 @@ public class ConnectedComponents extends EzPlug implements Block } } } - else - { - if (y == 0) - { + else { + if (y == 0) { int south = voxelOffset + width; - if (x == 0) - { + if (x == 0) { // e e e | e e e // e n n | e x . // e n n | e . . @@ -977,8 +843,7 @@ public class ConnectedComponents extends EzPlug implements Block neighborLabelValues[3] = _labelsInUpperSlice[south + 1]; neighborhoodSize = 4; } - else if (x == width - 1) - { + else if (x == width - 1) { // e e e | e e e // n n e | n x e // n n e | . . e @@ -990,8 +855,7 @@ public class ConnectedComponents extends EzPlug implements Block neighborLabelValues[4] = _labelsInCurrentSlice[voxelOffset - 1]; neighborhoodSize = 5; } - else - { + else { // e e e | e e e // n n n | n x . // n n n | . . . @@ -1006,12 +870,10 @@ public class ConnectedComponents extends EzPlug implements Block neighborhoodSize = 7; } } - else if (y == height - 1) - { + else if (y == height - 1) { int north = voxelOffset - width; - if (x == 0) - { + if (x == 0) { // e n n | e n n // e n n | e x . // e e e | e e e @@ -1024,8 +886,7 @@ public class ConnectedComponents extends EzPlug implements Block neighborLabelValues[5] = _labelsInCurrentSlice[north + 1]; neighborhoodSize = 6; } - else if (x == width - 1) - { + else if (x == width - 1) { // n n e | n n e // n n e | n x e // e e e | e e e @@ -1039,8 +900,7 @@ public class ConnectedComponents extends EzPlug implements Block neighborLabelValues[6] = _labelsInCurrentSlice[voxelOffset - 1]; neighborhoodSize = 7; } - else - { + else { // n n n | n n n // n n n | n x . // e e e | e e e @@ -1058,13 +918,11 @@ public class ConnectedComponents extends EzPlug implements Block neighborhoodSize = 10; } } - else - { + else { int north = voxelOffset - width; int south = voxelOffset + width; - if (x == 0) - { + if (x == 0) { // e n n | e n n // e n n | e x . // e n n | e . . @@ -1079,8 +937,7 @@ public class ConnectedComponents extends EzPlug implements Block neighborLabelValues[7] = _labelsInCurrentSlice[north + 1]; neighborhoodSize = 8; } - else if (x == width - 1) - { + else if (x == width - 1) { int northwest = north - 1; int west = voxelOffset - 1; @@ -1099,8 +956,7 @@ public class ConnectedComponents extends EzPlug implements Block neighborLabelValues[8] = _labelsInCurrentSlice[west]; neighborhoodSize = 9; } - else - { + else { int northwest = north - 1; int west = voxelOffset - 1; int northeast = north + 1; @@ -1137,8 +993,7 @@ public class ConnectedComponents extends EzPlug implements Block // find the minimum non-zero label in the neighborhood // and assign that minimum label right now - for (int i = 0; i < neighborhoodSize; i++) - { + for (int i = 0; i < neighborhoodSize; i++) { int neighborLabelValue = neighborLabelValues[i]; // zero labels are not interesting... @@ -1152,14 +1007,12 @@ public class ConnectedComponents extends EzPlug implements Block // here, the neighbor label is valid // => check if it is lower - if (neighborLabelValue < currentVoxelLabelValue) - { + if (neighborLabelValue < currentVoxelLabelValue) { currentVoxelLabelValue = neighborLabelValue; } } - if (currentVoxelLabelValue == Integer.MAX_VALUE) - { + if (currentVoxelLabelValue == Integer.MAX_VALUE) { // currentVoxelLabel didn't change // => no lower neighbor value found // => new label @@ -1167,8 +1020,7 @@ public class ConnectedComponents extends EzPlug implements Block currentVoxelLabelValue = highestKnownLabel; labels[currentVoxelLabelValue] = new Label(pixelValue, currentVoxelLabelValue); } - else - { + else { // currentVoxelLabelValue has been modified // -> browse its neighborhood again // -> find all neighbors with a higher label value @@ -1177,12 +1029,10 @@ public class ConnectedComponents extends EzPlug implements Block Label currentVoxelLabel = labels[currentVoxelLabelValue]; - for (int i = 0; i < neighborhoodSize; i++) - { + for (int i = 0; i < neighborhoodSize; i++) { int neighborLabelValue = neighborLabelValues[i]; - if (neighborLabelValue > currentVoxelLabelValue) - { + if (neighborLabelValue > currentVoxelLabelValue) { Label label = labels[neighborLabelValue]; if (type == ExtractionType.BACKGROUND_LABELED && label.imageValue != pixelValue) @@ -1194,13 +1044,11 @@ public class ConnectedComponents extends EzPlug implements Block if (currentVoxelLabel.targetLabelValue == finalLabelValue) continue; - if (currentVoxelLabelValue < finalLabelValue) - { + if (currentVoxelLabelValue < finalLabelValue) { finalLabel.targetLabel = currentVoxelLabel; finalLabel.targetLabelValue = currentVoxelLabelValue; } - else if (currentVoxelLabelValue > finalLabelValue) - { + else if (currentVoxelLabelValue > finalLabelValue) { currentVoxelLabel.targetLabel = finalLabel; currentVoxelLabel.targetLabelValue = finalLabelValue; } @@ -1224,7 +1072,7 @@ public class ConnectedComponents extends EzPlug implements Block // end of the first pass, all pixels have a label // (though might not be unique within a given component) - HashMap<Integer, ConnectedComponent> componentsMap = new HashMap<Integer, ConnectedComponent>(); + HashMap<Integer, ConnectedComponent> componentsMap = new HashMap<>(); // fusion strategy: fuse higher labels with lower ones // "highestKnownLabel" holds the highest known label @@ -1232,14 +1080,12 @@ public class ConnectedComponents extends EzPlug implements Block int finalLabel = 0; - for (int labelValue = highestKnownLabel; labelValue > 0; labelValue--) - { + for (int labelValue = highestKnownLabel; labelValue > 0; labelValue--) { Label label = labels[labelValue]; int targetLabelValue = label.targetLabelValue; - if (targetLabelValue < labelValue) - { + if (targetLabelValue < labelValue) { // label should be fused to targetLabel Label targetLabel = labels[targetLabelValue]; @@ -1255,28 +1101,24 @@ public class ConnectedComponents extends EzPlug implements Block // -> mark label to fuse with targetLabel label.targetLabel = labels[targetLabelValue]; } - else - { + else { // label has same labelValue and targetLabelValue // -> it cannot be fused to anything // -> this is a terminal label // -> check if it obeys to user constraints - if (label.size < minSize || label.size > maxSize) - { + if (label.size < minSize || label.size > maxSize) { // the component size is out of the given range // -> mark the object for deletion label.targetLabelValue = 0; } - else if ((noEdgeX && label.onEdgeX) || (noEdgeY && label.onEdgeY) || (noEdgeZ && label.onEdgeZ)) - { + else if ((noEdgeX && label.onEdgeX) || (noEdgeY && label.onEdgeY) || (noEdgeZ && label.onEdgeZ)) { // the component size is on an edge to discard // -> mark the object for deletion label.targetLabelValue = 0; } - else - { + else { // the label is clean and user-valid // -> assign its final labelValue (for the final image labeling pass) finalLabel++; @@ -1295,17 +1137,14 @@ public class ConnectedComponents extends EzPlug implements Block // 3) second image pass: replace all labels by their final values - for (int z = 0; z < depth; z++) - { + for (int z = 0; z < depth; z++) { final IcyBufferedImage sliceImage = labeledStack.getImage(z); int[] _outputSlice = sliceImage.getDataXYAsInt(0); int pixelOffset = 0; - for (int j = 0; j < height; j++) - { - for (int i = 0; i < width; i++, pixelOffset++) - { + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++, pixelOffset++) { int targetLabelValue = _outputSlice[pixelOffset]; if (targetLabelValue == 0) @@ -1329,17 +1168,14 @@ public class ConnectedComponents extends EzPlug implements Block sliceImage.setDataXY(0, _outputSlice); } - return new ArrayList<ConnectedComponent>(componentsMap.values()); + return new ArrayList<>(componentsMap.values()); } - public static DetectionResult convertToDetectionResult(Map<Integer, List<ConnectedComponent>> detections, - Sequence sequence) - { + public static DetectionResult convertToDetectionResult(Map<Integer, List<ConnectedComponent>> detections, Sequence sequence) { DetectionResult detectionResult = new DetectionResult(); for (Integer t : detections.keySet()) - for (ConnectedComponent cc : detections.get(t)) - { + for (ConnectedComponent cc : detections.get(t)) { // TODO: add points information Spot spot = new Spot(cc.getMassCenter().x, cc.getMassCenter().y, cc.getMassCenter().z); detectionResult.addDetection(t, spot); @@ -1353,17 +1189,12 @@ public class ConnectedComponents extends EzPlug implements Block /** * Fill the channel 0 of the given sequence with the list of components sorted using the given * comparator. The method does nothing if the given comparator is null - * - * @param output - * a sequence of type INT - * @param components - * Map of List of ConnectedComponent - * @param comparator - * Comparator of ConnectedComponents + * + * @param output a sequence of type INT + * @param components Map of List of ConnectedComponent + * @param comparator Comparator of ConnectedComponents */ - public static void createLabeledSequence(Sequence output, Map<Integer, List<ConnectedComponent>> components, - Comparator<ConnectedComponent> comparator) - { + public static void createLabeledSequence(Sequence output, Map<Integer, List<ConnectedComponent>> components, Comparator<ConnectedComponent> comparator) { if (comparator == null) return; @@ -1371,14 +1202,11 @@ public class ConnectedComponents extends EzPlug implements Block int[] ids = new int[output.getSizeC()]; Arrays.fill(ids, 1); - for (Integer t : components.keySet()) - { - for (int c = 0; c < output.getSizeC(); c++) - { + for (Integer t : components.keySet()) { + for (int c = 0; c < output.getSizeC(); c++) { // retrieve all objects in that channel - ArrayList<ConnectedComponent> cComponents = new ArrayList<ConnectedComponent>(); - for (ConnectedComponent cc : components.get(t)) - { + ArrayList<ConnectedComponent> cComponents = new ArrayList<>(); + for (ConnectedComponent cc : components.get(t)) { if (cc.getC() == -1 || cc.getC() == c) cComponents.add(cc); } @@ -1387,21 +1215,16 @@ public class ConnectedComponents extends EzPlug implements Block Arrays.sort(ccs, comparator); final boolean outputVolatile = output.isVolatile(); - try - { + try { int[][] data = output.getDataXYZAsInt(t, c); - for (ConnectedComponent cc : ccs) - { - for (Point3i pt : cc) - { + for (ConnectedComponent cc : ccs) { + for (Point3i pt : cc) { data[pt.z][pt.y * width + pt.x] = ids[c]; } ids[c]++; } - } - finally - { + } finally { // restore back volatile state output.setVolatile(outputVolatile); }