diff --git a/SLIC/.gitignore b/SLIC/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..966b6392642cecda2fe31cbad66206edf7296202
--- /dev/null
+++ b/SLIC/.gitignore
@@ -0,0 +1,6 @@
+/.gradle/
+/.settings/
+/bin/
+/.classpath
+/.project
+/build/
diff --git a/SLIC/build.gradle b/SLIC/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..93ab67e0809e71e9219144758b3ec03fb09c9801
--- /dev/null
+++ b/SLIC/build.gradle
@@ -0,0 +1,60 @@
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+version = '0.1.0'
+println project.name
+
+repositories {
+    mavenCentral()
+    jcenter()
+}
+
+dependencies {
+    compile 'org.slf4j:slf4j-api:1.7.21'
+    
+    compile files("${System.env.ICY_HOME}/icy.jar") // Icy core
+    compile files("${System.env.ICY_HOME}/lib/vecmath.jar") // javax vecmath
+    compile files("${System.env.ICY_HOME}/lib/bioformats.jar") // bioformats
+    compile files("${System.env.ICY_HOME}/plugins/adufour/ezplug/EzPlug.jar") // EzPlug
+    compile files("${System.env.ICY_HOME}/plugins/adufour/blocks/Blocks.jar") // Blocks
+    compile files("${System.env.ICY_HOME}/plugins/adufour/protocols/Protocols.jar") // Protocols
+
+    testCompile 'junit:junit:4.12'
+}
+
+// In this section source and test files are declared
+sourceSets {
+    main {
+        java {
+            srcDirs = ['src']
+            resources.srcDirs = ['res']
+        }
+        test {
+            java.srcDirs = ['test']
+            resources.srcDirs = ['res']
+        }
+        
+    }
+}
+
+// In this section we add the icy nature of the project in eclipse
+eclipse {
+  project {
+    natures += ['icy.icy4eclipse.core.icynature']
+  }
+}
+
+task sourcesJar(type: Jar, dependsOn: classes) {
+    classifier = 'sources'
+    from sourceSets.main.allSource
+}
+
+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/src/algorithms/danyfel80/islic/CIELab.java b/SLIC/src/algorithms/danyfel80/islic/CIELab.java
new file mode 100644
index 0000000000000000000000000000000000000000..4c6a7e1b2ee462e290326bdab6337af65bd7e570
--- /dev/null
+++ b/SLIC/src/algorithms/danyfel80/islic/CIELab.java
@@ -0,0 +1,192 @@
+package algorithms.danyfel80.islic;
+
+public class CIELab {
+
+	public static class XYZ {
+
+		private static final double[][]	M		= { { 0.4124, 0.3576, 0.1805 }, { 0.2126, 0.7152, 0.0722 },
+				{ 0.0193, 0.1192, 0.9505 } };
+		private static final double[][]	Mi	= { { 3.2406, -1.5372, -0.4986 }, { -0.9689, 1.8758, 0.0415 },
+				{ 0.0557, -0.2040, 1.0570 } };
+
+		public static double[] fromRGB(int[] rgb) {
+			return fromRGB(rgb[0], rgb[1], rgb[2]);
+		}
+
+		public static double[] fromRGB(int R, int G, int B) {
+			double[] result = new double[3];
+
+			// convert 0..255 into 0..1
+			double r = R / 255.0;
+			double g = G / 255.0;
+			double b = B / 255.0;
+
+			// assume sRGB
+			if (r <= 0.04045) {
+				r = r / 12.92;
+			} else {
+				r = Math.pow(((r + 0.055) / 1.055), 2.4);
+			}
+			if (g <= 0.04045) {
+				g = g / 12.92;
+			} else {
+				g = Math.pow(((g + 0.055) / 1.055), 2.4);
+			}
+			if (b <= 0.04045) {
+				b = b / 12.92;
+			} else {
+				b = Math.pow(((b + 0.055) / 1.055), 2.4);
+			}
+
+			r *= 100.0;
+			g *= 100.0;
+			b *= 100.0;
+
+			// [X Y Z] = [r g b][M]
+			result[0] = (r * M[0][0]) + (g * M[0][1]) + (b * M[0][2]);
+			result[1] = (r * M[1][0]) + (g * M[1][1]) + (b * M[1][2]);
+			result[2] = (r * M[2][0]) + (g * M[2][1]) + (b * M[2][2]);
+
+			return result;
+		}
+
+		public static int[] toRGB(double[] xyz) {
+			return toRGB(xyz[0], xyz[1], xyz[2]);
+		}
+
+		public static int[] toRGB(double X, double Y, double Z) {
+			int[] result = new int[3];
+
+			double x = X / 100.0;
+			double y = Y / 100.0;
+			double z = Z / 100.0;
+
+			// [r g b] = [X Y Z][Mi]
+			double r = (x * Mi[0][0]) + (y * Mi[0][1]) + (z * Mi[0][2]);
+			double g = (x * Mi[1][0]) + (y * Mi[1][1]) + (z * Mi[1][2]);
+			double b = (x * Mi[2][0]) + (y * Mi[2][1]) + (z * Mi[2][2]);
+
+			// assume sRGB
+			if (r > 0.0031308) {
+				r = ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055);
+			} else {
+				r = (r * 12.92);
+			}
+			if (g > 0.0031308) {
+				g = ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055);
+			} else {
+				g = (g * 12.92);
+			}
+			if (b > 0.0031308) {
+				b = ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055);
+			} else {
+				b = (b * 12.92);
+			}
+
+			r = (r < 0) ? 0 : r;
+			g = (g < 0) ? 0 : g;
+			b = (b < 0) ? 0 : b;
+
+			// convert 0..1 into 0..255
+			result[0] = (int) Math.round(r * 255);
+			result[1] = (int) Math.round(g * 255);
+			result[2] = (int) Math.round(b * 255);
+
+			return result;
+		}
+
+	}
+
+	private static final double[] whitePoint = { 95.0429, 100.0, 108.8900 }; // D65
+
+	public static double[] fromRGB(int[] rgb) {
+		return fromXYZ(XYZ.fromRGB(rgb));
+	}
+
+	public static double[] fromRGB(int r, int g, int b) {
+		return fromXYZ(XYZ.fromRGB(r, g, b));
+	}
+
+	private static double[] fromXYZ(double[] xyz) {
+		return fromXYZ(xyz[0], xyz[1], xyz[2]);
+	}
+
+	public static double[] fromXYZ(double X, double Y, double Z) {
+		double x = X / whitePoint[0];
+    double y = Y / whitePoint[1];
+    double z = Z / whitePoint[2];
+
+    if (x > 0.008856) {
+      x = Math.pow(x, 1.0 / 3.0);
+    }
+    else {
+      x = (7.787 * x) + (16.0 / 116.0);
+    }
+    if (y > 0.008856) {
+      y = Math.pow(y, 1.0 / 3.0);
+    }
+    else {
+      y = (7.787 * y) + (16.0 / 116.0);
+    }
+    if (z > 0.008856) {
+      z = Math.pow(z, 1.0 / 3.0);
+    }
+    else {
+      z = (7.787 * z) + (16.0 / 116.0);
+    }
+
+    double[] result = new double[3];
+
+    result[0] = (116.0 * y) - 16.0;
+    result[1] = 500.0 * (x - y);
+    result[2] = 200.0 * (y - z);
+
+    return result;
+	}
+	
+	public static int[] toRGB(double[] lab) {
+		return XYZ.toRGB(toXYZ(lab));
+	}
+
+	public static int[] toRGB(double L, double a, double b) {
+		return XYZ.toRGB(toXYZ(L, a, b));
+	}
+	
+	public static double[] toXYZ(double[] lab) {
+		return toXYZ(lab[0], lab[1], lab[2]);
+	}
+
+	public static double[] toXYZ(double L, double a, double b) {
+		double[] result = new double[3];
+
+		double y = (L + 16.0) / 116.0;
+		double y3 = Math.pow(y, 3.0);
+		double x = (a / 500.0) + y;
+		double x3 = Math.pow(x, 3.0);
+		double z = y - (b / 200.0);
+		double z3 = Math.pow(z, 3.0);
+
+		if (y3 > 0.008856) {
+			y = y3;
+		} else {
+			y = (y - (16.0 / 116.0)) / 7.787;
+		}
+		if (x3 > 0.008856) {
+			x = x3;
+		} else {
+			x = (x - (16.0 / 116.0)) / 7.787;
+		}
+		if (z3 > 0.008856) {
+			z = z3;
+		} else {
+			z = (z - (16.0 / 116.0)) / 7.787;
+		}
+
+		// results in 0...100
+		result[0] = x * whitePoint[0];
+		result[1] = y * whitePoint[1];
+		result[2] = z * whitePoint[2];
+
+		return result;
+	}
+}
diff --git a/SLIC/src/algorithms/danyfel80/islic/SLICTask.java b/SLIC/src/algorithms/danyfel80/islic/SLICTask.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc7aac788141476446b5830a42d03c708203e4d6
--- /dev/null
+++ b/SLIC/src/algorithms/danyfel80/islic/SLICTask.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright 2010-2016 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 <http://www.gnu.org/licenses/>.
+ */
+package algorithms.danyfel80.islic;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import javax.vecmath.Point2i;
+import javax.vecmath.Point3d;
+import javax.vecmath.Point3i;
+import javax.vecmath.Vector3d;
+
+import icy.image.IcyBufferedImage;
+import icy.roi.BooleanMask2D;
+import icy.roi.ROI;
+import icy.sequence.Sequence;
+import icy.type.DataType;
+import icy.type.collection.array.Array2DUtil;
+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<Point3i, double[]> colorLUT;
+	private Map<Point2i, 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) {
+		Point3i rgbPoint = new Point3i();
+		int[] rgbVal = new int[3];
+		IntStream.range(0, 3).forEach(c -> {
+			rgbVal[c] = (int) Math.round(255 * (sequenceData[c % sequence.getSizeC()][pos] / sequence.getDataTypeMax()));
+		});
+		rgbPoint.set(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);
+		Point2i dPt = new Point2i((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> labs = new ArrayList<>(SPnum);
+		List<Double> areas = new ArrayList<>(SPnum);
+		List<Point2i> firstPoints = new ArrayList<>(SPnum);
+
+		// fill known clusters
+		AtomicInteger usedLabels = new AtomicInteger(0);
+		IntStream.range(0, SPnum).forEach(i -> {
+			Point3d labCenter = new Point3d();
+			AtomicInteger area = new AtomicInteger(0);
+			Point2i p = new Point2i((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 labCenter = new Point3d();
+					AtomicInteger area = new AtomicInteger(0);
+					Point2i p = new Point2i(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(),
+						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.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,
+			Point2i p, Point3d labCenter, AtomicInteger area, int label) {
+		int posp = p.x + p.y * sequence.getWidth();
+		area.set(0);
+		labCenter.set(0, 0, 0);
+
+		Deque<Point2i> q = new LinkedList<>();
+		int val = clusters[posp];
+
+		visited[posp] = true;
+		q.add(p);
+
+		while (!q.isEmpty()) {
+			Point2i 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++) {
+				Point2i ptn = new Point2i(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, Point2i p) {
+		int posp = p.x + p.y * sequence.getWidth();
+
+		HashSet<Integer> neighs = new HashSet<>();
+
+		Deque<Point2i> q = new LinkedList<>();
+		int val = newClusters[posp];
+
+		visited[posp] = false;
+		q.add(p);
+
+		while (!q.isEmpty()) {
+			Point2i pti = q.pop();
+
+			int[] ds = new int[] {0, -1, 0, 1, 0};
+			for (int is = 1; is < ds.length; is++) {
+				Point2i ptn = new Point2i(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> labs, List<Double> areas, int i, Integer j) {
+		Vector3d diffLab = new Vector3d();
+		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, Point2i p, Point3d 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;
+	}
+}
diff --git a/SLIC/src/plugins/danyfel80/islic/LABToRGB.java b/SLIC/src/plugins/danyfel80/islic/LABToRGB.java
new file mode 100644
index 0000000000000000000000000000000000000000..769ac462517a3909b874cee58d7323c5238a66ec
--- /dev/null
+++ b/SLIC/src/plugins/danyfel80/islic/LABToRGB.java
@@ -0,0 +1,50 @@
+package plugins.danyfel80.islic;
+
+import java.util.stream.IntStream;
+
+import algorithms.danyfel80.islic.CIELab;
+import icy.image.IcyBufferedImage;
+import icy.sequence.Sequence;
+import icy.type.DataType;
+import icy.type.collection.array.Array2DUtil;
+import plugins.adufour.ezplug.EzPlug;
+import plugins.adufour.ezplug.EzVarSequence;
+
+public class LABToRGB extends EzPlug {
+	
+	EzVarSequence inLabSequence;
+
+	@Override
+	protected void initialize() {
+		inLabSequence = new EzVarSequence("LAB sequence");
+		addEzComponent(inLabSequence);
+	}
+
+	@Override
+	protected void execute() {
+		Sequence labSequence = inLabSequence.getValue();
+		Sequence rgbSequence = new Sequence(new IcyBufferedImage(labSequence.getWidth(), labSequence.getHeight(),3, DataType.UBYTE));
+		
+		double[][] labIm = Array2DUtil.arrayToDoubleArray(labSequence.getDataXYC(0, 0), labSequence.isSignedDataType());
+		
+		rgbSequence.beginUpdate();
+		double[][] rgbIm = Array2DUtil.arrayToDoubleArray(rgbSequence.getDataXYC(0, 0), rgbSequence.isSignedDataType());
+		IntStream.range(0, rgbIm[0].length).forEach(pos->{
+			double[] lab = IntStream.range(0, 3).mapToDouble(c->labIm[c][pos]).toArray();
+			int[] rgb = CIELab.toRGB(lab);
+			IntStream.range(0, 3).forEach(c->rgbIm[c][pos] = rgb[c]);
+		});
+		Array2DUtil.doubleArrayToArray(rgbIm, rgbSequence.getDataXYC(0, 0));
+		rgbSequence.dataChanged();
+		rgbSequence.endUpdate();
+		
+		
+		addSequence(rgbSequence);
+	}
+
+	@Override
+	public void clean() {
+		// TODO Auto-generated method stub
+	}
+
+}
diff --git a/SLIC/src/plugins/danyfel80/islic/RGBToLAB.java b/SLIC/src/plugins/danyfel80/islic/RGBToLAB.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac6139a254146b43809c93c8846c543b2c488d62
--- /dev/null
+++ b/SLIC/src/plugins/danyfel80/islic/RGBToLAB.java
@@ -0,0 +1,51 @@
+package plugins.danyfel80.islic;
+
+import java.util.stream.IntStream;
+
+import algorithms.danyfel80.islic.CIELab;
+import icy.image.IcyBufferedImage;
+import icy.sequence.Sequence;
+import icy.type.DataType;
+import icy.type.collection.array.Array2DUtil;
+import plugins.adufour.ezplug.EzPlug;
+import plugins.adufour.ezplug.EzVarSequence;
+
+public class RGBToLAB extends EzPlug {
+
+	EzVarSequence inRgbSequence;
+
+	@Override
+	protected void initialize() {
+		inRgbSequence = new EzVarSequence("RGB sequence");
+		addEzComponent(inRgbSequence);
+	}
+
+	@Override
+	protected void execute() {
+
+		Sequence rgbSequence = inRgbSequence.getValue();
+		Sequence labSequence = new Sequence(
+				new IcyBufferedImage(rgbSequence.getWidth(), rgbSequence.getHeight(), 3, DataType.DOUBLE));
+
+		double[][] rgbIm = Array2DUtil.arrayToDoubleArray(rgbSequence.getDataXYC(0, 0), rgbSequence.isSignedDataType());
+
+		labSequence.beginUpdate();
+		double[][] labIm = Array2DUtil.arrayToDoubleArray(labSequence.getDataXYC(0, 0), labSequence.isSignedDataType());
+		IntStream.range(0, labIm[0].length).forEach(pos -> {
+			int[] rgb = IntStream.range(0, 3).map(c -> (int) Math.round(255 * (rgbIm[c][pos] / rgbSequence.getDataTypeMax())))
+					.toArray();
+			double[] lab = CIELab.fromRGB(rgb);
+			IntStream.range(0, 3).forEach(c -> labIm[c][pos] = lab[c]);
+		});
+		Array2DUtil.doubleArrayToArray(labIm, labSequence.getDataXYC(0, 0));
+		labSequence.dataChanged();
+		labSequence.endUpdate();
+
+		labSequence.setName(rgbSequence.getName() + "_LAB");
+		addSequence(labSequence);
+	}
+
+	@Override
+	public void clean() {}
+
+}
diff --git a/SLIC/src/plugins/danyfel80/islic/SLIC.java b/SLIC/src/plugins/danyfel80/islic/SLIC.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f31909bfe710dea6f44aa2de83a91606b5c6698
--- /dev/null
+++ b/SLIC/src/plugins/danyfel80/islic/SLIC.java
@@ -0,0 +1,68 @@
+package plugins.danyfel80.islic;
+
+import algorithms.danyfel80.islic.SLICTask;
+import icy.gui.dialog.MessageDialog;
+import plugins.adufour.ezplug.EzPlug;
+import plugins.adufour.ezplug.EzVarBoolean;
+import plugins.adufour.ezplug.EzVarDouble;
+import plugins.adufour.ezplug.EzVarInteger;
+import plugins.adufour.ezplug.EzVarSequence;
+
+public class SLIC extends EzPlug {
+
+	private EzVarSequence	inSequence;
+	private EzVarInteger  inSPSize;
+	private EzVarDouble	  inSPReg;
+	private EzVarBoolean  inIsROIOutput;
+
+	@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
+	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();
+			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();
+			MessageDialog.showDialog("Runtime Error", String.format("SLIC could not run properly: " + e.getMessage()),
+					MessageDialog.ERROR_MESSAGE);
+			return;
+		}
+		
+		endTime = System.currentTimeMillis();
+		procTime = endTime - startTime;
+		MessageDialog.showDialog(String.format("SLIC finished in %d milliseconds", procTime));
+		System.out.println(String.format("SLIC finished in %d milliseconds", procTime));
+		addSequence(task.getResultSequence());
+	}
+
+	@Override
+	public void clean() {
+		// TODO Auto-generated method stub
+	}
+
+}