/* * This file is part of Caliph & Emir. * * Caliph & Emir 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 2 of the License, or * (at your option) any later version. * * Caliph & Emir 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 Caliph & Emir; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright statement: * -------------------- * (c) 2005 by Mathias Lux (mathias@juggle.at) * http://caliph-emir.sourceforge.net * * Note: The implementation is based on the code of Wolfgang Seiringer, * w.seiringer@utanet.at, who published the source without license or * copyright notice */ package at.lux.imageanalysis; import org.jdom.Element; import org.jdom.Namespace; import java.awt.image.BufferedImage; import java.io.StringWriter; import java.util.*; /** * Date: 03.10.2005 * Time: 19:57:18 * * @author Mathias Lux, mathias@juggle.at */ public class DominantColor implements JDomVisualDescriptor { at.lux.imageanalysis.DominantColorImplementation dc; private at.lux.imageanalysis.DominantColorImplementation.DominantColorValues dcData = null; /** * Td is the maximum distance for two colors to be considered similar, and dmax = alpha*Td. * A normal value for Td is between 10-20 in the CIE LUV color space and for alpha is * between 1.0-1.5. */ private static double alpha = 1.0; private static double Td = 10; /** * Where SC_Diff = abs(SpatialCoherency_1 � SpatialCoherency_2), SpatialCoherency_1 and * SpatialCoherency _2 are normalized from 0 to 1 and non-uniformly quantized in prior * to calculate SC_Diff. DC_Diff is the difference between two set of dominant colors, * W1 (e.g. set to 0.3) is the weight of the first term and W2 (e.g. set to 0.7) is the * weight of the second term. Set W1 to 0 if SpatialCoherency is not available. */ private static double w1 = 0.0; private double percentageSum = 0d; /** * Main constructor for creating and extracting the DominantColor * descriptor. * * @param img gives the image data. */ public DominantColor(BufferedImage img) { dc = new at.lux.imageanalysis.DominantColorImplementation(); dcData = dc.extractDescriptor(img); for (int i = 0; i < this.getSize(); i++) { percentageSum += ((double) getPercentage(i)); } } /** * Use this constructor if the pixel data was taken from * a non rectangular region. Please note that the neighbourhood * of pixels cannot be taken into account (No reliable spatial * coherency can be created). The pixels array goes like: * {pixel1[0], pixel1[1], pixel1[2], pixel2[0], pixel2[1], ...} * * @param pixels gives the pixels one int after another. */ public DominantColor(int[] pixels) { dc = new at.lux.imageanalysis.DominantColorImplementation(); dcData = dc.extractDescriptor(pixels); for (int i = 0; i < this.getSize(); i++) { percentageSum += ((double) getPercentage(i)); } } public DominantColor(Element descriptor) throws VisualDescriptorException { Namespace mpeg7, xsi; mpeg7 = Namespace.getNamespace("", "urn:mpeg:mpeg7:schema:2001"); xsi = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); if (!descriptor.getAttribute("type", xsi).getValue().equals("DominantColorType")) { throw new VisualDescriptorException("This is not a DominantColor descriptor!"); } else { dcData = new at.lux.imageanalysis.DominantColorImplementation.DominantColorValues(); String spatialCoh = descriptor.getChild("SpatialCoherency", mpeg7).getTextTrim(); dcData.setCoherency(Integer.parseInt(spatialCoh)); List values = descriptor.getChildren("Values", mpeg7); ArrayList<String> percentages = new ArrayList<String>(); LinkedList<String> colorValues = new LinkedList<String>(); for (Iterator iterator = values.iterator(); iterator.hasNext();) { Element element = (Element) iterator.next(); percentages.add(element.getChild("Percentage", mpeg7).getTextTrim()); colorValues.add(element.getChild("ColorValueIndex", mpeg7).getTextTrim()); } int count = 0; int[][] colorValuesArray = new int[colorValues.size()][3]; int[] percentagesArray = new int[colorValues.size()]; for (Iterator<String> iterator = colorValues.iterator(); iterator.hasNext();) { StringTokenizer st = new StringTokenizer(iterator.next()); int[] v = new int[3]; v[0] = Integer.parseInt(st.nextToken()); v[1] = Integer.parseInt(st.nextToken()); v[2] = Integer.parseInt(st.nextToken()); colorValuesArray[count] = v; percentagesArray[count] = Integer.parseInt(percentages.get(count)); count++; } dcData.setSize(colorValues.size()); dcData.setPercentages(percentagesArray); dcData.setColorValues(colorValuesArray); } } /** * Compares one descriptor to another. * * @param descriptor * @return the distance from [0,infinite) or -1 if descriptor type does not match */ public float getDistance(VisualDescriptor descriptor) { if (descriptor instanceof DominantColor) { DominantColor dc2 = (DominantColor) descriptor; double sc_diff = Math.abs(dc2.getSpatialCoherency() - this.getSpatialCoherency()); double dc_diff = 0; double firstSquare = 0; double secondSquare = 0; for (int i = 0; i < this.getSize(); i++) { firstSquare += (this.getPercentageNormalized(i) * this.getPercentageNormalized(i)); } for (int i = 0; i < dc2.getSize(); i++) { secondSquare += (dc2.getPercentageNormalized(i) * dc2.getPercentageNormalized(i)); } double dmax = alpha * Td; for (int i = 0; i < this.getSize(); i++) { for (int j = 0; j < dc2.getSize(); j++) { double aij = 0; double dkl = euclid(this.getColorValue(i), dc2.getColorValue(j)); if (dkl <= Td) { aij = 1 - dkl / dmax; // System.out.println(dkl + " " + Td + " " + aij); } dc_diff += aij * ((double) this.getPercentageNormalized(i)) * ((double) dc2.getPercentageNormalized(j)); } } dc_diff = firstSquare + secondSquare - 2 * dc_diff; return (float) (w1 * sc_diff * dc_diff + (1.0 - w1) * dc_diff); } else { return -1f; } } /** * Euclidean distance between the colors ... * * @param colorValue * @param colorValue1 * @return a double. */ private double euclid(int[] colorValue, int[] colorValue1) { double result = 0; for (int i = 0; i < colorValue.length; i++) { double quad = (double) (colorValue[i] - colorValue1[i]); result += (quad * quad); } Math.sqrt(result); return result; } /** * Returns the descriptor as JDOM Element * * @return the descriptor as JDOM Element. */ public Element getDescriptor() { // System.out.println(dc.toXMLString()); // TODO: generate real descriptor similar to the one created by the other descriptors Namespace mpeg7, xsi; mpeg7 = Namespace.getNamespace("", "urn:mpeg:mpeg7:schema:2001"); xsi = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); Element vdesc = new Element("VisualDescriptor", mpeg7).setAttribute("type", "DominantColorType", xsi); // vdesc.setAttribute("size", Integer.toString(getSize()), mpeg7); Element spatialCoherency = new Element("SpatialCoherency", mpeg7); vdesc.addContent(spatialCoherency); spatialCoherency.setText(getSpatialCoherency() + ""); for (int i = 0; i < getSize(); i++) { Element values = new Element("Value", mpeg7); Element percentage = new Element("Percentage", mpeg7); Element colorValueIndex = new Element("Index", mpeg7); values.addContent(percentage); percentage.setText(Integer.toString(getPercentage(i))); values.addContent(colorValueIndex); int[] colorValue = getColorValue(i); StringWriter sw = new StringWriter(colorValue.length * 3); for (int j = 0; j < colorValue.length; j++) { int value = colorValue[j]; sw.append(Integer.toString(value)); sw.append(" "); } colorValueIndex.setText(sw.toString().trim()); vdesc.addContent(values); } return vdesc; } public int getSize() { return dcData.getSize(); } public int getSpatialCoherency() { return dcData.getSpatialCoherency(); } public int getPercentage(int index) { return dcData.getPercentage(index); } /** * Normalize the percentage as in MPEG-7 part 8 is recommended for * distance calculation * * @param index defines which color percentage is needed. * @return the normalized percentage: The sum of all percentages adds up to 1. */ public double getPercentageNormalized(int index) { double percentage = dcData.getPercentage(index); return (percentage / percentageSum); } public int[] getColorValue(int index) { return dcData.getColorValue(index); } public String getStringRepresentation() { // TODO: implement this .. return null; } public void setStringRepresentation(String descriptor) { // TODO: implement this } }