package net.semanticmetadata.lire.imageanalysis.features.global.centrist;
import net.semanticmetadata.lire.imageanalysis.features.GlobalFeature;
import net.semanticmetadata.lire.imageanalysis.features.LireFeature;
import net.semanticmetadata.lire.utils.MetricsUtils;
import net.semanticmetadata.lire.utils.SerializationUtils;
import java.awt.image.BufferedImage;
/**
* <p>CENTRIST (CENsus TRansform hISTogram) descriptor based on the implementation described in Jianxin Wu; Rehg, J.M., "CENTRIST: A Visual Descriptor
* for Scene Categorization," Pattern Analysis and Machine Intelligence, IEEE Transactions on , vol.33, no.8,
* pp.1489,1501, Aug. 2011, doi: 10.1109/TPAMI.2010.224, http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=5674051&isnumber=5898466/<p>
*<p>This class provides the spatial pyramid version, please see {@link SimpleCentrist} for the global histogram.</p>
*
* @author Mathias Lux, mathias@juggle.at
* @see SimpleCentrist for the base descriptor of the paper.
*/
public class SpatialPyramidCentrist implements GlobalFeature {
private boolean applyMaxNorm = true;
private int histLength = 256;
int histogramSize = histLength * 6 + histLength * 4 * 4 + histLength * 9;
double[] histogram = new double[histogramSize];
@Override
public void extract(BufferedImage image) {
// level 0:
SimpleCentrist simpleCentrist = new SimpleCentrist();
simpleCentrist.extract(image);
System.arraycopy(simpleCentrist.getFeatureVector(), 0, histogram, 0, histLength);
// level 1:
int w = image.getWidth() / 2;
int h = image.getHeight() / 2;
simpleCentrist.extract(image.getSubimage(0, 0, w, h));
System.arraycopy(simpleCentrist.getFeatureVector(), 0, histogram, histLength * 1, histLength);
simpleCentrist.extract(image.getSubimage(w, 0, w, h));
System.arraycopy(simpleCentrist.getFeatureVector(), 0, histogram, histLength * 2, histLength);
simpleCentrist.extract(image.getSubimage(0, h, w, h));
System.arraycopy(simpleCentrist.getFeatureVector(), 0, histogram, histLength * 3, histLength);
simpleCentrist.extract(image.getSubimage(w, h, w, h));
System.arraycopy(simpleCentrist.getFeatureVector(), 0, histogram, histLength * 4, histLength);
// and that's the additional sub image in level one:
simpleCentrist.extract(image.getSubimage(w/2, h/2, w, h));
System.arraycopy(simpleCentrist.getFeatureVector(), 0, histogram, histLength * 5, histLength);
// level 2:
int wstep = image.getWidth() / 4;
int hstep = image.getHeight() / 4;
int binPos = 6; // the next free section in the histogram
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
simpleCentrist.extract(image.getSubimage(i * wstep, j * hstep, wstep, hstep));
System.arraycopy(simpleCentrist.getFeatureVector(), 0, histogram, histLength * binPos, histLength);
binPos++;
}
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
simpleCentrist.extract(image.getSubimage(wstep /2 + i * wstep, hstep / 2 + j * hstep, wstep, hstep));
System.arraycopy(simpleCentrist.getFeatureVector(), 0, histogram, histLength * binPos, histLength);
binPos++;
}
}
if (applyMaxNorm) normalize(histogram);
}
/**
* Applies max norm to the histogram.
* @param in
*/
private void normalize(double[] in) {
double max = 0d;
for (double d : in) {
max = Math.max(max, d);
}
for (int i = 0; i < in.length; i++) {
in[i] = in[i] / max;
}
}
@Override
public String getFeatureName() {
return "Centrist";
}
@Override
public String getFieldName() {
return "Centrist";
}
@Override
public byte[] getByteArrayRepresentation() {
return SerializationUtils.toByteArray(histogram);
}
@Override
public void setByteArrayRepresentation(byte[] featureData) {
setByteArrayRepresentation(featureData, 0, featureData.length);
}
@Override
public void setByteArrayRepresentation(byte[] featureData, int offset, int length) {
histogram = SerializationUtils.toDoubleArray(featureData, offset, length);
}
@Override
public double getDistance(LireFeature feature) {
if (feature instanceof SpatialPyramidCentrist)
return MetricsUtils.distL1(histogram, feature.getFeatureVector());
else
return -1d;
}
@Override
public double[] getFeatureVector() {
return histogram;
}
}