import java.awt.Rectangle; import java.io.*; import ij.*; import ij.plugin.*; import ij.plugin.frame.Recorder; import ij.process.*; import ij.gui.*; /** * Pixellate is a plugin filter for ImageJ that applies a coarsing effect * to the selected region of interest, like on TV when a face is * supposed to be obscured. * * This code was written by Ulf Dittmer, * and is hereby released under the same terms as ImageJ. */ public class Pixellate_ implements PlugIn, Runnable { static String COARSENESS_PREF_KEY = "Pixellate."; boolean canUsePrefs = false; // number of pixels in x and y direction to be averaged static int coarsenessX=16, coarsenessY=16; static double opacity = 100.0; int previousId; ImagePlus imp = null; ImageProcessor ip = null; public Pixellate_() { // This following trickery is necessary to outsmart the Java compiler. // Since ImageJ.VERSION is final, its value would normally be inserted // into the code, so we need to get it dynamically via reflection. try { Class ImageJClass = Class.forName("ij.ImageJ"); String vers = (String) ImageJClass.getField("VERSION").get(null); canUsePrefs = (vers.compareTo("1.32c") >= 0); } catch (Exception ex) { } if (canUsePrefs) { coarsenessX = (int) Prefs.get(COARSENESS_PREF_KEY+"coarseness.x", 16); coarsenessY = (int) Prefs.get(COARSENESS_PREF_KEY+"coarseness.y", 16); opacity = Prefs.get(COARSENESS_PREF_KEY+"opacity", 100.0); } } public void run (String arg) { String options = Macro.getOptions(); // if there are no options, then the plugin has been selected from the menu, // and the dialog should be shown if (options == null) { GenericDialog gd = new GenericDialog("Pixellate"); gd.addNumericField("Coarseness Horizontal: ", coarsenessX, 0); gd.addNumericField("Coarseness Vertical: ", coarsenessY, 0); gd.addNumericField("Opacity (%): ", opacity, 0); gd.showDialog(); if (gd.wasCanceled()) return; coarsenessX = (int) gd.getNextNumber(); if (gd.invalidNumber()) { IJ.showMessage("Pixellate", "Please enter a valid number."); return; } coarsenessY = (int) gd.getNextNumber(); if (gd.invalidNumber()) { IJ.showMessage("Pixellate", "Please enter a valid number."); return; } opacity = gd.getNextNumber(); if (gd.invalidNumber()) { IJ.showMessage("Pixellate", "Please enter a valid number."); return; } } else { String preset = Macro.getValue(options, "coarsenessX", null); if (preset != null) coarsenessX = Integer.parseInt(preset); preset = Macro.getValue(options, "coarsenessY", null); if (preset != null) coarsenessY = Integer.parseInt(preset); preset = Macro.getValue(options, "opacity", null); if (preset != null) opacity = Integer.parseInt(preset); } if (coarsenessX < 2) coarsenessX = 2; if (coarsenessY < 2) coarsenessY = 2; if (opacity < 0.0) opacity = 0.0; if (opacity > 100.0) opacity = 100.0; if (Recorder.record) { Recorder.setCommand("Pixellate "); Recorder.recordOption("coarsenessX", ""+coarsenessX); Recorder.recordOption("coarsenessY", ""+coarsenessY); Recorder.recordOption("opacity", ""+opacity); } if (canUsePrefs) { Prefs.set(COARSENESS_PREF_KEY+"coarseness.x", coarsenessX); Prefs.set(COARSENESS_PREF_KEY+"coarseness.y", coarsenessY); Prefs.set(COARSENESS_PREF_KEY+"opacity", opacity); } imp = WindowManager.getCurrentImage(); if (imp==null) { IJ.showStatus("No image"); previousId = 0; return; } if (!imp.lock()) { previousId = 0; return; } ip = imp.getProcessor(); if (imp.getID() != previousId) ip.snapshot(); previousId = imp.getID(); Undo.reset(); Undo.setup(Undo.FILTER, imp); new Thread(this).start(); // wait until image is unlocked before resuming execution; important for macros myWait(); } protected void myWait() { synchronized (this) { try { this.wait(); } catch (InterruptedException iex) { } } } protected void myNotify() { synchronized (this) { this.notify(); } } public void run() { try { runCommand(imp, ip); } catch (OutOfMemoryError e) { IJ.outOfMemory("Pixellate"); if (imp!=null) imp.unlock(); } catch (Exception ex) { CharArrayWriter caw = new CharArrayWriter(); PrintWriter pw = new PrintWriter(caw); ex.printStackTrace(pw); IJ.write(caw.toString()); IJ.showStatus(""); if (imp!=null) imp.unlock(); } myNotify(); } public void runCommand (ImagePlus imp, ImageProcessor ip) { IJ.showStatus("Pixellate ..."); ImageStack stack = imp.getStack(); int slices = stack.getSize(); int width = ip.getWidth(); int height = ip.getHeight(); Rectangle rect = ip.getRoi(); int rWidth = rect.width; int rHeight = rect.height; int type = imp.getType(); byte[] byteArr = null; short[] shortArr = null; int[] intArr = null; float[] floatArr = null; try { Roi roi = imp.getRoi(); long start = System.currentTimeMillis(); for (int i=0; i width) xu--; while (yu > height) yu--; switch (type) { case ImagePlus.GRAY8: case ImagePlus.COLOR_256: for (int ii=x; ii> 16; rsum += r<0 ? r+256 : r; g = (val & 0x00ff00) >> 8; gsum += g<0 ? g+256 : g; b = (val & 0x0000ff); bsum += b<0 ? b+256 : b; } int ival = ((((int) Math.floor(rsum / count + 0.5)) & 0xff) << 16) | ((((int) Math.floor(gsum / count + 0.5)) & 0xff) << 8) | (((int) Math.floor(bsum / count + 0.5)) & 0xff); for (int ii=x; ii> 16)) / 100.0) << 16) | ((int) ((opacity * gsum / count + (100.0 - opacity) * ((intArr[jj*width+ii] & 0x00ff00) >> 8)) / 100.0) << 8) | (int) ((opacity * bsum / count + (100.0 - opacity) * (intArr[jj*width+ii] & 0x0000ff)) / 100.0); break; } } } imp.changes = true; imp.setSlice(i+1); imp.updateAndRepaintWindow(); } float secs = (System.currentTimeMillis() - start) / 1000.0f; IJ.showStatus("Pixellate: " + secs + " seconds, " + (int) (slices*rWidth*rHeight/secs) + " pixels/second"); } catch (Throwable e) { e.printStackTrace(); System.err.println(e.getMessage()); } /* Roi roi = imp.getRoi(); if (roi != null) { ImageProcessor mask = roi.getMask(); if (mask != null) ip.reset(mask); } */ imp.updateAndDraw(); imp.unlock(); } }