You are on page 1of 37

class Test /* * To change this template, choose Tools | Templates * and open the template in the editor.

*/ package test1;

import imagertrival.imageanalysis.TamuraTest; import java.io.File; import java.util.ArrayList; import test1.ImageInfoDTO;

/** * * */ public class Test {

public static void main(String[] args) { TamuraTest test = new TamuraTest(); ImageInfoDTO[] rtrivalImageList = test.getRtrivalImageList(new File("c:/users/swapnil/desktop/backup/imageretrival/resources/small/1.jpg"), new File("c:/nine")); ArrayList<String> list = new ArrayList<String>(); // distanceDifference = Integer.parseInt((String) distanceComboBox.getSelectedItem()); for (int i = 0; i < rtrivalImageList.length; i++) { ImageInfoDTO dto = rtrivalImageList[i];

if (dto.getDistance() <= 0.50) { list.add(dto.getName()); } System.out.println(dto.getDistance()); } } } Splash package test1;

import java.awt.*; import java.awt.event.*;

/** * Copyright 2000 Webmind Inc. * Splash: <p> * * This class creates the splash screen for the GUI Application. * * @author Jason Torres (jtorres@webmind.com) * @version 1.1 */ public class Splash extends Window {

private Image splashImage;

private int imgWidth, imgHeight; private String imgName; private static final int BORDERSIZE = 5; private static final Color BORDERCOLOR = Color.blue; Toolkit tk;

public Splash(Frame f, String imgName) { super(f); this.imgName = imgName; tk = Toolkit.getDefaultToolkit(); splashImage = loadSplashImage(); showSplashScreen(); // } f.addWindowListener(new WindowListener());

public Image loadSplashImage() { MediaTracker tracker = new MediaTracker(this); Image result; result = tk.getImage(imgName); tracker.addImage(result, 0); try { tracker.waitForAll(); } catch (Exception e) { e.printStackTrace(); }

imgWidth = result.getWidth(this); imgHeight = result.getHeight(this); return (result); }

public void showSplashScreen() { Dimension screenSize = tk.getScreenSize(); setBackground(BORDERCOLOR); int w = imgWidth + (BORDERSIZE * 2); int h = imgHeight + (BORDERSIZE * 2); int x = (screenSize.width - w) / 2; int y = (screenSize.height - h) / 2; setBounds(x, y, w, h); setVisible(true); }

public void paint(Graphics g) { g.drawImage(splashImage, BORDERSIZE, BORDERSIZE, imgWidth, imgHeight, this); }

class WindowListener extends WindowAdapter {

public void windowActivated(WindowEvent we) { setVisible(false);

dispose(); } } } MyListModel /* * To change this template, choose Tools | Templates * and open the template in the editor. */

package test1;

import java.util.ArrayList; import javax.swing.AbstractListModel; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JLabel;

/** * * @author Swapnil */ public class MyListModel extends AbstractListModel{ private ArrayList<String> list=new ArrayList<String>();

public MyListModel(ArrayList<String> list) { this.list=list; }

public int getSize() { return list.size(); }

public Object getElementAt(int index) { JLabel l=new JLabel(); return (new ImageIcon(list.get(index))); }

} ImageInfoDTO /* * To change this template, choose Tools | Templates * and open the template in the editor. */

package test1;

/** *

* @author Swapnil */ public class ImageInfoDTO { private String name; private double distance;

public ImageInfoDTO(String name, double distance) { this.name = name; this.distance = distance; }

public double getDistance() { return distance; }

public void setDistance(double distance) { this.distance = distance; }

public String getName() { return name; }

public void setName(String name) { this.name = name;

} }

Imagertrival folder
\imageanalysis TamuraTest package imagertrival.imageanalysis;

import test1.ImageInfoDTO; import java.util.logging.Level; import java.util.logging.Logger; import junit.framework.TestCase;

import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList;

/** * *

*/ public class TamuraTest extends TestCase { ArrayList<ImageInfoDTO> list = new ArrayList<ImageInfoDTO>(); private String[] testFiles;// = new String[]{"img1.jpg", "img2.jpg", "img3.jpg", "img4.jpg", "img5.jpg", "img6.jpg", "img7.jpg", "img8.jpg", "img9.jpg", "img10.jpg"}; private String testFilesPath = "./resources/small/";

public void testExtraction() throws IOException { Tamura sch = new Tamura(); BufferedImage image = ImageIO.read(new FileInputStream(testFilesPath + testFiles[0])); System.out.println("image = " + image.getWidth() + " x " + image.getHeight()); sch.extract(image); System.out.println("sch = " + sch.getStringRepresentation()); }

public void testRetrieval() throws Exception { SimpleColorHistogram[] acc = new SimpleColorHistogram[testFiles.length]; LinkedList<String> vds = new LinkedList<String>(); for (int i = 0; i < acc.length; i++) { System.out.println("Extracting from number " + i); acc[i] = new SimpleColorHistogram(); acc[i].extract(ImageIO.read(new FileInputStream(testFilesPath + testFiles[i]))); vds.add(acc[i].getStringRepresentation()); }

System.out.println("Calculating distance for " + testFiles[5]);

for (int i = 0; i < acc.length; i++) { float distance = acc[i].getDistance(acc[5]); System.out.println(testFiles[i] + " distance = " + distance); } int count = 0; for (Iterator<String> iterator = vds.iterator(); iterator.hasNext();) { String s = iterator.next(); SimpleColorHistogram a = new SimpleColorHistogram(); a.setStringRepresentation(s); float distance = acc[count].getDistance(a); System.out.println(testFiles[count] + " distance = " + distance); count++; } }

public ImageInfoDTO[] getRtrivalImageList(File sourceFile, File targetFile) { File[] listFiles = targetFile.listFiles();

testFiles=new String[listFiles.length];

for (int i = 0; i < listFiles.length-1; i++) { testFiles[i] = listFiles[i].getAbsolutePath(); System.out.println(testFiles[i]); }

SimpleColorHistogram[] acc = new SimpleColorHistogram[testFiles.length-1]; LinkedList<String> vds = new LinkedList<String>(); SimpleColorHistogram[] searchImage = new SimpleColorHistogram[1]; for (int i = 0; i < searchImage.length; i++) { try { System.out.println("Extracting from number " + i); searchImage[i] = new SimpleColorHistogram(); searchImage[i].extract(ImageIO.read(new FileInputStream( sourceFile.getAbsolutePath()))); // vds.add(acc[i].getStringRepresentation());

} catch (IOException ex) { // } } for (int i = 0; i < acc.length; i++) { try { System.out.println("Extracting from number " + i); acc[i] = new SimpleColorHistogram(); acc[i].extract(ImageIO.read(new FileInputStream(testFiles[i]))); // vds.add(acc[i].getStringRepresentation()); } catch (IOException ex) { // } } Logger.getLogger(FCTHTest.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(CEDDTest.class.getName()).log(Level.SEVERE, null, ex);

System.out.println("Calculating distance for " + searchImage[0]); for (int i = 0; i < acc.length; i++) { float distance = acc[i].getDistance(searchImage[0]); list.add(new ImageInfoDTO(testFiles[i], distance)); System.out.println(testFiles[i] + " distance = " + distance); }

ImageInfoDTO imageList[]=new ImageInfoDTO[list.size()];

for (int i = 0; i < imageList.length; i++) { imageList[i]=list.get(i); }

for (int i = 0; i <=imageList.length-2; i++) { for (int j = i+1; j < imageList.length; j++) { ImageInfoDTO previousDto = imageList[i]; ImageInfoDTO nextDto = imageList[j]; ImageInfoDTO tempDto;

if(previousDto.getDistance()>nextDto.getDistance()) { tempDto=previousDto;

imageList[i]=nextDto; imageList[j]=tempDto; }

} return imageList; } public static void main(String[] args) { try { TamuraTest tamuraTest = new TamuraTest(); tamuraTest.testRetrieval(); } catch (Exception ex) { Logger.getLogger(TamuraTest.class.getName()).log(Level.SEVERE, null, ex); } }

Tamura /** *

* @author Swapnil */

package imagertrival.imageanalysis;

import at.lux.imageanalysis.VisualDescriptor; import imagertrival.utils.ImageUtils;

import java.awt.*; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorConvertOp; import java.awt.image.Raster; import java.util.StringTokenizer;

public class Tamura implements ImageRetrivalFeature { private static final int MAX_IMG_HEIGHT = 64; private int[][] grayScales; private int imgWidth, imgHeight; private double[] histogram; // stores all three tamura features in one histogram. private static final double[][] filterH = {{-1, 0, 1}, {-1, 0, 1}, {-1, 0, 1}}; private static final double[][] filterV = {{-1, -1, -1}, {0, 0, 0}, {1, 1, 1}}; private static final String TAMURA_NAME = "tamura";

public double coarseness(int n0, int n1) { double result = 0; for (int i = 1; i < n0 - 1; i ++) { for (int j = 1; j < n1 - 1; j ++) { result = result + Math.pow(2, this.sizeLeadDiffValue(i, j)); } } // fixed based on the patch by shen72@users.sourceforge.net result = (1.0/(n0*n1))*result; return result; }

public double averageOverNeighborhoods(int x, int y, int k) { double result = 0, border; border = Math.pow(2, 2*k); int x0 = 0, y0 = 0;

for (int i = 0; i < border; i ++) { for (int j = 0; j < border; j ++) { x0 = x - (int)Math.pow(2, k-1) + i; y0 = y - (int)Math.pow(2, k-1) + j; if (x0 < 0) x0 = 0; if (y0 < 0) y0 = 0; if (x0 >= imgWidth) x0 = imgWidth - 1;

if (y0 >= imgHeight) y0 = imgHeight - 1; result = result + grayScales[x0][y0]; } } result = (1/Math.pow(2, 2*k))*result; return result; }

public double differencesBetweenNeighborhoodsHorizontal(int x, int y, int k) { double result = 0; result = Math.abs(this.averageOverNeighborhoods(x + (int)Math.pow(2, k - 1), y, k) this.averageOverNeighborhoods(x - (int)Math.pow(2, k - 1), y, k)); return result; }

public double differencesBetweenNeighborhoodsVertical(int x, int y, int k) { double result = 0; result = Math.abs(this.averageOverNeighborhoods(x, y + (int)Math.pow(2, k - 1), k) this.averageOverNeighborhoods(x, y - (int)Math.pow(2, k - 1), k)); return result; }

public int sizeLeadDiffValue(int x, int y) { double result = 0, tmp; int maxK = 1;

for (int k = 0; k < 3; k ++) { tmp = Math.max(this.differencesBetweenNeighborhoodsHorizontal(x, y, k), this.differencesBetweenNeighborhoodsVertical(x, y, k)); if (result < tmp) { maxK = k; result = tmp; } } return maxK; }

public double contrast() { double result = 0, my, sigma, my4 = 0, alpha4 = 0; my = this.calculateMy(); sigma = this.calculateSigma(my);

for (int x = 0; x < this.imgWidth; x ++) { for (int y = 0; y < this.imgHeight; y ++) { my4 = my4 + Math.pow(this.grayScales[x][y] - my, 4); }

} alpha4 = my4/(Math.pow(sigma, 4)); // fixed based on the patches of shen72@users.sourceforge.net result = sigma/(Math.pow(alpha4, 0.25)); return result; }

public double calculateMy() { double mean = 0;

for (int x = 0; x < this.imgWidth; x ++) { for (int y = 0; y < this.imgHeight; y ++) { mean = mean + this.grayScales[x][y]; } } mean = mean / (this.imgWidth * this.imgHeight); return mean; }

public double calculateSigma(double mean) { double result = 0;

for (int x = 0; x < this.imgWidth; x ++) {

for (int y = 0; y < this.imgHeight; y ++) { result = result + Math.pow(this.grayScales[x][y] - mean, 2); } } result = result / (this.imgWidth * this.imgHeight); return Math.sqrt(result); }

public double[] directionality() { double[] histogram = new double[16]; double maxResult = 3; double binWindow = maxResult / (double)(histogram.length - 1); int bin = -1; for (int x = 1; x < this.imgWidth - 1; x ++) { for (int y = 1; y < this.imgHeight - 1; y ++) { bin = (int)((Math.PI/2 + Math.atan(this.calculateDeltaV(x,y)/this.calculateDeltaH(x,y)))/binWindow); histogram[bin] ++; } } return histogram; }

public double calculateDeltaH(int x, int y) {

double result = 0;

for (int i = 0; i < 3; i ++){ for (int j = 0; j < 3; j ++) { result = result + this.grayScales[x - 1 + i][ y - 1 + j] * filterH[i][j]; } } return result; }

public double calculateDeltaV(int x, int y) { double result = 0;

for (int i = 0; i < 3; i ++){ for (int j = 0; j < 3; j ++) { result = result + this.grayScales[x - 1 + i][y - 1 + j] * filterV[i][j]; } } return result; }

public double getDistance(double[] targetFeature, double[] queryFeature) {

double result = 0; for (int i = 2; i < targetFeature.length; i ++) { result += Math.pow(targetFeature[i] - queryFeature[i], 2); } return result; }

public void extract(BufferedImage image) { histogram = new double[18]; double[] directionality; ColorConvertOp op = new ColorConvertOp(image.getColorModel().getColorSpace(), ColorSpace.getInstance(ColorSpace.CS_GRAY), new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY)); BufferedImage bimg = op.filter(image, null); bimg = ImageUtils.scaleImage(bimg, MAX_IMG_HEIGHT); Raster raster = bimg.getRaster(); int[] tmp = new int[3]; this.grayScales = new int[raster.getWidth()][raster.getHeight()]; for (int i = 0; i < raster.getWidth(); i++) { for (int j = 0; j < raster.getHeight(); j++) { raster.getPixel(i, j, tmp); this.grayScales[i][j] = tmp[0]; } } imgWidth = bimg.getWidth();

imgHeight = bimg.getHeight(); histogram[0] = this.coarseness(bimg.getWidth(), bimg.getHeight()); histogram[1] = this.contrast(); directionality = this.directionality(); for (int i = 2; i < histogram.length; i ++) { histogram[i] = directionality[i - 2]; } }

public float getDistance(VisualDescriptor vd) { // Check if instance of the right class ... if (!(vd instanceof Tamura)) throw new UnsupportedOperationException("Wrong descriptor.");

// casting ... Tamura tamura = (Tamura) vd; return (float) getDistance(tamura.histogram, histogram); }

public String getStringRepresentation() { StringBuilder sb = new StringBuilder(histogram.length * 16); sb.append(TAMURA_NAME); sb.append(' '); sb.append(histogram.length); sb.append(' ');

for (int i = 0; i < histogram.length; i++) { sb.append(histogram[i]); sb.append(' '); } return sb.toString().trim(); }

public void setStringRepresentation(String s) { StringTokenizer st = new StringTokenizer(s); String name = st.nextToken(); if (!name.equals(TAMURA_NAME)) { throw new UnsupportedOperationException("This is not a Tamura feature string."); }

histogram=new double[Integer.parseInt(st.nextToken())];

for (int i = 0; i < histogram.length; i++) { if (!st.hasMoreTokens()) throw new IndexOutOfBoundsException("Too few numbers in string representation."); histogram[i] = Double.parseDouble(st.nextToken()); } } }

SimpleHistogramTest /** * * */ package imagertrival.imageanalysis;

import at.lux.imageanalysis.VisualDescriptor; import imagertrival.imageanalysis.utils.Quantization;

import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.util.StringTokenizer;

public class SimpleColorHistogram implements ImageRetrivalFeature { public static final int DEFAULT_NUMBER_OF_BINS = 512; public static HistogramType DEFAULT_HISTOGRAM_TYPE = HistogramType.RGB; public static DistanceFunction DEFAULT_DISTANCE_FUNCTION = DistanceFunction.L1;

private static final int[] quantTable = {1, 32, 4, 8, 16, 4, 16, 4, 16, 4, 0,1,2,3,4 for 256 levels 1, 16, 4, 4, 8, 4, 8, 4, 8, 4, 1, 8, 4, 4, 4, 4, 8, 2, 8, 1, 1, 8, 4, 4, 4, 4, 4, 1, 4, 1};

// Hue, Sum - subspace

// Hue, Sum - subspace 0,1,2,3,4 for 128 levels // Hue, Sum - subspace 0,1,2,3,4 for 64 levels // Hue, Sum - subspace 0,1,2,3,4 for 32 levels

/** * Lists possible types for the histogram class */ public enum HistogramType { RGB, HSV, Luminance, HMMD }

/** * Lists distance functions possible for this histogram class */ public enum DistanceFunction { L1, L2, TANIMOTO, JSD }

/** * Temporary pixel field ... re used for speed and memory issues ... */ private int[] pixel = new int[3]; private int[] histogram; private HistogramType histogramType; private DistanceFunction distFunc;

/**

* Default constructor */ public SimpleColorHistogram() { histogramType = DEFAULT_HISTOGRAM_TYPE; histogram = new int[DEFAULT_NUMBER_OF_BINS]; distFunc = DEFAULT_DISTANCE_FUNCTION; }

public SimpleColorHistogram(HistogramType histogramType, DistanceFunction distFunction) { this.histogramType = histogramType; distFunc = distFunction; histogram = new int[DEFAULT_NUMBER_OF_BINS]; }

public void extract(BufferedImage image) { if (image.getColorModel().getColorSpace().getType() != ColorSpace.TYPE_RGB) throw new UnsupportedOperationException("Color space not supported. Only RGB."); WritableRaster raster = image.getRaster(); for (int x = 0; x < image.getWidth(); x++) { for (int y = 0; y < image.getHeight(); y++) { raster.getPixel(x, y, pixel); if (histogramType == HistogramType.HSV) { rgb2hsv(pixel[0], pixel[1], pixel[2], pixel);

} else if (histogramType == HistogramType.Luminance) { rgb2yuv(pixel[0], pixel[1], pixel[2], pixel); } else if (histogramType == HistogramType.HMMD) { histogram[quantHmmd(rgb2hmmd(pixel[0], pixel[1], pixel[2]), DEFAULT_NUMBER_OF_BINS)]++; } if (histogramType != HistogramType.HMMD) histogram[quant(pixel)]++; } } normalize(histogram, image.getWidth() * image.getHeight()); }

private void normalize(int[] histogram, int numPixels) { // find max: int max = 0; // // // } for (int i = 0; i < histogram.length; i++) { histogram[i] = (histogram[i] * 1024) / numPixels; } } for (int i = 0; i < histogram.length; i++) { max = Math.max(histogram[i], max);

private int quant(int[] pixel) { int quantRgb = (256 * 256 * 256) / histogram.length; if (histogramType == HistogramType.HSV) {

// Todo: tune this one ... int qH = (pixel[0] * 8) / 360; // more granularity in color int qS = (pixel[2] * 4) / 100; return qH * qS + qS; } else if (histogramType == HistogramType.HMMD) { return quantHmmd(rgb2hmmd(pixel[0], pixel[1], pixel[2]), 256); } else if (histogramType == HistogramType.Luminance) { return (pixel[0] * histogram.length) / (256); } else { // return Quantization.quantDistributionBased(pixel, histogram.length, 512); return Quantization.quantUniformly(pixel, histogram.length, DEFAULT_NUMBER_OF_BINS); } }

public float getDistance(VisualDescriptor vd) { // Check if instance of the right class ... if (!(vd instanceof SimpleColorHistogram)) throw new UnsupportedOperationException("Wrong descriptor.");

// casting ... SimpleColorHistogram ch = (SimpleColorHistogram) vd;

// check if parameters are fitting ... if ((ch.histogram.length != histogram.length) || (ch.histogramType != histogramType)) throw new UnsupportedOperationException("Histogram lengths or color spaces do not match");

// do the comparison ... double sum = 0; if (distFunc == DistanceFunction.JSD) return (float) jsd(histogram, ch.histogram); else if (distFunc == DistanceFunction.TANIMOTO) return (float) tanimoto(histogram, ch.histogram); else if (distFunc == DistanceFunction.L1) return (float) distL1(histogram, ch.histogram); else return (float) distL2(histogram, ch.histogram); }

private static double distL1(int[] h1, int[] h2) { double sum = 0d; for (int i = 0; i < h1.length; i++) { sum += Math.abs(h1[i] - h2[i]); } return sum / h1.length; }

private static double distL2(int[] h1, int[] h2) { double sum = 0d;

for (int i = 0; i < h1.length; i++) { sum += (h1[i] - h2[i]) * (h1[i] - h2[i]); } return Math.sqrt(sum); }

private static double jsd(int[] h1, int[] h2) { double sum = 0d; for (int i = 0; i < h1.length; i++) { sum += h1[i] > 0 ? h1[i] * Math.log(2d * h1[i] / (h1[i] + h2[i])) : 0 + h2[i] > 0 ? h2[i] * Math.log(2d * h2[i] / (h1[i] + h2[i])) : 0; } return sum; }

private static double tanimoto(int[] h1, int[] h2) { double result = 0; double tmp1 = 0; double tmp2 = 0;

double tmpCnt1 = 0, tmpCnt2 = 0, tmpCnt3 = 0;

for (int i = 0; i < h1.length; i++) { tmp1 += h1[i];

tmp2 += h2[i]; }

if (tmp1 == 0 || tmp2 == 0) result = 100; if (tmp1 == 0 && tmp2 == 0) result = 0;

if (tmp1 > 0 && tmp2 > 0) { for (int i = 0; i < h1.length; i++) { tmpCnt1 += (h1[i] / tmp1) * (h2[i] / tmp2); tmpCnt2 += (h2[i] / tmp2) * (h2[i] / tmp2); tmpCnt3 += (h1[i] / tmp1) * (h1[i] / tmp1);

result = (100 - 100 * (tmpCnt1 / (tmpCnt2 + tmpCnt3 - tmpCnt1))); //Tanimoto } return result; }

public String getStringRepresentation() { StringBuilder sb = new StringBuilder(histogram.length * 4); sb.append(histogramType.name()); sb.append(' '); sb.append(histogram.length);

sb.append(' '); for (int i = 0; i < histogram.length; i++) { sb.append(histogram[i]); sb.append(' '); } return sb.toString().trim(); }

public void setStringRepresentation(String s) { StringTokenizer st = new StringTokenizer(s); histogramType = HistogramType.valueOf(st.nextToken()); histogram = new int[Integer.parseInt(st.nextToken())]; for (int i = 0; i < histogram.length; i++) { if (!st.hasMoreTokens()) throw new IndexOutOfBoundsException("Too few numbers in string representation!"); histogram[i] = Integer.parseInt(st.nextToken()); } }

public static void rgb2yuv(int r, int g, int b, int[] yuv) { int y = (int) (0.299 * r + 0.587 * g + 0.114 * b); int u = (int) ((b - y) * 0.492f); int v = (int) ((r - y) * 0.877f);

yuv[0] = y; yuv[1] = u; yuv[2] = v; }

public static void rgb2hsv(int r, int g, int b, int hsv[]) {

int min; //Min. value of RGB int max; //Max. value of RGB int delMax; //Delta RGB value

min = Math.min(r, g); min = Math.min(min, b);

max = Math.max(r, g); max = Math.max(max, b);

delMax = max - min;

//

System.out.println("hsv = " + hsv[0] + ", " + hsv[1] + ", " + hsv[2]);

float H = 0f, S = 0f; float V = max / 255f;

if (delMax == 0) { H = 0f; S = 0f; } else { S = delMax / 255f; if (r == max) { if (g >= b) { H = ((g / 255f - b / 255f) / (float) delMax / 255f) * 60; } else { H = ((g / 255f - b / 255f) / (float) delMax / 255f) * 60 + 360; } } else if (g == max) { H = (2 + (b / 255f - r / 255f) / (float) delMax / 255f) * 60; } else if (b == max) { H = (4 + (r / 255f - g / 255f) / (float) delMax / 255f) * 60; } } // System.out.println("H = " + H); hsv[0] = (int) (H); hsv[1] = (int) (S * 100); hsv[2] = (int) (V * 100); }

/** * Adapted under GPL from VizIR: author was adis@ims.tuwien.ac.at

*/ private static int[] rgb2hmmd(int ir, int ig, int ib) { int hmmd[] = new int[5];

float max = (float) Math.max(Math.max(ir, ig), Math.max(ig, ib)); float min = (float) Math.min(Math.min(ir, ig), Math.min(ig, ib));

float diff = (max - min); float sum = (float) ((max + min) / 2.);

float hue = 0; if (diff == 0) hue = 0; else if (ir == max && (ig - ib) > 0) hue = 60 * (ig - ib) / (max - min); else if (ir == max && (ig - ib) <= 0) hue = 60 * (ig - ib) / (max - min) + 360; else if (ig == max) hue = (float) (60 * (2. + (ib - ir) / (max - min))); else if (ib == max) hue = (float) (60 * (4. + (ir - ig) / (max - min)));

diff /= 2;

hmmd[0] = (int) (hue); hmmd[1] = (int) (max); hmmd[2] = (int) (min); hmmd[3] = (int) (diff); hmmd[4] = (int) (sum);

return (hmmd); }

private int quantHmmd(int[] hmmd, int quantizationLevels) { int h = 0; int offset = 0; // offset position in the quantization table int subspace = 0; int q = 0;

// define the subspace along the Diff axis

if (hmmd[3] < 7) subspace = 0; else if ((hmmd[3] > 6) && (hmmd[3] < 21)) subspace = 1; else if ((hmmd[3] > 19) && (hmmd[3] < 61)) subspace = 2; else if ((hmmd[3] > 59) && (hmmd[3] < 111)) subspace = 3; else if ((hmmd[3] > 109) && (hmmd[3] < 256)) subspace = 4;

// HMMD Color Space quantization // see MPEG7-CSD.pdf

if (quantizationLevels == 256) { offset = 0; h = (int) ((hmmd[0] / quantizationLevels) * quantTable[offset + subspace] + (hmmd[4] / quantizationLevels) * quantTable[offset + subspace + 1]); } else if (quantizationLevels == 128) {

offset = 10; h = (int) ((hmmd[0] / quantizationLevels) * quantTable[offset + subspace] + (hmmd[4] / quantizationLevels) * quantTable[offset + subspace + 1]); } else if (quantizationLevels == 64) { offset = 20; h = (int) ((hmmd[0] / quantizationLevels) * quantTable[offset + subspace] + (hmmd[4] / quantizationLevels) * quantTable[offset + subspace + 1]);

} else if (quantizationLevels == 32) { offset = 30; h = (int) ((hmmd[0] / quantizationLevels) * quantTable[offset + subspace] + (hmmd[4] / quantizationLevels) * quantTable[offset + subspace + 1]); }

return h; } }