View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.openimaj.image.analysis.colour;
31  
32  import org.openimaj.image.FImage;
33  import org.openimaj.image.MBFImage;
34  import org.openimaj.image.colour.ColourSpace;
35  import org.openimaj.image.combiner.ImageCombiner;
36  
37  /**
38   * Implementation of the CIE 2000 colour difference equation, 
39   * and a {@link ImageCombiner} to calculate a colour disparity map between
40   * two images.
41   * 
42   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
43   */
44  public class CIEDE2000 implements ImageCombiner<MBFImage, MBFImage, FImage> {
45  	/**
46  	 * Calculate the colour difference value between two colours in lab space.
47  	 * @param lab1 first colour
48  	 * @param lab2 second colour
49  	 * @return the CIE 2000 colour difference
50  	 */
51  	public static double calculateDeltaE(double [] lab1, double[] lab2) {
52  		return calculateDeltaE(lab1[0],lab1[1],lab1[2],lab2[0],lab2[1],lab2[2]);
53  	}
54  	
55  	/**
56  	 * Calculate the colour difference value between two colours in lab space.
57  	 * @param lab1 first colour
58  	 * @param lab2 second colour
59  	 * @return the CIE 2000 colour difference
60  	 */
61  	public static float calculateDeltaE(float [] lab1, float[] lab2) {
62  		return (float) calculateDeltaE(lab1[0],lab1[1],lab1[2],lab2[0],lab2[1],lab2[2]);
63  	}
64  	
65  	/**
66  	 * Calculate the colour difference value between two colours in lab space.
67  	 * @param lab1 first colour
68  	 * @param lab2 second colour
69  	 * @return the CIE 2000 colour difference
70  	 */
71  	public static float calculateDeltaE(Float [] lab1, Float[] lab2) {
72  		return (float) calculateDeltaE(lab1[0],lab1[1],lab1[2],lab2[0],lab2[1],lab2[2]);
73  	}
74  	
75  	/**
76  	 * Calculate the colour difference value between two colours in lab space.
77  	 * @param L1 first colour's L component
78  	 * @param a1 first colour's a component
79  	 * @param b1 first colour's b component
80  	 * @param L2 second colour's L component
81  	 * @param a2 second colour's a component
82  	 * @param b2 second colour's b component
83  	 * @return the CIE 2000 colour difference
84  	 */
85  	public static double calculateDeltaE(double L1, double a1, double b1, double L2, double a2, double b2) {
86  		double Lmean = (L1 + L2) / 2.0; //ok
87  		double C1 =  Math.sqrt(a1*a1 + b1*b1); //ok
88  		double C2 =  Math.sqrt(a2*a2 + b2*b2); //ok
89  		double Cmean = (C1 + C2) / 2.0; //ok
90  		
91  		double G =  ( 1 - Math.sqrt( Math.pow(Cmean, 7) / (Math.pow(Cmean, 7) + Math.pow(25, 7)) ) ) / 2; //ok
92  		double a1prime = a1 * (1 + G); //ok
93  		double a2prime = a2 * (1 + G); //ok
94  		
95  		double C1prime =  Math.sqrt(a1prime*a1prime + b1*b1); //ok
96  		double C2prime =  Math.sqrt(a2prime*a2prime + b2*b2); //ok
97  		double Cmeanprime = (C1prime + C2prime) / 2; //ok 
98  		
99  		double h1prime =  Math.atan2(b1, a1prime) + 2*Math.PI * (Math.atan2(b1, a1prime)<0 ? 1 : 0);
100 		double h2prime =  Math.atan2(b2, a2prime) + 2*Math.PI * (Math.atan2(b2, a2prime)<0 ? 1 : 0);
101 		double Hmeanprime =  ((Math.abs(h1prime - h2prime) > Math.PI) ? (h1prime + h2prime + 2*Math.PI) / 2 : (h1prime + h2prime) / 2); //ok
102 		
103 		double T =  1.0 - 0.17 * Math.cos(Hmeanprime - Math.PI/6.0) + 0.24 * Math.cos(2*Hmeanprime) + 0.32 * Math.cos(3*Hmeanprime + Math.PI/30) - 0.2 * Math.cos(4*Hmeanprime - 21*Math.PI/60); //ok
104 		
105 		double deltahprime =  ((Math.abs(h1prime - h2prime) <= Math.PI) ? h2prime - h1prime : (h2prime <= h1prime) ? h2prime - h1prime + 2*Math.PI : h2prime - h1prime - 2*Math.PI); //ok
106 		
107 		double deltaLprime = L2 - L1; //ok
108 		double deltaCprime = C2prime - C1prime; //ok
109 		double deltaHprime =  2.0 * Math.sqrt(C1prime*C2prime) * Math.sin(deltahprime / 2.0); //ok
110 		double SL =  1.0 + ( (0.015*(Lmean - 50)*(Lmean - 50)) / (Math.sqrt( 20 + (Lmean - 50)*(Lmean - 50) )) ); //ok
111 		double SC =  1.0 + 0.045 * Cmeanprime; //ok
112 		double SH =  1.0 + 0.015 * Cmeanprime * T; //ok
113 		
114 		double deltaTheta =  (30 * Math.PI / 180) * Math.exp(-((180/Math.PI*Hmeanprime-275)/25)*((180/Math.PI*Hmeanprime-275)/25));
115 		double RC =  (2 * Math.sqrt(Math.pow(Cmeanprime, 7) / (Math.pow(Cmeanprime, 7) + Math.pow(25, 7))));
116 		double RT =  (-RC * Math.sin(2 * deltaTheta));
117 		
118 		double KL = 1;
119 		double KC = 1;
120 		double KH = 1;
121 		
122 		double deltaE = Math.sqrt(
123 				((deltaLprime/(KL*SL)) * (deltaLprime/(KL*SL))) +
124 				((deltaCprime/(KC*SC)) * (deltaCprime/(KC*SC))) +
125 				((deltaHprime/(KH*SH)) * (deltaHprime/(KH*SH))) +
126 				(RT * (deltaCprime/(KC*SC)) * (deltaHprime/(KH*SH)))
127 				);
128 				
129 		return deltaE;
130 	}
131 
132 	/**
133 	 * Compute the disparity map between two images. If the images
134 	 * are not in Lab colour space, then copies in lab space will
135 	 * be created.
136 	 * @param im1 The first image.
137 	 * @param im2 The second image.
138 	 * @return the disparity map between the colours in the two images.
139 	 */
140 	public static FImage makeDisparityMap(MBFImage im1, MBFImage im2) {
141 		if (im1.colourSpace != ColourSpace.CIE_Lab) {
142 			im1 = ColourSpace.convert(im1, ColourSpace.CIE_Lab);
143 		}
144 		
145 		if (im2.colourSpace != ColourSpace.CIE_Lab) {
146 			im2 = ColourSpace.convert(im2, ColourSpace.CIE_Lab);
147 		}
148 		
149 		FImage disparity = new FImage(im1.getWidth(), im1.getHeight());
150 		for (int y=0; y<disparity.height; y++) {
151 			for (int x=0; x<disparity.width; x++) {
152 				disparity.pixels[y][x] = calculateDeltaE(im1.getPixel(x, y), im2.getPixel(x, y));
153 			}
154 		}
155 		
156 		return disparity;
157 	}
158 	
159 	@Override
160 	public FImage combine(MBFImage image1, MBFImage image2) {
161 		return makeDisparityMap(image2, image2);
162 	}
163 }