001 /* 002 * MeanDifference 003 * 004 * Copyright (c) 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.color.analysis; 009 010 import net.sourceforge.jiu.data.GrayIntegerImage; 011 import net.sourceforge.jiu.data.Palette; 012 import net.sourceforge.jiu.data.Paletted8Image; 013 import net.sourceforge.jiu.data.PixelImage; 014 import net.sourceforge.jiu.data.RGB24Image; 015 import net.sourceforge.jiu.data.RGBIndex; 016 import net.sourceforge.jiu.data.RGBIntegerImage; 017 import net.sourceforge.jiu.ops.MissingParameterException; 018 import net.sourceforge.jiu.ops.Operation; 019 import net.sourceforge.jiu.ops.WrongParameterException; 020 021 /** 022 * This operation determines the mean difference between two images. 023 * It requires two images of the same resolution and adds the absolute difference 024 * of all samples. 025 * Then it divides by the number of samples in the image (width times height times 026 * number of channels). 027 * <h3>Supported combinations of image types</h3> 028 * <ul> 029 * <li>One of the two images is of type {@link net.sourceforge.jiu.data.RGB24Image}, 030 * the other of type {@link net.sourceforge.jiu.data.Paletted8Image}.</li> 031 * <li>Both images are of the same type and that type implements {@link net.sourceforge.jiu.data.RGBIntegerImage}.</li> 032 * <li>Both images are of the same type and that type implements {@link net.sourceforge.jiu.data.GrayIntegerImage}.</li> 033 * </ul> 034 * <h3>Usage example</h3> 035 * <pre> 036 * MeanDifference diff = new MeanDifference(); 037 * diff.setImages(img1, img2); 038 * diff.process(); 039 * double meanDifference = diff.getDifference(); 040 * </pre> 041 * @author Marco Schmidt 042 * @since 0.11.0 043 */ 044 public class MeanDifference extends Operation 045 { 046 private double diff; 047 private PixelImage image1; 048 private PixelImage image2; 049 050 /** 051 * Returns abs(a - b). 052 * @param a first number 053 * @param b second number 054 * @return abs(a - b) 055 */ 056 private static int computeDiff(int a, int b) 057 { 058 int diff = a - b; 059 if (diff < 0) 060 { 061 return -diff; 062 } 063 else 064 { 065 return diff; 066 } 067 } 068 069 /** 070 * After a call to process, returns the determined mean difference value. 071 * @return difference value, 0.0 or larger 072 */ 073 public double getDifference() 074 { 075 return diff; 076 } 077 078 public void process() throws MissingParameterException, WrongParameterException 079 { 080 if (image1 == null) 081 { 082 throw new MissingParameterException("You must specify images using setImages."); 083 } 084 boolean sameType = image1.getImageType() == image2.getImageType(); 085 if (image1 instanceof RGB24Image && image2 instanceof Paletted8Image) 086 { 087 process((RGB24Image)image1, (Paletted8Image)image2); 088 } 089 else 090 if (image2 instanceof RGB24Image && image1 instanceof Paletted8Image) 091 { 092 process((RGB24Image)image2, (Paletted8Image)image1); 093 } 094 else 095 if (sameType && image1 instanceof RGBIntegerImage) 096 { 097 process((RGBIntegerImage)image1, (RGBIntegerImage)image2); 098 } 099 else 100 if (sameType && image1 instanceof GrayIntegerImage) 101 { 102 process((GrayIntegerImage)image1, (GrayIntegerImage)image2); 103 } 104 else 105 { 106 throw new WrongParameterException("Not a supported image type combination."); 107 } 108 } 109 110 private void process(GrayIntegerImage image1, GrayIntegerImage image2) 111 { 112 final int HEIGHT = image1.getHeight(); 113 final int WIDTH = image1.getWidth(); 114 long sum = 0; 115 for (int y = 0; y < HEIGHT; y++) 116 { 117 for (int x = 0; x < WIDTH; x++) 118 { 119 sum += computeDiff(image1.getSample(x, y), image2.getSample(x, y)); 120 } 121 setProgress(y, HEIGHT); 122 } 123 setDifference((double)sum / (WIDTH * HEIGHT)); 124 } 125 126 private void process(RGB24Image image1, Paletted8Image image2) 127 { 128 final int HEIGHT = image1.getHeight(); 129 final int WIDTH = image1.getWidth(); 130 long sum = 0; 131 Palette pal = image2.getPalette(); 132 int[] red = pal.getSamples(RGBIndex.INDEX_RED); 133 int[] green = pal.getSamples(RGBIndex.INDEX_GREEN); 134 int[] blue = pal.getSamples(RGBIndex.INDEX_BLUE); 135 for (int y = 0; y < HEIGHT; y++) 136 { 137 for (int x = 0; x < WIDTH; x++) 138 { 139 int palSample = image2.getSample(x, y); 140 sum += computeDiff(image1.getSample(RGBIndex.INDEX_RED, x, y), red[palSample]); 141 sum += computeDiff(image1.getSample(RGBIndex.INDEX_GREEN, x, y), green[palSample]); 142 sum += computeDiff(image1.getSample(RGBIndex.INDEX_BLUE, x, y), blue[palSample]); 143 } 144 setProgress(y, HEIGHT); 145 } 146 setDifference((double)sum / (WIDTH * HEIGHT * 3)); 147 } 148 149 private void process(RGBIntegerImage image1, RGBIntegerImage image2) 150 { 151 final int HEIGHT = image1.getHeight(); 152 final int WIDTH = image1.getWidth(); 153 long sum = 0; 154 for (int y = 0; y < HEIGHT; y++) 155 { 156 for (int x = 0; x < WIDTH; x++) 157 { 158 sum += computeDiff(image1.getSample(RGBIndex.INDEX_RED, x, y), image2.getSample(RGBIndex.INDEX_RED, x, y)); 159 sum += computeDiff(image1.getSample(RGBIndex.INDEX_GREEN, x, y), image2.getSample(RGBIndex.INDEX_GREEN, x, y)); 160 sum += computeDiff(image1.getSample(RGBIndex.INDEX_BLUE, x, y), image2.getSample(RGBIndex.INDEX_BLUE, x, y)); 161 } 162 setProgress(y, HEIGHT); 163 } 164 setDifference((double)sum / (WIDTH * HEIGHT * 3)); 165 } 166 167 private void setDifference(double newValue) 168 { 169 diff = newValue; 170 } 171 172 /** 173 * Sets the two images for which the mean difference is to be 174 * determined. 175 * @param firstImage first image 176 * @param secondImage second image 177 * @throws IllegalArgumentException if either of the images is null, 178 * if their resolution is different or if their types are not supported 179 * by this operation 180 */ 181 public void setImages(PixelImage firstImage, PixelImage secondImage) 182 { 183 if (firstImage == null || secondImage == null) 184 { 185 throw new IllegalArgumentException("Both image arguments must be non-null."); 186 } 187 if (firstImage.getWidth() != secondImage.getWidth()) 188 { 189 throw new IllegalArgumentException("The images must have the same width."); 190 } 191 if (firstImage.getHeight() != secondImage.getHeight()) 192 { 193 throw new IllegalArgumentException("The images must have the same height."); 194 } 195 image1 = firstImage; 196 image2 = secondImage; 197 } 198 }