/* * Copyright 2010, 2011 Institut Pasteur. * * This file is part of NHerve Main Toolbox, which is an ICY plugin. * * NHerve Main Toolbox 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. * * NHerve Main Toolbox 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 NHerve Main Toolbox. If not, see <http://www.gnu.org/licenses/>. */ package plugins.nherve.toolbox.image.feature.descriptor; import icy.image.IcyBufferedImage; import java.awt.Shape; import java.util.HashMap; import java.util.Map; import plugins.nherve.toolbox.image.feature.ConvolutionKernel2D; import plugins.nherve.toolbox.image.feature.SegmentableIcyBufferedImage; import plugins.nherve.toolbox.image.feature.IcySupportRegion; import plugins.nherve.toolbox.image.feature.SupportRegion; import plugins.nherve.toolbox.image.feature.region.IcyPixel; import plugins.nherve.toolbox.image.feature.signature.SignatureException; import plugins.nherve.toolbox.image.feature.signature.DefaultVectorSignature; /** * The Class EdgeOrientationHistogram. * * @author Nicolas HERVE - nicolas.herve@pasteur.fr */ public class EdgeOrientationHistogram extends GlobalAndLocalDescriptor<SegmentableIcyBufferedImage, DefaultVectorSignature> { /** The Constant DEFAULT_DIMENSION. */ public final static int DEFAULT_DIMENSION = 7; /** The Constant DEFAULT_HYST_HIGH. */ public final static double DEFAULT_HYST_HIGH = 0.5; /** The Constant DEFAULT_HYST_LOW. */ public final static double DEFAULT_HYST_LOW = 0.3; /** The Constant DEFAULT_SIGMA. */ public final static double DEFAULT_SIGMA = 1.0; /** The bin centers. */ private double[] binCenters; /** The bin step. */ private double binStep; /** The do linear smoothing. */ private boolean doLinearSmoothing; /** The cache image contour. */ private Map<SegmentableIcyBufferedImage, boolean[]> cacheImageContour; /** The cache image gradient amplitude. */ private Map<SegmentableIcyBufferedImage, double[]> cacheImageGradientAmplitude; /** The cache image gradient orientation. */ private Map<SegmentableIcyBufferedImage, double[]> cacheImageGradientOrientation; /** The p dimension. */ private int pDimension; /** The p hyst high. */ private double pHystHigh; /** The p hyst low. */ private double pHystLow; /** The p sigma. */ private double pSigma; /** * Instantiates a new edge orientation histogram. * * @param doLinearSmoothing * the do linear smoothing * @param display * the display */ public EdgeOrientationHistogram(boolean doLinearSmoothing, boolean display) { super(display); cacheImageContour = new HashMap<SegmentableIcyBufferedImage, boolean[]>(); cacheImageGradientAmplitude = new HashMap<SegmentableIcyBufferedImage, double[]>(); cacheImageGradientOrientation = new HashMap<SegmentableIcyBufferedImage, double[]>(); this.doLinearSmoothing = doLinearSmoothing; setpDimension(DEFAULT_DIMENSION); setpHystHigh(DEFAULT_HYST_HIGH); setpHystLow(DEFAULT_HYST_LOW); setpSigma(DEFAULT_SIGMA); binCenters = null; binStep = 0; } /** * Inits the. */ private synchronized void init() { if (doLinearSmoothing && (binCenters == null)) { binCenters = new double[getpDimension()]; binStep = Math.PI / getpDimension(); double current = binStep / 2.0; for (int d = 0; d < getpDimension(); d++) { binCenters[d] = current; current += binStep; } } } /* (non-Javadoc) * @see plugins.nherve.toolbox.image.feature.descriptor.DefaultDescriptorImpl#getSignatureSize() */ public int getSignatureSize() { return getpDimension(); } /* (non-Javadoc) * @see plugins.nherve.toolbox.image.feature.descriptor.LocalDescriptor#extractLocalSignature(plugins.nherve.toolbox.image.feature.Segmentable, java.awt.Shape) */ @Override public DefaultVectorSignature extractLocalSignature(SegmentableIcyBufferedImage img, Shape shp) throws SignatureException { throw new RuntimeException("EdgeOrientationHistogram.extractSignature(SegmentableBufferedImage img, Shape shp) not implemented"); } /* (non-Javadoc) * @see plugins.nherve.toolbox.image.feature.descriptor.LocalDescriptor#extractLocalSignature(plugins.nherve.toolbox.image.feature.Segmentable, plugins.nherve.toolbox.image.feature.SupportRegion) */ @Override public DefaultVectorSignature extractLocalSignature(SegmentableIcyBufferedImage img, SupportRegion<IcyPixel> reg) throws SignatureException { boolean[] currentImageContour = null; double[] currentImageGradientAmplitude = null; double[] currentImageGradientOrientation = null; synchronized (cacheImageContour) { currentImageContour = cacheImageContour.get(img); currentImageGradientAmplitude = cacheImageGradientAmplitude.get(img); currentImageGradientOrientation = cacheImageGradientOrientation.get(img); } if ((currentImageContour == null) || (currentImageGradientAmplitude == null) || (currentImageGradientOrientation == null)) { throw new SignatureException("Canny operator not launched for current image (" + img.getName() + ")"); } init(); DefaultVectorSignature sig = getEmptySignature(); double angle = 0; int idx = 0; int adx = 0; int adxNext = 0; double adxCoef = 0; double adxNextCoef = 0; int w = img.getWidth(); int h = img.getHeight(); int x, y; for (IcyPixel p : reg) { x = (int)p.x; y = (int)p.y; idx = x + y * w; if ((x >= 0) && (x < w) && (y >= 0) && (y < h) && currentImageContour[idx]) { // angle in [0, 2PI[ angle = currentImageGradientOrientation[idx]; while (angle < 0.0) { angle += 2.0 * Math.PI; } while (angle >= 2.0 * Math.PI) { angle -= 2.0 * Math.PI; } // angle in [0, PI[ if (angle >= Math.PI) { angle -= Math.PI; } // angle in [0, 1[ adx = (int) Math.floor(angle * getSignatureSize() / Math.PI); if (doLinearSmoothing) { adxCoef = angle - binCenters[adx]; if (adxCoef > 0) { adxNext = adx + 1; if (adxNext >= getSignatureSize()) { adxNext = 0; } } else if (adxCoef < 0) { adxNext = adx - 1; if (adxNext < 0) { adxNext = getSignatureSize() - 1; } } else { sig.addTo(adx, currentImageGradientAmplitude[idx]); continue; } adxCoef = 1.0 - Math.abs(adxCoef / binStep); adxNextCoef = 1.0 - adxCoef; sig.addTo(adx, adxCoef * currentImageGradientAmplitude[idx]); sig.addTo(adxNext, adxNextCoef * currentImageGradientAmplitude[idx]); } else { sig.addTo(adx, currentImageGradientAmplitude[idx]); } } } sig.normalizeSumToOne(true); return sig; } /* (non-Javadoc) * @see plugins.nherve.toolbox.image.feature.Descriptor#postProcess(plugins.nherve.toolbox.image.feature.Segmentable) */ @Override public void postProcess(SegmentableIcyBufferedImage img) throws SignatureException { synchronized (cacheImageContour) { // log("postProcess(" + img.getName() + ") : " + // cacheImageContour.size()); cacheImageContour.remove(img); cacheImageGradientAmplitude.remove(img); cacheImageGradientOrientation.remove(img); } } /* (non-Javadoc) * @see plugins.nherve.toolbox.image.feature.Descriptor#preProcess(plugins.nherve.toolbox.image.feature.Segmentable) */ @Override public void preProcess(SegmentableIcyBufferedImage img) throws SignatureException { // log("preProcess(" + img.getName() + ") : " + // cacheImageContour.size()); IcyBufferedImage bimg = img.getImage(); int w = img.getWidth(); int h = img.getHeight(); int size = w * h; boolean[] currentImageContour = new boolean[size]; double[] currentImageGradientAmplitude = new double[size]; double[] currentImageGradientOrientation = new double[size]; double[] data = ConvolutionKernel2D.getIntensity(bimg); ConvolutionKernel2D.canny(data, w, h, getpSigma(), getpHystLow(), getpHystHigh(), currentImageContour, currentImageGradientAmplitude, currentImageGradientOrientation); synchronized (cacheImageContour) { cacheImageContour.put(img, currentImageContour); cacheImageGradientAmplitude.put(img, currentImageGradientAmplitude); cacheImageGradientOrientation.put(img, currentImageGradientOrientation); } } /** * Gets the p dimension. * * @return the p dimension */ public int getpDimension() { return pDimension; } /** * Sets the p dimension. * * @param pDimension * the new p dimension */ public void setpDimension(int pDimension) { this.pDimension = pDimension; } /** * Gets the p hyst high. * * @return the p hyst high */ public double getpHystHigh() { return pHystHigh; } /** * Sets the p hyst high. * * @param pHystHigh * the new p hyst high */ public void setpHystHigh(double pHystHigh) { this.pHystHigh = pHystHigh; } /** * Gets the p hyst low. * * @return the p hyst low */ public double getpHystLow() { return pHystLow; } /** * Sets the p hyst low. * * @param pHystLow * the new p hyst low */ public void setpHystLow(double pHystLow) { this.pHystLow = pHystLow; } /** * Gets the p sigma. * * @return the p sigma */ public double getpSigma() { return pSigma; } /** * Sets the p sigma. * * @param pSigma * the new p sigma */ public void setpSigma(double pSigma) { this.pSigma = pSigma; } /* (non-Javadoc) * @see plugins.nherve.toolbox.image.feature.descriptor.DefaultDescriptorImpl#toString() */ @Override public String toString() { return "EdgeOrientationHistogram"; } /* (non-Javadoc) * @see plugins.nherve.toolbox.image.feature.Descriptor#needToLoadSegmentable() */ @Override public boolean needToLoadSegmentable() { return true; } }