diff --git a/pom.xml b/pom.xml index 107ccb9feff3336eba517ca04d328fd3cec64b6a..d7827329d567dc7eee839ff9cea5f39b754e5b37 100644 --- a/pom.xml +++ b/pom.xml @@ -7,11 +7,11 @@ <parent> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>pom-icy</artifactId> - <version>3.0.0-a.1</version> + <version>3.0.0-a.3</version> </parent> <artifactId>connected-components</artifactId> - <version>5.0.0-a.1</version> + <version>5.0.0-a.2</version> <name>ConnectedComponents</name> <description> @@ -47,12 +47,4 @@ <artifactId>jama</artifactId> </dependency> </dependencies> - - <repositories> - <repository> - <id>icy</id> - <url>https://nexus-icy.pasteur.cloud/repository/icy/</url> - </repository> - </repositories> - </project> \ No newline at end of file diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponent.java b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponent.java index fa8fa12154b7508e4079ba0d7f4cee12fcd6db53..9187727616c3debf5ee26b87ca72c40c4228793b 100644 --- a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponent.java +++ b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024. Institut Pasteur. + * Copyright (c) 2010-2025. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -18,7 +18,6 @@ package plugins.adufour.connectedcomponents; - import org.bioimageanalysis.extension.kernel.roi.roi2d.ROI2DArea; import org.bioimageanalysis.extension.kernel.roi.roi3d.ROI3DArea; import org.bioimageanalysis.icy.common.collection.array.Array1DUtil; @@ -174,7 +173,7 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> { * @param t int * @return an array containing the average intensity of the component in each band */ - public double[] computeMeanIntensity(final Sequence sequence, final int t) { + public double[] computeMeanIntensity(final @NotNull Sequence sequence, final int t) { final double[] intensitySum = new double[sequence.getSizeC()]; for (final Point3i point : points) { @@ -249,7 +248,7 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> { * @param t int * @return an array containing the average intensity of the component in each band */ - public double[] computeMaxIntensity(final Sequence sequence, final int t) { + public double[] computeMaxIntensity(final @NotNull Sequence sequence, final int t) { final double[] maxIntensity = new double[sequence.getSizeC()]; for (final Point3i point : points) { @@ -529,7 +528,7 @@ public class ConnectedComponent extends Detection implements Iterable<Point3i> { * @return true if this component intersects (e.g. has at least one voxel overlapping with) the * specified component */ - public boolean intersects(final ConnectedComponent component) { + public boolean intersects(final @NotNull ConnectedComponent component) { final int thisSize = getSize(); final int componentSize = component.getSize(); diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponentDescriptor.java b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponentDescriptor.java index abe0eeb05d47329ff0d3a403bada3671896933b6..b681d04c55ef603d023e3292de01c414b3ee88ce 100644 --- a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponentDescriptor.java +++ b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponentDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024. Institut Pasteur. + * Copyright (c) 2010-2025. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -22,9 +22,12 @@ import Jama.EigenvalueDecomposition; import Jama.Matrix; import org.bioimageanalysis.icy.common.type.DataType; import org.bioimageanalysis.icy.extension.plugin.abstract_.Plugin; -import org.bioimageanalysis.icy.extension.plugin.interface_.PluginBundled; +import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginIcon; +import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginName; import org.bioimageanalysis.icy.model.image.IcyBufferedImage; import org.bioimageanalysis.icy.model.sequence.Sequence; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import plugins.adufour.blocks.lang.Block; import plugins.adufour.blocks.util.VarList; import plugins.adufour.quickhull.QuickHull2D; @@ -38,7 +41,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class ConnectedComponentDescriptor extends Plugin implements PluginBundled, Block { +@IcyPluginName("Connected Components Block") +@IcyPluginIcon(path = "/plugins/adufour/connectedcomponents/Connected_Components.png") +public class ConnectedComponentDescriptor extends Plugin implements Block { Var<ConnectedComponent> varCC = new Var<>("Connected component", ConnectedComponent.class); VarDouble perimeter = new VarDouble("perimeter", 0.0); @@ -54,17 +59,12 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle VarDouble shortAxisZ = new VarDouble("short diameter (Z)", 0.0); @Override - public String getMainPluginClassName() { - return ConnectedComponents.class.getName(); - } - - @Override - public void declareInput(final VarList inputMap) { + public void declareInput(final @NotNull VarList inputMap) { inputMap.add("component", varCC); } @Override - public void declareOutput(final VarList outputMap) { + public void declareOutput(final @NotNull VarList outputMap) { outputMap.add("perimeter", perimeter); outputMap.add("long diameter", longAxis); outputMap.add("short diameter", shortAxis); @@ -168,7 +168,11 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle * @param bsCenter the computed center of the bounding sphere * @param bsRadius the computed radius of the bounding sphere */ - public void computeBoundingSphere(final ConnectedComponent cc, final Point3d bsCenter, final VarDouble bsRadius) { + public void computeBoundingSphere( + final @NotNull ConnectedComponent cc, + final @NotNull Point3d bsCenter, + final @NotNull VarDouble bsRadius + ) { bsCenter.set(cc.getMassCenter()); bsRadius.setValue(cc.getMaxDistanceTo(bsCenter)); } @@ -181,7 +185,11 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle * extracted contour * @return The 3D perimeter (or 3D surface) of this component */ - public double computePerimeter(final ConnectedComponent cc, final ArrayList<Point3i> contourPoints, final Sequence outputSequence) { + public double computePerimeter( + final ConnectedComponent cc, + final ArrayList<Point3i> contourPoints, + final Sequence outputSequence + ) { double perimeter = 0; if (contourPoints != null) @@ -368,7 +376,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle * object. The 2 values are returned together because their computation is simultaneous * (in the 3D case only) */ - public double[] computeConvexAreaAndVolume(final ConnectedComponent cc) { + public double[] computeConvexAreaAndVolume(final @NotNull ConnectedComponent cc) { int i = 0; final int n = cc.getSize(); if (n == 1) @@ -447,7 +455,7 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle * @param r the moment order along Z (set to 0 if the object is 2D) * @return the geometric moment */ - public double computeGeometricMoment(final ConnectedComponent cc, final int p, final int q, final int r) { + public double computeGeometricMoment(final @NotNull ConnectedComponent cc, final int p, final int q, final int r) { double moment = 0; final Point3d center = cc.getMassCenter(); @@ -478,8 +486,13 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle * @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(final ConnectedComponent cc, final Point3d center, final Point3d radii, final Vector3d[] eigenVectors, - final double[] equation) throws IllegalArgumentException { + public void computeEllipse( + final @NotNull ConnectedComponent cc, + final Point3d center, + final Point3d radii, + final Vector3d[] eigenVectors, + final double[] equation + ) throws IllegalArgumentException { final int nPoints = cc.getSize(); if (nPoints < 9) { throw new IllegalArgumentException("Too few points; need at least 9 to calculate a unique ellipsoid"); @@ -560,8 +573,13 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle * 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(final ConnectedComponent cc, final Point2d center, final Point2d radii, final VarDouble angle, final double[] equation) - throws RuntimeException { + public void computeEllipse( + final @NotNull ConnectedComponent cc, + final Point2d center, + final Point2d radii, + final VarDouble angle, + final double[] equation + ) throws RuntimeException { final Point3i[] points = cc.getPoints(); final Point3d ccenter = cc.getMassCenter(); @@ -595,8 +613,11 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle final Matrix M = S1.plus(S2.times(T)); final double[][] m = M.getArray(); - final double[][] n = {{m[2][0] / 2, m[2][1] / 2, m[2][2] / 2}, {-m[1][0], -m[1][1], -m[1][2]}, - {m[0][0] / 2, m[0][1] / 2, m[0][2] / 2}}; + final double[][] n = { + {m[2][0] / 2, m[2][1] / 2, m[2][2] / 2}, + {-m[1][0], -m[1][1], -m[1][2]}, + {m[0][0] / 2, m[0][1] / 2, m[0][2] / 2} + }; final Matrix N = new Matrix(n); @@ -692,7 +713,8 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle return minBB.z == maxBB.z; } - private Matrix diag(final Matrix matrix) { + @Contract("_ -> new") + private @NotNull Matrix diag(final @NotNull Matrix matrix) { final int min = Math.min(matrix.getRowDimension(), matrix.getColumnDimension()); final double[][] diag = new double[min][1]; for (int i = 0; i < min; i++) { @@ -701,7 +723,8 @@ public class ConnectedComponentDescriptor extends Plugin implements PluginBundle return new Matrix(diag); } - private Matrix ones(final int m, final int n) { + @Contract("_, _ -> new") + private @NotNull Matrix ones(final int m, final int n) { final double[][] array = new double[m][n]; for (final double[] row : array) Arrays.fill(row, 1.0); diff --git a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents.java b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents.java index f8113aab490c93a7ff99014e453cf3371815f2e1..2d6690ea376171c8ffc2530ac411438654dae07c 100644 --- a/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents.java +++ b/src/main/java/plugins/adufour/connectedcomponents/ConnectedComponents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024. Institut Pasteur. + * Copyright (c) 2010-2025. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -40,6 +40,8 @@ import org.bioimageanalysis.icy.model.sequence.SequenceDataIterator; import org.bioimageanalysis.icy.model.sequence.VolumetricImage; import org.bioimageanalysis.icy.model.swimmingPool.SwimmingObject; import org.bioimageanalysis.icy.system.IcyHandledException; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import plugins.adufour.blocks.lang.Block; import plugins.adufour.blocks.util.VarList; import plugins.adufour.ezplug.*; @@ -58,7 +60,7 @@ import java.io.IOException; import java.util.*; @IcyPluginName("Connected Components") -@IcyPluginIcon(path = "/icon/Connected_Components.png") +@IcyPluginIcon(path = "/plugins/adufour/connectedcomponents/Connected_Components.png") public class ConnectedComponents extends EzPlug implements Block { /** * List of extraction methods suitable for the Connected Components plugin @@ -517,6 +519,7 @@ public class ConnectedComponents extends EzPlug implements Block { * @param value the pixel value * @param label the label value */ + @Contract(pure = true) Label(final double value, final int label) { this.imageValue = value; this.targetLabelValue = label; @@ -525,6 +528,7 @@ public class ConnectedComponents extends EzPlug implements Block { /** * Retrieves the final object label (recursively) */ + @Contract(pure = true) int getFinalLabelValue() { return targetLabel == null ? targetLabelValue : targetLabel.getFinalLabelValue(); } @@ -542,7 +546,11 @@ public class ConnectedComponents extends EzPlug implements Block { * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(final Sequence inputSequence, final Sequence labeledSequence) { + @Contract("null, _ -> fail") + public static @NotNull Map<Integer, List<ConnectedComponent>> extractConnectedComponents( + final Sequence inputSequence, + final Sequence labeledSequence + ) { return extractConnectedComponents(inputSequence, false, 0, Integer.MAX_VALUE, labeledSequence); } @@ -560,7 +568,13 @@ public class ConnectedComponents extends EzPlug implements Block { * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(final Sequence inputSequence, final int minSize, final int maxSize, final Sequence labeledSequence) { + @Contract("null, _, _, _ -> fail") + public static @NotNull Map<Integer, List<ConnectedComponent>> extractConnectedComponents( + final Sequence inputSequence, + final int minSize, + final int maxSize, + final Sequence labeledSequence + ) { return extractConnectedComponents(inputSequence, false, minSize, maxSize, labeledSequence); } @@ -579,10 +593,22 @@ public class ConnectedComponents extends EzPlug implements Block { * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(final Sequence inputSequence, final boolean isInputLabeled, final int minSize, final int maxSize, final Sequence labeledSequence) { - return extractConnectedComponents(inputSequence, 0, - isInputLabeled ? ExtractionType.BACKGROUND_LABELED : ExtractionType.BACKGROUND, minSize, maxSize, - labeledSequence); + @Contract("null, _, _, _, _ -> fail") + public static @NotNull Map<Integer, List<ConnectedComponent>> extractConnectedComponents( + final Sequence inputSequence, + final boolean isInputLabeled, + final int minSize, + final int maxSize, + final Sequence labeledSequence + ) { + return extractConnectedComponents( + inputSequence, + 0, + isInputLabeled ? ExtractionType.BACKGROUND_LABELED : ExtractionType.BACKGROUND, + minSize, + maxSize, + labeledSequence + ); } /** @@ -601,8 +627,26 @@ public class ConnectedComponents extends EzPlug implements Block { * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents(final Sequence inputSequence, final double value, final ExtractionType type, final int minSize, final int maxSize, final Sequence labeledSequence) { - return extractConnectedComponents(inputSequence, value, type, false, false, false, minSize, maxSize, labeledSequence); + @Contract("null, _, _, _, _, _ -> fail") + public static @NotNull Map<Integer, List<ConnectedComponent>> extractConnectedComponents( + final Sequence inputSequence, + final double value, + final ExtractionType type, + final int minSize, + final int maxSize, + final Sequence labeledSequence + ) { + return extractConnectedComponents( + inputSequence, + value, + type, + false, + false, + false, + minSize, + maxSize, + labeledSequence + ); } /** @@ -627,8 +671,17 @@ public class ConnectedComponents extends EzPlug implements Block { * @see ExtractionType * @see ConnectedComponent */ - public static Map<Integer, List<ConnectedComponent>> extractConnectedComponents( - final Sequence inputSequence, final double value, final ExtractionType type, final boolean noEdgeX, final boolean noEdgeY, final boolean noEdgeZ, final int minSize, final int maxSize, Sequence labeledSequence + @Contract("null, _, _, _, _, _, _, _, _ -> fail") + public static @NotNull Map<Integer, List<ConnectedComponent>> extractConnectedComponents( + final Sequence inputSequence, + final double value, + final ExtractionType type, + final boolean noEdgeX, + final boolean noEdgeY, + final boolean noEdgeZ, + final int minSize, + final int maxSize, + Sequence labeledSequence ) { if (inputSequence == null || inputSequence.getSizeT() == 0) throw new IllegalArgumentException("Cannot extract connected components from an empty sequence !"); @@ -688,10 +741,28 @@ public class ConnectedComponents extends EzPlug implements Block { * @see ExtractionType * @see ConnectedComponent */ - public static List<ConnectedComponent> extractConnectedComponents( - final VolumetricImage stack, final double value, final ExtractionType type, final boolean noEdgeX, final boolean noEdgeY, final boolean noEdgeZ, final int minSize, final int maxSize + @Contract("_, _, _, _, _, _, _, _ -> new") + public static @NotNull List<ConnectedComponent> extractConnectedComponents( + final VolumetricImage stack, + final double value, + final ExtractionType type, + final boolean noEdgeX, + final boolean noEdgeY, + final boolean noEdgeZ, + final int minSize, + final int maxSize ) { - return extractConnectedComponents(stack, value, type, noEdgeX, noEdgeY, noEdgeZ, minSize, maxSize, new VolumetricImage()); + return extractConnectedComponents( + stack, + value, + type, + noEdgeX, + noEdgeY, + noEdgeZ, + minSize, + maxSize, + new VolumetricImage() + ); } /** @@ -719,8 +790,17 @@ public class ConnectedComponents extends EzPlug implements Block { * @see ExtractionType * @see ConnectedComponent */ - public static List<ConnectedComponent> extractConnectedComponents( - final VolumetricImage stack, final double value, final ExtractionType type, final boolean noEdgeX, final boolean noEdgeY, final boolean noEdgeZ, final int minSize, final int maxSize, final VolumetricImage labeledStack + @Contract("_, _, _, _, _, _, _, _, _ -> new") + public static @NotNull List<ConnectedComponent> extractConnectedComponents( + final @NotNull VolumetricImage stack, + final double value, + final ExtractionType type, + final boolean noEdgeX, + final boolean noEdgeY, + final boolean noEdgeZ, + final int minSize, + final int maxSize, + final VolumetricImage labeledStack ) throws NullPointerException { final int width = stack.getFirstImage().getSizeX(); final int height = stack.getFirstImage().getSizeY(); @@ -1181,8 +1261,10 @@ public class ConnectedComponents extends EzPlug implements Block { return new ArrayList<>(componentsMap.values()); } - public static DetectionResult convertToDetectionResult(final Map<Integer, List<ConnectedComponent>> detections, final Sequence sequence) { - + public static @NotNull DetectionResult convertToDetectionResult( + final @NotNull Map<Integer, List<ConnectedComponent>> detections, + final Sequence sequence + ) { final DetectionResult detectionResult = new DetectionResult(); for (final Integer t : detections.keySet()) for (final ConnectedComponent cc : detections.get(t)) { @@ -1204,7 +1286,11 @@ public class ConnectedComponents extends EzPlug implements Block { * @param components Map of List of ConnectedComponent * @param comparator Comparator of ConnectedComponents */ - public static void createLabeledSequence(final Sequence output, final Map<Integer, List<ConnectedComponent>> components, final Comparator<ConnectedComponent> comparator) { + public static void createLabeledSequence( + final Sequence output, + final Map<Integer, List<ConnectedComponent>> components, + final Comparator<ConnectedComponent> comparator + ) { if (comparator == null) return; diff --git a/src/main/resources/icon/Connected_Components.png b/src/main/resources/plugins/adufour/connectedcomponents/Connected_Components.png similarity index 100% rename from src/main/resources/icon/Connected_Components.png rename to src/main/resources/plugins/adufour/connectedcomponents/Connected_Components.png