package gr.iti.mklab.visual.aggregation;
import gr.iti.mklab.visual.utilities.Normalization;
import java.util.ArrayList;
/**
* This class computes multiple vocabulary VLAD vectors as described in: <br>
*
* <em>Jégou, H., & Chum, O. (2012). Negative evidences and co-occurences in image retrieval: The benefit of PCA and whitening. In ECCV 2012.</em>
* <br>
* <br>
*
* multiVLAD vectors are generated independently from each vocabulary and then concatenated in a single
* vector. Standard VLAD vectors can also be generated using this class if only 1 vocabulary is provided.
*
* @author Eleftherios Spyromitros-Xioufis
*/
public class VladAggregatorMultipleVocabularies {
/**
* Whether to apply the default normalization, i.e. power+L2 normalization on the subvectors and L2
* normalization on the concatenated vector.
*/
private boolean normalizationsOn = true;
/**
* Length of the final vector.
*/
private int vectorLength;
/**
* One standard VladAggregator is initialized for each vocabulary.
*/
private VladAggregator[] vladAggregators;
/**
* Constructor. Takes as input a 3-dimensional array that contains the multiple codebooks.
*
* @param codebook
*/
public VladAggregatorMultipleVocabularies(double[][][] codebooks) {
vladAggregators = new VladAggregator[codebooks.length];
for (int i = 0; i < codebooks.length; i++) {
vladAggregators[i] = new VladAggregator(codebooks[i]);
vectorLength += vladAggregators[i].getNumCentroids() * vladAggregators[i].getDescriptorLength();
}
}
/**
* Takes as input an ArrayList of double arrays which contains the set of local descriptors of an image.
* Returns the multiVLAD representation of the image using the codebooks supplied in the constructor.
*
* @param descriptors
* @return the multiVLAD vector
* @throws Exception
*/
public double[] aggregate(ArrayList<double[]> descriptors) throws Exception {
double[] multiVlad = new double[vectorLength];
int vectorShift = 0;
for (int i = 0; i < vladAggregators.length; i++) {
double[] subVlad = vladAggregators[i].aggregate(descriptors);
if (normalizationsOn) {
Normalization.normalizePower(subVlad, 0.5);
Normalization.normalizeL2(subVlad);
}
System.arraycopy(subVlad, 0, multiVlad, vectorShift, subVlad.length);
vectorShift += vladAggregators[i].getNumCentroids() * vladAggregators[i].getDescriptorLength();
}
// re-apply l2 normalization on the concatenated vector, if we have more than 1 vocabularies
if (vladAggregators.length > 1 && normalizationsOn) {
Normalization.normalizeL2(multiVlad);
}
return multiVlad;
}
/**
* Same as {@link #aggregate(ArrayList)} but takes a two-dimensional array as input.
*
* @param descriptors
* @return the multiVLAD vector
* @throws Exception
*/
public double[] aggregate(double[][] descriptors) throws Exception {
double[] multiVlad = new double[vectorLength];
int vectorShift = 0;
for (int i = 0; i < vladAggregators.length; i++) {
double[] subVlad = vladAggregators[i].aggregate(descriptors);
if (normalizationsOn) {
Normalization.normalizePower(subVlad, 0.5);
Normalization.normalizeL2(subVlad);
}
System.arraycopy(subVlad, 0, multiVlad, vectorShift, subVlad.length);
vectorShift += vladAggregators[i].getNumCentroids() * vladAggregators[i].getDescriptorLength();
}
// re-apply l2 normalization on the concatenated vector, if we have more than 1 vocabularies
if (vladAggregators.length > 1 && normalizationsOn) {
Normalization.normalizeL2(multiVlad);
}
return multiVlad;
}
public int getVectorLength() {
return vectorLength;
}
public boolean isNormalizationsOn() {
return normalizationsOn;
}
public void setNormalizationsOn(boolean normalizationsOn) {
this.normalizationsOn = normalizationsOn;
}
}