diff --git a/build/classes/SRM.class b/build/classes/SRM.class new file mode 100644 index 0000000..582fad5 Binary files /dev/null and b/build/classes/SRM.class differ diff --git a/build/classes/filtro_chido/Filtrado$1.class b/build/classes/filtro_chido/Filtrado$1.class index 704bfec..a65c092 100644 Binary files a/build/classes/filtro_chido/Filtrado$1.class and b/build/classes/filtro_chido/Filtrado$1.class differ diff --git a/build/classes/filtro_chido/Filtrado$2.class b/build/classes/filtro_chido/Filtrado$2.class index 330b832..520b482 100644 Binary files a/build/classes/filtro_chido/Filtrado$2.class and b/build/classes/filtro_chido/Filtrado$2.class differ diff --git a/build/classes/filtro_chido/Filtrado$3.class b/build/classes/filtro_chido/Filtrado$3.class index 3387816..25d4e11 100644 Binary files a/build/classes/filtro_chido/Filtrado$3.class and b/build/classes/filtro_chido/Filtrado$3.class differ diff --git a/build/classes/filtro_chido/Filtrado$4.class b/build/classes/filtro_chido/Filtrado$4.class new file mode 100644 index 0000000..7115655 Binary files /dev/null and b/build/classes/filtro_chido/Filtrado$4.class differ diff --git a/build/classes/filtro_chido/Filtrado.class b/build/classes/filtro_chido/Filtrado.class index 2117ea4..0f6ec56 100644 Binary files a/build/classes/filtro_chido/Filtrado.class and b/build/classes/filtro_chido/Filtrado.class differ diff --git a/build/classes/filtro_chido/Filtrado.form b/build/classes/filtro_chido/Filtrado.form index add86a5..26e16f9 100644 --- a/build/classes/filtro_chido/Filtrado.form +++ b/build/classes/filtro_chido/Filtrado.form @@ -64,6 +64,7 @@ + @@ -83,6 +84,8 @@ + + @@ -172,6 +175,14 @@ + + + + + + + + diff --git a/build/classes/filtro_chido/Filtro_Chido.class b/build/classes/filtro_chido/Filtro_Chido.class index 95678ac..0785071 100644 Binary files a/build/classes/filtro_chido/Filtro_Chido.class and b/build/classes/filtro_chido/Filtro_Chido.class differ diff --git a/build/classes/filtro_chido/SRM.class b/build/classes/filtro_chido/SRM.class new file mode 100644 index 0000000..8a9d73d Binary files /dev/null and b/build/classes/filtro_chido/SRM.class differ diff --git a/src/filtro_chido/Filtrado.form b/src/filtro_chido/Filtrado.form index add86a5..26e16f9 100644 --- a/src/filtro_chido/Filtrado.form +++ b/src/filtro_chido/Filtrado.form @@ -64,6 +64,7 @@ + @@ -83,6 +84,8 @@ + + @@ -172,6 +175,14 @@ + + + + + + + + diff --git a/src/filtro_chido/Filtrado.java b/src/filtro_chido/Filtrado.java index 87289f7..c10a626 100644 --- a/src/filtro_chido/Filtrado.java +++ b/src/filtro_chido/Filtrado.java @@ -36,6 +36,7 @@ public class Filtrado extends javax.swing.JFrame { jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); jButton3 = new javax.swing.JButton(); + jButton4 = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setName("Panel_Principal"); // NOI18N @@ -91,6 +92,13 @@ public class Filtrado extends javax.swing.JFrame { } }); + jButton4.setText("SRM"); + jButton4.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton4ActionPerformed(evt); + } + }); + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -107,7 +115,8 @@ public class Filtrado extends javax.swing.JFrame { .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(jButton1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jButton2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addComponent(jButton3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jButton4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(0, 176, Short.MAX_VALUE)) ); @@ -121,7 +130,9 @@ public class Filtrado extends javax.swing.JFrame { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 36, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) - .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton4, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 27, Short.MAX_VALUE) .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -157,6 +168,10 @@ public class Filtrado extends javax.swing.JFrame { }//GEN-LAST:event_jButton3ActionPerformed + private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed + SRM(); + }//GEN-LAST:event_jButton4ActionPerformed + /** * @param args the command line arguments */ @@ -196,6 +211,7 @@ public class Filtrado extends javax.swing.JFrame { private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; private javax.swing.JButton jButton3; + private javax.swing.JButton jButton4; private javax.swing.JLabel jLabel1; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel2; @@ -206,4 +222,8 @@ public class Filtrado extends javax.swing.JFrame { AdaptiveMedian mediana =new AdaptiveMedian(); mediana.showDialog(null, null, null); } + private void SRM (){ + SRM srm1 = new SRM(); + + } } diff --git a/src/filtro_chido/Filtro_Chido.java b/src/filtro_chido/Filtro_Chido.java index d4cb929..a891bd0 100644 --- a/src/filtro_chido/Filtro_Chido.java +++ b/src/filtro_chido/Filtro_Chido.java @@ -15,6 +15,7 @@ public class Filtro_Chido { public static void main(String[] args) { Filtrado fl = new Filtrado(); fl.show(); + } } diff --git a/src/filtro_chido/SRM.java b/src/filtro_chido/SRM.java new file mode 100644 index 0000000..f55b10d --- /dev/null +++ b/src/filtro_chido/SRM.java @@ -0,0 +1,378 @@ + +package filtro_chido; + +import ij.IJ; +import ij.ImagePlus; +import ij.ImageStack; +import ij.gui.GenericDialog; +import ij.plugin.filter.PlugInFilter; +import static ij.plugin.filter.PlugInFilter.DOES_8G; +import static ij.plugin.filter.PlugInFilter.NO_CHANGES; +import ij.process.ByteProcessor; +import ij.process.FloatProcessor; +import ij.process.ImageProcessor; +import ij.process.ShortProcessor; + +import java.util.Arrays; + + +public class SRM implements PlugInFilter { + ImagePlus image; + + public int setup(String arg, ImagePlus image) { + this.image = image; + return DOES_8G | NO_CHANGES; + } + + public void run(ImageProcessor ip) { + boolean isStack = image.getStackSize() > 1; + + GenericDialog gd = new GenericDialog("SRM"); + gd.addNumericField("Q", Q, 2); + gd.addCheckbox("showAverages", true); + if (isStack) + gd.addCheckbox("3d", true); + gd.showDialog(); + + if (gd.wasCanceled()) + return; + + Q = (float)gd.getNextNumber(); + boolean showAverages = gd.getNextBoolean(); + boolean do3D = isStack ? gd.getNextBoolean() : false; + if (do3D) + srm3D(showAverages).show(); + else + srm2D(ip, showAverages).show(); + } + + final float g = 256; // number of different intensity values + protected float Q = 25; //25; // complexity of the assumed distributions + protected float delta; + + protected float factor, logDelta; + + float[] average; + int[] count; + int[] regionIndex; // if < 0, it is -1 - actual_regionIndex + + int[] nextNeighbor, neighborBucket; + + protected ImagePlus srm3D(boolean showAverages) { + int w = image.getWidth(), h = image.getHeight(); + int d = image.getStackSize(); + + delta = 1f / (6 * w * h * d); + + factor = g * g / 2 / Q; + logDelta = 2f * (float)Math.log(6 * w * h * d); + + IJ.showStatus("Initializing regions"); + initializeRegions3D(w, h, d); + IJ.showStatus("Initializing neighbors"); + initializeNeighbors3D(w, h, d); + IJ.showStatus("Merging neighbors"); + mergeAllNeighbors3D(w, h); + IJ.showStatus("Making stack"); + + ImageStack stack = new ImageStack(w, h); + if (showAverages) + for (int k = 0; k < d; k++) { + int off = k * w * h; + float[] p = new float[w * h]; + for (int i = 0; i < w * h; i++) + p[i] = average[getRegionIndex(i + off)]; + stack.addSlice(null, new FloatProcessor(w, h, + p, null)); + } + else { + int regionCount = consolidateRegions(); + + if (regionCount > 1<<16) + IJ.showMessage("Found " + regionCount + + " regions, which does not fit" + + " in 16-bit."); + + for (int k = 0; k < d; k++) { + ImageProcessor ip; + + int off = k * w * h; + + if (regionCount > 1<<8) { + short[] p = new short[w * h]; + for (int i = 0; i < p.length; i++) + p[i] = (short)regionIndex[i + + off]; + ip = new ShortProcessor(w, h, p, null); + } + else { + byte[] p = new byte[w * h]; + for (int i = 0; i < p.length; i++) + p[i] = (byte)regionIndex[i + + off]; + ip = new ByteProcessor(w, h, p, null); + } + stack.addSlice(null, ip); + } + } + + IJ.showStatus(""); + String title = image.getTitle() + " (SRM3D Q=" + Q + ")"; + return new ImagePlus(title, stack); + } + + protected ImagePlus srm2D(ImageProcessor ip, boolean showAverages) { + int w = ip.getWidth(), h = ip.getHeight(); + + delta = 1f / (6 * w * h); + + factor = g * g / 2 / Q; + logDelta = 2f * (float)Math.log(6 * w * h); + + byte[] pixel = (byte[])ip.getPixels(); + initializeRegions2D(pixel, ip.getWidth(), ip.getHeight()); + initializeNeighbors2D(pixel, w, h); + mergeAllNeighbors2D(w); + + if (showAverages) { + for (int i = 0; i < average.length; i++) + average[i] = average[getRegionIndex(i)]; + ip = new FloatProcessor(w, h, average, null); + } + else { + int regionCount = consolidateRegions(); + + if (regionCount > 1<<8) { + if (regionCount > 1<<16) + IJ.showMessage("Found " + regionCount + + " regions, which does not fit" + + " in 16-bit."); + short[] pixel16 = new short[w * h]; + for (int i = 0; i < pixel16.length; i++) + pixel16[i] = (short)regionIndex[i]; + ip = new ShortProcessor(w, h, pixel16, null); + } + else { + pixel = new byte[w * h]; + for (int i = 0; i < pixel.length; i++) + pixel[i] = (byte)regionIndex[i]; + ip = new ByteProcessor(w, h, pixel, null); + } + } + + String title = image.getTitle() + " (SRM Q=" + Q + ")"; + return new ImagePlus(title, ip); + } + + void initializeRegions2D(byte[] pixel, int w, int h) { + average = new float[w * h]; + count = new int[w * h]; + regionIndex = new int[w * h]; + + for (int i = 0; i < average.length; i++) { + average[i] = pixel[i] & 0xff; + count[i] = 1; + regionIndex[i] = i; + } + } + + void initializeRegions3D(int w, int h, int d) { + average = new float[w * h * d]; + count = new int[w * h * d]; + regionIndex = new int[w * h * d]; + + for (int j = 0; j < d; j++) { + byte[] pixel = + (byte[])image.getStack().getProcessor(j + + 1).getPixels(); + int offset = j * w * h; + for (int i = 0; i < w * h; i++) { + average[offset + i] = pixel[i] & 0xff; + count[offset + i] = 1; + regionIndex[offset + i] = offset + i; + } + } + } + + protected void addNeighborPair(int neighborIndex, + byte[] pixel, int i1, int i2) { + int difference = Math.abs((pixel[i1] & 0xff) + - (pixel[i2] & 0xff)); + nextNeighbor[neighborIndex] = neighborBucket[difference]; + neighborBucket[difference] = neighborIndex; + } + + void initializeNeighbors2D(byte[] pixel, int w, int h) { + nextNeighbor = new int[2 * w * h]; + + // bucket sort + neighborBucket = new int[256]; + Arrays.fill(neighborBucket, -1); + + for (int j = h - 1; j >= 0; j--) + for (int i = w - 1; i >= 0; i--) { + int index = i + w * j; + int neighborIndex = 2 * index; + + // vertical + if (j < h - 1) + addNeighborPair(neighborIndex + 1, + pixel, index, index + w); + + // horizontal + if (i < w - 1) + addNeighborPair(neighborIndex, + pixel, index, index + 1); + } + } + + protected void addNeighborPair(int neighborIndex, + byte[] pixel, byte[] nextPixel, int i) { + int difference = Math.abs((pixel[i] & 0xff) + - (nextPixel[i] & 0xff)); + nextNeighbor[neighborIndex] = neighborBucket[difference]; + neighborBucket[difference] = neighborIndex; + } + + void initializeNeighbors3D(int w, int h, int d) { + nextNeighbor = new int[3 * w * h * d]; + + // bucket sort + neighborBucket = new int[256]; + Arrays.fill(neighborBucket, -1); + + byte[] nextPixel = null; + for (int k = d - 1; k >= 0; k--) { + byte[] pixel = + (byte[])image.getStack().getProcessor(k + + 1).getPixels(); + for (int j = h - 1; j >= 0; j--) + for (int i = w - 1; i >= 0; i--) { + int index = i + w * j; + int neighborIndex = + 3 * (index + k * w * h); + + // depth + if (nextPixel != null) + addNeighborPair(neighborIndex + + 2, pixel, + nextPixel, index); + + // vertical + if (j < h - 1) + addNeighborPair(neighborIndex + + 1, pixel, + index, index + w); + + // horizontal + if (i < w - 1) + addNeighborPair(neighborIndex, + pixel, + index, index + 1); + } + nextPixel = pixel; + } + } + + // recursively find out the region index for this pixel + int getRegionIndex(int i) { + i = regionIndex[i]; + while (i < 0) + i = regionIndex[-1 - i]; + return i; + } + + // should regions i1 and i2 be merged? + boolean predicate(int i1, int i2) { + float difference = average[i1] - average[i2]; + /* + * This would be the non-relaxed predicate mentioned in the + * paper. + * + * return difference * difference < + factor * (1f / count[i1] + 1f / count[i2]); + * + */ + float log1 = (float)Math.log(1 + count[i1]) + * (g < count[i1] ? g : count[i1]); + float log2 = (float)Math.log(1 + count[i2]) + * (g < count[i2] ? g : count[i2]); + return difference * difference < + .1f * factor * ((log1 + logDelta) / count[i1] + + ((log2 + logDelta) / count[i2])); + } + + void mergeAllNeighbors2D(int w) { + for (int i = 0; i < neighborBucket.length; i++) { + int neighborIndex = neighborBucket[i]; + while (neighborIndex >= 0) { + int i1 = neighborIndex / 2; + int i2 = i1 + + (0 == (neighborIndex & 1) ? 1 : w); + + i1 = getRegionIndex(i1); + i2 = getRegionIndex(i2); + + if (predicate(i1, i2)) + mergeRegions(i1, i2); + + neighborIndex = nextNeighbor[neighborIndex]; + } + } + } + + void mergeAllNeighbors3D(int w, int h) { + for (int i = 0; i < neighborBucket.length; i++) { + int neighborIndex = neighborBucket[i]; + IJ.showProgress(i, neighborBucket.length); + while (neighborIndex >= 0) { + int i1 = neighborIndex / 3; + int i2 = i1 + + (0 == (neighborIndex % 3) ? 1 : + (1 == (neighborIndex % 3) ? w : + w * h)); + + i1 = getRegionIndex(i1); + i2 = getRegionIndex(i2); + + if (i1 != i2 && predicate(i1, i2)) + mergeRegions(i1, i2); + + neighborIndex = nextNeighbor[neighborIndex]; + } + } + IJ.showProgress(neighborBucket.length, neighborBucket.length); + } + + void mergeRegions(int i1, int i2) { + if (i1 == i2) + return; + int mergedCount = count[i1] + count[i2]; + float mergedAverage = (average[i1] * count[i1] + + average[i2] * count[i2]) / mergedCount; + + // merge larger index into smaller index + if (i1 > i2) { + average[i2] = mergedAverage; + count[i2] = mergedCount; + regionIndex[i1] = -1 - i2; + } + else { + average[i1] = mergedAverage; + count[i1] = mergedCount; + regionIndex[i2] = -1 - i1; + } + } + + int consolidateRegions() { + + int count = 0; + for (int i = 0; i < regionIndex.length; i++) + if (regionIndex[i] < 0) + regionIndex[i] = + regionIndex[-1 - regionIndex[i]]; + else + regionIndex[i] = count++; + return count; + } +}