diff --git a/.gitignore b/.gitignore index 30c9a54acde67825b40daec3db2670ef0c6a64ea..57f16fb67c1b1589981416b323d7a9debc728665 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,41 @@ +/build* +/workspace +setting.xml +release/ target/ -workspace/ -.settings/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +icy.log + +### IntelliJ IDEA ### .idea/ -setting.xml -.project +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated .classpath -*.prefs -*.jardesc -*.jar -**/.DS_Store \ No newline at end of file +.factorypath +.project +.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 8f4dc7ab62fe4ffdbf502b1ceb266184abf0a391..09be7c043bb4b3f893d32057dead94d713d1b0fd 100644 --- a/pom.xml +++ b/pom.xml @@ -8,12 +8,12 @@ <parent> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>pom-icy</artifactId> - <version>2.2.0</version> + <version>3.0.0-a.1</version> </parent> <!-- Project Information --> <artifactId>intensity-projection</artifactId> - <version>2.0.0</version> + <version>2.0.0-a.1</version> <name>Intensity Projection plugin</name> <description>Intensity projection along depth or time with multiple algorithms: mean, max, median, variance, standard deviation, saturated sum. Projection can be restricted to ROI.</description> @@ -47,15 +47,11 @@ </developer> </developers> - <!-- List of project's dependencies --> <dependencies> - <!-- The EzPlug library, simplifies writing UI for Icy plugins. --> <dependency> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>ezplug</artifactId> </dependency> - - <!-- The EzPlug library, simplifies writing UI for Icy plugins. --> <dependency> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>protocols</artifactId> @@ -66,8 +62,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/org/bioimageanalysis/icy/image/projection/ProjectionAxis.java b/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionAxis.java index bc9d7dede3fbc424fb0c66b2bc54fad1c63f8c7d..a3a3799a0965964df0cd98c7bb759d8032266bee 100644 --- a/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionAxis.java +++ b/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionAxis.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2023. Institut Pasteur. + * Copyright (c) 2010-2024. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -18,6 +18,9 @@ package org.bioimageanalysis.icy.image.projection; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + /** * Indicates the direction on which the projection should be performed. * @@ -28,12 +31,14 @@ public enum ProjectionAxis { private final String name; - ProjectionAxis(final String name) { + @Contract(pure = true) + ProjectionAxis(final @NotNull String name) { this.name = name; } + @Contract(pure = true) @Override - public String toString() { + public @NotNull String toString() { return name; } } diff --git a/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionCalculator.java b/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionCalculator.java index d96316f941189c7b9f06b82dfe50167a3b817387..e744a89f3c49892846169abd492ca3aceef1845c 100644 --- a/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionCalculator.java +++ b/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionCalculator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2023. Institut Pasteur. + * Copyright (c) 2010-2024. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -18,15 +18,17 @@ package org.bioimageanalysis.icy.image.projection; -import icy.image.IcyBufferedImage; -import icy.image.IcyBufferedImageCursor; -import icy.math.ArrayMath; -import icy.roi.ROI; -import icy.sequence.Sequence; -import icy.sequence.VolumetricImage; -import icy.type.DataType; -import icy.util.OMEUtil; +import org.bioimageanalysis.icy.common.math.ArrayMath; +import org.bioimageanalysis.icy.common.type.DataType; import org.bioimageanalysis.icy.image.projection.util.MessageProgressListener; +import org.bioimageanalysis.icy.model.OMEUtil; +import org.bioimageanalysis.icy.model.image.IcyBufferedImage; +import org.bioimageanalysis.icy.model.image.IcyBufferedImageCursor; +import org.bioimageanalysis.icy.model.roi.ROI; +import org.bioimageanalysis.icy.model.sequence.Sequence; +import org.bioimageanalysis.icy.model.sequence.VolumetricImage; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import java.util.*; import java.util.concurrent.*; @@ -38,7 +40,6 @@ import java.util.concurrent.*; * @author Daniel Felipe Gonzalez Obando */ public class ProjectionCalculator implements Callable<Sequence> { - /** * Builder class for {@link ProjectionCalculator}s. This allows users to specify the sequence, ROIs, axis and operation before creating the calculator. * @@ -57,6 +58,7 @@ public class ProjectionCalculator implements Callable<Sequence> { * * @param s Target sequence for the projection. */ + @Contract("null -> fail") public Builder(final Sequence s) { if (s == null || s.isEmpty()) throw new IllegalArgumentException("Input sequence is null or empty."); @@ -138,6 +140,7 @@ public class ProjectionCalculator implements Callable<Sequence> { private final List<MessageProgressListener> progressListeners; private boolean computed; + @Contract(pure = true) private ProjectionCalculator() { progressListeners = new ArrayList<>(); computed = false; @@ -184,15 +187,17 @@ public class ProjectionCalculator implements Callable<Sequence> { private Sequence result; private void createResultSequence() throws InterruptedException { - result = new Sequence(OMEUtil.createOMEXMLMetadata(seq.getOMEXMLMetadata()), - op + " " + axis + "-projection of " + seq.getName()); + result = new Sequence( + OMEUtil.createOMEXMLMetadata(seq.getOMEXMLMetadata()), + op + " " + axis + "-projection of " + seq.getName() + ); final int width = axis == ProjectionAxis.X ? 1 : seq.getSizeX(); final int height = axis == ProjectionAxis.Y ? 1 : seq.getSizeY(); final int depth = axis == ProjectionAxis.Z ? 1 : seq.getSizeZ(); final int frames = axis == ProjectionAxis.T ? 1 : seq.getSizeT(); final int channels = axis == ProjectionAxis.C ? 1 : seq.getSizeC(); - final DataType dataType = seq.getDataType_(); + final DataType dataType = seq.getDataType(); for (int t = 0; t < frames; t++) { final VolumetricImage vol = new VolumetricImage(); @@ -209,18 +214,14 @@ public class ProjectionCalculator implements Callable<Sequence> { for (int c = 0; c < seq.getSizeC(); c++) result.getColorModel().setColorMap(c, seq.getColorMap(c), true); } - notifyProgress(0.01, "Result sequence instatiated"); + notifyProgress(0.01, "Result sequence instantiated"); } private void computeProjection() throws InterruptedException, ExecutionException { switch (axis) { case X: - //startProjectionOnPlane(); - //break; case C: - //startProjectionOnPlane(); - //break; case Y: startProjectionOnPlane(); break; @@ -252,54 +253,52 @@ public class ProjectionCalculator implements Callable<Sequence> { } } - final ForkJoinPool generalTaskPool = (ForkJoinPool) Executors - .newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() - 1)); - - final List<Future<IcyBufferedImage>> futures = new ArrayList<>(frames * depth); - for (t = 0; t < frames; t++) { - tOff = t * depth; - for (z = 0; z < depth; z++) { - final Callable<IcyBufferedImage> task; - switch (axis) { - case X: - task = getImageXProjectionTask(inputPlanes[tOff + z], outputPlanes[tOff + z], t, z, channels, + try (final ForkJoinPool generalTaskPool = (ForkJoinPool) Executors.newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() - 1))) { + final List<Future<IcyBufferedImage>> futures = new ArrayList<>(frames * depth); + for (t = 0; t < frames; t++) { + tOff = t * depth; + for (z = 0; z < depth; z++) { + final Callable<IcyBufferedImage> task = switch (axis) { + case X -> getImageXProjectionTask(inputPlanes[tOff + z], outputPlanes[tOff + z], t, z, channels, height, width); - break; - case Y: - task = getImageYProjectionTask(inputPlanes[tOff + z], outputPlanes[tOff + z], t, z, channels, + case Y -> getImageYProjectionTask(inputPlanes[tOff + z], outputPlanes[tOff + z], t, z, channels, height, width); - break; - case C: - task = getImageCProjectionTask(inputPlanes[tOff + z], outputPlanes[tOff + z], t, z, channels, + case C -> getImageCProjectionTask(inputPlanes[tOff + z], outputPlanes[tOff + z], t, z, channels, height, width); - break; - default: - throw new IllegalArgumentException("Wrong axis parameter"); + default -> throw new IllegalArgumentException("Wrong axis parameter"); + }; + futures.add(generalTaskPool.submit(task)); } - futures.add(generalTaskPool.submit(task)); } - } - generalTaskPool.shutdown(); + generalTaskPool.shutdown(); - int pos; - for (pos = 0, t = 0; t < frames; t++) { - for (z = 0; z < depth; z++) { - try { - futures.get(pos++).get(); - } - catch (final InterruptedException | ExecutionException e) { - generalTaskPool.shutdownNow(); - throw e; + int pos; + for (pos = 0, t = 0; t < frames; t++) { + for (z = 0; z < depth; z++) { + try { + futures.get(pos++).get(); + } + catch (final InterruptedException | ExecutionException e) { + generalTaskPool.shutdownNow(); + throw e; + } } + notifyProgress(0.01 + 0.99 * ((double) t / frames), axis + "-projection: Processed t=" + t); } - notifyProgress(0.01 + 0.99 * ((double) t / frames), axis + "-projection: Processed t=" + t); } } - private Callable<IcyBufferedImage> getImageXProjectionTask(final IcyBufferedImage inputPlane, - final IcyBufferedImage outputPlane, final int t, final int z, final int channels, final int height, - final int width) { + @Contract(pure = true) + private @NotNull Callable<IcyBufferedImage> getImageXProjectionTask( + final IcyBufferedImage inputPlane, + final IcyBufferedImage outputPlane, + final int t, + final int z, + final int channels, + final int height, + final int width + ) { return () -> { int channel, line, column, elementsCount; final double[] elements = new double[width]; @@ -336,9 +335,16 @@ public class ProjectionCalculator implements Callable<Sequence> { }; } - private Callable<IcyBufferedImage> getImageYProjectionTask(final IcyBufferedImage inputPlane, - final IcyBufferedImage outputPlane, final int t, final int z, final int channels, final int height, - final int width) { + @Contract(pure = true) + private @NotNull Callable<IcyBufferedImage> getImageYProjectionTask( + final IcyBufferedImage inputPlane, + final IcyBufferedImage outputPlane, + final int t, + final int z, + final int channels, + final int height, + final int width + ) { return () -> { int channel, line, column, elementsCount; final double[] elements = new double[height]; @@ -376,8 +382,16 @@ public class ProjectionCalculator implements Callable<Sequence> { }; } - private Callable<IcyBufferedImage> getImageCProjectionTask(final IcyBufferedImage inputPlane, - final IcyBufferedImage outputPlane, final int t, final int z, final int channels, final int height, final int width) { + @Contract(pure = true) + private @NotNull Callable<IcyBufferedImage> getImageCProjectionTask( + final IcyBufferedImage inputPlane, + final IcyBufferedImage outputPlane, + final int t, + final int z, + final int channels, + final int height, + final int width + ) { return () -> { int channel, line, column, elementsCount; final double[] elements = new double[height]; @@ -430,46 +444,57 @@ public class ProjectionCalculator implements Callable<Sequence> { final int frames = seq.getSizeT(); final int channels = seq.getSizeC(); - final ForkJoinPool generalTaskPool = (ForkJoinPool) Executors - .newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() / 2)); - final ForkJoinPool volumeTaskPool = (ForkJoinPool) Executors - .newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() - 1)); - int t, c; - VolumetricImage inputVolume, resultVolume; - final List<Future<VolumetricImage>> futures = new ArrayList<>(frames * depth); - for (t = 0; t < frames; t++) { - inputVolume = seq.getVolumetricImage(t); - resultVolume = result.getVolumetricImage(t); - for (c = 0; c < channels; c++) { - futures.add(generalTaskPool.submit(getImageZProjectionTask(t, c, inputVolume, resultVolume, channels, - height, width, depth, volumeTaskPool))); + try ( + final ForkJoinPool generalTaskPool = (ForkJoinPool) Executors.newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() / 2)); + final ForkJoinPool volumeTaskPool = (ForkJoinPool) Executors.newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() - 1)) + ) { + int t, c; + VolumetricImage inputVolume, resultVolume; + final List<Future<VolumetricImage>> futures = new ArrayList<>(frames * depth); + for (t = 0; t < frames; t++) { + inputVolume = seq.getVolumetricImage(t); + resultVolume = result.getVolumetricImage(t); + for (c = 0; c < channels; c++) { + futures.add(generalTaskPool.submit(getImageZProjectionTask(t, c, inputVolume, resultVolume, channels, + height, width, depth, volumeTaskPool))); + } } - } - generalTaskPool.shutdown(); + generalTaskPool.shutdown(); - try { - int pos; - for (pos = 0, t = 0; t < frames; t++) { - for (c = 0; c < channels; c++) { - futures.get(pos++).get(); + try { + int pos; + for (pos = 0, t = 0; t < frames; t++) { + for (c = 0; c < channels; c++) { + futures.get(pos++).get(); + } + notifyProgress(0.01 + 0.99 * ((double) t / frames), axis + "-projection: Processed t=" + t); } - notifyProgress(0.01 + 0.99 * ((double) t / frames), axis + "-projection: Processed t=" + t); + } + catch (final InterruptedException | ExecutionException e) { + generalTaskPool.shutdownNow(); + throw e; + } + finally { + volumeTaskPool.shutdown(); } } - catch (final InterruptedException | ExecutionException e) { - generalTaskPool.shutdownNow(); - throw e; - } - finally { - volumeTaskPool.shutdown(); - } - } - private Callable<VolumetricImage> getImageZProjectionTask(final int t, final int c, final VolumetricImage inputVolume, - final VolumetricImage resultVolume, final int channels, final int height, final int width, final int depth, final ForkJoinPool volumeTaskPool) { + @Contract(pure = true) + @SuppressWarnings("StatementWithEmptyBody") + private @NotNull Callable<VolumetricImage> getImageZProjectionTask( + final int t, + final int c, + final VolumetricImage inputVolume, + final VolumetricImage resultVolume, + final int channels, + final int height, + final int width, + final int depth, + final ForkJoinPool volumeTaskPool + ) { return () -> { int slice, line, column; @@ -517,7 +542,10 @@ public class ProjectionCalculator implements Callable<Sequence> { try { for (line = 0; line < height; line++) { - final double[] lineElements = futureLines.get(line).get(); + notifyProgress(0.01 + 0.99 * ((double) line / height), axis + "-projection: Processed line=" + line); + final Future<double[]> future = futureLines.get(line); + while (!future.isDone()) ; + final double[] lineElements = future.get(); for (column = 0; column < width; column++) { outputCursor.setSafe(column, line, c, lineElements[column]); } @@ -539,46 +567,58 @@ public class ProjectionCalculator implements Callable<Sequence> { final int frames = seq.getSizeT(); final int channels = seq.getSizeC(); - final ForkJoinPool generalTaskPool = (ForkJoinPool) Executors - .newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() / 2)); - final ForkJoinPool temporalTaskPool = (ForkJoinPool) Executors - .newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() - 1)); - int z, c; - Sequence inputS, resultS; - final List<Future<Sequence>> futures = new ArrayList<>(depth); - for (z = 0; z < depth; z++) { - for (c = 0; c < channels; c++) { - inputS = seq; - resultS = result; - { - futures.add(generalTaskPool.submit(getImageTProjectionTask(z, c, inputS, resultS, channels, height, - width, depth, frames, temporalTaskPool))); + try ( + final ForkJoinPool generalTaskPool = (ForkJoinPool) Executors.newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() / 2)); + final ForkJoinPool temporalTaskPool = (ForkJoinPool) Executors.newWorkStealingPool(Math.max(1, Runtime.getRuntime().availableProcessors() - 1)) + ) { + int z, c; + Sequence inputS, resultS; + final List<Future<Sequence>> futures = new ArrayList<>(depth); + for (z = 0; z < depth; z++) { + for (c = 0; c < channels; c++) { + inputS = seq; + resultS = result; + { + futures.add(generalTaskPool.submit(getImageTProjectionTask(z, c, inputS, resultS, channels, height, width, depth, frames, temporalTaskPool))); + } } } - } - generalTaskPool.shutdown(); + generalTaskPool.shutdown(); - try { - int pos; - for (pos = 0, z = 0; z < depth; z++) { - for (c = 0; c < channels; c++) { - futures.get(pos++).get(); + try { + int pos; + for (pos = 0, z = 0; z < depth; z++) { + for (c = 0; c < channels; c++) { + futures.get(pos++).get(); + } + notifyProgress(0.01 + 0.99 * ((double) z / depth), axis + "-projection: Processed z=" + z); } - notifyProgress(0.01 + 0.99 * ((double) z / depth), axis + "-projection: Processed z=" + z); } - } - catch (final InterruptedException | ExecutionException e) { - generalTaskPool.shutdownNow(); - throw e; - } - finally { - temporalTaskPool.shutdown(); + catch (final InterruptedException | ExecutionException e) { + generalTaskPool.shutdownNow(); + throw e; + } + finally { + temporalTaskPool.shutdown(); + } } } - private Callable<Sequence> getImageTProjectionTask(final int z, final int c, final Sequence inputS, final Sequence resultS, final int channels, - final int height, final int width, final int depth, final int frames, final ForkJoinPool temporalTaskPool) { + @Contract(pure = true) + @SuppressWarnings("StatementWithEmptyBody") + private @NotNull Callable<Sequence> getImageTProjectionTask( + final int z, + final int c, + final Sequence inputS, + final Sequence resultS, + final int channels, + final int height, + final int width, + final int depth, + final int frames, + final ForkJoinPool temporalTaskPool + ) { return () -> { int frame, line, column; final IcyBufferedImageCursor[] inputCursors = new IcyBufferedImageCursor[frames]; @@ -624,7 +664,10 @@ public class ProjectionCalculator implements Callable<Sequence> { try { for (line = 0; line < height; line++) { - final double[] lineElements = futureLines.get(line).get(); + notifyProgress(0.01 + 0.99 * ((double) line / height), axis + "-projection: Processed line=" + line); + final Future<double[]> future = futureLines.get(line); + while (!future.isDone()) ; + final double[] lineElements = future.get(); for (column = 0; column < width; column++) { outputCursor.setSafe(column, line, c, lineElements[column]); } @@ -663,6 +706,7 @@ public class ProjectionCalculator implements Callable<Sequence> { progressListeners.forEach(l -> l.onProgress(progress, message)); } + @Contract(pure = true) private Sequence getResultSequence() { return result; } diff --git a/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionOperationType.java b/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionOperationType.java index 49f4cfdf65951b6ff764e2e431ae36823ac7f2b7..12ba559034dd0580da8c37b3b1640525319b8f84 100644 --- a/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionOperationType.java +++ b/src/main/java/org/bioimageanalysis/icy/image/projection/ProjectionOperationType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2023. Institut Pasteur. + * Copyright (c) 2010-2024. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -18,22 +18,32 @@ package org.bioimageanalysis.icy.image.projection; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + /** * Represents the operation to be applied when performing an intensity projection on a sequence. * * @author Daniel Felipe Gonzalez Obando */ public enum ProjectionOperationType { - MAX("Maximum"), MEAN("Average"), MED("Median"), MIN("Minimum"), STD("Standard Deviation"), SATSUM("Saturated Sum"); + MAX("Maximum"), + MEAN("Average"), + MED("Median"), + MIN("Minimum"), + STD("Standard Deviation"), + SATSUM("Saturated Sum"); private final String description; - ProjectionOperationType(final String description) { + @Contract(pure = true) + ProjectionOperationType(final @NotNull String description) { this.description = description; } + @Contract(pure = true) @Override - public String toString() { + public @NotNull String toString() { return description; } } diff --git a/src/main/java/org/bioimageanalysis/icy/image/projection/util/MessageProgressListener.java b/src/main/java/org/bioimageanalysis/icy/image/projection/util/MessageProgressListener.java index a1e70cd553497e12a946cf6de94c39fb767ea36f..ef2088abb3f70826c2703ce42cf5ee7895199990 100644 --- a/src/main/java/org/bioimageanalysis/icy/image/projection/util/MessageProgressListener.java +++ b/src/main/java/org/bioimageanalysis/icy/image/projection/util/MessageProgressListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2023. Institut Pasteur. + * Copyright (c) 2010-2024. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -18,6 +18,8 @@ package org.bioimageanalysis.icy.image.projection.util; +import org.jetbrains.annotations.Nullable; + /** * A Progress event listener that handles events associating a value and a message to them. * @@ -30,5 +32,5 @@ public interface MessageProgressListener { * @param progress The progress value from 0.0 to 1.0. {@link Double#NaN} value should be taken as no change and -1.0 as indeterminate progress. * @param message The event message. A null value should be taken as no change on the message. */ - void onProgress(double progress, String message); + void onProgress(double progress, @Nullable String message); } diff --git a/src/main/java/org/bioimageanalysis/icy/image/projection/util/SequenceCursor.java b/src/main/java/org/bioimageanalysis/icy/image/projection/util/SequenceCursor.java index 166ae994caf8c91eef57b3d0c1b91a93929589b1..a0255efa05d70f7ab50e446f75dc133eb20805e6 100644 --- a/src/main/java/org/bioimageanalysis/icy/image/projection/util/SequenceCursor.java +++ b/src/main/java/org/bioimageanalysis/icy/image/projection/util/SequenceCursor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2023. Institut Pasteur. + * Copyright (c) 2010-2024. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -18,7 +18,8 @@ package org.bioimageanalysis.icy.image.projection.util; -import icy.sequence.Sequence; +import org.bioimageanalysis.icy.model.sequence.Sequence; +import org.jetbrains.annotations.NotNull; import java.util.concurrent.atomic.AtomicBoolean; @@ -40,7 +41,7 @@ public class SequenceCursor { * * @param seq Sequence. */ - public SequenceCursor(final Sequence seq) { + public SequenceCursor(final @NotNull Sequence seq) { this.seq = seq; this.volumeCursors = new VolumetricImageCursor[seq.getSizeT()]; this.sequenceChanged = new AtomicBoolean(); @@ -75,8 +76,7 @@ public class SequenceCursor { * @throws IndexOutOfBoundsException If the position is not in the image. * @throws RuntimeException If the data type is not a valid format. */ - public synchronized void set(final int x, final int y, final int z, final int t, final int c, final double val) - throws IndexOutOfBoundsException, RuntimeException { + public synchronized void set(final int x, final int y, final int z, final int t, final int c, final double val) throws IndexOutOfBoundsException, RuntimeException { getVolumeCursor(t).set(x, y, z, c, val); sequenceChanged.set(true); } @@ -94,8 +94,7 @@ public class SequenceCursor { * @throws IndexOutOfBoundsException If the position is not in the image. * @throws RuntimeException If the data type is not a valid format. */ - public synchronized void setSafe(final int x, final int y, final int z, final int t, final int c, final double val) - throws IndexOutOfBoundsException, RuntimeException { + public synchronized void setSafe(final int x, final int y, final int z, final int t, final int c, final double val) throws IndexOutOfBoundsException, RuntimeException { getVolumeCursor(t).setSafe(x, y, z, c, val); sequenceChanged.set(true); } @@ -103,7 +102,7 @@ public class SequenceCursor { private int currentT; private VolumetricImageCursor currentCursor; - private synchronized VolumetricImageCursor getVolumeCursor(final int t) throws IndexOutOfBoundsException { + private synchronized @NotNull VolumetricImageCursor getVolumeCursor(final int t) throws IndexOutOfBoundsException { if (currentT != t) { if (volumeCursors[t] == null) { volumeCursors[t] = new VolumetricImageCursor(seq, t); @@ -129,7 +128,7 @@ public class SequenceCursor { } @Override - public String toString() { + public @NotNull String toString() { return "last T=" + currentT + " " + ((currentCursor != null) ? currentCursor.toString() : ""); } } diff --git a/src/main/java/org/bioimageanalysis/icy/image/projection/util/VolumetricImageCursor.java b/src/main/java/org/bioimageanalysis/icy/image/projection/util/VolumetricImageCursor.java index 7d3f1083a251bfec37d79023b96be84ddf3f8ed0..c12d681b8b7ed00c5b40ac9f17a0f096ebfc19ab 100644 --- a/src/main/java/org/bioimageanalysis/icy/image/projection/util/VolumetricImageCursor.java +++ b/src/main/java/org/bioimageanalysis/icy/image/projection/util/VolumetricImageCursor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2023. Institut Pasteur. + * Copyright (c) 2010-2024. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -18,9 +18,10 @@ package org.bioimageanalysis.icy.image.projection.util; -import icy.image.IcyBufferedImageCursor; -import icy.sequence.Sequence; -import icy.sequence.VolumetricImage; +import org.bioimageanalysis.icy.model.image.IcyBufferedImageCursor; +import org.bioimageanalysis.icy.model.sequence.Sequence; +import org.bioimageanalysis.icy.model.sequence.VolumetricImage; +import org.jetbrains.annotations.NotNull; import java.util.concurrent.atomic.AtomicBoolean; @@ -44,7 +45,7 @@ public class VolumetricImageCursor { * * @param vol Target volume. */ - public VolumetricImageCursor(final VolumetricImage vol) { + public VolumetricImageCursor(final @NotNull VolumetricImage vol) { this.vol = vol; planeCursors = new IcyBufferedImageCursor[vol.getSize()]; volumeChanged = new AtomicBoolean(false); @@ -57,7 +58,7 @@ public class VolumetricImageCursor { * @param seq Target sequence. * @param t Time position where the volume is located in {@code seq}. */ - public VolumetricImageCursor(final Sequence seq, final int t) { + public VolumetricImageCursor(final @NotNull Sequence seq, final int t) { this(seq.getVolumetricImage(t)); } @@ -88,8 +89,7 @@ public class VolumetricImageCursor { * @throws IndexOutOfBoundsException If the position is not in the image. * @throws RuntimeException If the data type is not a valid format. */ - public synchronized void set(final int x, final int y, final int z, final int c, final double val) - throws IndexOutOfBoundsException, RuntimeException { + public synchronized void set(final int x, final int y, final int z, final int c, final double val) throws IndexOutOfBoundsException, RuntimeException { getPlaneCursor(z).set(x, y, c, val); volumeChanged.set(true); } @@ -106,8 +106,7 @@ public class VolumetricImageCursor { * @throws IndexOutOfBoundsException If the position is not in the image. * @throws RuntimeException If the data type is not a valid format. */ - public synchronized void setSafe(final int x, final int y, final int z, final int c, final double val) - throws IndexOutOfBoundsException, RuntimeException { + public synchronized void setSafe(final int x, final int y, final int z, final int c, final double val) throws IndexOutOfBoundsException, RuntimeException { getPlaneCursor(z).setSafe(x, y, c, val); volumeChanged.set(true); } @@ -115,7 +114,7 @@ public class VolumetricImageCursor { private IcyBufferedImageCursor currentCursor; private int currentZ; - private synchronized IcyBufferedImageCursor getPlaneCursor(final int z) throws IndexOutOfBoundsException { + private synchronized @NotNull IcyBufferedImageCursor getPlaneCursor(final int z) throws IndexOutOfBoundsException { if (currentZ != z) { if (planeCursors[z] == null) { planeCursors[z] = new IcyBufferedImageCursor(vol.getImage(z)); diff --git a/src/main/java/plugins/adufour/projection/Projection.java b/src/main/java/plugins/adufour/projection/Projection.java index 6a5e9a0001048b3668e9bcf51e087ee7b7911b74..b8640ee496820abda39be2e759e2511ea813f769 100644 --- a/src/main/java/plugins/adufour/projection/Projection.java +++ b/src/main/java/plugins/adufour/projection/Projection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2023. Institut Pasteur. + * Copyright (c) 2010-2024. Institut Pasteur. * * This file is part of Icy. * Icy is free software: you can redistribute it and/or modify @@ -18,16 +18,17 @@ package plugins.adufour.projection; -import icy.main.Icy; -import icy.plugin.PluginLauncher; -import icy.plugin.PluginLoader; -import icy.roi.ROI; -import icy.sequence.Sequence; -import icy.system.IcyExceptionHandler; -import icy.system.IcyHandledException; +import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginIcon; +import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginName; import org.bioimageanalysis.icy.image.projection.ProjectionAxis; import org.bioimageanalysis.icy.image.projection.ProjectionCalculator; import org.bioimageanalysis.icy.image.projection.ProjectionOperationType; +import org.bioimageanalysis.icy.model.roi.ROI; +import org.bioimageanalysis.icy.model.sequence.Sequence; +import org.bioimageanalysis.icy.system.logging.IcyLogger; +import org.jetbrains.annotations.Contract; +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.*; @@ -36,21 +37,29 @@ import plugins.adufour.vars.lang.VarSequence; import java.util.Collections; import java.util.List; +@IcyPluginName("Intensity Projection") +@IcyPluginIcon(path = "/plugins/adufour/projection/icon/icon_32.png") public class Projection extends EzPlug implements Block, EzStoppable { public enum ProjectionDirection { Z, T, C, Y, X } public enum ProjectionType { - MAX("Maximum"), MEAN("Average"), MED("Median"), MIN("Minimum"), STD("Standard Deviation"), + MAX("Maximum"), + MEAN("Average"), + MED("Median"), + MIN("Minimum"), + STD("Standard Deviation"), SATSUM("Saturated Sum"); private final String description; + @Contract(pure = true) ProjectionType(final String description) { this.description = description; } + @Contract(pure = true) @Override public String toString() { return description; @@ -79,7 +88,7 @@ public class Projection extends EzPlug implements Block, EzStoppable { } @Override - public void declareInput(final VarList inputMap) { + public void declareInput(final @NotNull VarList inputMap) { initCommonVars(); inputMap.add("input", input.getVariable()); inputMap.add("projection direction", projectionDir.getVariable()); @@ -96,7 +105,7 @@ public class Projection extends EzPlug implements Block, EzStoppable { } @Override - public void declareOutput(final VarList outputMap) { + public void declareOutput(final @NotNull VarList outputMap) { output = new VarSequence("projected sequence", null); outputMap.add("projection output", output); } @@ -113,8 +122,8 @@ public class Projection extends EzPlug implements Block, EzStoppable { computeCalculator(); } catch (final Exception e) { - IcyExceptionHandler.showErrorMessage(e, true); - throw new IcyHandledException("Error while projecting: " + e.getMessage(), e); + IcyLogger.error(this.getClass(), e, "Error while projecting."); + return; } setOutput(); internalClean(); @@ -132,42 +141,27 @@ public class Projection extends EzPlug implements Block, EzStoppable { rois = restrictToROI.getValue(true) ? inputSequence.getROIs() : Collections.emptyList(); } - private static ProjectionAxis getAxis(final ProjectionDirection direction) { - switch (direction) { - case X: - return ProjectionAxis.X; - case Y: - return ProjectionAxis.Y; - case C: - return ProjectionAxis.C; - case Z: - return ProjectionAxis.Z; - case T: - return ProjectionAxis.T; - - default: - throw new IllegalArgumentException("" + direction); - } + @Contract(pure = true) + private static @NotNull ProjectionAxis getAxis(final @NotNull ProjectionDirection direction) { + return switch (direction) { + case X -> ProjectionAxis.X; + case Y -> ProjectionAxis.Y; + case C -> ProjectionAxis.C; + case Z -> ProjectionAxis.Z; + case T -> ProjectionAxis.T; + }; } - private static ProjectionOperationType getOperation(final ProjectionType type) { - switch (type) { - case MAX: - return ProjectionOperationType.MAX; - case MEAN: - return ProjectionOperationType.MEAN; - case MED: - return ProjectionOperationType.MED; - case MIN: - return ProjectionOperationType.MIN; - case SATSUM: - return ProjectionOperationType.SATSUM; - case STD: - return ProjectionOperationType.STD; - - default: - throw new IllegalArgumentException("" + type); - } + @Contract(pure = true) + private static @NotNull ProjectionOperationType getOperation(final @NotNull ProjectionType type) { + return switch (type) { + case MAX -> ProjectionOperationType.MAX; + case MEAN -> ProjectionOperationType.MEAN; + case MED -> ProjectionOperationType.MED; + case MIN -> ProjectionOperationType.MIN; + case SATSUM -> ProjectionOperationType.SATSUM; + case STD -> ProjectionOperationType.STD; + }; } private Sequence resultSequence; @@ -188,7 +182,7 @@ public class Projection extends EzPlug implements Block, EzStoppable { } } - private void onProgress(final double progress, final String message) { + private void onProgress(final double progress, final @Nullable String message) { if (!isHeadLess()) { getUI().setProgressBarValue(progress); getUI().setProgressBarMessage(message); @@ -242,7 +236,11 @@ public class Projection extends EzPlug implements Block, EzStoppable { * @throws Exception If the projection cannot be correctly done. * @see ProjectionCalculator */ - public static Sequence zProjection(final Sequence in, final ProjectionType projection, final boolean multiThread) throws Exception { + public static Sequence zProjection( + final Sequence in, + final ProjectionType projection, + final boolean multiThread + ) throws Exception { return zProjection(in, projection, multiThread, false); } @@ -259,10 +257,19 @@ public class Projection extends EzPlug implements Block, EzStoppable { * @throws Exception If the projection cannot be correctly done. * @see ProjectionCalculator */ - public static Sequence zProjection(final Sequence in, final ProjectionType projection, final boolean multiThread, final boolean restrictToROI) throws Exception { + public static Sequence zProjection( + final Sequence in, + final ProjectionType projection, + final boolean multiThread, + final boolean restrictToROI + ) throws Exception { final List<ROI> rois = restrictToROI ? in.getROIs() : Collections.emptyList(); - final ProjectionCalculator calculator = new ProjectionCalculator.Builder(in).axis(ProjectionAxis.Z) - .operation(getOperation(projection)).addRois(rois).build(); + final ProjectionCalculator calculator = new ProjectionCalculator + .Builder(in) + .axis(ProjectionAxis.Z) + .operation(getOperation(projection)) + .addRois(rois) + .build(); return calculator.call(); } @@ -277,7 +284,11 @@ public class Projection extends EzPlug implements Block, EzStoppable { * @throws Exception If the projection cannot be correctly done. * @see ProjectionCalculator */ - public static Sequence tProjection(final Sequence in, final ProjectionType projection, final boolean multiThread) throws Exception { + public static Sequence tProjection( + final Sequence in, + final ProjectionType projection, + final boolean multiThread + ) throws Exception { return tProjection(in, projection, multiThread, false); } @@ -294,23 +305,19 @@ public class Projection extends EzPlug implements Block, EzStoppable { * @throws Exception If the projection cannot be correctly done. * @see ProjectionCalculator */ - public static Sequence tProjection(final Sequence in, final ProjectionType projection, final boolean multiThread, final boolean restrictToROI) throws Exception { + public static Sequence tProjection( + final Sequence in, + final ProjectionType projection, + final boolean multiThread, + final boolean restrictToROI + ) throws Exception { final List<ROI> rois = restrictToROI ? in.getROIs() : Collections.emptyList(); - final ProjectionCalculator calculator = new ProjectionCalculator.Builder(in).axis(ProjectionAxis.T) - .operation(getOperation(projection)).addRois(rois).build(); + final ProjectionCalculator calculator = new ProjectionCalculator + .Builder(in) + .axis(ProjectionAxis.T) + .operation(getOperation(projection)) + .addRois(rois) + .build(); return calculator.call(); } - - /** - * Main method used for testing. - * - * @param args Input arguments. - */ - public static void main(final String[] args) { - // start icy - Icy.main(args); - - // then start plugin - PluginLauncher.start(PluginLoader.getPlugin(Projection.class.getName())); - } } diff --git a/src/main/resources/plugins/adufour/projection/icon/icon_32.png b/src/main/resources/plugins/adufour/projection/icon/icon_32.png new file mode 100644 index 0000000000000000000000000000000000000000..5dc83272bac2de94fcc0cd3feba5bdb227752022 Binary files /dev/null and b/src/main/resources/plugins/adufour/projection/icon/icon_32.png differ