1 /* LCh2CMC
2  *
3  * Modified:
4  * 2/11/09
5  * 	- gtkdoc
6  * 19/9/12
7  * 	- redone as a class
8  * 24/9/14
9  * 	- rechecked against original paper, seems OK
10  */
11 
12 /*
13 
14     This file is part of VIPS.
15 
16     VIPS is free software; you can redistribute it and/or modify
17     it under the terms of the GNU Lesser General Public License as published by
18     the Free Software Foundation; either version 2 of the License, or
19     (at your option) any later version.
20 
21     This program is distributed in the hope that it will be useful,
22     but WITHOUT ANY WARRANTY; without even the implied warranty of
23     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24     GNU Lesser General Public License for more details.
25 
26     You should have received a copy of the GNU Lesser General Public License
27     along with this program; if not, write to the Free Software
28     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29     02110-1301  USA
30 
31  */
32 
33 /*
34 
35     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
36 
37  */
38 
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif /*HAVE_CONFIG_H*/
42 #include <vips/intl.h>
43 
44 #include <stdio.h>
45 #include <math.h>
46 
47 #include <vips/vips.h>
48 
49 #include "pcolour.h"
50 
51 typedef VipsColourTransform VipsLCh2CMC;
52 typedef VipsColourTransformClass VipsLCh2CMCClass;
53 
54 G_DEFINE_TYPE( VipsLCh2CMC, vips_LCh2CMC, VIPS_TYPE_COLOUR_TRANSFORM );
55 
56 /* I ordered this paper from the library and it took ages. For reference, the
57  * recommended short formula are:
58  *
59  * Lucs
60  * 	= 1.744 * L, L < 16
61  * 	= (1/l) * (21.75 * ln(L) + 0.3838 * L - 38.54), otherwise
62  *
63  * Cucs = (l/c) * (0.162 * C + 10.92 * (ln(0.638 + 0.07216 * C)) + 4.907)
64  *
65  * hucs = h + D * f
66  * where
67  *	D = k4 + k5 * P * | P | ** k6
68  *	P = cos(k7 * h + k8)
69  *	f = (C ** 4 / (C ** 4 + 1900)) ** 0.5
70  *
71  * h		k4	k5	k6	k7 	k8
72  * 0 - 49	133.87	-134.5	-0.924	1.727	340
73  * 49 - 110	11.78	-12.7	-0.218	2.120	333
74  * 110 - 269.5	13.87	10.93	0.140	1.000	-83
75  * 269.5 - 360	0.14	5.23	0.170	1.610	233
76  *
77  * They have a much more complicated but slightly more accurate formula for
78  * hucs. This one is pretty good, simple approximation.
79  */
80 
81 /**
82  * vips_col_L2Lcmc:
83  * @L: CIE L*
84  *
85  * Calculate Lcmc from L.
86  *
87  * Returns: Lcmc
88  */
89 float
vips_col_L2Lcmc(float L)90 vips_col_L2Lcmc( float L )
91 {
92 	float Lcmc;
93 
94 	if( L < 16.0 )
95 		Lcmc = 1.744 * L;
96 	else
97 		Lcmc = 21.75 * log( L ) + 0.3838 * L - 38.54;
98 
99 	return( Lcmc );
100 }
101 
102 /**
103  * vips_col_C2Ccmc:
104  * @C: Chroma
105  *
106  * Calculate Ccmc from C.
107  *
108  * Returns: Ccmc.
109  */
110 float
vips_col_C2Ccmc(float C)111 vips_col_C2Ccmc( float C )
112 {
113 	float Ccmc;
114 
115 	Ccmc = 0.162 * C + 10.92 * log( 0.638 + 0.07216 * C ) + 4.907;
116 	if( Ccmc < 0 )
117 		Ccmc = 0;
118 
119 	return( Ccmc );
120 }
121 
122 /**
123  * vips_col_Ch2hcmc:
124  * @C: Chroma
125  * @h: Hue (degrees)
126  *
127  * Calculate hcmc from C and h.
128  *
129  * Returns: hcmc.
130  */
131 float
vips_col_Ch2hcmc(float C,float h)132 vips_col_Ch2hcmc( float C, float h )
133 {
134 	float P, D, f, g;
135 	float k4, k5, k6, k7, k8;
136 	float hcmc;
137 
138 	if( h < 49.1 ) {
139 		k4 = 133.87;
140 		k5 = -134.5;
141 		k6 = -.924;
142 		k7 = 1.727;
143 		k8 = 340.0;
144 	}
145 	else if( h < 110.1 ) {
146 		k4 = 11.78;
147 		k5 = -12.7;
148 		k6 = -.218;
149 		k7 = 2.12;
150 		k8 = 333.0;
151 	}
152 	else if( h < 269.6 ) {
153 		k4 = 13.87;
154 		k5 = 10.93;
155 		k6 = 0.14;
156 		k7 = 1.0;
157 		k8 = -83.0;
158 	}
159 	else {
160 		k4 = .14;
161 		k5 = 5.23;
162 		k6 = .17;
163 		k7 = 1.61;
164 		k8 = 233.0;
165 	}
166 
167 	P = cos( VIPS_RAD( k7 * h + k8 ) );
168 	D = k4 + k5 * P * pow( VIPS_FABS( P ), k6 );
169 	g = C * C * C * C;
170 	f = sqrt( g / (g + 1900.0) );
171 	hcmc = h + D * f;
172 
173 	return( hcmc );
174 }
175 
176 static void
vips_LCh2CMC_line(VipsColour * colour,VipsPel * out,VipsPel ** in,int width)177 vips_LCh2CMC_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width )
178 {
179 	float *p = (float *) in[0];
180 	float *q = (float *) out;
181 
182 	int x;
183 
184 	for( x = 0; x < width; x++ ) {
185 		float L = p[0];
186 		float C = p[1];
187 		float h = p[2];
188 
189 		p += 3;
190 
191 		q[0] = vips_col_L2Lcmc( L );
192 		q[1] = vips_col_C2Ccmc( C );
193 		q[2] = vips_col_Ch2hcmc( C, h );
194 
195 		q += 3;
196 	}
197 }
198 
199 static void
vips_LCh2CMC_class_init(VipsLCh2CMCClass * class)200 vips_LCh2CMC_class_init( VipsLCh2CMCClass *class )
201 {
202 	VipsObjectClass *object_class = (VipsObjectClass *) class;
203 	VipsColourClass *colour_class = VIPS_COLOUR_CLASS( class );
204 
205 	object_class->nickname = "LCh2CMC";
206 	object_class->description = _( "transform LCh to CMC" );
207 
208 	colour_class->process_line = vips_LCh2CMC_line;
209 }
210 
211 static void
vips_LCh2CMC_init(VipsLCh2CMC * LCh2CMC)212 vips_LCh2CMC_init( VipsLCh2CMC *LCh2CMC )
213 {
214 	VipsColour *colour = VIPS_COLOUR( LCh2CMC );
215 
216 	colour->interpretation = VIPS_INTERPRETATION_CMC;
217 }
218 
219 /**
220  * vips_LCh2CMC: (method)
221  * @in: input image
222  * @out: (out): output image
223  * @...: %NULL-terminated list of optional named arguments
224  *
225  * Turn LCh to CMC.
226  *
227  * The CMC colourspace is described in "Uniform Colour Space Based on the
228  * CMC(l:c) Colour-difference Formula", M R Luo and B Rigg, Journal of the
229  * Society of Dyers and Colourists, vol 102, 1986. Distances in this
230  * colourspace approximate, within 10% or so, differences in the CMC(l:c)
231  * colour difference formula.
232  *
233  * This operation generates CMC(1:1). For CMC(2:1), halve Lucs and double
234  * Cucs.
235  *
236  * See also: vips_CMC2LCh().
237  *
238  * Returns: 0 on success, -1 on error
239  */
240 int
vips_LCh2CMC(VipsImage * in,VipsImage ** out,...)241 vips_LCh2CMC( VipsImage *in, VipsImage **out, ... )
242 {
243 	va_list ap;
244 	int result;
245 
246 	va_start( ap, out );
247 	result = vips_call_split( "LCh2CMC", ap, in, out );
248 	va_end( ap );
249 
250 	return( result );
251 }
252