/src/skia/third_party/externals/dng_sdk/source/dng_color_spec.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /*****************************************************************************/ |
2 | | // Copyright 2006-2008 Adobe Systems Incorporated |
3 | | // All Rights Reserved. |
4 | | // |
5 | | // NOTICE: Adobe permits you to use, modify, and distribute this file in |
6 | | // accordance with the terms of the Adobe license agreement accompanying it. |
7 | | /*****************************************************************************/ |
8 | | |
9 | | /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_color_spec.cpp#1 $ */ |
10 | | /* $DateTime: 2012/05/30 13:28:51 $ */ |
11 | | /* $Change: 832332 $ */ |
12 | | /* $Author: tknoll $ */ |
13 | | |
14 | | #include "dng_color_spec.h" |
15 | | |
16 | | #include "dng_assertions.h" |
17 | | #include "dng_camera_profile.h" |
18 | | #include "dng_exceptions.h" |
19 | | #include "dng_matrix.h" |
20 | | #include "dng_negative.h" |
21 | | #include "dng_temperature.h" |
22 | | #include "dng_utils.h" |
23 | | #include "dng_xy_coord.h" |
24 | | |
25 | | /*****************************************************************************/ |
26 | | |
27 | | dng_matrix_3by3 MapWhiteMatrix (const dng_xy_coord &white1, |
28 | | const dng_xy_coord &white2) |
29 | 0 | { |
30 | | |
31 | | // Use the linearized Bradford adaptation matrix. |
32 | | |
33 | 0 | dng_matrix_3by3 Mb ( 0.8951, 0.2664, -0.1614, |
34 | 0 | -0.7502, 1.7135, 0.0367, |
35 | 0 | 0.0389, -0.0685, 1.0296); |
36 | | |
37 | 0 | dng_vector_3 w1 = Mb * XYtoXYZ (white1); |
38 | 0 | dng_vector_3 w2 = Mb * XYtoXYZ (white2); |
39 | | |
40 | | // Negative white coordinates are kind of meaningless. |
41 | | |
42 | 0 | w1 [0] = Max_real64 (w1 [0], 0.0); |
43 | 0 | w1 [1] = Max_real64 (w1 [1], 0.0); |
44 | 0 | w1 [2] = Max_real64 (w1 [2], 0.0); |
45 | | |
46 | 0 | w2 [0] = Max_real64 (w2 [0], 0.0); |
47 | 0 | w2 [1] = Max_real64 (w2 [1], 0.0); |
48 | 0 | w2 [2] = Max_real64 (w2 [2], 0.0); |
49 | | |
50 | | // Limit scaling to something reasonable. |
51 | | |
52 | 0 | dng_matrix_3by3 A; |
53 | | |
54 | 0 | A [0] [0] = Pin_real64 (0.1, w1 [0] > 0.0 ? w2 [0] / w1 [0] : 10.0, 10.0); |
55 | 0 | A [1] [1] = Pin_real64 (0.1, w1 [1] > 0.0 ? w2 [1] / w1 [1] : 10.0, 10.0); |
56 | 0 | A [2] [2] = Pin_real64 (0.1, w1 [2] > 0.0 ? w2 [2] / w1 [2] : 10.0, 10.0); |
57 | | |
58 | 0 | dng_matrix_3by3 B = Invert (Mb) * A * Mb; |
59 | | |
60 | 0 | return B; |
61 | | |
62 | 0 | } |
63 | | |
64 | | /******************************************************************************/ |
65 | | |
66 | | dng_color_spec::dng_color_spec (const dng_negative &negative, |
67 | | const dng_camera_profile *profile) |
68 | | |
69 | | : fChannels (negative.ColorChannels ()) |
70 | | |
71 | | , fTemperature1 (0.0) |
72 | | , fTemperature2 (0.0) |
73 | | |
74 | | , fColorMatrix1 () |
75 | | , fColorMatrix2 () |
76 | | |
77 | | , fForwardMatrix1 () |
78 | | , fForwardMatrix2 () |
79 | | |
80 | | , fReductionMatrix1 () |
81 | | , fReductionMatrix2 () |
82 | | |
83 | | , fCameraCalibration1 () |
84 | | , fCameraCalibration2 () |
85 | | |
86 | | , fAnalogBalance () |
87 | | |
88 | | , fWhiteXY () |
89 | | |
90 | | , fCameraWhite () |
91 | | , fCameraToPCS () |
92 | | |
93 | | , fPCStoCamera () |
94 | | |
95 | 0 | { |
96 | | |
97 | 0 | if (fChannels > 1) |
98 | 0 | { |
99 | | |
100 | 0 | if (!profile || !profile->IsValid (fChannels)) |
101 | 0 | { |
102 | 0 | ThrowBadFormat (); |
103 | 0 | } |
104 | | |
105 | 0 | if (profile->WasStubbed ()) |
106 | 0 | { |
107 | 0 | ThrowProgramError ("Using stubbed profile"); |
108 | 0 | } |
109 | | |
110 | 0 | fTemperature1 = profile->CalibrationTemperature1 (); |
111 | 0 | fTemperature2 = profile->CalibrationTemperature2 (); |
112 | | |
113 | 0 | fColorMatrix1 = profile->ColorMatrix1 (); |
114 | 0 | fColorMatrix2 = profile->ColorMatrix2 (); |
115 | | |
116 | 0 | fForwardMatrix1 = profile->ForwardMatrix1 (); |
117 | 0 | fForwardMatrix2 = profile->ForwardMatrix2 (); |
118 | | |
119 | 0 | fReductionMatrix1 = profile->ReductionMatrix1 (); |
120 | 0 | fReductionMatrix2 = profile->ReductionMatrix2 (); |
121 | | |
122 | 0 | fCameraCalibration1.SetIdentity (fChannels); |
123 | 0 | fCameraCalibration2.SetIdentity (fChannels); |
124 | |
|
125 | 0 | if (negative. CameraCalibrationSignature () == |
126 | 0 | profile->ProfileCalibrationSignature ()) |
127 | 0 | { |
128 | | |
129 | 0 | if (negative.CameraCalibration1 ().Rows () == fChannels && |
130 | 0 | negative.CameraCalibration1 ().Cols () == fChannels) |
131 | 0 | { |
132 | | |
133 | 0 | fCameraCalibration1 = negative.CameraCalibration1 (); |
134 | | |
135 | 0 | } |
136 | | |
137 | 0 | if (negative.CameraCalibration2 ().Rows () == fChannels && |
138 | 0 | negative.CameraCalibration2 ().Cols () == fChannels) |
139 | 0 | { |
140 | | |
141 | 0 | fCameraCalibration2 = negative.CameraCalibration2 (); |
142 | | |
143 | 0 | } |
144 | | |
145 | 0 | } |
146 | |
|
147 | 0 | fAnalogBalance = dng_matrix (fChannels, fChannels); |
148 | | |
149 | 0 | for (uint32 j = 0; j < fChannels; j++) |
150 | 0 | { |
151 | | |
152 | 0 | fAnalogBalance [j] [j] = negative.AnalogBalance (j); |
153 | | |
154 | 0 | } |
155 | |
|
156 | 0 | dng_camera_profile::NormalizeForwardMatrix (fForwardMatrix1); |
157 | | |
158 | 0 | fColorMatrix1 = fAnalogBalance * fCameraCalibration1 * fColorMatrix1; |
159 | | |
160 | 0 | if (!profile->HasColorMatrix2 () || |
161 | 0 | fTemperature1 <= 0.0 || |
162 | 0 | fTemperature2 <= 0.0 || |
163 | 0 | fTemperature1 == fTemperature2) |
164 | 0 | { |
165 | | |
166 | 0 | fTemperature1 = 5000.0; |
167 | 0 | fTemperature2 = 5000.0; |
168 | | |
169 | 0 | fColorMatrix2 = fColorMatrix1; |
170 | 0 | fForwardMatrix2 = fForwardMatrix1; |
171 | 0 | fReductionMatrix2 = fReductionMatrix1; |
172 | 0 | fCameraCalibration2 = fCameraCalibration1; |
173 | | |
174 | 0 | } |
175 | | |
176 | 0 | else |
177 | 0 | { |
178 | | |
179 | 0 | dng_camera_profile::NormalizeForwardMatrix (fForwardMatrix2); |
180 | | |
181 | 0 | fColorMatrix2 = fAnalogBalance * fCameraCalibration2 * fColorMatrix2; |
182 | | |
183 | | // Swap values if temperatures are out of order. |
184 | | |
185 | 0 | if (fTemperature1 > fTemperature2) |
186 | 0 | { |
187 | | |
188 | 0 | real64 temp = fTemperature1; |
189 | 0 | fTemperature1 = fTemperature2; |
190 | 0 | fTemperature2 = temp; |
191 | | |
192 | 0 | dng_matrix T = fColorMatrix1; |
193 | 0 | fColorMatrix1 = fColorMatrix2; |
194 | 0 | fColorMatrix2 = T; |
195 | | |
196 | 0 | T = fForwardMatrix1; |
197 | 0 | fForwardMatrix1 = fForwardMatrix2; |
198 | 0 | fForwardMatrix2 = T; |
199 | | |
200 | 0 | T = fReductionMatrix1; |
201 | 0 | fReductionMatrix1 = fReductionMatrix2; |
202 | 0 | fReductionMatrix2 = T; |
203 | | |
204 | 0 | T = fCameraCalibration1; |
205 | 0 | fCameraCalibration1 = fCameraCalibration2; |
206 | 0 | fCameraCalibration2 = T; |
207 | | |
208 | 0 | } |
209 | | |
210 | 0 | } |
211 | | |
212 | 0 | } |
213 | | |
214 | 0 | } |
215 | | |
216 | | /*****************************************************************************/ |
217 | | |
218 | | dng_matrix dng_color_spec::FindXYZtoCamera (const dng_xy_coord &white, |
219 | | dng_matrix *forwardMatrix, |
220 | | dng_matrix *reductionMatrix, |
221 | | dng_matrix *cameraCalibration) |
222 | 0 | { |
223 | | |
224 | | // Convert to temperature/offset space. |
225 | | |
226 | 0 | dng_temperature td (white); |
227 | | |
228 | | // Find fraction to weight the first calibration. |
229 | | |
230 | 0 | real64 g; |
231 | | |
232 | 0 | if (td.Temperature () <= fTemperature1) |
233 | 0 | g = 1.0; |
234 | | |
235 | 0 | else if (td.Temperature () >= fTemperature2) |
236 | 0 | g = 0.0; |
237 | | |
238 | 0 | else |
239 | 0 | { |
240 | | |
241 | 0 | real64 invT = 1.0 / td.Temperature (); |
242 | | |
243 | 0 | g = (invT - (1.0 / fTemperature2)) / |
244 | 0 | ((1.0 / fTemperature1) - (1.0 / fTemperature2)); |
245 | | |
246 | 0 | } |
247 | | |
248 | | // Interpolate the color matrix. |
249 | | |
250 | 0 | dng_matrix colorMatrix; |
251 | | |
252 | 0 | if (g >= 1.0) |
253 | 0 | colorMatrix = fColorMatrix1; |
254 | | |
255 | 0 | else if (g <= 0.0) |
256 | 0 | colorMatrix = fColorMatrix2; |
257 | | |
258 | 0 | else |
259 | 0 | colorMatrix = (g ) * fColorMatrix1 + |
260 | 0 | (1.0 - g) * fColorMatrix2; |
261 | | |
262 | | // Interpolate forward matrix, if any. |
263 | | |
264 | 0 | if (forwardMatrix) |
265 | 0 | { |
266 | | |
267 | 0 | bool has1 = fForwardMatrix1.NotEmpty (); |
268 | 0 | bool has2 = fForwardMatrix2.NotEmpty (); |
269 | | |
270 | 0 | if (has1 && has2) |
271 | 0 | { |
272 | | |
273 | 0 | if (g >= 1.0) |
274 | 0 | *forwardMatrix = fForwardMatrix1; |
275 | | |
276 | 0 | else if (g <= 0.0) |
277 | 0 | *forwardMatrix = fForwardMatrix2; |
278 | | |
279 | 0 | else |
280 | 0 | *forwardMatrix = (g ) * fForwardMatrix1 + |
281 | 0 | (1.0 - g) * fForwardMatrix2; |
282 | | |
283 | 0 | } |
284 | | |
285 | 0 | else if (has1) |
286 | 0 | { |
287 | | |
288 | 0 | *forwardMatrix = fForwardMatrix1; |
289 | | |
290 | 0 | } |
291 | | |
292 | 0 | else if (has2) |
293 | 0 | { |
294 | | |
295 | 0 | *forwardMatrix = fForwardMatrix2; |
296 | | |
297 | 0 | } |
298 | | |
299 | 0 | else |
300 | 0 | { |
301 | | |
302 | 0 | forwardMatrix->Clear (); |
303 | | |
304 | 0 | } |
305 | | |
306 | 0 | } |
307 | | |
308 | | // Interpolate reduction matrix, if any. |
309 | | |
310 | 0 | if (reductionMatrix) |
311 | 0 | { |
312 | | |
313 | 0 | bool has1 = fReductionMatrix1.NotEmpty (); |
314 | 0 | bool has2 = fReductionMatrix2.NotEmpty (); |
315 | | |
316 | 0 | if (has1 && has2) |
317 | 0 | { |
318 | | |
319 | 0 | if (g >= 1.0) |
320 | 0 | *reductionMatrix = fReductionMatrix1; |
321 | | |
322 | 0 | else if (g <= 0.0) |
323 | 0 | *reductionMatrix = fReductionMatrix2; |
324 | | |
325 | 0 | else |
326 | 0 | *reductionMatrix = (g ) * fReductionMatrix1 + |
327 | 0 | (1.0 - g) * fReductionMatrix2; |
328 | | |
329 | 0 | } |
330 | | |
331 | 0 | else if (has1) |
332 | 0 | { |
333 | | |
334 | 0 | *reductionMatrix = fReductionMatrix1; |
335 | | |
336 | 0 | } |
337 | | |
338 | 0 | else if (has2) |
339 | 0 | { |
340 | | |
341 | 0 | *reductionMatrix = fReductionMatrix2; |
342 | | |
343 | 0 | } |
344 | | |
345 | 0 | else |
346 | 0 | { |
347 | | |
348 | 0 | reductionMatrix->Clear (); |
349 | | |
350 | 0 | } |
351 | | |
352 | 0 | } |
353 | | |
354 | | // Interpolate camera calibration matrix. |
355 | | |
356 | 0 | if (cameraCalibration) |
357 | 0 | { |
358 | | |
359 | 0 | if (g >= 1.0) |
360 | 0 | *cameraCalibration = fCameraCalibration1; |
361 | | |
362 | 0 | else if (g <= 0.0) |
363 | 0 | *cameraCalibration = fCameraCalibration2; |
364 | | |
365 | 0 | else |
366 | 0 | *cameraCalibration = (g ) * fCameraCalibration1 + |
367 | 0 | (1.0 - g) * fCameraCalibration2; |
368 | | |
369 | 0 | } |
370 | | |
371 | | // Return the interpolated color matrix. |
372 | | |
373 | 0 | return colorMatrix; |
374 | | |
375 | 0 | } |
376 | | |
377 | | /*****************************************************************************/ |
378 | | |
379 | | void dng_color_spec::SetWhiteXY (const dng_xy_coord &white) |
380 | 0 | { |
381 | | |
382 | 0 | fWhiteXY = white; |
383 | | |
384 | | // Deal with monochrome cameras. |
385 | | |
386 | 0 | if (fChannels == 1) |
387 | 0 | { |
388 | | |
389 | 0 | fCameraWhite.SetIdentity (1); |
390 | | |
391 | 0 | fCameraToPCS = PCStoXYZ ().AsColumn (); |
392 | | |
393 | 0 | return; |
394 | | |
395 | 0 | } |
396 | | |
397 | | // Interpolate an matric values for this white point. |
398 | | |
399 | 0 | dng_matrix colorMatrix; |
400 | 0 | dng_matrix forwardMatrix; |
401 | 0 | dng_matrix reductionMatrix; |
402 | 0 | dng_matrix cameraCalibration; |
403 | | |
404 | 0 | colorMatrix = FindXYZtoCamera (fWhiteXY, |
405 | 0 | &forwardMatrix, |
406 | 0 | &reductionMatrix, |
407 | 0 | &cameraCalibration); |
408 | | |
409 | | // Find the camera white values. |
410 | | |
411 | 0 | fCameraWhite = colorMatrix * XYtoXYZ (fWhiteXY); |
412 | |
|
413 | 0 | real64 cameraWhiteMaxEntry = MaxEntry (fCameraWhite); |
414 | 0 | if (cameraWhiteMaxEntry == 0) |
415 | 0 | { |
416 | 0 | ThrowBadFormat (); |
417 | 0 | } |
418 | 0 | real64 whiteScale = 1.0 / cameraWhiteMaxEntry; |
419 | | |
420 | 0 | for (uint32 j = 0; j < fChannels; j++) |
421 | 0 | { |
422 | | |
423 | | // We don't support non-positive values for camera neutral values. |
424 | | |
425 | 0 | fCameraWhite [j] = Pin_real64 (0.001, |
426 | 0 | whiteScale * fCameraWhite [j], |
427 | 0 | 1.0); |
428 | | |
429 | 0 | } |
430 | | |
431 | | // Find PCS to Camera transform. Scale matrix so PCS white can just be |
432 | | // reached when the first camera channel saturates |
433 | | |
434 | 0 | fPCStoCamera = colorMatrix * MapWhiteMatrix (PCStoXY (), fWhiteXY); |
435 | | |
436 | 0 | real64 scale = MaxEntry (fPCStoCamera * PCStoXYZ ()); |
437 | |
|
438 | 0 | if (scale == 0) |
439 | 0 | { |
440 | 0 | ThrowBadFormat (); |
441 | 0 | } |
442 | 0 | fPCStoCamera = (1.0 / scale) * fPCStoCamera; |
443 | | |
444 | | // If we have a forward matrix, then just use that. |
445 | | |
446 | 0 | if (forwardMatrix.NotEmpty ()) |
447 | 0 | { |
448 | | |
449 | 0 | dng_matrix individualToReference = Invert (fAnalogBalance * cameraCalibration); |
450 | | |
451 | 0 | dng_vector refCameraWhite = individualToReference * fCameraWhite; |
452 | | |
453 | 0 | fCameraToPCS = forwardMatrix * |
454 | 0 | Invert (refCameraWhite.AsDiagonal ()) * |
455 | 0 | individualToReference; |
456 | | |
457 | 0 | } |
458 | | |
459 | | // Else we need to use the adapt in XYZ method. |
460 | | |
461 | 0 | else |
462 | 0 | { |
463 | | |
464 | | // Invert this PCS to camera matrix. Note that if there are more than three |
465 | | // camera channels, this inversion is non-unique. |
466 | | |
467 | 0 | fCameraToPCS = Invert (fPCStoCamera, reductionMatrix); |
468 | | |
469 | 0 | } |
470 | | |
471 | 0 | } |
472 | | |
473 | | /*****************************************************************************/ |
474 | | |
475 | | const dng_xy_coord & dng_color_spec::WhiteXY () const |
476 | 0 | { |
477 | | |
478 | 0 | DNG_ASSERT (fWhiteXY.IsValid (), "Using invalid WhiteXY"); |
479 | | |
480 | 0 | return fWhiteXY; |
481 | |
|
482 | 0 | } |
483 | | |
484 | | /*****************************************************************************/ |
485 | | |
486 | | const dng_vector & dng_color_spec::CameraWhite () const |
487 | 0 | { |
488 | | |
489 | 0 | DNG_ASSERT (fCameraWhite.NotEmpty (), "Using invalid CameraWhite"); |
490 | | |
491 | 0 | return fCameraWhite; |
492 | | |
493 | 0 | } |
494 | | |
495 | | /*****************************************************************************/ |
496 | | |
497 | | const dng_matrix & dng_color_spec::CameraToPCS () const |
498 | 0 | { |
499 | | |
500 | 0 | DNG_ASSERT (fCameraToPCS.NotEmpty (), "Using invalid CameraToPCS"); |
501 | | |
502 | 0 | return fCameraToPCS; |
503 | | |
504 | 0 | } |
505 | | |
506 | | /*****************************************************************************/ |
507 | | |
508 | | const dng_matrix & dng_color_spec::PCStoCamera () const |
509 | 0 | { |
510 | | |
511 | 0 | DNG_ASSERT (fPCStoCamera.NotEmpty (), "Using invalid PCStoCamera"); |
512 | | |
513 | 0 | return fPCStoCamera; |
514 | | |
515 | 0 | } |
516 | | |
517 | | /*****************************************************************************/ |
518 | | |
519 | | dng_xy_coord dng_color_spec::NeutralToXY (const dng_vector &neutral) |
520 | 0 | { |
521 | | |
522 | 0 | const uint32 kMaxPasses = 30; |
523 | | |
524 | 0 | if (fChannels == 1) |
525 | 0 | { |
526 | | |
527 | 0 | return PCStoXY (); |
528 | | |
529 | 0 | } |
530 | | |
531 | 0 | dng_xy_coord last = D50_xy_coord (); |
532 | | |
533 | 0 | for (uint32 pass = 0; pass < kMaxPasses; pass++) |
534 | 0 | { |
535 | | |
536 | 0 | dng_matrix xyzToCamera = FindXYZtoCamera (last); |
537 | | |
538 | 0 | dng_xy_coord next = XYZtoXY (Invert (xyzToCamera) * neutral); |
539 | | |
540 | 0 | if (Abs_real64 (next.x - last.x) + |
541 | 0 | Abs_real64 (next.y - last.y) < 0.0000001) |
542 | 0 | { |
543 | | |
544 | 0 | return next; |
545 | | |
546 | 0 | } |
547 | | |
548 | | // If we reach the limit without converging, we are most likely |
549 | | // in a two value oscillation. So take the average of the last |
550 | | // two estimates and give up. |
551 | | |
552 | 0 | if (pass == kMaxPasses - 1) |
553 | 0 | { |
554 | | |
555 | 0 | next.x = (last.x + next.x) * 0.5; |
556 | 0 | next.y = (last.y + next.y) * 0.5; |
557 | | |
558 | 0 | } |
559 | | |
560 | 0 | last = next; |
561 | | |
562 | 0 | } |
563 | | |
564 | 0 | return last; |
565 | | |
566 | 0 | } |
567 | | |
568 | | /*****************************************************************************/ |