/*
* This file is part of the JFeatureLib project: https://github.com/locked-fg/JFeatureLib
* JFeatureLib 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.
*
* JFeatureLib 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 JFeatureLib; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* You are kindly asked to refer to the papers of the according authors which
* should be mentioned in the Javadocs of the respective classes as well as the
* JFeatureLib project itself.
*
* Hints how to cite the projects can be found at
* https://github.com/locked-fg/JFeatureLib/wiki/Citation
*/
package de.lmu.ifi.dbs.jfeaturelib.features;
import de.lmu.ifi.dbs.jfeaturelib.utils.Histogram;
import de.lmu.ifi.dbs.jfeaturelib.LibProperties;
import de.lmu.ifi.dbs.jfeaturelib.Progress;
import de.lmu.ifi.dbs.jfeaturelib.utils.IntegralImage;
import ij.measure.Measurements;
import ij.process.ByteProcessor;
import ij.process.ImageProcessor;
import ij.process.ImageStatistics;
import java.awt.Rectangle;
import java.io.IOException;
import java.util.EnumSet;
/**
* This descriptor calculates a histogram of mean intensities of specified neighborhood size.
* <p>
* For each pixel in the image, a patch of size
* <code>1 + 2 * size</code> is extracted, where
* <code>size</code> is determined by {@link #setSize(int)}. Mean intensities of all patches are summarized in a
* histogram.
* </p><p>
* Mean intensities are computed efficiently by using {@link IntegralImage}.
* </p>
*
* @author sebp
*/
public class MeanPatchIntensityHistogram extends AbstractFeatureDescriptor {
protected int m_size;
protected int m_bins;
protected double m_histMin;
protected double m_histMax;
protected int m_patchSize;
protected int m_patchArea;
protected IntegralImage m_integralImage;
public MeanPatchIntensityHistogram() {
}
@Override
public String getDescription() {
return "Mean patch intensities";
}
@Override
public EnumSet<Supports> supports() {
return EnumSet.of(
Supports.NoChanges,
Supports.DOES_8G);
}
@Override
public void setProperties(LibProperties properties) throws IOException {
setSize(properties.getInteger(LibProperties.MEAN_PATCH_INTENSITIES_PATCH_SIZE));
setNumberOfBins(properties.getInteger(LibProperties.MEAN_PATCH_INTENSITIES_BINS));
setHistogramRange(
properties.getDouble(LibProperties.MEAN_PATCH_INTENSITIES_HIST_MIN),
properties.getDouble(LibProperties.MEAN_PATCH_INTENSITIES_HIST_MAX));
}
@Override
public void run(ImageProcessor ip) {
firePropertyChange(Progress.START);
createIntegralImage((ByteProcessor) ip);
int yStart, xStart, yEnd, xEnd;
yStart = m_size;
xStart = m_size;
yEnd = ip.getHeight() - m_size;
xEnd = ip.getWidth() - m_size;
if (m_histMin == 0 && m_histMax == 0) {
retrieveMinAndMaxFromImage(ip);
// Histogram class excludes the maximum value,
// therefore increase it by 1
m_histMax++;
}
Histogram hist = new Histogram(m_bins, m_histMin, m_histMax);
for (int y = yStart; y < yEnd; y++) {
for (int x = xStart; x < xEnd; x++) {
hist.add(getMeanIntensity(x, y));
}
int p = (int) (y / (double) yEnd * 100);
firePropertyChange(new Progress(p));
}
addData(hist.getHistogramm());
m_integralImage = null;
firePropertyChange(Progress.END);
}
protected void createIntegralImage(ByteProcessor ip) {
m_integralImage = new IntegralImage();
m_integralImage.compute(ip);
}
protected void retrieveMinAndMaxFromImage(ImageProcessor ip) {
ImageStatistics stats = ImageStatistics.getStatistics(ip, Measurements.MIN_MAX, null);
m_histMin = stats.min;
m_histMax = stats.max;
}
protected float getMeanIntensity(final int x, final int y) {
Rectangle rect = new Rectangle(x - m_size, y - m_size, m_patchSize, m_patchSize);
return m_integralImage.get(rect) / (float) m_patchArea;
}
public int getSize() {
return m_size;
}
/**
* Set size of neighborhood which determines the patch size.
* <p>
* The outer most <tt>size</tt> pixels will not be considered because they have incomplete neighborhoods.
* </p>
*
* @param size
* @throws IllegalArgumentException if <code>size <= 0</code>
*/
public void setSize(int size) {
if (size <= 0) {
throw new IllegalArgumentException("size must be greater zero, but got " + size);
}
m_size = size;
m_patchSize = 1 + 2 * size;
m_patchArea = m_patchSize * m_patchSize;
}
public int getNumberOfBins() {
return m_bins;
}
/**
* Set the number of bins of the histogram.
*
* @param bins
* @throws IllegalArgumentException if <code>bins <= 0</code>
*/
public void setNumberOfBins(int bins) {
if (bins <= 0) {
throw new IllegalArgumentException("number of bins must be greater zero, but got " + bins);
}
m_bins = bins;
}
public double getHistogramMin() {
return m_histMin;
}
public double getHistogramMax() {
return m_histMax;
}
/**
* Set the minimum and maximum value that the histogram should consider.
* <p>
* If both min and max are set to zero (default), limits are retrieved from data.
* </p>
*
* @param min
* @param max
* @throws IllegalArgumentException if <code>min > max</code>
*/
public void setHistogramRange(double min, double max) {
if (min > max) {
throw new IllegalArgumentException("min must be smaller than or equal to max");
}
m_histMin = min;
m_histMax = max;
}
}