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