001 /* 002 * LogLuvConversion 003 * 004 * Copyright (c) 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.color.conversion; 009 010 /** 011 * Convert from LogLuv color representation to RGB color space and 012 * from LogL to grayscale. 013 * <p> 014 * This implementation is based on the file <code>tif_luv.c</code> which 015 * is part of the TIFF library <a target="_top" href="http://www.libtiff.org">libtiff</a>. 016 * The original implementation was written by Greg W. Larson. 017 * <p> 018 * Learn more about the color type and its encoding on Greg's page 019 * <a target="_top" href="http://positron.cs.berkeley.edu/~gwlarson/pixformat/tiffluv.html">LogLuv 020 * Encoding for TIFF Images</a>. 021 * @author Marco Schmidt 022 * @since 0.10.0 023 */ 024 public class LogLuvConversion 025 { 026 private static final double M_LN2 = 0.69314718055994530942; 027 private static final double UVSCALE = 410.0; 028 029 // constants from libtiff's uv_decode.h 030 private static final float UV_SQSIZ = 0.003500f; 031 private static final int UV_NDIVS = 16289; 032 private static final float UV_VSTART = 0.016940f; 033 private static final int UV_NVS = 163; 034 private static final double U_NEU = 0.210526316; 035 private static final double V_NEU = 0.473684211; 036 private static final double[] USTART = 037 { 038 0.247663, 039 0.243779, 040 0.241684, 041 0.237874, 042 0.235906, 043 0.232153, 044 0.228352, 045 0.226259, 046 0.222371, 047 0.220410, 048 0.214710, 049 0.212714, 050 0.210721, 051 0.204976, 052 0.202986, 053 0.199245, 054 0.195525, 055 0.193560, 056 0.189878, 057 0.186216, 058 0.186216, 059 0.182592, 060 0.179003, 061 0.175466, 062 0.172001, 063 0.172001, 064 0.168612, 065 0.168612, 066 0.163575, 067 0.158642, 068 0.158642, 069 0.158642, 070 0.153815, 071 0.153815, 072 0.149097, 073 0.149097, 074 0.142746, 075 0.142746, 076 0.142746, 077 0.138270, 078 0.138270, 079 0.138270, 080 0.132166, 081 0.132166, 082 0.126204, 083 0.126204, 084 0.126204, 085 0.120381, 086 0.120381, 087 0.120381, 088 0.120381, 089 0.112962, 090 0.112962, 091 0.112962, 092 0.107450, 093 0.107450, 094 0.107450, 095 0.107450, 096 0.100343, 097 0.100343, 098 0.100343, 099 0.095126, 100 0.095126, 101 0.095126, 102 0.095126, 103 0.088276, 104 0.088276, 105 0.088276, 106 0.088276, 107 0.081523, 108 0.081523, 109 0.081523, 110 0.081523, 111 0.074861, 112 0.074861, 113 0.074861, 114 0.074861, 115 0.068290, 116 0.068290, 117 0.068290, 118 0.068290, 119 0.063573, 120 0.063573, 121 0.063573, 122 0.063573, 123 0.057219, 124 0.057219, 125 0.057219, 126 0.057219, 127 0.050985, 128 0.050985, 129 0.050985, 130 0.050985, 131 0.050985, 132 0.044859, 133 0.044859, 134 0.044859, 135 0.044859, 136 0.040571, 137 0.040571, 138 0.040571, 139 0.040571, 140 0.036339, 141 0.036339, 142 0.036339, 143 0.036339, 144 0.032139, 145 0.032139, 146 0.032139, 147 0.032139, 148 0.027947, 149 0.027947, 150 0.027947, 151 0.023739, 152 0.023739, 153 0.023739, 154 0.023739, 155 0.019504, 156 0.019504, 157 0.019504, 158 0.016976, 159 0.016976, 160 0.016976, 161 0.016976, 162 0.012639, 163 0.012639, 164 0.012639, 165 0.009991, 166 0.009991, 167 0.009991, 168 0.009016, 169 0.009016, 170 0.009016, 171 0.006217, 172 0.006217, 173 0.005097, 174 0.005097, 175 0.005097, 176 0.003909, 177 0.003909, 178 0.002340, 179 0.002389, 180 0.001068, 181 0.001653, 182 0.000717, 183 0.001614, 184 0.000270, 185 0.000484, 186 0.001103, 187 0.001242, 188 0.001188, 189 0.001011, 190 0.000709, 191 0.000301, 192 0.002416, 193 0.003251, 194 0.003246, 195 0.004141, 196 0.005963, 197 0.008839, 198 0.010490, 199 0.016994, 200 0.023659, 201 }; 202 203 private static final short[] NCUM = 204 { 205 0, 206 4, 207 10, 208 17, 209 26, 210 36, 211 48, 212 62, 213 77, 214 94, 215 112, 216 133, 217 155, 218 178, 219 204, 220 231, 221 260, 222 291, 223 323, 224 357, 225 393, 226 429, 227 467, 228 507, 229 549, 230 593, 231 637, 232 683, 233 729, 234 778, 235 830, 236 882, 237 934, 238 989, 239 1044, 240 1102, 241 1160, 242 1222, 243 1284, 244 1346, 245 1411, 246 1476, 247 1541, 248 1610, 249 1679, 250 1752, 251 1825, 252 1898, 253 1975, 254 2052, 255 2129, 256 2206, 257 2288, 258 2370, 259 2452, 260 2538, 261 2624, 262 2710, 263 2796, 264 2887, 265 2978, 266 3069, 267 3164, 268 3259, 269 3354, 270 3449, 271 3549, 272 3649, 273 3749, 274 3849, 275 3954, 276 4059, 277 4164, 278 4269, 279 4379, 280 4489, 281 4599, 282 4709, 283 4824, 284 4939, 285 5054, 286 5169, 287 5288, 288 5407, 289 5526, 290 5645, 291 5769, 292 5893, 293 6017, 294 6141, 295 6270, 296 6399, 297 6528, 298 6657, 299 6786, 300 6920, 301 7054, 302 7188, 303 7322, 304 7460, 305 7598, 306 7736, 307 7874, 308 8016, 309 8158, 310 8300, 311 8442, 312 8588, 313 8734, 314 8880, 315 9026, 316 9176, 317 9326, 318 9476, 319 9630, 320 9784, 321 9938, 322 10092, 323 10250, 324 10408, 325 10566, 326 10727, 327 10888, 328 11049, 329 11210, 330 11375, 331 11540, 332 11705, 333 11873, 334 12041, 335 12209, 336 12379, 337 12549, 338 12719, 339 12892, 340 13065, 341 13240, 342 13415, 343 13590, 344 13767, 345 13944, 346 14121, 347 14291, 348 14455, 349 14612, 350 14762, 351 14905, 352 15041, 353 15170, 354 15293, 355 15408, 356 15517, 357 15620, 358 15717, 359 15806, 360 15888, 361 15964, 362 16033, 363 16095, 364 16150, 365 16197, 366 16237, 367 16268, 368 }; 369 370 private LogLuvConversion() 371 { 372 } 373 374 /** 375 * Converts an unsigned 10 bit value (the argument must lie in the 376 * interval 0 to 1023) to a <code>double</code> luminance 377 * (brightness) value between <code>0.0</code> and <code>1.0</code>. 378 * This conversion is needed by both LogLuv to XYZ and LogL to grayscale. 379 * @param p10 input LogL value 380 * @return double value with luminance, between 0 and 1 381 */ 382 public static double convertLogL10toY(int p10) 383 { 384 if (p10 == 0) 385 { 386 return 0.0; 387 } 388 else 389 { 390 return Math.exp(M_LN2 / 64.0 * (p10 + 0.5) - M_LN2 * 12.0); 391 } 392 } 393 394 /** 395 * Converts a signed 16 bit value (the argument must lie in the 396 * interval -32768 to 32767) to a <code>double</code> luminance 397 * (brightness) value between <code>0.0</code> and <code>1.0</code>. 398 * This conversion is needed by both LogLuv to XYZ and LogL to grayscale. 399 * @param p16 input LogL value 400 * @return double value with luminance, between 0 and 1 401 */ 402 public static double convertLogL16toY(int p16) 403 { 404 int Le = p16 & 0x7fff; 405 if (Le == 0) 406 { 407 return 0.0; 408 } 409 double Y = Math.exp(M_LN2 / 256.0 * (Le + 0.5) - M_LN2 * 64.0); 410 if ((p16 & 0x8000) == 0) 411 { 412 return Y; 413 } 414 else 415 { 416 return -Y; 417 } 418 } 419 420 private static byte convertDoubleToByte(double d) 421 { 422 if (d <= 0.0) 423 { 424 return 0; 425 } 426 else 427 if (d >= 1.0) 428 { 429 return (byte)255; 430 } 431 else 432 { 433 double result = 255.0 * Math.sqrt(d); 434 return (byte)result; 435 } 436 } 437 438 /** 439 * Converts a number of 24 bit LogLuv pixels to 24 bit RGB pixels. 440 * Each LogLuv pixel is stored as three consecutive bytes in the <code>logluv</code> byte array. 441 * The first byte and the top two bits of the second are the LogL value, the remaining 442 * 14 bits are an index that encodes u and v. 443 * @param logluv byte array with LogLuv data, must be at least num * 3 bytes large 444 * @param red the byte samples for the red channel will be written to this array 445 * @param green the byte samples for the green channel will be written to this array 446 * @param blue the byte samples for the blue channel will be written to this array 447 * @param num number of pixels to be converted 448 */ 449 public static void convertLogLuv24InterleavedtoRGB24Planar(byte[] logluv, byte[] red, byte[] green, byte[] blue, int num) 450 { 451 int srcOffs = 0; 452 int destOffs = 0; 453 while (num-- != 0) 454 { 455 // convert from LogLuv24 to XYZ 456 float X = 0.0f; 457 float Y = 0.0f; 458 float Z = 0.0f; 459 // first byte and top two bits of second make 10 bit value L10 460 int v1 = logluv[srcOffs++] & 0xff; 461 int v2 = logluv[srcOffs++] & 0xff; 462 int v3 = logluv[srcOffs++] & 0xff; 463 double L = convertLogL10toY(v1 << 2 | ((v2 >> 6) & 0x03)); 464 if (L > 0.0) 465 { 466 int c = ((v2 & 0x3f) << 8) | v3; 467 double u = U_NEU; 468 double v = V_NEU; 469 // uv_decode in tif_luv.c 470 int upper = UV_NVS; 471 int lower = 0; 472 if (c >= 0 && c < UV_NDIVS) 473 { 474 lower = 0; 475 upper = UV_NVS; 476 int ui = 0; 477 int vi = 0; 478 while (upper - lower > 1) 479 { 480 vi = (lower + upper) >> 1; 481 ui = c - NCUM[vi]; 482 if (ui > 0) 483 { 484 lower = vi; 485 } 486 else 487 if (ui < 0) 488 { 489 upper = vi; 490 } 491 else 492 { 493 lower = vi; 494 break; 495 } 496 } 497 vi = lower; 498 ui = c - NCUM[vi]; 499 u = USTART[vi] + (ui + 0.5) * UV_SQSIZ; 500 v = UV_VSTART + (vi + 0.5) * UV_SQSIZ; 501 } 502 double s = 1.0 / (6.0 * u - 16.0 * v + 12.0); 503 double x = 9.0 * u * s; 504 double y = 4.0 * v * s; 505 X = (float)(x / y * L); 506 Y = (float)L; 507 Z = (float)((1.0 - x - y) / y * L); 508 } 509 // convert from XYZ to RGB 510 double r = 2.690 * X + -1.276 * Y + -0.414 * Z; 511 double g = -1.022 * X + 1.978 * Y + 0.044 * Z; 512 double b = 0.061 * X + -0.224 * Y + 1.163 * Z; 513 red[destOffs] = convertDoubleToByte(r); 514 green[destOffs] = convertDoubleToByte(g); 515 blue[destOffs] = convertDoubleToByte(b); 516 destOffs++; 517 } 518 } 519 520 /** 521 * Converts a number of 32 bit LogLuv pixels to 24 bit RGB pixels. 522 * Each LogLuv pixel is stored as four consecutive bytes in the <code>logluv</code> byte array. 523 * The first two bytes represent the LogL value (most significant bytefirst), followed 524 * by the u value and then the v value. 525 * @param logluv byte array with LogLuv data, must be at least num * 4 bytes large 526 * @param red the byte samples for the red channel will be written to this array 527 * @param green the byte samples for the green channel will be written to this array 528 * @param blue the byte samples for the blue channel will be written to this array 529 * @param num number of pixels to be converted 530 */ 531 public static void convertLogLuv32InterleavedtoRGB24Planar(byte[] logluv, byte[] red, byte[] green, byte[] blue, int num) 532 { 533 int srcOffs = 0; 534 int destOffs = 0; 535 while (num-- != 0) 536 { 537 // convert from LogLuv32 to XYZ 538 float X = 0.0f; 539 float Y = 0.0f; 540 float Z = 0.0f; 541 int v1 = logluv[srcOffs++] & 0xff; 542 int v2 = logluv[srcOffs++] & 0xff; 543 double L = convertLogL16toY((short)((v1 << 8) | v2)); 544 if (L > 0.0) 545 { 546 // decode color 547 double u = 1.0 / UVSCALE * ((logluv[srcOffs++] & 0xff) + 0.5); 548 double v = 1.0 / UVSCALE * ((logluv[srcOffs++] & 0xff) + 0.5); 549 double s = 1.0 / (6.0 * u - 16.0 * v + 12.0); 550 double x = 9.0 * u * s; 551 double y = 4.0 * v * s; 552 X = (float)(x / y * L); 553 Y = (float)L; 554 Z = (float)((1.0 - x - y) / y * L); 555 } 556 // convert from XYZ to RGB 557 double r = 2.690 * X + -1.276 * Y + -0.414 * Z; 558 double g = -1.022 * X + 1.978 * Y + 0.044 * Z; 559 double b = 0.061 * X + -0.224 * Y + 1.163 * Z; 560 red[destOffs] = convertDoubleToByte(r); 561 green[destOffs] = convertDoubleToByte(g); 562 blue[destOffs] = convertDoubleToByte(b); 563 destOffs++; 564 } 565 } 566 567 /** 568 * Converts a number of 16 bit LogL samples to 8 bit grayscale samples. 569 * @param logl byte array with LogL samples, each 16 bit sample is stored as 570 * two consecutive bytes in order most-significant-byte least-significant-byte (network byte order); 571 * the array must be at least num * 2 entries large 572 * @param gray the byte array to which the converted samples will be written 573 * @param num the number of samples to be converted 574 */ 575 public static void convertLogL16toGray8(byte[] logl, byte[] gray, int num) 576 { 577 int srcOffs = 0; 578 int destOffs = 0; 579 while (num-- != 0) 580 { 581 int v1 = logl[srcOffs++] & 0xff; 582 int v2 = logl[srcOffs++] & 0xff; 583 double L = convertLogL16toY((short)((v1 << 8) | v2)); 584 gray[destOffs++] = convertDoubleToByte(L); 585 } 586 } 587 }