diff --git a/SLIC/.gitignore b/SLIC/.gitignore index 34ef6b1bc2942265008040c686460cf675dc44a2..1522ef951c7d719126cbb5504e128e2f71a7ac19 100644 --- a/SLIC/.gitignore +++ b/SLIC/.gitignore @@ -5,3 +5,4 @@ /.project /build/ /ecbuild/ +/target/ diff --git a/SLIC/build.gradle b/SLIC/build.gradle deleted file mode 100644 index 61de65f3ea41ad95b6efb01c56b48088769ff758..0000000000000000000000000000000000000000 --- a/SLIC/build.gradle +++ /dev/null @@ -1,52 +0,0 @@ -apply plugin: 'java-library' -apply plugin: 'eclipse' - -version = '0.1.0' - -println "ICY_HOME=${System.env.ICY_HOME}" -println project.name + " " + version - -configurations { - extraLibs // configuration that holds jars to include in the jar - implementation.extendsFrom(extraLibs) -} - -repositories { - mavenCentral() - jcenter() -} - -dependencies { - extraLibs 'org.slf4j:slf4j-api:1.7.21' - - implementation files("${System.env.ICY_HOME}/icy.jar") // Icy core - implementation files("${System.env.ICY_HOME}/lib/bioformats.jar") // bioformats - implementation files("${System.env.ICY_HOME}/plugins/adufour/ezplug/EzPlug.jar") // EzPlug - implementation files("${System.env.ICY_HOME}/plugins/adufour/blocks/Blocks.jar") // Blocks - implementation files("${System.env.ICY_HOME}/plugins/adufour/protocols/Protocols.jar") // Protocols - - testImplementation 'junit:junit:4.12' -} - -// In this section we add the icy nature of the project in eclipse -eclipse { - classpath.downloadJavadoc = true - project.natures += ['icy.icy4eclipse.core.icynature'] -} - -task sourcesJar(type: Jar) { - classifier = 'sources' - from sourceSets.main.output - from sourceSets.main.java - from configurations.extraLibs.collect { it.isDirectory() ? it : zipTree(it) } -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} \ No newline at end of file diff --git a/SLIC/gradle/wrapper/gradle-wrapper.jar b/SLIC/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..0000000000000000000000000000000000000000 Binary files a/SLIC/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/SLIC/gradle/wrapper/gradle-wrapper.properties b/SLIC/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index b17c75f10d9ef7fa22219bdf3ac40849d84702da..0000000000000000000000000000000000000000 --- a/SLIC/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/SLIC/gradlew b/SLIC/gradlew deleted file mode 100644 index 83f2acfdc319a24e8766cca78f32474ad7a22dd6..0000000000000000000000000000000000000000 --- a/SLIC/gradlew +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/SLIC/gradlew.bat b/SLIC/gradlew.bat deleted file mode 100644 index 9618d8d9607cd91a0efb866bcac4810064ba6fac..0000000000000000000000000000000000000000 --- a/SLIC/gradlew.bat +++ /dev/null @@ -1,100 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/SLIC/pom.xml b/SLIC/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..52f1eb106f5798b7677f289aee11154c91f56450 --- /dev/null +++ b/SLIC/pom.xml @@ -0,0 +1,31 @@ +<?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"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>parent-pom-plugin</artifactId> + <version>1.0.4</version> + </parent> + <artifactId>slic</artifactId> + <version>1.0.1</version> + <name>SLIC - Superpixels</name> + <description/> + <build> + </build> + <dependencies> + <dependency> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>ezplug</artifactId> + </dependency> + <dependency> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>protocols</artifactId> + </dependency> + </dependencies> + <repositories> + <repository> + <id>icy</id> + <url>https://icy-nexus.pasteur.fr/repository/Icy/</url> + </repository> + </repositories> +</project> \ No newline at end of file diff --git a/SLIC/src/main/java/algorithms/danyfel80/islic/SLICTask.java b/SLIC/src/main/java/algorithms/danyfel80/islic/SLICTask.java index 44c705c3ed43706c98bf0ead4e53150b8e5a873c..cf4420dde7819030de753251afda5a5c92c76a1a 100644 --- a/SLIC/src/main/java/algorithms/danyfel80/islic/SLICTask.java +++ b/SLIC/src/main/java/algorithms/danyfel80/islic/SLICTask.java @@ -20,6 +20,7 @@ package algorithms.danyfel80.islic; import java.awt.Color; import java.awt.Point; +import java.awt.Rectangle; import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; @@ -44,517 +45,585 @@ import plugins.kernel.roi.roi2d.ROI2DArea; /** * @author Daniel Felipe Gonzalez Obando */ -public class SLICTask { - - private static final double EPSILON = 0.25; - static int MAX_ITERATIONS = 10; - static double ERROR_THRESHOLD = 1e-2; - - private Sequence sequence; - private int S; - private int Sx; - private int Sy; - private int SPnum; - private double rigidity; - - private boolean computeROIs; - private List<ROI> rois; - private Sequence superPixelsResult; - - private Map<Point3D.Integer, double[]> colorLUT; - private Map<Point, Double> distanceLUT; - - public SLICTask(Sequence sequence, int SPSize, double rigidity, boolean computeROIs) { - this.sequence = sequence; - this.S = SPSize; - this.Sx = (sequence.getWidth() + S / 2 - 1) / S; - this.Sy = (sequence.getHeight() + S / 2 - 1) / S; - this.SPnum = Sx * Sy; - this.rigidity = rigidity; - - this.computeROIs = computeROIs; - - colorLUT = new HashMap<>(); - distanceLUT = new HashMap<>(); - } - - public void execute() { - - double[] ls = new double[SPnum]; - double[] as = new double[SPnum]; - double[] bs = new double[SPnum]; - - double[] cxs = new double[SPnum]; - double[] cys = new double[SPnum]; - - double[][] sequenceData = Array2DUtil.arrayToDoubleArray(sequence.getDataXYC(0, 0), sequence.isSignedDataType()); - - initialize(ls, as, bs, cxs, cys, sequenceData); - - int[] clusters = new int[sequence.getWidth() * sequence.getHeight()]; - double[] distances = Arrays.stream(clusters).mapToDouble(i -> Double.MAX_VALUE).toArray(); - - int iterations = 0; - double error = 0; - do { - assignClusters(sequenceData, clusters, distances, ls, as, bs, cxs, cys); - error = updateClusters(sequenceData, clusters, ls, as, bs, cxs, cys); - iterations++; - System.out.format("Iteration=%d, Error=%f%n", iterations, error); - } while (error > ERROR_THRESHOLD && iterations < MAX_ITERATIONS); - System.out.format("End of iterations%n"); - - computeSuperpixels(sequenceData, clusters, ls, as, bs, cxs, cys); - } - - private void initialize(double[] ls, double[] as, double[] bs, double[] cxs, double[] cys, double[][] sequenceData) { - - IntStream.range(0, SPnum).forEach(sp -> { - int x, y, yOff, bestx, besty, bestyOff, i, j, yjOff; - double bestGradient, candidateGradient; - - int spx = sp % Sx; - int spy = sp / Sx; - - x = S / 2 + (spx) * S; - y = S / 2 + (spy) * S; - yOff = y * sequence.getWidth(); - - cxs[sp] = bestx = x; - cys[sp] = besty = y; - bestyOff = yOff; - bestGradient = Double.MAX_VALUE; - - for (j = -1; j <= 1; j++) { - if (y + j >= 0 && y + j < sequence.getHeight()) { - yjOff = (y + j) * sequence.getWidth(); - for (i = -1; i <= 1; i++) { - if (x >= 0 && x < sequence.getWidth() && x + i >= 0 && x + i < sequence.getWidth()) { - candidateGradient = getGradient(sequenceData, x + yOff, (x + i) + yjOff); - if (candidateGradient < bestGradient) { - bestx = x + i; - besty = y + j; - bestyOff = yjOff; - bestGradient = candidateGradient; - } - } - } - } - } - - cxs[sp] = bestx; - cys[sp] = besty; - int bestPos = bestx + bestyOff; - - double[] bestLAB = getCIELab(sequenceData, bestPos); - ls[sp] = bestLAB[0]; - as[sp] = bestLAB[1]; - bs[sp] = bestLAB[2]; - - }); - - } - - private double getGradient(double[][] data, int pos1, int pos2) { - int[] valRGB1 = new int[3]; - int[] valRGB2 = new int[3]; - IntStream.range(0, 3).forEach(c -> { - try { - valRGB1[c] = (int) Math.round(255 * (data[c % sequence.getSizeC()][pos1] / sequence.getDataTypeMax())); - valRGB2[c] = (int) Math.round(255 * (data[c % sequence.getSizeC()][pos2] / sequence.getDataTypeMax())); - } catch (Exception e) { - throw e; - } - }); - - double[] val1 = CIELab.fromRGB(valRGB1); - double[] val2 = CIELab.fromRGB(valRGB2); - - double avgGrad = IntStream.range(0, 3).mapToDouble(c -> val2[c] - val1[c]).average().getAsDouble(); - - return avgGrad; - } - - private double[] getCIELab(double[][] sequenceData, int pos) { - - int[] rgbVal = new int[3]; - IntStream.range(0, 3).forEach(c -> { - rgbVal[c] = (int) Math.round(255 * (sequenceData[c % sequence.getSizeC()][pos] / sequence.getDataTypeMax())); - }); - Point3D.Integer rgbPoint = new Point3D.Integer(rgbVal); - - double[] LAB = colorLUT.get(rgbPoint); - if (LAB == null) { - int[] RGB = new int[3]; - IntStream.range(0, 3).forEach(c -> { - RGB[c] = (int) Math.round(255 * (sequenceData[c % sequence.getSizeC()][pos] / sequence.getDataTypeMax())); - }); - - LAB = CIELab.fromRGB(RGB); - colorLUT.put(rgbPoint, LAB); - } - return LAB; - } - - private void assignClusters(double[][] sequenceData, int[] clusters, double[] distances, double[] ls, double[] as, - double[] bs, double[] cxs, double[] cys) { - // Each cluster - IntStream.range(0, SPnum).forEach(k -> { - double ckx = cxs[k], cky = cys[k], lk = ls[k], ak = as[k], bk = bs[k]; - int posk = ((int) ckx) + ((int) cky) * sequence.getWidth(); - distances[posk] = 0; - clusters[posk] = k; - - // 2Sx2S window assignment - int j, i, yOff; - for (j = -S; j < S; j++) { - int ciy = (int) (cky + j); - if (ciy >= 0 && ciy < sequence.getHeight()) { - yOff = ciy * sequence.getWidth(); - for (i = -S; i < S; i++) { - int cix = (int) (ckx + i); - if (cix >= 0 && cix < sequence.getWidth()) { - int posi = cix + yOff; - double[] LABi = getCIELab(sequenceData, posi); - double li = LABi[0]; - double ai = LABi[1]; - double bi = LABi[2]; - - double di = getDistance(ckx, cky, lk, ak, bk, i, j, li, ai, bi); - if (di < distances[posi]) { - distances[posi] = di; - clusters[posi] = clusters[posk]; - } - } - } - } - } - }); - } - - private double getDistance(double ckx, double cky, double lk, double ak, double bk, int dx, int dy, double li, - double ai, double bi) { - - double diffl, diffa, diffb; - diffl = li - lk; - diffa = ai - ak; - diffb = bi - bk; - - double dc = Math.sqrt(diffl * diffl + diffa * diffa + diffb * diffb); - Point dPt = new Point((int) Math.min(dx, dy), (int) Math.max(dx, dy)); - Double ds = distanceLUT.get(dPt); - if (ds == null) { - ds = Math.sqrt(dx * dx + dy * dy); - distanceLUT.put(dPt, ds); - } - return Math.sqrt(dc * dc + (ds * ds) * (rigidity * rigidity * S)); - } - - private double updateClusters(double[][] sequenceData, int[] clusters, double[] ls, double[] as, double[] bs, - double[] cxs, double[] cys) { - - double[] newls = new double[SPnum]; - double[] newas = new double[SPnum]; - double[] newbs = new double[SPnum]; - double[] newcxs = new double[SPnum]; - double[] newcys = new double[SPnum]; - int[] cants = new int[SPnum]; - - // int blockSize = 500; - // IntStream.iterate(0, sy -> sy+blockSize).limit((int)Math.ceil(sequence.getHeight()/blockSize)).forEach(y0->{ - // int y1 = Math.min(y0+blockSize, sequence.getHeight() - y0); - // IntStream.iterate(0, sx -> sx+blockSize).limit((int)Math.ceil(sequence.getWidth()/blockSize)).forEach(x0->{ - // int x1 = Math.min(x0+blockSize, sequence.getWidth() - x0); - // }); - // }); - for (int y = 0, yOff; y < sequence.getHeight(); y++) { - yOff = y * sequence.getWidth(); - for (int x = 0, pos; x < sequence.getWidth(); x++) { - pos = x + yOff; - int sp = clusters[pos]; - double[] lab = getCIELab(sequenceData, pos); - - cants[sp]++; - - newls[sp] += (lab[0] - newls[sp]) / cants[sp]; - newas[sp] += (lab[1] - newas[sp]) / cants[sp]; - newbs[sp] += (lab[2] - newbs[sp]) / cants[sp]; - newcxs[sp] += (x - newcxs[sp]) / cants[sp]; - newcys[sp] += (y - newcys[sp]) / cants[sp]; - } - } - - double error = IntStream.range(0, SPnum).mapToDouble(sp -> { - //double diffl = ls[sp] - newls[sp]; - //double diffa = as[sp] - newas[sp]; - //double diffb = bs[sp] - newbs[sp]; - double diffx = cxs[sp] - newcxs[sp]; - double diffy = cys[sp] - newcys[sp]; - - ls[sp] = newls[sp]; - as[sp] = newas[sp]; - bs[sp] = newbs[sp]; - cxs[sp] = newcxs[sp]; - cys[sp] = newcys[sp]; - - return Math.sqrt(diffx * diffx - + diffy * diffy/* + diffl * diffl + diffa * diffa + diffb * diffb */); - }).sum() / SPnum; - - return error; - } - - private void computeSuperpixels(double[][] sequenceData, int[] clusters, double[] ls, double[] as, double[] bs, - double[] cxs, double[] cys) { - - boolean[] visited = new boolean[clusters.length]; - int[] finalClusters = new int[clusters.length]; - - List<Point3D.Double> labs = new ArrayList<>(SPnum); - List<Double> areas = new ArrayList<>(SPnum); - List<Point> firstPoints = new ArrayList<>(SPnum); - - // fill known clusters - AtomicInteger usedLabels = new AtomicInteger(0); - IntStream.range(0, SPnum).forEach(i -> { - Point3D.Double labCenter = new Point3D.Double(); - AtomicInteger area = new AtomicInteger(0); - Point p = new Point((int) Math.round(cxs[i]), (int) Math.round(cys[i])); - int pPos = p.x + p.y * sequence.getWidth(); - - if (clusters[pPos] == i && !visited[pPos]) { - findAreaAndColor(sequenceData, clusters, finalClusters, visited, p, labCenter, area, - usedLabels.getAndIncrement()); - labs.add(labCenter); - areas.add(area.get() / (double) (S * S)); - firstPoints.add(p); - } - - }); - - int firstUnknownCluster = usedLabels.get(); - - // fill independent clusters - for (int y = 0; y < sequence.getHeight(); y++) { - int yOff = y * sequence.getWidth(); - for (int x = 0; x < sequence.getWidth(); x++) { - int pos = x + yOff; - if (!visited[pos]) { - Point3D.Double labCenter = new Point3D.Double(); - AtomicInteger area = new AtomicInteger(0); - Point p = new Point(x, y); - - findAreaAndColor(sequenceData, clusters, finalClusters, visited, p, labCenter, area, - usedLabels.getAndIncrement()); - - labs.add(labCenter); - areas.add(area.get() / (double) (S * S)); - firstPoints.add(p); - } - } - } - - //System.out.println("unvisited = " + IntStream.range(0, visited.length).filter(i -> visited[i] == false).sum()); - - // find neighbors and merge independent clusters - boolean[] mergedClusters = new boolean[usedLabels.get()]; - int[] mergedRefs = IntStream.range(0, usedLabels.get()).toArray(); - IntStream.range(firstUnknownCluster, usedLabels.get()).forEach(i -> { - List<Integer> neighbors = findNeighbors(finalClusters, visited, firstPoints.get(i)); - if (neighbors.size() > 0) { - int bestNeighbour = neighbors.get(0); - double bestL = Double.MAX_VALUE; - - //boolean found = false; - for (Integer j: neighbors) { - if (j < i) { - //found = true; - double l = computeL(labs, areas, i, j); - if (l < bestL) { - bestNeighbour = j; - bestL = l; - } - } - } - /* - * if (!found) { for (Integer j: neighbors) { double l = computeL(labs, - * areas, i, j); if (l < bestL) { bestNeighbour = j; bestL = l; } } } - * - * if (found) { - */ - double rArea = areas.get(i); - double relSPSize = rArea / 4; - relSPSize *= relSPSize; - - double coeff = relSPSize * (1.0 + bestL); - if (coeff < EPSILON) { - mergedClusters[i] = true; - mergedRefs[i] = bestNeighbour; - } - // } else { - // mergedClusters[i] = true; - // mergedRefs[i] = bestNeighbour; - // } - } else { - System.err.format("Cluster at (%d, %d) has no neighbors", firstPoints.get(i).x, firstPoints.get(i).y); - } - }); - IntStream.range(firstUnknownCluster, usedLabels.get()).forEach(i -> { - if (mergedClusters[i]) { - int appliedLabel = i; - while (appliedLabel != mergedRefs[appliedLabel]) { - appliedLabel = mergedRefs[appliedLabel]; - } - findAreaAndColor(sequenceData, clusters, finalClusters, visited, firstPoints.get(i), new Point3D.Double(), - new AtomicInteger(0), appliedLabel); - } - - }); - - if (this.computeROIs) { - // Create and add ROIs to sequence - this.rois = new ArrayList<>(); - IntStream.range(0, usedLabels.get()).forEach(i -> { - if (!mergedClusters[i]) { - ROI2DArea roi = defineROI(finalClusters, firstPoints.get(i), labs.get(i)); - rois.add(roi); - } - }); - sequence.addROIs(rois, false); - } else { - List<int[]> rgbs = labs.stream().map(lab -> CIELab.toRGB(lab.x, lab.y, lab.z)).collect(Collectors.toList()); - superPixelsResult = new Sequence( - new IcyBufferedImage(sequence.getWidth(), sequence.getHeight(), 3, DataType.UBYTE)); - - superPixelsResult.setPixelSizeX(sequence.getPixelSizeX()); - superPixelsResult.setPixelSizeY(sequence.getPixelSizeY()); - superPixelsResult.setPixelSizeZ(sequence.getPixelSizeZ()); - superPixelsResult.setPositionX(sequence.getPositionX()); - superPixelsResult.setPositionY(sequence.getPositionY()); - superPixelsResult.setPositionZ(sequence.getPositionZ()); - - superPixelsResult.beginUpdate(); - double[][] spData = Array2DUtil.arrayToDoubleArray(superPixelsResult.getDataXYC(0, 0), - superPixelsResult.isSignedDataType()); - for (int y = 0, yOff; y < sequence.getHeight(); y++) { - yOff = y * sequence.getWidth(); - for (int x = 0; x < sequence.getWidth(); x++) { - final int pos = x + yOff; - int[] rgbVal = rgbs.get(finalClusters[pos]); - - IntStream.range(0, 3).forEach(c -> { - spData[c][pos] = rgbVal[c]; - }); - - } - } - Array2DUtil.doubleArrayToArray(spData, superPixelsResult.getDataXYC(0, 0)); - superPixelsResult.dataChanged(); - superPixelsResult.endUpdate(); - } - } - - private void findAreaAndColor(double[][] sequenceData, int[] clusters, int[] newClusters, boolean[] visited, Point p, - Point3D.Double labCenter, AtomicInteger area, int label) { - int posp = p.x + p.y * sequence.getWidth(); - area.set(0); - labCenter.x = 0d; - labCenter.y = 0d; - labCenter.z = 0d; - - Deque<Point> q = new LinkedList<>(); - int val = clusters[posp]; - - visited[posp] = true; - q.add(p); - - while (!q.isEmpty()) { - Point pti = q.pop(); - int posi = pti.x + pti.y * sequence.getWidth(); - - newClusters[posi] = label; - area.getAndIncrement(); - double[] labi = getCIELab(sequenceData, posi); - labCenter.x += (labi[0] - labCenter.x) / area.get(); - labCenter.y += (labi[1] - labCenter.y) / area.get(); - labCenter.z += (labi[2] - labCenter.z) / area.get(); - - int[] ds = new int[] {0, -1, 0, 1, 0}; - for (int is = 1; is < ds.length; is++) { - Point ptn = new Point(pti.x + ds[is - 1], pti.y + ds[is]); - int posn = ptn.x + ptn.y * sequence.getWidth(); - if (sequence.getBounds2D().contains(ptn.x, ptn.y) && !visited[posn] && clusters[posn] == val) { - visited[posn] = true; - q.add(ptn); - } - } - - } - } - - private List<Integer> findNeighbors(int[] newClusters, boolean[] visited, Point p) { - int posp = p.x + p.y * sequence.getWidth(); - - HashSet<Integer> neighs = new HashSet<>(); - - Deque<Point> q = new LinkedList<>(); - int val = newClusters[posp]; - - visited[posp] = false; - q.add(p); - - while (!q.isEmpty()) { - Point pti = q.pop(); - - int[] ds = new int[] {0, -1, 0, 1, 0}; - for (int is = 1; is < ds.length; is++) { - Point ptn = new Point(pti.x + ds[is - 1], pti.y + ds[is]); - int posn = ptn.x + ptn.y * sequence.getWidth(); - if (sequence.getBounds2D().contains(ptn.x, ptn.y)) { - if (newClusters[posn] == val) { - if (visited[posn]) { - visited[posn] = false; - q.add(ptn); - } - } else { - neighs.add(newClusters[posn]); - } - } - } - - } - return new ArrayList<Integer>(neighs); - } - - private double computeL(List<Point3D.Double> labs, List<Double> areas, int i, Integer j) { - Point3D.Double diffLab = new Point3D.Double(); - diffLab.x = labs.get(j).x - labs.get(i).x; - diffLab.y = labs.get(j).y - labs.get(i).y; - diffLab.z = labs.get(j).z - labs.get(i).z; - - try { - return diffLab.length() / areas.get(j); - } catch (Exception e) { - throw e; - } - } - - private ROI2DArea defineROI(int[] newClusters, Point p, Point3D.Double labP) { - int posp = p.x + p.y * sequence.getWidth(); - double[] lab = new double[] {labP.x, labP.y, labP.z}; - int[] rgb = CIELab.toRGB(lab); - - boolean[] bMask = new boolean[newClusters.length]; - int val = newClusters[posp]; - IntStream.range(0, newClusters.length).filter(pi -> newClusters[pi] == val).forEach(pi -> bMask[pi] = true); - - BooleanMask2D mask = new BooleanMask2D(sequence.getBounds2D(), bMask); - ROI2DArea roi = new ROI2DArea(mask); - roi.setColor(new Color(rgb[0], rgb[1], rgb[2])); - return roi; - } - - public Sequence getResultSequence() { - return this.superPixelsResult; - } +public class SLICTask +{ + + private static final double EPSILON = 0.25; + static int MAX_ITERATIONS = 10; + static double ERROR_THRESHOLD = 1e-2; + + private Sequence sequence; + private int S; + private int Sx; + private int Sy; + private int SPnum; + private double rigidity; + + private boolean computeROIs; + private List<ROI> rois; + private Sequence superPixelsResult; + + private Map<Point3D.Integer, double[]> colorLUT; + private Map<Point, Double> distanceLUT; + + public SLICTask(Sequence sequence, int SPSize, double rigidity, boolean computeROIs) + { + this.sequence = sequence; + this.S = SPSize; + this.Sx = (sequence.getWidth() + S / 2 - 1) / S; + this.Sy = (sequence.getHeight() + S / 2 - 1) / S; + this.SPnum = Sx * Sy; + this.rigidity = rigidity; + + this.computeROIs = computeROIs; + + colorLUT = new HashMap<>(); + distanceLUT = new HashMap<>(); + } + + public void execute() + { + + double[] ls = new double[SPnum]; + double[] as = new double[SPnum]; + double[] bs = new double[SPnum]; + + double[] cxs = new double[SPnum]; + double[] cys = new double[SPnum]; + + double[][] sequenceData = Array2DUtil.arrayToDoubleArray(sequence.getDataXYC(0, 0), + sequence.isSignedDataType()); + + initialize(ls, as, bs, cxs, cys, sequenceData); + + int[] clusters = new int[sequence.getWidth() * sequence.getHeight()]; + double[] distances = Arrays.stream(clusters).mapToDouble(i -> Double.MAX_VALUE).toArray(); + + int iterations = 0; + double error = 0; + do + { + assignClusters(sequenceData, clusters, distances, ls, as, bs, cxs, cys); + error = updateClusters(sequenceData, clusters, ls, as, bs, cxs, cys); + iterations++; + System.out.format("Iteration=%d, Error=%f%n", iterations, error); + } + while (error > ERROR_THRESHOLD && iterations < MAX_ITERATIONS); + System.out.format("End of iterations%n"); + + computeSuperpixels(sequenceData, clusters, ls, as, bs, cxs, cys); + } + + private void initialize(double[] ls, double[] as, double[] bs, double[] cxs, double[] cys, double[][] sequenceData) + { + + IntStream.range(0, SPnum).forEach(sp -> { + int x, y, yOff, bestx, besty, bestyOff, i, j, yjOff; + double bestGradient, candidateGradient; + + int spx = sp % Sx; + int spy = sp / Sx; + + x = S / 2 + (spx) * S; + y = S / 2 + (spy) * S; + yOff = y * sequence.getWidth(); + + cxs[sp] = bestx = x; + cys[sp] = besty = y; + bestyOff = yOff; + bestGradient = Double.MAX_VALUE; + + for (j = -1; j <= 1; j++) + { + if (y + j >= 0 && y + j < sequence.getHeight()) + { + yjOff = (y + j) * sequence.getWidth(); + for (i = -1; i <= 1; i++) + { + if (x >= 0 && x < sequence.getWidth() && x + i >= 0 && x + i < sequence.getWidth()) + { + candidateGradient = getGradient(sequenceData, x + yOff, (x + i) + yjOff); + if (candidateGradient < bestGradient) + { + bestx = x + i; + besty = y + j; + bestyOff = yjOff; + bestGradient = candidateGradient; + } + } + } + } + } + + cxs[sp] = bestx; + cys[sp] = besty; + int bestPos = bestx + bestyOff; + + double[] bestLAB = getCIELab(sequenceData, bestPos); + ls[sp] = bestLAB[0]; + as[sp] = bestLAB[1]; + bs[sp] = bestLAB[2]; + + }); + + } + + private double getGradient(double[][] data, int pos1, int pos2) + { + int[] valRGB1 = new int[3]; + int[] valRGB2 = new int[3]; + IntStream.range(0, 3).forEach(c -> { + try + { + valRGB1[c] = (int) Math.round(255 * (data[c % sequence.getSizeC()][pos1] / sequence.getDataTypeMax())); + valRGB2[c] = (int) Math.round(255 * (data[c % sequence.getSizeC()][pos2] / sequence.getDataTypeMax())); + } + catch (Exception e) + { + throw e; + } + }); + + double[] val1 = CIELab.fromRGB(valRGB1); + double[] val2 = CIELab.fromRGB(valRGB2); + + double avgGrad = IntStream.range(0, 3).mapToDouble(c -> val2[c] - val1[c]).average().getAsDouble(); + + return avgGrad; + } + + private double[] getCIELab(double[][] sequenceData, int pos) + { + + int[] rgbVal = new int[3]; + IntStream.range(0, 3).forEach(c -> { + rgbVal[c] = (int) Math + .round(255 * (sequenceData[c % sequence.getSizeC()][pos] / sequence.getDataTypeMax())); + }); + Point3D.Integer rgbPoint = new Point3D.Integer(rgbVal); + + double[] LAB = colorLUT.get(rgbPoint); + if (LAB == null) + { + int[] RGB = new int[3]; + IntStream.range(0, 3).forEach(c -> { + RGB[c] = (int) Math + .round(255 * (sequenceData[c % sequence.getSizeC()][pos] / sequence.getDataTypeMax())); + }); + + LAB = CIELab.fromRGB(RGB); + colorLUT.put(rgbPoint, LAB); + } + return LAB; + } + + private void assignClusters(double[][] sequenceData, int[] clusters, double[] distances, double[] ls, double[] as, + double[] bs, double[] cxs, double[] cys) + { + // Each cluster + IntStream.range(0, SPnum).forEach(k -> { + double ckx = cxs[k], cky = cys[k], lk = ls[k], ak = as[k], bk = bs[k]; + int posk = ((int) ckx) + ((int) cky) * sequence.getWidth(); + distances[posk] = 0; + clusters[posk] = k; + + // 2Sx2S window assignment + int j, i, yOff; + for (j = -S; j < S; j++) + { + int ciy = (int) (cky + j); + if (ciy >= 0 && ciy < sequence.getHeight()) + { + yOff = ciy * sequence.getWidth(); + for (i = -S; i < S; i++) + { + int cix = (int) (ckx + i); + if (cix >= 0 && cix < sequence.getWidth()) + { + int posi = cix + yOff; + double[] LABi = getCIELab(sequenceData, posi); + double li = LABi[0]; + double ai = LABi[1]; + double bi = LABi[2]; + + double di = getDistance(ckx, cky, lk, ak, bk, i, j, li, ai, bi); + if (di < distances[posi]) + { + distances[posi] = di; + clusters[posi] = clusters[posk]; + } + } + } + } + } + }); + } + + private double getDistance(double ckx, double cky, double lk, double ak, double bk, int dx, int dy, double li, + double ai, double bi) + { + + double diffl, diffa, diffb; + diffl = li - lk; + diffa = ai - ak; + diffb = bi - bk; + + double dc = Math.sqrt(diffl * diffl + diffa * diffa + diffb * diffb); + Point dPt = new Point((int) Math.min(dx, dy), (int) Math.max(dx, dy)); + Double ds = distanceLUT.get(dPt); + if (ds == null) + { + ds = Math.sqrt(dx * dx + dy * dy); + distanceLUT.put(dPt, ds); + } + return Math.sqrt(dc * dc + (ds * ds) * (rigidity * rigidity * S)); + } + + private double updateClusters(double[][] sequenceData, int[] clusters, double[] ls, double[] as, double[] bs, + double[] cxs, double[] cys) + { + + double[] newls = new double[SPnum]; + double[] newas = new double[SPnum]; + double[] newbs = new double[SPnum]; + double[] newcxs = new double[SPnum]; + double[] newcys = new double[SPnum]; + int[] cants = new int[SPnum]; + + // int blockSize = 500; + // IntStream.iterate(0, sy -> sy+blockSize).limit((int)Math.ceil(sequence.getHeight()/blockSize)).forEach(y0->{ + // int y1 = Math.min(y0+blockSize, sequence.getHeight() - y0); + // IntStream.iterate(0, sx -> sx+blockSize).limit((int)Math.ceil(sequence.getWidth()/blockSize)).forEach(x0->{ + // int x1 = Math.min(x0+blockSize, sequence.getWidth() - x0); + // }); + // }); + for (int y = 0, yOff; y < sequence.getHeight(); y++) + { + yOff = y * sequence.getWidth(); + for (int x = 0, pos; x < sequence.getWidth(); x++) + { + pos = x + yOff; + int sp = clusters[pos]; + double[] lab = getCIELab(sequenceData, pos); + + cants[sp]++; + + newls[sp] += (lab[0] - newls[sp]) / cants[sp]; + newas[sp] += (lab[1] - newas[sp]) / cants[sp]; + newbs[sp] += (lab[2] - newbs[sp]) / cants[sp]; + newcxs[sp] += (x - newcxs[sp]) / cants[sp]; + newcys[sp] += (y - newcys[sp]) / cants[sp]; + } + } + + double error = IntStream.range(0, SPnum).mapToDouble(sp -> { + // double diffl = ls[sp] - newls[sp]; + // double diffa = as[sp] - newas[sp]; + // double diffb = bs[sp] - newbs[sp]; + double diffx = cxs[sp] - newcxs[sp]; + double diffy = cys[sp] - newcys[sp]; + + ls[sp] = newls[sp]; + as[sp] = newas[sp]; + bs[sp] = newbs[sp]; + cxs[sp] = newcxs[sp]; + cys[sp] = newcys[sp]; + + return Math.sqrt(diffx * diffx + diffy * diffy/* + diffl * diffl + diffa * diffa + diffb * diffb */); + }).sum() / SPnum; + + return error; + } + + private void computeSuperpixels(double[][] sequenceData, int[] clusters, double[] ls, double[] as, double[] bs, + double[] cxs, double[] cys) + { + + boolean[] visited = new boolean[clusters.length]; + int[] finalClusters = new int[clusters.length]; + + List<Point3D.Double> labs = new ArrayList<>(SPnum); + List<Double> areas = new ArrayList<>(SPnum); + List<Point> firstPoints = new ArrayList<>(SPnum); + + // fill known clusters + AtomicInteger usedLabels = new AtomicInteger(0); + IntStream.range(0, SPnum).forEach(i -> { + Point3D.Double labCenter = new Point3D.Double(); + AtomicInteger area = new AtomicInteger(0); + Point p = new Point((int) Math.round(cxs[i]), (int) Math.round(cys[i])); + int pPos = p.x + p.y * sequence.getWidth(); + + if (clusters[pPos] == i && !visited[pPos]) + { + findAreaAndColor(sequenceData, clusters, finalClusters, visited, p, labCenter, area, + usedLabels.getAndIncrement()); + labs.add(labCenter); + areas.add(area.get() / (double) (S * S)); + firstPoints.add(p); + } + + }); + + int firstUnknownCluster = usedLabels.get(); + + // fill independent clusters + for (int y = 0; y < sequence.getHeight(); y++) + { + int yOff = y * sequence.getWidth(); + for (int x = 0; x < sequence.getWidth(); x++) + { + int pos = x + yOff; + if (!visited[pos]) + { + Point3D.Double labCenter = new Point3D.Double(); + AtomicInteger area = new AtomicInteger(0); + Point p = new Point(x, y); + + findAreaAndColor(sequenceData, clusters, finalClusters, visited, p, labCenter, area, + usedLabels.getAndIncrement()); + + labs.add(labCenter); + areas.add(area.get() / (double) (S * S)); + firstPoints.add(p); + } + } + } + + // System.out.println("unvisited = " + IntStream.range(0, visited.length).filter(i -> visited[i] == false).sum()); + + // find neighbors and merge independent clusters + boolean[] mergedClusters = new boolean[usedLabels.get()]; + int[] mergedRefs = IntStream.range(0, usedLabels.get()).toArray(); + IntStream.range(firstUnknownCluster, usedLabels.get()).forEach(i -> { + List<Integer> neighbors = findNeighbors(finalClusters, visited, firstPoints.get(i)); + if (neighbors.size() > 0) + { + int bestNeighbour = neighbors.get(0); + double bestL = Double.MAX_VALUE; + + // boolean found = false; + for (Integer j : neighbors) + { + if (j < i) + { + // found = true; + double l = computeL(labs, areas, i, j); + if (l < bestL) + { + bestNeighbour = j; + bestL = l; + } + } + } + /* + * if (!found) { for (Integer j: neighbors) { double l = computeL(labs, + * areas, i, j); if (l < bestL) { bestNeighbour = j; bestL = l; } } } + * + * if (found) { + */ + double rArea = areas.get(i); + double relSPSize = rArea / 4; + relSPSize *= relSPSize; + + double coeff = relSPSize * (1.0 + bestL); + if (coeff < EPSILON) + { + mergedClusters[i] = true; + mergedRefs[i] = bestNeighbour; + } + // } else { + // mergedClusters[i] = true; + // mergedRefs[i] = bestNeighbour; + // } + } + else + { + System.err.format("Cluster at (%d, %d) has no neighbors", firstPoints.get(i).x, firstPoints.get(i).y); + } + }); + IntStream.range(firstUnknownCluster, usedLabels.get()).forEach(i -> { + if (mergedClusters[i]) + { + int appliedLabel = i; + while (appliedLabel != mergedRefs[appliedLabel]) + { + appliedLabel = mergedRefs[appliedLabel]; + } + findAreaAndColor(sequenceData, clusters, finalClusters, visited, firstPoints.get(i), + new Point3D.Double(), new AtomicInteger(0), appliedLabel); + } + + }); + + if (this.computeROIs) + { + // Create and add ROIs to sequence + this.rois = new ArrayList<>(); + IntStream.range(0, usedLabels.get()).forEach(i -> { + if (!mergedClusters[i]) + { + ROI2DArea roi = defineROI(finalClusters, firstPoints.get(i), labs.get(i)); + rois.add(roi); + } + }); + sequence.addROIs(rois, false); + } + else + { + List<int[]> rgbs = labs.stream().map(lab -> CIELab.toRGB(lab.x, lab.y, lab.z)).collect(Collectors.toList()); + superPixelsResult = new Sequence( + new IcyBufferedImage(sequence.getWidth(), sequence.getHeight(), 3, DataType.UBYTE)); + + superPixelsResult.setPixelSizeX(sequence.getPixelSizeX()); + superPixelsResult.setPixelSizeY(sequence.getPixelSizeY()); + superPixelsResult.setPixelSizeZ(sequence.getPixelSizeZ()); + superPixelsResult.setPositionX(sequence.getPositionX()); + superPixelsResult.setPositionY(sequence.getPositionY()); + superPixelsResult.setPositionZ(sequence.getPositionZ()); + + superPixelsResult.beginUpdate(); + double[][] spData = Array2DUtil.arrayToDoubleArray(superPixelsResult.getDataXYC(0, 0), + superPixelsResult.isSignedDataType()); + for (int y = 0, yOff; y < sequence.getHeight(); y++) + { + yOff = y * sequence.getWidth(); + for (int x = 0; x < sequence.getWidth(); x++) + { + final int pos = x + yOff; + int[] rgbVal = rgbs.get(finalClusters[pos]); + + IntStream.range(0, 3).forEach(c -> { + spData[c][pos] = rgbVal[c]; + }); + + } + } + Array2DUtil.doubleArrayToArray(spData, superPixelsResult.getDataXYC(0, 0)); + superPixelsResult.dataChanged(); + superPixelsResult.endUpdate(); + } + } + + private void findAreaAndColor(double[][] sequenceData, int[] clusters, int[] newClusters, boolean[] visited, + Point p, Point3D.Double labCenter, AtomicInteger area, int label) + { + int posp = p.x + p.y * sequence.getWidth(); + area.set(0); + labCenter.x = 0d; + labCenter.y = 0d; + labCenter.z = 0d; + + Deque<Point> q = new LinkedList<>(); + int val = clusters[posp]; + + visited[posp] = true; + q.add(p); + + while (!q.isEmpty()) + { + Point pti = q.pop(); + int posi = pti.x + pti.y * sequence.getWidth(); + + newClusters[posi] = label; + area.getAndIncrement(); + double[] labi = getCIELab(sequenceData, posi); + labCenter.x += (labi[0] - labCenter.x) / area.get(); + labCenter.y += (labi[1] - labCenter.y) / area.get(); + labCenter.z += (labi[2] - labCenter.z) / area.get(); + + int[] ds = new int[] {0, -1, 0, 1, 0}; + for (int is = 1; is < ds.length; is++) + { + Point ptn = new Point(pti.x + ds[is - 1], pti.y + ds[is]); + int posn = ptn.x + ptn.y * sequence.getWidth(); + if (sequence.getBounds2D().contains(ptn.x, ptn.y) && !visited[posn] && clusters[posn] == val) + { + visited[posn] = true; + q.add(ptn); + } + } + + } + } + + private List<Integer> findNeighbors(int[] newClusters, boolean[] visited, Point p) + { + int posp = p.x + p.y * sequence.getWidth(); + + HashSet<Integer> neighs = new HashSet<>(); + + Deque<Point> q = new LinkedList<>(); + int val = newClusters[posp]; + + visited[posp] = false; + q.add(p); + + while (!q.isEmpty()) + { + Point pti = q.pop(); + + int[] ds = new int[] {0, -1, 0, 1, 0}; + for (int is = 1; is < ds.length; is++) + { + Point ptn = new Point(pti.x + ds[is - 1], pti.y + ds[is]); + int posn = ptn.x + ptn.y * sequence.getWidth(); + if (sequence.getBounds2D().contains(ptn.x, ptn.y)) + { + if (newClusters[posn] == val) + { + if (visited[posn]) + { + visited[posn] = false; + q.add(ptn); + } + } + else + { + neighs.add(newClusters[posn]); + } + } + } + + } + return new ArrayList<Integer>(neighs); + } + + private double computeL(List<Point3D.Double> labs, List<Double> areas, int i, Integer j) + { + Point3D.Double diffLab = new Point3D.Double(); + diffLab.x = labs.get(j).x - labs.get(i).x; + diffLab.y = labs.get(j).y - labs.get(i).y; + diffLab.z = labs.get(j).z - labs.get(i).z; + + try + { + return diffLab.length() / areas.get(j); + } + catch (Exception e) + { + throw e; + } + } + + private ROI2DArea defineROI(int[] newClusters, Point p, Point3D.Double labP) + { + int posp = p.x + p.y * sequence.getWidth(); + double[] lab = new double[] {labP.x, labP.y, labP.z}; + int[] rgb = CIELab.toRGB(lab); + + int val = newClusters[posp]; + Rectangle seqBounds = sequence.getBounds2D(); + ROI2DArea roi1 = new ROI2DArea(new BooleanMask2D()); + IntStream.range(0, newClusters.length).filter(pi -> newClusters[pi] == val) + .forEach(pi -> roi1.addPoint(pi % seqBounds.width, pi / seqBounds.width));// .forEach(pi -> bMask[pi] = true); + + roi1.setColor(new Color(rgb[0], rgb[1], rgb[2])); + return roi1; + } + + public Sequence getResultSequence() + { + return this.superPixelsResult; + } } diff --git a/SLIC/src/main/java/plugins/danyfel80/islic/SLIC.java b/SLIC/src/main/java/plugins/danyfel80/islic/SLIC.java index 1c467d266c7be94474adba4fc68fd9fc55affa14..ea1dc51dd027f4865f799ac472768effd2621e9f 100644 --- a/SLIC/src/main/java/plugins/danyfel80/islic/SLIC.java +++ b/SLIC/src/main/java/plugins/danyfel80/islic/SLIC.java @@ -2,6 +2,9 @@ package plugins.danyfel80.islic; import algorithms.danyfel80.islic.SLICTask; import icy.gui.dialog.MessageDialog; +import icy.main.Icy; +import icy.plugin.PluginLauncher; +import icy.plugin.PluginLoader; import plugins.adufour.blocks.lang.Block; import plugins.adufour.blocks.util.VarList; import plugins.adufour.ezplug.EzPlug; @@ -10,99 +13,123 @@ import plugins.adufour.ezplug.EzVarDouble; import plugins.adufour.ezplug.EzVarInteger; import plugins.adufour.ezplug.EzVarSequence; -public class SLIC extends EzPlug implements Block { - - private EzVarSequence inSequence; - private EzVarInteger inSPSize; - private EzVarDouble inSPReg; - private EzVarBoolean inIsROIOutput; - - private EzVarSequence outSequence; - - @Override - protected void initialize() { - inSequence = new EzVarSequence("Sequence"); - inSPSize = new EzVarInteger("Superpixel size"); - inSPReg = new EzVarDouble("Superpixels regularity"); - inIsROIOutput = new EzVarBoolean("Output as ROIs", false); - - inSPSize.setValue(30); - inSPReg.setValue(0.2); - - addEzComponent(inSequence); - addEzComponent(inSPSize); - addEzComponent(inSPReg); - addEzComponent(inIsROIOutput); - } - - @Override - public void declareInput(VarList inputMap) { - inSequence = new EzVarSequence("Sequence"); - inSPSize = new EzVarInteger("Superpixel size"); - inSPReg = new EzVarDouble("Superpixels regularity"); - inIsROIOutput = new EzVarBoolean("Output as ROIs", false); - - inSPSize.setValue(30); - inSPReg.setValue(0.2); - - inputMap.add(inSequence.name, inSequence.getVariable()); - inputMap.add(inSPSize.name, inSPSize.getVariable()); - inputMap.add(inSPReg.name, inSPReg.getVariable()); - inputMap.add(inIsROIOutput.name, inIsROIOutput.getVariable()); - } - - @Override - public void declareOutput(VarList outputMap) { - outSequence = new EzVarSequence("Result"); - - outputMap.add(outSequence.name, outSequence.getVariable()); - } - - @Override - protected void execute() { - long procTime, startTime, endTime; - SLICTask task; - try { - task = new SLICTask(inSequence.getValue(), inSPSize.getValue(), inSPReg.getValue(), inIsROIOutput.getValue()); - } catch (Exception e) { - e.printStackTrace(); - if (!this.isHeadLess()) { - MessageDialog.showDialog("Initialization Error", - String.format("SLIC could not start properly: " + e.getMessage()), MessageDialog.ERROR_MESSAGE); - } - return; - } - startTime = System.currentTimeMillis(); - try { - task.execute(); - } catch (Exception e) { - e.printStackTrace(); - if (!this.isHeadLess()) { - MessageDialog.showDialog("Runtime Error", String.format("SLIC could not run properly: " + e.getMessage()), - MessageDialog.ERROR_MESSAGE); - } - return; - } - - endTime = System.currentTimeMillis(); - procTime = endTime - startTime; - System.out.println(String.format("SLIC finished in %d milliseconds", procTime)); - if (!this.inIsROIOutput.getValue()) { - task.getResultSequence().setName(inSequence.getValue().getName() - + String.format("_SLIC(size=%s,reg=%.2f)", inSPSize.getValue().intValue(), inSPReg.getValue().doubleValue())); - } - if (!this.isHeadLess()) { - MessageDialog.showDialog(String.format("SLIC finished in %d milliseconds", procTime)); - addSequence(task.getResultSequence()); - } else { - outSequence.setValue(task.getResultSequence()); - } - - } - - @Override - public void clean() { - // TODO Auto-generated method stub - } - +public class SLIC extends EzPlug implements Block +{ + + private EzVarSequence inSequence; + private EzVarInteger inSPSize; + private EzVarDouble inSPReg; + private EzVarBoolean inIsROIOutput; + + private EzVarSequence outSequence; + + @Override + protected void initialize() + { + inSequence = new EzVarSequence("Sequence"); + inSPSize = new EzVarInteger("Superpixel size"); + inSPReg = new EzVarDouble("Superpixels regularity"); + inIsROIOutput = new EzVarBoolean("Output as ROIs", false); + + inSPSize.setValue(30); + inSPReg.setValue(0.2); + + addEzComponent(inSequence); + addEzComponent(inSPSize); + addEzComponent(inSPReg); + addEzComponent(inIsROIOutput); + } + + @Override + public void declareInput(VarList inputMap) + { + inSequence = new EzVarSequence("Sequence"); + inSPSize = new EzVarInteger("Superpixel size"); + inSPReg = new EzVarDouble("Superpixels regularity"); + inIsROIOutput = new EzVarBoolean("Output as ROIs", false); + + inSPSize.setValue(30); + inSPReg.setValue(0.2); + + inputMap.add(inSequence.name, inSequence.getVariable()); + inputMap.add(inSPSize.name, inSPSize.getVariable()); + inputMap.add(inSPReg.name, inSPReg.getVariable()); + inputMap.add(inIsROIOutput.name, inIsROIOutput.getVariable()); + } + + @Override + public void declareOutput(VarList outputMap) + { + outSequence = new EzVarSequence("Result"); + + outputMap.add(outSequence.name, outSequence.getVariable()); + } + + @Override + protected void execute() + { + long procTime, startTime, endTime; + SLICTask task; + try + { + task = new SLICTask(inSequence.getValue(), inSPSize.getValue(), inSPReg.getValue(), + inIsROIOutput.getValue()); + } + catch (Exception e) + { + e.printStackTrace(); + if (!this.isHeadLess()) + { + MessageDialog.showDialog("Initialization Error", + String.format("SLIC could not start properly: " + e.getMessage()), MessageDialog.ERROR_MESSAGE); + } + return; + } + startTime = System.currentTimeMillis(); + try + { + task.execute(); + } + catch (Exception e) + { + e.printStackTrace(); + if (!this.isHeadLess()) + { + MessageDialog.showDialog("Runtime Error", + String.format("SLIC could not run properly: " + e.getMessage()), MessageDialog.ERROR_MESSAGE); + } + return; + } + + endTime = System.currentTimeMillis(); + procTime = endTime - startTime; + System.out.println(String.format("SLIC finished in %d milliseconds", procTime)); + if (!this.inIsROIOutput.getValue()) + { + task.getResultSequence().setName(inSequence.getValue().getName() + String.format("_SLIC(size=%s,reg=%.2f)", + inSPSize.getValue().intValue(), inSPReg.getValue().doubleValue())); + } + if (!this.isHeadLess()) + { + MessageDialog.showDialog(String.format("SLIC finished in %d milliseconds", procTime)); + addSequence(task.getResultSequence()); + } + else + { + outSequence.setValue(task.getResultSequence()); + } + + } + + @Override + public void clean() + { + // TODO Auto-generated method stub + } + + public static void main(String[] args) + { + Icy.main(args); + PluginLauncher.start(PluginLoader.getPlugin(SLIC.class.getName())); + } }