/** Generate output image whose type is same as input image. */ private ImagePlus makeOutputImage(ImagePlus imp, FloatProcessor fp, int ptype) { int width = imp.getWidth(); int height = imp.getHeight(); float[] pixels = (float[]) fp.getPixels(); ImageProcessor oip = null; // Create output image consistent w/ type of input image. int size = pixels.length; switch (ptype) { case BYTE_TYPE: oip = imp.getProcessor().createProcessor(width, height); byte[] pixels8 = (byte[]) oip.getPixels(); for (int i = 0; i < size; i++) pixels8[i] = (byte) pixels[i]; break; case SHORT_TYPE: oip = imp.getProcessor().createProcessor(width, height); short[] pixels16 = (short[]) oip.getPixels(); for (int i = 0; i < size; i++) pixels16[i] = (short) pixels[i]; break; case FLOAT_TYPE: oip = new FloatProcessor(width, height, pixels, null); break; } // Adjust for display. // Calling this on non-ByteProcessors ensures image // processor is set up to correctly display image. oip.resetMinAndMax(); // Create new image plus object. Don't use // ImagePlus.createImagePlus here because there may be // attributes of input image that are not appropriate for // projection. return new ImagePlus(makeTitle(), oip); }
void Bernsen(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) { // Bernsen recommends WIN_SIZE = 31 and CONTRAST_THRESHOLD = 15. // 1) Bernsen J. (1986) "Dynamic Thresholding of Grey-Level Images" // Proc. of the 8th Int. Conf. on Pattern Recognition, pp. 1251-1255 // 2) Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding // Techniques and Quantitative Performance Evaluation" Journal of // Electronic Imaging, 13(1): 146-165 // http://citeseer.ist.psu.edu/sezgin04survey.html // Ported to ImageJ plugin from E Celebi's fourier_0.8 routines // This version uses a circular local window, instead of a rectagular one ImagePlus Maximp, Minimp; ImageProcessor ip = imp.getProcessor(), ipMax, ipMin; int contrast_threshold = 15; int local_contrast; int mid_gray; byte object; byte backg; int temp; if (par1 != 0) { IJ.log("Bernsen: changed contrast_threshold from :" + contrast_threshold + " to:" + par1); contrast_threshold = (int) par1; } if (doIwhite) { object = (byte) 0xff; backg = (byte) 0; } else { object = (byte) 0; backg = (byte) 0xff; } Maximp = duplicateImage(ip); ipMax = Maximp.getProcessor(); RankFilters rf = new RankFilters(); rf.rank(ipMax, radius, rf.MAX); // Maximum // Maximp.show(); Minimp = duplicateImage(ip); ipMin = Minimp.getProcessor(); rf.rank(ipMin, radius, rf.MIN); // Minimum // Minimp.show(); byte[] pixels = (byte[]) ip.getPixels(); byte[] max = (byte[]) ipMax.getPixels(); byte[] min = (byte[]) ipMin.getPixels(); for (int i = 0; i < pixels.length; i++) { local_contrast = (int) ((max[i] & 0xff) - (min[i] & 0xff)); mid_gray = (int) ((min[i] & 0xff) + (max[i] & 0xff)) / 2; temp = (int) (pixels[i] & 0x0000ff); if (local_contrast < contrast_threshold) pixels[i] = (mid_gray >= 128) ? object : backg; // Low contrast region else pixels[i] = (temp >= mid_gray) ? object : backg; } // imp.updateAndDraw(); return; }
void createMaskFromThreshold(ImagePlus imp) { ImageProcessor ip = imp.getProcessor(); if (ip.getMinThreshold() == ImageProcessor.NO_THRESHOLD) { IJ.error("Create Mask", "Area selection or thresholded image required"); return; } double t1 = ip.getMinThreshold(); double t2 = ip.getMaxThreshold(); IJ.run("Duplicate...", "title=mask"); ImagePlus imp2 = WindowManager.getCurrentImage(); ImageProcessor ip2 = imp2.getProcessor(); ip2.setThreshold(t1, t2, ImageProcessor.NO_LUT_UPDATE); IJ.run("Convert to Mask"); }
void createEllipse(ImagePlus imp) { IJ.showStatus("Fitting ellipse"); Roi roi = imp.getRoi(); if (roi == null) { noRoi("Fit Ellipse"); return; } if (roi.isLine()) { IJ.error("Fit Ellipse", "\"Fit Ellipse\" does not work with line selections"); return; } ImageProcessor ip = imp.getProcessor(); ip.setRoi(roi); int options = Measurements.CENTROID + Measurements.ELLIPSE; ImageStatistics stats = ImageStatistics.getStatistics(ip, options, null); double dx = stats.major * Math.cos(stats.angle / 180.0 * Math.PI) / 2.0; double dy = -stats.major * Math.sin(stats.angle / 180.0 * Math.PI) / 2.0; double x1 = stats.xCentroid - dx; double x2 = stats.xCentroid + dx; double y1 = stats.yCentroid - dy; double y2 = stats.yCentroid + dy; double aspectRatio = stats.minor / stats.major; imp.killRoi(); imp.setRoi(new EllipseRoi(x1, y1, x2, y2, aspectRatio)); }
void createMask(ImagePlus imp) { Roi roi = imp.getRoi(); boolean useInvertingLut = Prefs.useInvertingLut; Prefs.useInvertingLut = false; if (roi == null || !(roi.isArea() || roi.getType() == Roi.POINT)) { createMaskFromThreshold(imp); Prefs.useInvertingLut = useInvertingLut; return; } ImagePlus maskImp = null; Frame frame = WindowManager.getFrame("Mask"); if (frame != null && (frame instanceof ImageWindow)) maskImp = ((ImageWindow) frame).getImagePlus(); if (maskImp == null) { ImageProcessor ip = new ByteProcessor(imp.getWidth(), imp.getHeight()); if (!Prefs.blackBackground) ip.invertLut(); maskImp = new ImagePlus("Mask", ip); maskImp.show(); } ImageProcessor ip = maskImp.getProcessor(); ip.setRoi(roi); ip.setValue(255); ip.fill(ip.getMask()); maskImp.updateAndDraw(); Prefs.useInvertingLut = useInvertingLut; }
void Contrast(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) { // G. Landini, 2013 // Based on a simple contrast toggle. This procedure does not have user-provided paramters other // than the kernel radius // Sets the pixel value to either white or black depending on whether its current value is // closest to the local Max or Min respectively // The procedure is similar to Toggle Contrast Enhancement (see Soille, Morphological Image // Analysis (2004), p. 259 ImagePlus Maximp, Minimp; ImageProcessor ip = imp.getProcessor(), ipMax, ipMin; int c_value = 0; int mid_gray; byte object; byte backg; if (doIwhite) { object = (byte) 0xff; backg = (byte) 0; } else { object = (byte) 0; backg = (byte) 0xff; } Maximp = duplicateImage(ip); ipMax = Maximp.getProcessor(); RankFilters rf = new RankFilters(); rf.rank(ipMax, radius, rf.MAX); // Maximum // Maximp.show(); Minimp = duplicateImage(ip); ipMin = Minimp.getProcessor(); rf.rank(ipMin, radius, rf.MIN); // Minimum // Minimp.show(); byte[] pixels = (byte[]) ip.getPixels(); byte[] max = (byte[]) ipMax.getPixels(); byte[] min = (byte[]) ipMin.getPixels(); for (int i = 0; i < pixels.length; i++) { pixels[i] = ((Math.abs((int) (max[i] & 0xff - pixels[i] & 0xff)) <= Math.abs((int) (pixels[i] & 0xff - min[i] & 0xff)))) ? object : backg; } // imp.updateAndDraw(); return; }
private ImagePlus duplicateImage(ImageProcessor iProcessor) { int w = iProcessor.getWidth(); int h = iProcessor.getHeight(); ImagePlus iPlus = NewImage.createByteImage("Image", w, h, 1, NewImage.FILL_BLACK); ImageProcessor imageProcessor = iPlus.getProcessor(); imageProcessor.copyBits(iProcessor, 0, 0, Blitter.COPY); return iPlus; }
void MidGrey(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) { // See: Image Processing Learning Resourches HIPR2 // http://homepages.inf.ed.ac.uk/rbf/HIPR2/adpthrsh.htm ImagePlus Maximp, Minimp; ImageProcessor ip = imp.getProcessor(), ipMax, ipMin; int c_value = 0; int mid_gray; byte object; byte backg; if (par1 != 0) { IJ.log("MidGrey: changed c_value from :" + c_value + " to:" + par1); c_value = (int) par1; } if (doIwhite) { object = (byte) 0xff; backg = (byte) 0; } else { object = (byte) 0; backg = (byte) 0xff; } Maximp = duplicateImage(ip); ipMax = Maximp.getProcessor(); RankFilters rf = new RankFilters(); rf.rank(ipMax, radius, rf.MAX); // Maximum // Maximp.show(); Minimp = duplicateImage(ip); ipMin = Minimp.getProcessor(); rf.rank(ipMin, radius, rf.MIN); // Minimum // Minimp.show(); byte[] pixels = (byte[]) ip.getPixels(); byte[] max = (byte[]) ipMax.getPixels(); byte[] min = (byte[]) ipMin.getPixels(); for (int i = 0; i < pixels.length; i++) { pixels[i] = ((int) (pixels[i] & 0xff) > (int) (((max[i] & 0xff) + (min[i] & 0xff)) / 2) - c_value) ? object : backg; } // imp.updateAndDraw(); return; }
/** * Execute the plugin functionality: duplicate and scale the given image. * * @return an Object[] array with the name and the scaled ImagePlus. Does NOT show the new, image; * just returns it. */ public Object[] exec( ImagePlus imp, String myMethod, int radius, double par1, double par2, boolean doIwhite) { // 0 - Check validity of parameters if (null == imp) return null; ImageProcessor ip = imp.getProcessor(); int xe = ip.getWidth(); int ye = ip.getHeight(); // int [] data = (ip.getHistogram()); IJ.showStatus("Thresholding..."); long startTime = System.currentTimeMillis(); // 1 Do it if (imp.getStackSize() == 1) { ip.snapshot(); Undo.setup(Undo.FILTER, imp); } // Apply the selected algorithm if (myMethod.equals("Bernsen")) { Bernsen(imp, radius, par1, par2, doIwhite); } else if (myMethod.equals("Contrast")) { Contrast(imp, radius, par1, par2, doIwhite); } else if (myMethod.equals("Mean")) { Mean(imp, radius, par1, par2, doIwhite); } else if (myMethod.equals("Median")) { Median(imp, radius, par1, par2, doIwhite); } else if (myMethod.equals("MidGrey")) { MidGrey(imp, radius, par1, par2, doIwhite); } else if (myMethod.equals("Niblack")) { Niblack(imp, radius, par1, par2, doIwhite); } else if (myMethod.equals("Otsu")) { Otsu(imp, radius, par1, par2, doIwhite); } else if (myMethod.equals("Phansalkar")) { Phansalkar(imp, radius, par1, par2, doIwhite); } else if (myMethod.equals("Sauvola")) { Sauvola(imp, radius, par1, par2, doIwhite); } // IJ.showProgress((double)(255-i)/255); imp.updateAndDraw(); imp.getProcessor().setThreshold(255, 255, ImageProcessor.NO_LUT_UPDATE); // 2 - Return the threshold and the image IJ.showStatus("\nDone " + (System.currentTimeMillis() - startTime) / 1000.0); return new Object[] {imp}; }
private void doRGBProjection(ImageStack stack) { ImageStack[] channels = ChannelSplitter.splitRGB(stack, true); ImagePlus red = new ImagePlus("Red", channels[0]); ImagePlus green = new ImagePlus("Green", channels[1]); ImagePlus blue = new ImagePlus("Blue", channels[2]); imp.unlock(); ImagePlus saveImp = imp; imp = red; color = "(red)"; doProjection(); ImagePlus red2 = projImage; imp = green; color = "(green)"; doProjection(); ImagePlus green2 = projImage; imp = blue; color = "(blue)"; doProjection(); ImagePlus blue2 = projImage; int w = red2.getWidth(), h = red2.getHeight(), d = red2.getStackSize(); if (method == SD_METHOD) { ImageProcessor r = red2.getProcessor(); ImageProcessor g = green2.getProcessor(); ImageProcessor b = blue2.getProcessor(); double max = 0; double rmax = r.getStatistics().max; if (rmax > max) max = rmax; double gmax = g.getStatistics().max; if (gmax > max) max = gmax; double bmax = b.getStatistics().max; if (bmax > max) max = bmax; double scale = 255 / max; r.multiply(scale); g.multiply(scale); b.multiply(scale); red2.setProcessor(r.convertToByte(false)); green2.setProcessor(g.convertToByte(false)); blue2.setProcessor(b.convertToByte(false)); } RGBStackMerge merge = new RGBStackMerge(); ImageStack stack2 = merge.mergeStacks(w, h, d, red2.getStack(), green2.getStack(), blue2.getStack(), true); imp = saveImp; projImage = new ImagePlus(makeTitle(), stack2); }
void lineWidth() { int width = (int) IJ.getNumber("Line Width:", Line.getWidth()); if (width == IJ.CANCELED) return; Line.setWidth(width); LineWidthAdjuster.update(); ImagePlus imp = WindowManager.getCurrentImage(); if (imp != null && imp.isProcessor()) { ImageProcessor ip = imp.getProcessor(); ip.setLineWidth(Line.getWidth()); Roi roi = imp.getRoi(); if (roi != null && roi.isLine()) imp.draw(); } }
void Mean(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) { // See: Image Processing Learning Resourches HIPR2 // http://homepages.inf.ed.ac.uk/rbf/HIPR2/adpthrsh.htm ImagePlus Meanimp; ImageProcessor ip = imp.getProcessor(), ipMean; int c_value = 0; byte object; byte backg; if (par1 != 0) { IJ.log("Mean: changed c_value from :" + c_value + " to:" + par1); c_value = (int) par1; } if (doIwhite) { object = (byte) 0xff; backg = (byte) 0; } else { object = (byte) 0; backg = (byte) 0xff; } Meanimp = duplicateImage(ip); ImageConverter ic = new ImageConverter(Meanimp); ic.convertToGray32(); ipMean = Meanimp.getProcessor(); RankFilters rf = new RankFilters(); rf.rank(ipMean, radius, rf.MEAN); // Mean // Meanimp.show(); byte[] pixels = (byte[]) ip.getPixels(); float[] mean = (float[]) ipMean.getPixels(); for (int i = 0; i < pixels.length; i++) pixels[i] = ((int) (pixels[i] & 0xff) > (int) (mean[i] - c_value)) ? object : backg; // imp.updateAndDraw(); return; }
public void doHyperStackProjection(boolean allTimeFrames) { int start = startSlice; int stop = stopSlice; int firstFrame = 1; int lastFrame = imp.getNFrames(); if (!allTimeFrames) firstFrame = lastFrame = imp.getFrame(); ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight()); int channels = imp.getNChannels(); int slices = imp.getNSlices(); if (slices == 1) { slices = imp.getNFrames(); firstFrame = lastFrame = 1; } int frames = lastFrame - firstFrame + 1; increment = channels; boolean rgb = imp.getBitDepth() == 24; for (int frame = firstFrame; frame <= lastFrame; frame++) { for (int channel = 1; channel <= channels; channel++) { startSlice = (frame - 1) * channels * slices + (start - 1) * channels + channel; stopSlice = (frame - 1) * channels * slices + (stop - 1) * channels + channel; if (rgb) doHSRGBProjection(imp); else doProjection(); stack.addSlice(null, projImage.getProcessor()); } } projImage = new ImagePlus(makeTitle(), stack); projImage.setDimensions(channels, 1, frames); if (channels > 1) { projImage = new CompositeImage(projImage, 0); ((CompositeImage) projImage).copyLuts(imp); if (method == SUM_METHOD || method == SD_METHOD) ((CompositeImage) projImage).resetDisplayRanges(); } if (frames > 1) projImage.setOpenAsHyperStack(true); Overlay overlay = imp.getOverlay(); if (overlay != null) { startSlice = start; stopSlice = stop; if (imp.getType() == ImagePlus.COLOR_RGB) projImage.setOverlay(projectRGBHyperStackRois(overlay)); else projImage.setOverlay(projectHyperStackRois(overlay)); } IJ.showProgress(1, 1); }
void createSelectionFromMask(ImagePlus imp) { ImageProcessor ip = imp.getProcessor(); if (ip.getMinThreshold() != ImageProcessor.NO_THRESHOLD) { IJ.runPlugIn("ij.plugin.filter.ThresholdToSelection", ""); return; } if (!ip.isBinary()) { IJ.error( "Create Selection", "This command creates a composite selection from\n" + "a mask (8-bit binary image with white background)\n" + "or from an image that has been thresholded using\n" + "the Image>Adjust>Threshold tool. The current\n" + "image is not a mask and has not been thresholded."); return; } int threshold = ip.isInvertedLut() ? 255 : 0; ip.setThreshold(threshold, threshold, ImageProcessor.NO_LUT_UPDATE); IJ.runPlugIn("ij.plugin.filter.ThresholdToSelection", ""); }
void createMask(ImagePlus imp) { Roi roi = imp.getRoi(); boolean useInvertingLut = Prefs.useInvertingLut; Prefs.useInvertingLut = false; boolean selectAll = roi != null && roi.getType() == Roi.RECTANGLE && roi.getBounds().width == imp.getWidth() && roi.getBounds().height == imp.getHeight() && imp.isThreshold(); if (roi == null || !(roi.isArea() || roi.getType() == Roi.POINT) || selectAll) { createMaskFromThreshold(imp); Prefs.useInvertingLut = useInvertingLut; return; } ImagePlus maskImp = null; Frame frame = WindowManager.getFrame("Mask"); if (frame != null && (frame instanceof ImageWindow)) maskImp = ((ImageWindow) frame).getImagePlus(); if (maskImp == null) { ImageProcessor ip = new ByteProcessor(imp.getWidth(), imp.getHeight()); if (!Prefs.blackBackground) ip.invertLut(); maskImp = new ImagePlus("Mask", ip); maskImp.show(); } ImageProcessor ip = maskImp.getProcessor(); ip.setRoi(roi); ip.setValue(255); ip.fill(ip.getMask()); Calibration cal = imp.getCalibration(); if (cal.scaled()) { Calibration cal2 = maskImp.getCalibration(); cal2.pixelWidth = cal.pixelWidth; cal2.pixelHeight = cal.pixelHeight; cal2.setUnit(cal.getUnit()); } maskImp.updateAndRepaintWindow(); Prefs.useInvertingLut = useInvertingLut; }
void Otsu(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) { // Otsu's threshold algorithm // C++ code by Jordan Bevik <*****@*****.**> // ported to ImageJ plugin by G.Landini. Same algorithm as in Auto_Threshold, this time on local // circular regions int[] data; int w = imp.getWidth(); int h = imp.getHeight(); int position; int radiusx2 = radius * 2; ImageProcessor ip = imp.getProcessor(); byte[] pixels = (byte[]) ip.getPixels(); byte[] pixelsOut = new byte [pixels.length]; // need this to avoid changing the image data (and further histograms) byte object; byte backg; if (doIwhite) { object = (byte) 0xff; backg = (byte) 0; } else { object = (byte) 0; backg = (byte) 0xff; } int k, kStar; // k = the current threshold; kStar = optimal threshold int N1, N; // N1 = # points with intensity <=k; N = total number of points double BCV, BCVmax; // The current Between Class Variance and maximum BCV double num, denom; // temporary bookeeping int Sk; // The total intensity for all histogram points <=k int S, L = 256; // The total intensity of the image. Need to hange here if modifying for >8 bits // images int roiy; Roi roi = new OvalRoi(0, 0, radiusx2, radiusx2); // ip.setRoi(roi); for (int y = 0; y < h; y++) { IJ.showProgress( (double) (y) / (h - 1)); // this method is slow, so let's show the progress bar roiy = y - radius; for (int x = 0; x < w; x++) { roi.setLocation(x - radius, roiy); ip.setRoi(roi); // ip.setRoi(new OvalRoi(x-radius, roiy, radiusx2, radiusx2)); position = x + y * w; data = ip.getHistogram(); // Initialize values: S = N = 0; for (k = 0; k < L; k++) { S += k * data[k]; // Total histogram intensity N += data[k]; // Total number of data points } Sk = 0; N1 = data[0]; // The entry for zero intensity BCV = 0; BCVmax = 0; kStar = 0; // Look at each possible threshold value, // calculate the between-class variance, and decide if it's a max for (k = 1; k < L - 1; k++) { // No need to check endpoints k = 0 or k = L-1 Sk += k * data[k]; N1 += data[k]; // The float casting here is to avoid compiler warning about loss of precision and // will prevent overflow in the case of large saturated images denom = (double) (N1) * (N - N1); // Maximum value of denom is (N^2)/4 = approx. 3E10 if (denom != 0) { // Float here is to avoid loss of precision when dividing num = ((double) N1 / N) * S - Sk; // Maximum value of num = 255*N = approx 8E7 BCV = (num * num) / denom; } else BCV = 0; if (BCV >= BCVmax) { // Assign the best threshold found so far BCVmax = BCV; kStar = k; } } // kStar += 1; // Use QTI convention that intensity -> 1 if intensity >= k // (the algorithm was developed for I-> 1 if I <= k.) // return kStar; pixelsOut[position] = ((int) (pixels[position] & 0xff) > kStar) ? object : backg; } } for (position = 0; position < w * h; position++) pixels[position] = pixelsOut[position]; // update with thresholded pixels }
public void run(String arg) { imp = IJ.getImage(); int stackSize = imp.getStackSize(); if (imp == null) { IJ.noImage(); return; } // Make sure input image is a stack. if (stackSize == 1) { IJ.error("Z Project", "Stack required"); return; } // Check for inverting LUT. if (imp.getProcessor().isInvertedLut()) { if (!IJ.showMessageWithCancel("ZProjection", lutMessage)) return; } // Set default bounds. int channels = imp.getNChannels(); int frames = imp.getNFrames(); int slices = imp.getNSlices(); isHyperstack = imp.isHyperStack() || (ij.macro.Interpreter.isBatchMode() && ((frames > 1 && frames < stackSize) || (slices > 1 && slices < stackSize))); boolean simpleComposite = channels == stackSize; if (simpleComposite) isHyperstack = false; startSlice = 1; if (isHyperstack) { int nSlices = imp.getNSlices(); if (nSlices > 1) stopSlice = nSlices; else stopSlice = imp.getNFrames(); } else stopSlice = stackSize; // Build control dialog GenericDialog gd = buildControlDialog(startSlice, stopSlice); gd.showDialog(); if (gd.wasCanceled()) return; if (!imp.lock()) return; // exit if in use long tstart = System.currentTimeMillis(); setStartSlice((int) gd.getNextNumber()); setStopSlice((int) gd.getNextNumber()); method = gd.getNextChoiceIndex(); Prefs.set(METHOD_KEY, method); if (isHyperstack) { allTimeFrames = imp.getNFrames() > 1 && imp.getNSlices() > 1 ? gd.getNextBoolean() : false; doHyperStackProjection(allTimeFrames); } else if (imp.getType() == ImagePlus.COLOR_RGB) doRGBProjection(true); else doProjection(true); if (arg.equals("") && projImage != null) { long tstop = System.currentTimeMillis(); projImage.setCalibration(imp.getCalibration()); if (simpleComposite) IJ.run(projImage, "Grays", ""); projImage.show("ZProjector: " + IJ.d2s((tstop - tstart) / 1000.0, 2) + " seconds"); } imp.unlock(); IJ.register(ZProjector.class); return; }
/** Ask for parameters and then execute. */ public void run(String arg) { // 1 - Obtain the currently active image: ImagePlus imp = IJ.getImage(); if (null == imp) { IJ.showMessage("There must be at least one image open"); return; } if (imp.getBitDepth() != 8) { IJ.showMessage("Error", "Only 8-bit images are supported"); return; } // 2 - Ask for parameters: GenericDialog gd = new GenericDialog("Auto Local Threshold"); String[] methods = { "Try all", "Bernsen", "Contrast", "Mean", "Median", "MidGrey", "Niblack", "Otsu", "Phansalkar", "Sauvola" }; gd.addMessage("Auto Local Threshold v1.5"); gd.addChoice("Method", methods, methods[0]); gd.addNumericField("Radius", 15, 0); gd.addMessage("Special paramters (if different from default)"); gd.addNumericField("Parameter_1", 0, 0); gd.addNumericField("Parameter_2", 0, 0); gd.addCheckbox("White objects on black background", true); if (imp.getStackSize() > 1) { gd.addCheckbox("Stack", false); } gd.addMessage("Thresholded result is always shown in white [255]."); gd.showDialog(); if (gd.wasCanceled()) return; // 3 - Retrieve parameters from the dialog String myMethod = gd.getNextChoice(); int radius = (int) gd.getNextNumber(); double par1 = (double) gd.getNextNumber(); double par2 = (double) gd.getNextNumber(); boolean doIwhite = gd.getNextBoolean(); boolean doIstack = false; int stackSize = imp.getStackSize(); if (stackSize > 1) doIstack = gd.getNextBoolean(); // 4 - Execute! // long start = System.currentTimeMillis(); if (myMethod.equals("Try all")) { ImageProcessor ip = imp.getProcessor(); int xe = ip.getWidth(); int ye = ip.getHeight(); int ml = methods.length; ImagePlus imp2, imp3; ImageStack tstack = null, stackNew; if (stackSize > 1 && doIstack) { boolean doItAnyway = true; if (stackSize > 25) { YesNoCancelDialog d = new YesNoCancelDialog( IJ.getInstance(), "Auto Local Threshold", "You might run out of memory.\n \nDisplay " + stackSize + " slices?\n \n \'No\' will process without display and\noutput results to the log window."); if (!d.yesPressed()) { // doIlog=true; //will show in the log window doItAnyway = false; } if (d.cancelPressed()) return; } for (int j = 1; j <= stackSize; j++) { imp.setSlice(j); ip = imp.getProcessor(); tstack = new ImageStack(xe, ye); for (int k = 1; k < ml; k++) tstack.addSlice(methods[k], ip.duplicate()); imp2 = new ImagePlus("Auto Threshold", tstack); imp2.updateAndDraw(); for (int k = 1; k < ml; k++) { imp2.setSlice(k); Object[] result = exec(imp2, methods[k], radius, par1, par2, doIwhite); } // if (doItAnyway){ CanvasResizer cr = new CanvasResizer(); stackNew = cr.expandStack(tstack, (xe + 2), (ye + 18), 1, 1); imp3 = new ImagePlus("Auto Threshold", stackNew); imp3.updateAndDraw(); MontageMaker mm = new MontageMaker(); mm.makeMontage(imp3, 3, 3, 1.0, 1, (ml - 1), 1, 0, true); // 3 columns and 3 rows } imp.setSlice(1); // if (doItAnyway) IJ.run("Images to Stack", "method=[Copy (center)] title=Montage"); return; } else { // single image try all tstack = new ImageStack(xe, ye); for (int k = 1; k < ml; k++) tstack.addSlice(methods[k], ip.duplicate()); imp2 = new ImagePlus("Auto Threshold", tstack); imp2.updateAndDraw(); for (int k = 1; k < ml; k++) { imp2.setSlice(k); // IJ.log("analyzing slice with "+methods[k]); Object[] result = exec(imp2, methods[k], radius, par1, par2, doIwhite); } // imp2.setSlice(1); CanvasResizer cr = new CanvasResizer(); stackNew = cr.expandStack(tstack, (xe + 2), (ye + 18), 1, 1); imp3 = new ImagePlus("Auto Threshold", stackNew); imp3.updateAndDraw(); MontageMaker mm = new MontageMaker(); mm.makeMontage(imp3, 3, 3, 1.0, 1, (ml - 1), 1, 0, true); return; } } else { // selected a method if (stackSize > 1 && doIstack) { // whole stack // if (doIstackHistogram) {// one global histogram // Object[] result = exec(imp, myMethod, noWhite, noBlack, doIwhite, doIset, doIlog, // doIstackHistogram ); // } // else{ // slice by slice for (int k = 1; k <= stackSize; k++) { imp.setSlice(k); Object[] result = exec(imp, myMethod, radius, par1, par2, doIwhite); } // } imp.setSlice(1); } else { // just one slice Object[] result = exec(imp, myMethod, radius, par1, par2, doIwhite); } // 5 - If all went well, show the image: // not needed here as the source image is binarised } }
void Niblack(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) { // Niblack recommends K_VALUE = -0.2 for images with black foreground // objects, and K_VALUE = +0.2 for images with white foreground objects. // Niblack W. (1986) "An introduction to Digital Image Processing" Prentice-Hall. // Ported to ImageJ plugin from E Celebi's fourier_0.8 routines // This version uses a circular local window, instead of a rectagular one ImagePlus Meanimp, Varimp; ImageProcessor ip = imp.getProcessor(), ipMean, ipVar; double k_value; int c_value = 0; byte object; byte backg; if (doIwhite) { k_value = 0.2; object = (byte) 0xff; backg = (byte) 0; } else { k_value = -0.2; object = (byte) 0; backg = (byte) 0xff; } if (par1 != 0) { IJ.log("Niblack: changed k_value from :" + k_value + " to:" + par1); k_value = par1; } if (par2 != 0) { IJ.log( "Niblack: changed c_value from :" + c_value + " to:" + par2); // requested feature, not in original c_value = (int) par2; } Meanimp = duplicateImage(ip); ImageConverter ic = new ImageConverter(Meanimp); ic.convertToGray32(); ipMean = Meanimp.getProcessor(); RankFilters rf = new RankFilters(); rf.rank(ipMean, radius, rf.MEAN); // Mean // Meanimp.show(); Varimp = duplicateImage(ip); ic = new ImageConverter(Varimp); ic.convertToGray32(); ipVar = Varimp.getProcessor(); rf.rank(ipVar, radius, rf.VARIANCE); // Variance // Varimp.show(); byte[] pixels = (byte[]) ip.getPixels(); float[] mean = (float[]) ipMean.getPixels(); float[] var = (float[]) ipVar.getPixels(); for (int i = 0; i < pixels.length; i++) pixels[i] = ((int) (pixels[i] & 0xff) > (int) (mean[i] + k_value * Math.sqrt(var[i]) - c_value)) ? object : backg; // imp.updateAndDraw(); return; }
private void makeBand(ImagePlus imp) { Roi roi = imp.getRoi(); if (roi == null) { noRoi("Make Band"); return; } if (!roi.isArea()) { IJ.error("Make Band", "Area selection required"); return; } Calibration cal = imp.getCalibration(); double pixels = bandSize; double size = pixels * cal.pixelWidth; int decimalPlaces = 0; if ((int) size != size) decimalPlaces = 2; GenericDialog gd = new GenericDialog("Make Band"); gd.addNumericField("Band Size:", size, decimalPlaces, 4, cal.getUnits()); gd.showDialog(); if (gd.wasCanceled()) return; size = gd.getNextNumber(); if (Double.isNaN(size)) { IJ.error("Make Band", "invalid number"); return; } int n = (int) Math.round(size / cal.pixelWidth); if (n > 255) { IJ.error("Make Band", "Cannot make bands wider that 255 pixels"); return; } int width = imp.getWidth(); int height = imp.getHeight(); Rectangle r = roi.getBounds(); ImageProcessor ip = roi.getMask(); if (ip == null) { ip = new ByteProcessor(r.width, r.height); ip.invert(); } ImageProcessor mask = new ByteProcessor(width, height); mask.insert(ip, r.x, r.y); ImagePlus edm = new ImagePlus("mask", mask); boolean saveBlackBackground = Prefs.blackBackground; Prefs.blackBackground = false; IJ.run(edm, "Distance Map", ""); Prefs.blackBackground = saveBlackBackground; ip = edm.getProcessor(); ip.setThreshold(0, n, ImageProcessor.NO_LUT_UPDATE); int xx = -1, yy = -1; for (int x = r.x; x < r.x + r.width; x++) { for (int y = r.y; y < r.y + r.height; y++) { if (ip.getPixel(x, y) < n) { xx = x; yy = y; break; } } if (xx >= 0 || yy >= 0) break; } int count = IJ.doWand(edm, xx, yy, 0, null); if (count <= 0) { IJ.error("Make Band", "Unable to make band"); return; } ShapeRoi roi2 = new ShapeRoi(edm.getRoi()); if (!(roi instanceof ShapeRoi)) roi = new ShapeRoi(roi); ShapeRoi roi1 = (ShapeRoi) roi; roi2 = roi2.not(roi1); imp.setRoi(roi2); bandSize = n; }
public void run(ImageProcessor ip) { String[] imageNames = getOpenImageNames(); if (imageNames[0] == "None") { IJ.error("need at least 2 binary open images"); return; } double previousMinOverlap = Prefs.get("BVTB.BinaryFeatureExtractor.minOverlap", 0); boolean previousCombine = Prefs.get("BVTB.BinaryFeatureExtractor.combine", false); GenericDialog gd = new GenericDialog("Binary Feature Extractor"); gd.addChoice("Objects image", imageNames, imageNames[0]); gd.addChoice("Selector image", imageNames, imageNames[1]); gd.addNumericField("Object_overlap in % (0=off)", previousMinOverlap, 0, 9, ""); gd.addCheckbox("Combine objects and selectors", previousCombine); gd.addCheckbox("Count output", true); gd.addCheckbox("Analysis tables", false); gd.showDialog(); if (gd.wasCanceled()) { return; } String objectsImgTitle = gd.getNextChoice(); String selectorsImgTitle = gd.getNextChoice(); double minOverlap = gd.getNextNumber(); boolean combineImages = gd.getNextBoolean(); boolean showCountOutput = gd.getNextBoolean(); boolean showAnalysis = gd.getNextBoolean(); if (gd.invalidNumber() || minOverlap < 0 || minOverlap > 100) { IJ.error("invalid number"); return; } Prefs.set("BVTB.BinaryFeatureExtractor.minOverlap", minOverlap); Prefs.set("BVTB.BinaryFeatureExtractor.combine", combineImages); if (objectsImgTitle.equals(selectorsImgTitle)) { IJ.error("images need to be different"); return; } ImagePlus objectsImp = WindowManager.getImage(objectsImgTitle); ImageProcessor objectsIP = objectsImp.getProcessor(); ImagePlus selectorsImp = WindowManager.getImage(selectorsImgTitle); ImageProcessor selectorsIP = selectorsImp.getProcessor(); if (!objectsIP.isBinary() || !selectorsIP.isBinary()) { IJ.error("works with 8-bit binary images only"); return; } if ((objectsImp.getWidth() != selectorsImp.getWidth()) || objectsImp.getHeight() != selectorsImp.getHeight()) { IJ.error("images need to be of the same size"); return; } // close any existing RoiManager before instantiating a new one for this analysis RoiManager oldRM = RoiManager.getInstance2(); if (oldRM != null) { oldRM.close(); } RoiManager objectsRM = new RoiManager(true); ResultsTable objectsRT = new ResultsTable(); ParticleAnalyzer analyzeObjects = new ParticleAnalyzer(analyzerOptions, measurementFlags, objectsRT, 0.0, 999999999.9); analyzeObjects.setRoiManager(objectsRM); analyzeObjects.analyze(objectsImp); objectsRM.runCommand("Show None"); int objectNumber = objectsRT.getCounter(); Roi[] objectRoi = objectsRM.getRoisAsArray(); ResultsTable measureSelectorsRT = new ResultsTable(); Analyzer overlapAnalyzer = new Analyzer(selectorsImp, measurementFlags, measureSelectorsRT); ImagePlus outputImp = IJ.createImage("output", "8-bit black", objectsImp.getWidth(), objectsImp.getHeight(), 1); ImageProcessor outputIP = outputImp.getProcessor(); double[] measuredOverlap = new double[objectNumber]; outputIP.setValue(255.0); for (int o = 0; o < objectNumber; o++) { selectorsImp.killRoi(); selectorsImp.setRoi(objectRoi[o]); overlapAnalyzer.measure(); measuredOverlap[o] = measureSelectorsRT.getValue("%Area", o); if (minOverlap != 0.0 && measuredOverlap[o] >= minOverlap) { outputIP.fill(objectRoi[o]); finalCount++; } else if (minOverlap == 0.0 && measuredOverlap[o] > 0.0) { outputIP.fill(objectRoi[o]); finalCount++; } } // measureSelectorsRT.show("Objects"); selectorsImp.killRoi(); RoiManager selectorRM = new RoiManager(true); ResultsTable selectorRT = new ResultsTable(); ParticleAnalyzer.setRoiManager(selectorRM); ParticleAnalyzer analyzeSelectors = new ParticleAnalyzer(analyzerOptions, measurementFlags, selectorRT, 0.0, 999999999.9); analyzeSelectors.analyze(selectorsImp); selectorRM.runCommand("Show None"); int selectorNumber = selectorRT.getCounter(); if (combineImages) { outputImp.updateAndDraw(); Roi[] selectorRoi = selectorRM.getRoisAsArray(); ResultsTable measureObjectsRT = new ResultsTable(); Analyzer selectorAnalyzer = new Analyzer(outputImp, measurementFlags, measureObjectsRT); double[] selectorOverlap = new double[selectorNumber]; outputIP.setValue(255.0); for (int s = 0; s < selectorNumber; s++) { outputImp.killRoi(); outputImp.setRoi(selectorRoi[s]); selectorAnalyzer.measure(); selectorOverlap[s] = measureObjectsRT.getValue("%Area", s); if (selectorOverlap[s] > 0.0d) { outputIP.fill(selectorRoi[s]); } } selectorRoi = null; selectorAnalyzer = null; measureObjectsRT = null; } // selectorRT.show("Selectors"); outputImp.killRoi(); String outputImageTitle = WindowManager.getUniqueName("Extracted_" + objectsImgTitle); outputImp.setTitle(outputImageTitle); outputImp.show(); outputImp.changes = true; if (showCountOutput) { String[] openTextWindows = WindowManager.getNonImageTitles(); boolean makeNewTable = true; for (int w = 0; w < openTextWindows.length; w++) { if (openTextWindows[w].equals("BFE_Results")) { makeNewTable = false; } } TextWindow existingCountTable = ResultsTable.getResultsWindow(); if (makeNewTable) { countTable = new ResultsTable(); countTable.setPrecision(0); countTable.setValue("Image", 0, outputImageTitle); countTable.setValue("Objects", 0, objectNumber); countTable.setValue("Selectors", 0, selectorNumber); countTable.setValue("Extracted", 0, finalCount); countTable.show("BFE_Results"); } else { IJ.renameResults("BFE_Results", "Results"); countTable = ResultsTable.getResultsTable(); countTable.setPrecision(0); countTable.incrementCounter(); countTable.addValue("Image", outputImageTitle); countTable.addValue("Objects", objectNumber); countTable.addValue("Selectors", selectorNumber); countTable.addValue("Extracted", finalCount); IJ.renameResults("Results", "BFE_Results"); countTable.show("BFE_Results"); } } if (showAnalysis) { ResultsTable extractedRT = new ResultsTable(); ParticleAnalyzer analyzeExtracted = new ParticleAnalyzer( ParticleAnalyzer.CLEAR_WORKSHEET | ParticleAnalyzer.RECORD_STARTS, measurementFlags, extractedRT, 0.0, 999999999.9); analyzeExtracted.analyze(outputImp); objectsRT.show("Objects"); selectorRT.show("Selectors"); extractedRT.show("Extracted"); } else { objectsRT = null; selectorRT = null; } objectsRM = null; measureSelectorsRT = null; analyzeObjects = null; overlapAnalyzer = null; objectRoi = null; selectorRM = null; objectsImp.killRoi(); objectsImp.changes = false; selectorsImp.changes = false; }
void Sauvola(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) { // Sauvola recommends K_VALUE = 0.5 and R_VALUE = 128. // This is a modification of Niblack's thresholding method. // Sauvola J. and Pietaksinen M. (2000) "Adaptive Document Image Binarization" // Pattern Recognition, 33(2): 225-236 // http://www.ee.oulu.fi/mvg/publications/show_pdf.php?ID=24 // Ported to ImageJ plugin from E Celebi's fourier_0.8 routines // This version uses a circular local window, instead of a rectagular one ImagePlus Meanimp, Varimp; ImageProcessor ip = imp.getProcessor(), ipMean, ipVar; double k_value = 0.5; double r_value = 128; byte object; byte backg; if (par1 != 0) { IJ.log("Sauvola: changed k_value from :" + k_value + " to:" + par1); k_value = par1; } if (par2 != 0) { IJ.log("Sauvola: changed r_value from :" + r_value + " to:" + par2); r_value = par2; } if (doIwhite) { object = (byte) 0xff; backg = (byte) 0; } else { object = (byte) 0; backg = (byte) 0xff; } Meanimp = duplicateImage(ip); ImageConverter ic = new ImageConverter(Meanimp); ic.convertToGray32(); ipMean = Meanimp.getProcessor(); RankFilters rf = new RankFilters(); rf.rank(ipMean, radius, rf.MEAN); // Mean // Meanimp.show(); Varimp = duplicateImage(ip); ic = new ImageConverter(Varimp); ic.convertToGray32(); ipVar = Varimp.getProcessor(); rf.rank(ipVar, radius, rf.VARIANCE); // Variance // Varimp.show(); byte[] pixels = (byte[]) ip.getPixels(); float[] mean = (float[]) ipMean.getPixels(); float[] var = (float[]) ipVar.getPixels(); for (int i = 0; i < pixels.length; i++) pixels[i] = ((int) (pixels[i] & 0xff) > (int) (mean[i] * (1.0 + k_value * ((Math.sqrt(var[i]) / r_value) - 1.0)))) ? object : backg; // imp.updateAndDraw(); return; }
/* if selection is closed shape, create a circle with the same area and centroid, otherwise use<br> the Pratt method to fit a circle to the points that define the line or multi-point selection.<br> Reference: Pratt V., Direct least-squares fitting of algebraic surfaces", Computer Graphics, Vol. 21, pages 145-152 (1987).<br> Original code: Nikolai Chernov's MATLAB script for Newton-based Pratt fit.<br> (http://www.math.uab.edu/~chernov/cl/MATLABcircle.html)<br> Java version: https://github.com/mdoube/BoneJ/blob/master/src/org/doube/geometry/FitCircle.java<br> @authors Nikolai Chernov, Michael Doube, Ved Sharma */ void fitCircle(ImagePlus imp) { Roi roi = imp.getRoi(); if (roi == null) { noRoi("Fit Circle"); return; } if (roi.isArea()) { // create circle with the same area and centroid ImageProcessor ip = imp.getProcessor(); ip.setRoi(roi); ImageStatistics stats = ImageStatistics.getStatistics(ip, Measurements.AREA + Measurements.CENTROID, null); double r = Math.sqrt(stats.pixelCount / Math.PI); imp.killRoi(); int d = (int) Math.round(2.0 * r); IJ.makeOval( (int) Math.round(stats.xCentroid - r), (int) Math.round(stats.yCentroid - r), d, d); return; } Polygon poly = roi.getPolygon(); int n = poly.npoints; int[] x = poly.xpoints; int[] y = poly.ypoints; if (n < 3) { IJ.error("Fit Circle", "At least 3 points are required to fit a circle."); return; } // calculate point centroid double sumx = 0, sumy = 0; for (int i = 0; i < n; i++) { sumx = sumx + poly.xpoints[i]; sumy = sumy + poly.ypoints[i]; } double meanx = sumx / n; double meany = sumy / n; // calculate moments double[] X = new double[n], Y = new double[n]; double Mxx = 0, Myy = 0, Mxy = 0, Mxz = 0, Myz = 0, Mzz = 0; for (int i = 0; i < n; i++) { X[i] = x[i] - meanx; Y[i] = y[i] - meany; double Zi = X[i] * X[i] + Y[i] * Y[i]; Mxy = Mxy + X[i] * Y[i]; Mxx = Mxx + X[i] * X[i]; Myy = Myy + Y[i] * Y[i]; Mxz = Mxz + X[i] * Zi; Myz = Myz + Y[i] * Zi; Mzz = Mzz + Zi * Zi; } Mxx = Mxx / n; Myy = Myy / n; Mxy = Mxy / n; Mxz = Mxz / n; Myz = Myz / n; Mzz = Mzz / n; // calculate the coefficients of the characteristic polynomial double Mz = Mxx + Myy; double Cov_xy = Mxx * Myy - Mxy * Mxy; double Mxz2 = Mxz * Mxz; double Myz2 = Myz * Myz; double A2 = 4 * Cov_xy - 3 * Mz * Mz - Mzz; double A1 = Mzz * Mz + 4 * Cov_xy * Mz - Mxz2 - Myz2 - Mz * Mz * Mz; double A0 = Mxz2 * Myy + Myz2 * Mxx - Mzz * Cov_xy - 2 * Mxz * Myz * Mxy + Mz * Mz * Cov_xy; double A22 = A2 + A2; double epsilon = 1e-12; double ynew = 1e+20; int IterMax = 20; double xnew = 0; int iterations = 0; // Newton's method starting at x=0 for (int iter = 1; iter <= IterMax; iter++) { iterations = iter; double yold = ynew; ynew = A0 + xnew * (A1 + xnew * (A2 + 4. * xnew * xnew)); if (Math.abs(ynew) > Math.abs(yold)) { if (IJ.debugMode) IJ.log("Fit Circle: wrong direction: |ynew| > |yold|"); xnew = 0; break; } double Dy = A1 + xnew * (A22 + 16 * xnew * xnew); double xold = xnew; xnew = xold - ynew / Dy; if (Math.abs((xnew - xold) / xnew) < epsilon) break; if (iter >= IterMax) { if (IJ.debugMode) IJ.log("Fit Circle: will not converge"); xnew = 0; } if (xnew < 0) { if (IJ.debugMode) IJ.log("Fit Circle: negative root: x = " + xnew); xnew = 0; } } if (IJ.debugMode) IJ.log("Fit Circle: n=" + n + ", xnew=" + IJ.d2s(xnew, 2) + ", iterations=" + iterations); // calculate the circle parameters double DET = xnew * xnew - xnew * Mz + Cov_xy; double CenterX = (Mxz * (Myy - xnew) - Myz * Mxy) / (2 * DET); double CenterY = (Myz * (Mxx - xnew) - Mxz * Mxy) / (2 * DET); double radius = Math.sqrt(CenterX * CenterX + CenterY * CenterY + Mz + 2 * xnew); if (Double.isNaN(radius)) { IJ.error("Fit Circle", "Points are collinear."); return; } CenterX = CenterX + meanx; CenterY = CenterY + meany; imp.killRoi(); IJ.makeOval( (int) Math.round(CenterX - radius), (int) Math.round(CenterY - radius), (int) Math.round(2 * radius), (int) Math.round(2 * radius)); }
void Phansalkar(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) { // This is a modification of Sauvola's thresholding method to deal with low contrast images. // Phansalskar N. et al. Adaptive local thresholding for detection of nuclei in diversity // stained // cytology images.International Conference on Communications and Signal Processing (ICCSP), // 2011, // 218 - 220. // In this method, the threshold t = mean*(1+p*exp(-q*mean)+k*((stdev/r)-1)) // Phansalkar recommends k = 0.25, r = 0.5, p = 2 and q = 10. In this plugin, k and r are the // parameters 1 and 2 respectively, but the values of p and q are fixed. // // Implemented from Phansalkar's paper description by G. Landini // This version uses a circular local window, instead of a rectagular one ImagePlus Meanimp, Varimp, Orimp; ImageProcessor ip = imp.getProcessor(), ipMean, ipVar, ipOri; double k_value = 0.25; double r_value = 0.5; double p_value = 2.0; double q_value = 10.0; byte object; byte backg; if (par1 != 0) { IJ.log("Phansalkar: changed k_value from :" + k_value + " to:" + par1); k_value = par1; } if (par2 != 0) { IJ.log("Phansalkar: changed r_value from :" + r_value + " to:" + par2); r_value = par2; } if (doIwhite) { object = (byte) 0xff; backg = (byte) 0; } else { object = (byte) 0; backg = (byte) 0xff; } Meanimp = duplicateImage(ip); ContrastEnhancer ce = new ContrastEnhancer(); ce.stretchHistogram(Meanimp, 0.0); ImageConverter ic = new ImageConverter(Meanimp); ic.convertToGray32(); ipMean = Meanimp.getProcessor(); ipMean.multiply(1.0 / 255); Orimp = duplicateImage(ip); ce.stretchHistogram(Orimp, 0.0); ic = new ImageConverter(Orimp); ic.convertToGray32(); ipOri = Orimp.getProcessor(); ipOri.multiply(1.0 / 255); // original to compare // Orimp.show(); RankFilters rf = new RankFilters(); rf.rank(ipMean, radius, rf.MEAN); // Mean // Meanimp.show(); Varimp = duplicateImage(ip); ce.stretchHistogram(Varimp, 0.0); ic = new ImageConverter(Varimp); ic.convertToGray32(); ipVar = Varimp.getProcessor(); ipVar.multiply(1.0 / 255); rf.rank(ipVar, radius, rf.VARIANCE); // Variance ipVar.sqr(); // SD // Varimp.show(); byte[] pixels = (byte[]) ip.getPixels(); float[] ori = (float[]) ipOri.getPixels(); float[] mean = (float[]) ipMean.getPixels(); float[] sd = (float[]) ipVar.getPixels(); for (int i = 0; i < pixels.length; i++) pixels[i] = ((ori[i]) > (mean[i] * (1.0 + p_value * Math.exp(-q_value * mean[i]) + k_value * ((sd[i] / r_value) - 1.0)))) ? object : backg; // imp.updateAndDraw(); return; }