package ini.trakem2.imaging.filters; import ij.process.ImageProcessor; import ij.process.ShortProcessor; import ini.trakem2.imaging.IntegralHistogram2d; import java.util.Map; public class CorrectBackground implements IFilter { /** Radius for integral median filter */ protected int medianRadius = 512; /** Number of histogram bins. RAM requirements scale relative to this value: choose wisely. Test first. */ protected int nBins = 32; /** Whether to process the approximated median filter with a Gaussian */ protected boolean postGaussian = true; /** Sigma of the Gaussian */ protected float sigma = 5; public CorrectBackground() {} public CorrectBackground(final int radius, final float sigma) { this.medianRadius = radius; this.sigma = sigma; } public CorrectBackground(Map<String,String> params) { try { this.medianRadius = Integer.parseInt(params.get("medianradius")); this.nBins = Integer.parseInt(params.get("nbins")); this.postGaussian = Boolean.parseBoolean(params.get("postgaussian")); this.sigma = Float.parseFloat(params.get("sigma")); } catch (Exception e) { throw new IllegalArgumentException("Cound not create CorrectBackground filter!", e); } } @Override public ImageProcessor process(final ImageProcessor ip) { final ShortProcessor sp; try { sp = (ShortProcessor)ip; } catch (ClassCastException cce) { System.out.println("CorrectBackground supports 16-bit images only!"); return ip; } final double min = ip.getMin(), max = ip.getMax(); final long[] hist = IntegralHistogram2d.integralHistogram2d((short[])sp.getPixels(), sp.getWidth(), sp.getHeight(), nBins, min, max); final short[] median = IntegralHistogram2d.median(sp.getWidth(), sp.getHeight(), hist, nBins, min, max, this.medianRadius); if (this.postGaussian) { ij.plugin.filter.GaussianBlur g = new ij.plugin.filter.GaussianBlur(); g.blurGaussian(new ShortProcessor(sp.getWidth(), sp.getHeight(), median, null), this.sigma, this.sigma, 0.02); } // Approximate mean image value (within min-max range) from the histogram present in the last set of nBins in the integral histogram final double binInc = (max - min + 1) / nBins; double sum = 0; long count = 0; for (int i=hist.length-nBins, j=0; j<nBins; ++i, ++j) { sum += hist[i] * (min + (j * binInc)); count += hist[i]; } // TODO error the sum of hist[-16] to hist[-1] doesn't add up to width*height !!! final double mean = sum / count; // can't use median.length or p.length, there is an erroneous mismatch final short[] p = (short[]) sp.getPixels(); // Divide image by median (which plays the role of an approximated flat-field brightness image) // and multiply by the mean. // The pixels of the provided image for (int i=0; i<p.length; ++i) { final double m = median[i] & 0xffff; p[i] = (short)(((p[i]&0xffff) / m) * mean); } return ip; } @Override public String toXML(String indent) { return new StringBuilder(indent) .append("<t2_filter class=\"").append(getClass().getName()) .append("\" medianRadius=\"").append(medianRadius) .append("\" nBins=\"").append(nBins) .append("\" postGaussian=\"").append(postGaussian) .append("\" sigma=\"").append(sigma).append("\" />\n").toString(); } @Override public boolean equals(final Object o) { if (null == o) return false; if (o.getClass() == getClass()) { final CorrectBackground cb = (CorrectBackground)o; return cb.medianRadius == medianRadius && cb.nBins == nBins && cb.sigma == sigma && cb.postGaussian == postGaussian; } return false; } }