From 90a52b84a7f4b378f9981331aa054f01209ab0b1 Mon Sep 17 00:00:00 2001 From: Thomas <thomas.musset@pasteur.fr> Date: Sat, 6 Jul 2024 18:24:02 +0200 Subject: [PATCH] updated pom to v2.0.0-a.1, fix classes accordingly to new architecture, added icon, updated .gitignore --- .gitignore | 41 ++- pom.xml | 41 +-- .../roi/quantify/PathIntensityProfiler.java | 324 +++++++++--------- .../resources/path-intensity-profiler.png | Bin 0 -> 9625 bytes 4 files changed, 206 insertions(+), 200 deletions(-) create mode 100644 src/main/resources/path-intensity-profiler.png diff --git a/.gitignore b/.gitignore index 3d47f98..57f16fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,41 @@ -.idea/ +/build* +/workspace +setting.xml +release/ target/ -.settings/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +icy.log + +### IntelliJ IDEA ### +.idea/ +*.iws *.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath .project -.classpath \ No newline at end of file +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +**/.DS_Store +Icon? \ No newline at end of file diff --git a/pom.xml b/pom.xml index c87f039..cff32f4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,23 +6,21 @@ <!-- Inherited Icy Parent POM --> <parent> - <artifactId>pom-icy</artifactId> + <artifactId>pom-icy</artifactId> <groupId>org.bioimageanalysis.icy</groupId> - <version>2.1.0</version> - </parent> + <version>3.0.0-a.1</version> + </parent> <!-- Project Information --> <artifactId>path-intensity-profiler</artifactId> - <version>1.2.3</version> - - <packaging>jar</packaging> + <version>2.0.0-a.1</version> <name>Path Intensity Profiler</name> <description> This plugin will compute the intensity profile of ROI contour along Z (3D) and T (timelaps) dimension. It generates a workbook document containing all the intensity values which can be used to display graph or be saved as XLSX or CSV format. The plugin is compatible with Protocols. </description> - <url>http://icy.bioimageanalysis.org/plugin/path-intensity-profil/</url> + <url>https://icy.bioimageanalysis.org/plugin/path-intensity-profil/</url> <inceptionYear>2020</inceptionYear> <organization> @@ -56,40 +54,24 @@ </developer> </developers> - <!-- Project properties --> - <properties> - - </properties> - - <profiles> - <profile> - <id>icy-plugin</id> - <activation> - <activeByDefault>true</activeByDefault> - </activation> - </profile> - </profiles> - <!-- List of project's dependencies --> <dependencies> - <!-- The core of Icy --> <dependency> <groupId>org.bioimageanalysis.icy</groupId> - <artifactId>icy-kernel</artifactId> - <version>${icy-kernel.version}</version> + <artifactId>kernel-extensions</artifactId> </dependency> - - <!-- The EzPlug library, simplifies writing UI for Icy plugins. --> <dependency> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>ezplug</artifactId> - <version>${ezplug.version}</version> + </dependency> + <dependency> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>protocols</artifactId> </dependency> <dependency> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>workbooks</artifactId> - <version>${workbooks.version}</version> </dependency> </dependencies> @@ -97,8 +79,7 @@ <repositories> <repository> <id>icy</id> - <name>Icy's Nexus</name> - <url>https://icy-nexus.pasteur.fr/repository/Icy/</url> + <url>https://nexus-icy.pasteur.cloud/repository/icy/</url> </repository> </repositories> </project> diff --git a/src/main/java/plugins/stef/roi/quantify/PathIntensityProfiler.java b/src/main/java/plugins/stef/roi/quantify/PathIntensityProfiler.java index 462a505..4d9beae 100644 --- a/src/main/java/plugins/stef/roi/quantify/PathIntensityProfiler.java +++ b/src/main/java/plugins/stef/roi/quantify/PathIntensityProfiler.java @@ -1,30 +1,47 @@ -package plugins.stef.roi.quantify; - -import java.awt.Dimension; -import java.awt.Point; -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.List; +/* + * Copyright (c) 2010-2024. Institut Pasteur. + * + * This file is part of Icy. + * Icy is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Icy is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Icy. If not, see <https://www.gnu.org/licenses/>. + */ -import javax.swing.JComponent; +package plugins.stef.roi.quantify; import org.apache.poi.xssf.usermodel.XSSFWorkbook; - -import icy.gui.dialog.MessageDialog; -import icy.gui.frame.progress.FailedAnnounceFrame; -import icy.math.Line3DIterator; -import icy.math.MathUtil; -import icy.roi.BooleanMask2D; -import icy.roi.ROI; -import icy.roi.ROI2D; -import icy.roi.ROI3D; -import icy.sequence.Sequence; -import icy.system.IcyExceptionHandler; -import icy.type.collection.CollectionUtil; -import icy.type.geom.Line3D; -import icy.type.point.Point3D; -import icy.type.point.Point4D; -import icy.type.rectangle.Rectangle5D; +import org.bioimageanalysis.extension.kernel.roi.roi2d.*; +import org.bioimageanalysis.extension.kernel.roi.roi3d.ROI3DLine; +import org.bioimageanalysis.extension.kernel.roi.roi3d.ROI3DPoint; +import org.bioimageanalysis.extension.kernel.roi.roi3d.ROI3DPolyLine; +import org.bioimageanalysis.extension.kernel.roi.roi3d.ROI3DShape; +import org.bioimageanalysis.icy.common.collection.CollectionUtil; +import org.bioimageanalysis.icy.common.geom.line.Line3D; +import org.bioimageanalysis.icy.common.geom.line.Line3DIterator; +import org.bioimageanalysis.icy.common.geom.point.Point3D; +import org.bioimageanalysis.icy.common.geom.point.Point4D; +import org.bioimageanalysis.icy.common.geom.rectangle.Rectangle5D; +import org.bioimageanalysis.icy.common.math.MathUtil; +import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginIcon; +import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginName; +import org.bioimageanalysis.icy.gui.dialog.MessageDialog; +import org.bioimageanalysis.icy.model.roi.ROI; +import org.bioimageanalysis.icy.model.roi.ROI2D; +import org.bioimageanalysis.icy.model.roi.ROI3D; +import org.bioimageanalysis.icy.model.roi.mask.BooleanMask2D; +import org.bioimageanalysis.icy.model.sequence.Sequence; +import org.bioimageanalysis.icy.system.logging.IcyLogger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import plugins.adufour.blocks.lang.Block; import plugins.adufour.blocks.util.VarList; import plugins.adufour.ezplug.EzGUI; @@ -38,34 +55,31 @@ import plugins.adufour.vars.lang.VarWorkbook; import plugins.adufour.workbooks.IcySpreadSheet; import plugins.adufour.workbooks.Workbooks; import plugins.adufour.workbooks.Workbooks.WorkbookFormat; -import plugins.kernel.roi.roi2d.ROI2DLine; -import plugins.kernel.roi.roi2d.ROI2DPoint; -import plugins.kernel.roi.roi2d.ROI2DPolyLine; -import plugins.kernel.roi.roi2d.ROI2DPolygon; -import plugins.kernel.roi.roi2d.ROI2DRectangle; -import plugins.kernel.roi.roi2d.ROI2DShape; -import plugins.kernel.roi.roi3d.ROI3DLine; -import plugins.kernel.roi.roi3d.ROI3DPoint; -import plugins.kernel.roi.roi3d.ROI3DPolyLine; -import plugins.kernel.roi.roi3d.ROI3DShape; + +import javax.swing.*; +import java.awt.*; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; /** * Path profiler plugin class.<br> * Computes intensity profil along path (contour for area ROI) for the given list of ROI and return result in XLSX workbook format * where we have one sheet per ROI. - * - * @author Stephane + * + * @author Stephane Dallongeville */ -public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable -{ +@IcyPluginName("Path Intensity Profiler") +@IcyPluginIcon(path = "/path-intensity-profiler.png") +public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable { // VAR public final VarSequence varSequence; public final VarROIArray varRois; public final EzVarBoolean varRealUnit; public final VarWorkbook varWorkbook; - public PathIntensityProfiler() - { + public PathIntensityProfiler() { super(); varSequence = new VarSequence("Sequence", null); @@ -75,8 +89,7 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable } @Override - protected void initialize() - { + protected void initialize() { final WorkbookEditor viewer = (WorkbookEditor) varWorkbook.createVarViewer(); viewer.setEnabled(true); @@ -90,71 +103,60 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable } @Override - public void declareInput(VarList inputMap) - { + public void declareInput(@NotNull final VarList inputMap) { inputMap.add("sequence", varSequence); inputMap.add("rois", varRois); inputMap.add("realUnit", varRealUnit.getVariable()); } @Override - public void declareOutput(VarList outputMap) - { + public void declareOutput(@NotNull final VarList outputMap) { outputMap.add("workbook", varWorkbook); } @Override - public void clean() - { + public void clean() { // set empty workbook to release resources varWorkbook.setValue(Workbooks.createEmptyWorkbook(WorkbookFormat.XLSX)); } @Override - protected void execute() - { + protected void execute() { // set empty workbook by default (also release resources from previous run) varWorkbook.setValue(Workbooks.createEmptyWorkbook(WorkbookFormat.XLSX)); // interactive mode (not in protocol) - if (!isHeadLess()) - { + if (!isHeadLess()) { final Sequence seq = getActiveSequence(); // set variables - if (seq != null) - { + if (seq != null) { varSequence.setValue(seq); varRois.setValue(seq.getROIs().toArray(new ROI[0])); } - else - { + else { // inform user - MessageDialog.showDialog("You need to open an image containing ROI(s).", - MessageDialog.INFORMATION_MESSAGE); + MessageDialog.showDialog("You need to open an image containing ROI(s).", MessageDialog.INFORMATION_MESSAGE); return; } } final Sequence sequence = varSequence.getValue(); - if (sequence != null) - { + if (sequence != null) { final ROI[] rois = varRois.getValue(); - final List<ROI> validRois = new ArrayList<ROI>(); + final List<ROI> validRois = new ArrayList<>(); // we can only compute path intensity on path ROI or 2D ROI - for (ROI roi : rois) - { + for (final ROI roi : rois) { if (roi instanceof ROI2D) validRois.add(roi); - // else if ((roi instanceof ROI3DPoint) || (roi instanceof ROI3DLine) || (roi instanceof ROI3DPolyLine)) + // else if ((roi instanceof ROI3DPoint) || (roi instanceof ROI3DLine) || (roi instanceof ROI3DPolyLine)) else if (roi instanceof ROI3D) validRois.add(roi); } - if (validRois.size() == 0) - { + if (validRois.isEmpty()) { // inform user if (!isHeadLess()) MessageDialog.showDialog( @@ -167,25 +169,20 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable if (!isHeadLess()) getUI().setProgressBarMessage("Computing..."); - try - { + try { // set result varWorkbook.setValue( - getPathIntensityProfil(sequence, validRois, varRealUnit.getValue().booleanValue(), getUI())); + getPathIntensityProfil(sequence, validRois, varRealUnit.getValue(), getUI())); } - catch (IllegalArgumentException e) - { - IcyExceptionHandler.handleException(e, true); + catch (final IllegalArgumentException e) { + IcyLogger.error(this.getClass(), e); } - catch (InterruptedException e) - { - new FailedAnnounceFrame("Path intensity profile process interrupted.."); + catch (final InterruptedException e) { + IcyLogger.error(this.getClass(), e, "Path intensity profile process interrupted."); } - finally - { + finally { // interactive mode - if (!isHeadLess()) - { + if (!isHeadLess()) { getUI().setProgressBarMessage("Done"); getUI().setProgressBarValue(0); } @@ -195,13 +192,13 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable /** * Computes intensity profil along path (contour for area ROI) for the given list of ROI. - * + * * @return result in a XLSX workbook where we have one sheet per ROI. * @throws InterruptedException */ - private static XSSFWorkbook getPathIntensityProfil(Sequence sequence, List<ROI> rois, boolean useRealUnit, EzGUI ui) - throws InterruptedException - { + @SuppressWarnings({"unchecked", "rawtypes"}) + private static XSSFWorkbook getPathIntensityProfil(final Sequence sequence, @NotNull final List<ROI> rois, final boolean useRealUnit, final EzGUI ui) + throws InterruptedException { final XSSFWorkbook result = (XSSFWorkbook) Workbooks.createEmptyWorkbook(WorkbookFormat.XLSX); // nothing to do @@ -214,15 +211,13 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable final Point4D.Double unitScale = new Point4D.Double(); - if (useRealUnit) - { + if (useRealUnit) { unitScale.x = sequence.getPixelSizeX(); unitScale.y = sequence.getPixelSizeY(); unitScale.z = sequence.getPixelSizeZ(); unitScale.t = sequence.getTimeInterval(); } - else - { + else { unitScale.x = 1d; unitScale.y = 1d; unitScale.z = 1d; @@ -233,11 +228,10 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable int roiIndex = 0; // can process now - for (ROI roi : rois) - { + for (final ROI roi : rois) { // create a sheet for each ROI final IcySpreadSheet sh = new IcySpreadSheet(result.createSheet( - String.format("S%04d - ", Integer.valueOf(roiIndex)) + roi.getName().replace(':', '_'))); + String.format("S%04d - ", roiIndex) + roi.getName().replace(':', '_'))); // write columns headers int indCol = 0; @@ -254,11 +248,9 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable // start int row = 1; - for (int t = 0; t < sizeT; t++) - { + for (int t = 0; t < sizeT; t++) { // in case of 2D ROI, we need to handle the specific T = -1 (ALL) - if (roi instanceof ROI2D) - { + if (roi instanceof ROI2D) { final int roiT = ((ROI2D) roi).getT(); // ROI not on current T position ? --> next @@ -266,8 +258,7 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable continue; } // in case of 3D ROI, we need to handle the specific T = -1 (ALL) - else if (roi instanceof ROI3D) - { + else if (roi instanceof ROI3D) { final int roiT = ((ROI3D) roi).getT(); // ROI not on current T position ? --> next @@ -278,11 +269,9 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable else if ((roiBounds.getMinT() > t) || (roiBounds.getMaxT() < t)) continue; - for (int z = 0; z < sizeZ; z++) - { + for (int z = 0; z < sizeZ; z++) { // in case of 2D ROI, we need to handle the specific Z = -1 (ALL) - if (roi instanceof ROI2D) - { + if (roi instanceof ROI2D) { final int roiZ = ((ROI2D) roi).getZ(); // ROI not on current Z position ? --> next @@ -300,15 +289,13 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable // point index int ptIndex = 0; - for (int c = 0, zStartRow = row; c < sizeC; c++) - { + for (int c = 0, zStartRow = row; c < sizeC; c++) { // ROI does not contain current C position ? --> next if ((roiBounds.getMinC() > c) || (roiBounds.getMaxC() < c)) continue; // display progress - if (ui != null) - { + if (ui != null) { // compute progress bar avoid integer type loss double progress = roiIndex; progress *= sizeT * sizeZ * sizeC; @@ -330,58 +317,58 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable // 3D line path ROI ? if ((roi instanceof ROI3DPoint) || (roi instanceof ROI3DLine) || (roi instanceof ROI3DPolyLine)) pts = ((ROI3DShape) roi).getPoints(); - // 2D straight line path / area ROI (we shouldn't consider Arc / Ellipse shape ROI here) ? + // 2D straight line path / area ROI (we shouldn't consider Arc / Ellipse shape ROI here) ? else if ((roi instanceof ROI2DPoint) || (roi instanceof ROI2DLine) || (roi instanceof ROI2DPolyLine) || (roi instanceof ROI2DPolygon) || (roi instanceof ROI2DRectangle)) pts = ((ROI2DShape) roi).getPoints(); - else - { + else { // initialize - pts = new ArrayList(); + pts = new ArrayList<>(); // no link here interpolate = false; // 2D ROI so we can use the contour of the mask - if (roi instanceof ROI2D) - { + if (roi instanceof ROI2D) { /// iterate over all component (separate object if any) - for (BooleanMask2D mask : ((ROI2D) roi).getBooleanMask(true).getComponents()) + for (final BooleanMask2D mask : ((ROI2D) roi).getBooleanMask(true).getComponents()) // add points in contour order pts.addAll(mask.getConnectedContourPoints()); } // 3D ROI so we can use the surface area points - else if (roi instanceof ROI3D) - { + else if (roi instanceof ROI3D) { // add points in whatever order - pts.addAll( - CollectionUtil.asList(((ROI3D) roi).getBooleanMask(true).getContourPoints())); + pts.addAll(CollectionUtil.asList(((ROI3D) roi).getBooleanMask(true).getContourPoints())); } // not supported - else - { - + else { + IcyLogger.warn(PathIntensityProfiler.class, "Not supported."); } } // control points that need interpolation in-between ? - if (interpolate) - { + if (interpolate) { // get the first obj - Point3D startPt = getPoint3D(pts.get(0), z); + Point3D startPt = getPoint3D(pts.getFirst(), z); // specific case where we have only a single point if (pts.size() == 1) - writeRow(sh, row++, ptIndex++, t, c, sequence, startPt, unitScale); - else - { + writeRow( + sh, + row++, + ptIndex++, + t, + c, + sequence, + Objects.requireNonNull(startPt), + unitScale + ); + else { // otherwise we use a special way to interpolate through 2 controls points - for (int i = 1; i < pts.size(); i++) - { + for (int i = 1; i < pts.size(); i++) { final Point3D endPt = getPoint3D(pts.get(i), z); // get line iterator - final Line3DIterator lineIt = new Line3DIterator(new Line3D(startPt, endPt), 1d, - false); + final Line3DIterator lineIt = new Line3DIterator(new Line3D(startPt, endPt), 1d, false); // iterate over line points while (lineIt.hasNext()) @@ -393,10 +380,18 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable } } // contour points ? - else - { - for (int i = 0; i < pts.size(); i++) - writeRow(sh, row++, ptIndex++, t, c, sequence, getPoint3D(pts.get(i), z), unitScale); + else { + for (final Object pt : pts) + writeRow( + sh, + row++, + ptIndex++, + t, + c, + sequence, + Objects.requireNonNull(getPoint3D(pt, z)), + unitScale + ); } } } @@ -411,50 +406,45 @@ public class PathIntensityProfiler extends EzPlug implements Block, EzStoppable /** * Computes intensity profil along path (contour for area ROI) for the given list of ROI.<br> - * + * * @return result in a XLSX workbook where we have one sheet per ROI. * @throws InterruptedException */ - public static XSSFWorkbook getPathIntensityProfil(Sequence sequence, List<ROI> rois, boolean useRealUnit) - throws InterruptedException - { + public static XSSFWorkbook getPathIntensityProfil(final Sequence sequence, final List<ROI> rois, final boolean useRealUnit) throws InterruptedException { return getPathIntensityProfil(sequence, rois, useRealUnit, null); } - private static Point3D getPoint3D(Object obj, int curZ) - { - final Point3D result; - - if (obj instanceof Point) - { - final Point pt2d = (Point) obj; - result = new Point3D.Double(pt2d.getX(), pt2d.getY(), curZ); - } - else if (obj instanceof Point2D) - { - final Point2D pt2d = (Point2D) obj; - result = new Point3D.Double(pt2d.getX(), pt2d.getY(), curZ); - } - else if (obj instanceof Point3D) - result = (Point3D) obj; - else - result = null; - - return result; + private static @Nullable Point3D getPoint3D(final Object obj, final int curZ) { + return switch (obj) { + case final Point pt2d -> new Point3D.Double(pt2d.getX(), pt2d.getY(), curZ); + case final Point2D pt2d -> new Point3D.Double(pt2d.getX(), pt2d.getY(), curZ); + case final Point3D point3D -> point3D; + case null, default -> null; + }; } - private static void writeRow(IcySpreadSheet sh, int row, int ind, int t, int c, Sequence sequence, Point3D pt, - Point4D unitScale) - { - sh.setValue(row, 0, Integer.valueOf(ind)); - sh.setValue(row, 1, Double.valueOf(MathUtil.roundSignificant(pt.getX() * unitScale.getX(), 5, true))); - sh.setValue(row, 2, Double.valueOf(MathUtil.roundSignificant(pt.getY() * unitScale.getY(), 5, true))); - sh.setValue(row, 3, Double.valueOf(MathUtil.roundSignificant(pt.getZ() * unitScale.getZ(), 5, true))); + private static void writeRow( + @NotNull final IcySpreadSheet sh, + final int row, + final int ind, + final int t, + final int c, + final Sequence sequence, + @NotNull final Point3D pt, + @NotNull final Point4D unitScale + ) { + sh.setValue(row, 0, ind); + sh.setValue(row, 1, MathUtil.roundSignificant(pt.getX() * unitScale.getX(), 5, true)); + sh.setValue(row, 2, MathUtil.roundSignificant(pt.getY() * unitScale.getY(), 5, true)); + sh.setValue(row, 3, MathUtil.roundSignificant(pt.getZ() * unitScale.getZ(), 5, true)); if (unitScale.getT() == 1d) - sh.setValue(row, 4, Integer.valueOf(t)); + sh.setValue(row, 4, t); else - sh.setValue(row, 4, Double.valueOf(MathUtil.roundSignificant(t * unitScale.getT(), 5, true))); - sh.setValue(row, 5 + c, Double.valueOf(MathUtil - .roundSignificant(sequence.getDataInterpolated(t, pt.getZ(), c, pt.getY(), pt.getX()), 5, true))); + sh.setValue(row, 4, MathUtil.roundSignificant(t * unitScale.getT(), 5, true)); + sh.setValue(row, 5 + c, MathUtil.roundSignificant( + sequence.getDataInterpolated(t, pt.getZ(), c, pt.getY(), pt.getX()), + 5, + true + )); } } diff --git a/src/main/resources/path-intensity-profiler.png b/src/main/resources/path-intensity-profiler.png new file mode 100644 index 0000000000000000000000000000000000000000..db2f4ef1363e1fe984098fd2c948ebba18815e8c GIT binary patch literal 9625 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 P00000NkvXXu0mjf_kE3u literal 0 HcmV?d00001 -- GitLab