001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image.colour; 031 032import org.openimaj.image.FImage; 033import org.openimaj.image.MBFImage; 034 035/** 036 * Different colour space types with conversion methods. 037 * 038 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 039 */ 040public enum ColourSpace { 041 /** 042 * RGB colour space 043 */ 044 RGB { 045 @Override 046 public MBFImage convertFromRGB(final MBFImage input) { 047 return input; 048 } 049 050 @Override 051 public int getNumBands() { 052 return 3; 053 } 054 055 @Override 056 public MBFImage convertToRGB(final MBFImage input) { 057 return input; 058 } 059 060 @Override 061 public float computeIntensity(float[] colour) { 062 return (colour[0] + colour[1] + colour[2]) / 3f; 063 } 064 }, 065 /** 066 * HSV colour space 067 */ 068 HSV { 069 @Override 070 public MBFImage convertFromRGB(final MBFImage input) { 071 return Transforms.RGB_TO_HSV(input); 072 } 073 074 @Override 075 public int getNumBands() { 076 return 3; 077 } 078 079 @Override 080 public MBFImage convertToRGB(final MBFImage input) { 081 return Transforms.HSV_TO_RGB(input); 082 } 083 084 @Override 085 public float computeIntensity(float[] colour) { 086 return colour[2]; 087 } 088 }, 089 /** 090 * HSI colour space 091 */ 092 HSI { 093 @Override 094 public MBFImage convertFromRGB(final MBFImage input) { 095 return Transforms.RGB_TO_HSI(input); 096 } 097 098 @Override 099 public int getNumBands() { 100 return 3; 101 } 102 103 @Override 104 public MBFImage convertToRGB(final MBFImage input) { 105 throw new UnsupportedOperationException("colour transform not implemented"); 106 } 107 108 @Override 109 public float computeIntensity(float[] colour) { 110 return colour[2]; 111 } 112 }, 113 /** 114 * H2SV colour space 115 * 116 * @see Transforms#RGB_TO_H2SV 117 */ 118 H2SV { 119 @Override 120 public MBFImage convertFromRGB(final MBFImage input) { 121 return Transforms.RGB_TO_H2SV(input); 122 } 123 124 @Override 125 public int getNumBands() { 126 return 4; 127 } 128 129 @Override 130 public MBFImage convertToRGB(final MBFImage input) { 131 return Transforms.HSV_TO_RGB(Transforms.H2SV_TO_HSV_Simple(input)); 132 } 133 134 @Override 135 public float computeIntensity(float[] colour) { 136 return colour[3]; 137 } 138 }, 139 /** 140 * H2SV_2 colour space 141 * 142 * @see Transforms#RGB_TO_H2SV_2 143 */ 144 H2SV_2 { 145 @Override 146 public MBFImage convertFromRGB(final MBFImage input) { 147 return Transforms.RGB_TO_H2SV_2(input); 148 } 149 150 @Override 151 public int getNumBands() { 152 return 4; 153 } 154 155 @Override 156 public MBFImage convertToRGB(final MBFImage input) { 157 return Transforms.HSV_TO_RGB(Transforms.H2SV2_TO_HSV_Simple(input)); 158 } 159 160 @Override 161 public float computeIntensity(float[] colour) { 162 return colour[3]; 163 } 164 }, 165 /** 166 * H2S colour space 167 * 168 * @see Transforms#RGB_TO_H2S 169 */ 170 H2S { 171 @Override 172 public MBFImage convertFromRGB(final MBFImage input) { 173 return Transforms.RGB_TO_H2S(input); 174 } 175 176 @Override 177 public int getNumBands() { 178 return 3; 179 } 180 181 @Override 182 public MBFImage convertToRGB(final MBFImage input) { 183 throw new UnsupportedOperationException("colour transform not implemented"); 184 } 185 186 @Override 187 public float computeIntensity(float[] colour) { 188 return 0; 189 } 190 }, 191 /** 192 * H2S_2 colour space 193 * 194 * @see Transforms#RGB_TO_H2S_2 195 */ 196 H2S_2 { 197 @Override 198 public MBFImage convertFromRGB(final MBFImage input) { 199 return Transforms.RGB_TO_H2S_2(input); 200 } 201 202 @Override 203 public int getNumBands() { 204 return 3; 205 } 206 207 @Override 208 public MBFImage convertToRGB(final MBFImage input) { 209 throw new UnsupportedOperationException("colour transform not implemented"); 210 } 211 212 @Override 213 public float computeIntensity(float[] colour) { 214 return 0; 215 } 216 }, 217 /** 218 * LUMINANCE colour space from averaging RGB 219 */ 220 LUMINANCE_AVG { 221 @Override 222 public MBFImage convertFromRGB(final MBFImage input) { 223 return new MBFImage(this, Transforms.calculateIntensity(input)); 224 } 225 226 @Override 227 public int getNumBands() { 228 return 1; 229 } 230 231 @Override 232 public MBFImage convertToRGB(final MBFImage input) { 233 return new MBFImage(input.bands.get(0).clone(), input.bands.get(0).clone(), input.bands.get(0).clone()); 234 } 235 236 @Override 237 public float computeIntensity(float[] colour) { 238 return colour[0]; 239 } 240 }, 241 /** 242 * LUMINANCE colour space using NTSC perceptual weightings 243 */ 244 LUMINANCE_NTSC { 245 @Override 246 public MBFImage convertFromRGB(final MBFImage input) { 247 return new MBFImage(this, Transforms.calculateIntensityNTSC(input)); 248 } 249 250 @Override 251 public int getNumBands() { 252 return 1; 253 } 254 255 @Override 256 public MBFImage convertToRGB(final MBFImage input) { 257 return new MBFImage(input.bands.get(0).clone(), input.bands.get(0).clone(), input.bands.get(0).clone()); 258 } 259 260 @Override 261 public float computeIntensity(float[] colour) { 262 return colour[0]; 263 } 264 }, 265 /** 266 * Hue colour space 267 */ 268 HUE { 269 @Override 270 public MBFImage convertFromRGB(final MBFImage input) { 271 return new MBFImage(this, Transforms.calculateHue(input)); 272 } 273 274 @Override 275 public int getNumBands() { 276 return 1; 277 } 278 279 @Override 280 public MBFImage convertToRGB(final MBFImage input) { 281 return new MBFImage(input.bands.get(0).clone(), input.bands.get(0).clone(), input.bands.get(0).clone()); 282 } 283 284 @Override 285 public float computeIntensity(float[] colour) { 286 return 0; 287 } 288 }, 289 /** 290 * Saturation colour space 291 */ 292 SATURATION { 293 @Override 294 public MBFImage convertFromRGB(final MBFImage input) { 295 return new MBFImage(this, Transforms.calculateSaturation(input)); 296 } 297 298 @Override 299 public int getNumBands() { 300 return 1; 301 } 302 303 @Override 304 public MBFImage convertToRGB(final MBFImage input) { 305 return new MBFImage(input.bands.get(0).clone(), input.bands.get(0).clone(), input.bands.get(0).clone()); 306 } 307 308 @Override 309 public float computeIntensity(float[] colour) { 310 return 0; 311 } 312 }, 313 /** 314 * Intensity normalised RGB colour space using normalisation 315 */ 316 RGB_INTENSITY_NORMALISED { 317 @Override 318 public MBFImage convertFromRGB(final MBFImage input) { 319 return Transforms.RGB_TO_RGB_NORMALISED(input); 320 } 321 322 @Override 323 public int getNumBands() { 324 return 3; 325 } 326 327 @Override 328 public MBFImage convertToRGB(final MBFImage input) { 329 return input; 330 } 331 332 @Override 333 public float computeIntensity(float[] colour) { 334 return (colour[0] + colour[1] + colour[2]) / 3f; 335 } 336 }, 337 /** 338 * A custom (unknown) colour space 339 */ 340 CUSTOM { 341 @Override 342 public MBFImage convertFromRGB(final MBFImage input) { 343 throw new UnsupportedOperationException("Cannot convert to the custom color-space"); 344 } 345 346 @Override 347 public int getNumBands() { 348 return 1; 349 } 350 351 @Override 352 public MBFImage convertToRGB(final MBFImage input) { 353 throw new UnsupportedOperationException("colour transform not implemented"); 354 } 355 356 @Override 357 public float computeIntensity(float[] colour) { 358 return 0; 359 } 360 }, 361 /** 362 * RGB with alpha colour space 363 */ 364 RGBA { 365 @Override 366 public MBFImage convertFromRGB(final MBFImage input) { 367 return new MBFImage(input.bands.get(0), input.bands.get(1), input.bands.get(2), new FImage( 368 input.bands.get(0).width, input.bands.get(0).height).addInplace(1.0f)); 369 } 370 371 @Override 372 public int getNumBands() { 373 return 4; 374 } 375 376 @Override 377 public MBFImage convertToRGB(final MBFImage input) { 378 return new MBFImage(input.bands.get(0).clone(), input.bands.get(1).clone(), input.bands.get(2).clone()); 379 } 380 381 @Override 382 public float computeIntensity(float[] colour) { 383 return (colour[0] + colour[1] + colour[2]) / 3f; 384 } 385 }, 386 /** 387 * HSL colour space 388 */ 389 HSL { 390 @Override 391 public MBFImage convertFromRGB(final MBFImage input) { 392 return Transforms.RGB_TO_HSL(input); 393 } 394 395 @Override 396 public MBFImage convertToRGB(final MBFImage input) { 397 throw new UnsupportedOperationException("colour transform not implemented"); 398 } 399 400 @Override 401 public int getNumBands() { 402 return 3; 403 } 404 405 @Override 406 public float computeIntensity(float[] colour) { 407 return colour[2]; 408 } 409 }, 410 /** 411 * HSY colour space 412 */ 413 HSY { 414 @Override 415 public MBFImage convertFromRGB(final MBFImage input) { 416 return Transforms.RGB_TO_HSY(input); 417 } 418 419 @Override 420 public MBFImage convertToRGB(final MBFImage input) { 421 throw new UnsupportedOperationException("colour transform not implemented"); 422 } 423 424 @Override 425 public int getNumBands() { 426 return 3; 427 } 428 429 @Override 430 public float computeIntensity(float[] colour) { 431 return colour[2]; 432 } 433 }, 434 /** 435 * HS colour space 436 */ 437 HS { 438 @Override 439 public MBFImage convertFromRGB(final MBFImage input) { 440 return Transforms.RGB_TO_HS(input); 441 } 442 443 @Override 444 public MBFImage convertToRGB(final MBFImage input) { 445 throw new UnsupportedOperationException("colour transform not implemented"); 446 } 447 448 @Override 449 public int getNumBands() { 450 return 2; 451 } 452 453 @Override 454 public float computeIntensity(float[] colour) { 455 return 0; 456 } 457 }, 458 /** 459 * HS_2 colour space 460 */ 461 HS_2 { 462 @Override 463 public MBFImage convertFromRGB(final MBFImage input) { 464 return Transforms.RGB_TO_HS_2(input); 465 } 466 467 @Override 468 public MBFImage convertToRGB(final MBFImage input) { 469 throw new UnsupportedOperationException("colour transform not implemented"); 470 } 471 472 @Override 473 public int getNumBands() { 474 return 2; 475 } 476 477 @Override 478 public float computeIntensity(float[] colour) { 479 return 0; 480 } 481 }, 482 /** 483 * H1H2 colour space (two component hue) 484 * 485 * @see Transforms#H_TO_H1H2 486 */ 487 H1H2 { 488 @Override 489 public MBFImage convertFromRGB(final MBFImage input) { 490 return Transforms.H_TO_H1H2(Transforms.calculateHue(input)); 491 } 492 493 @Override 494 public MBFImage convertToRGB(final MBFImage input) { 495 throw new UnsupportedOperationException("colour transform not implemented"); 496 } 497 498 @Override 499 public int getNumBands() { 500 return 2; 501 } 502 503 @Override 504 public float computeIntensity(float[] colour) { 505 return 0; 506 } 507 }, 508 /** 509 * H1H2_2 colour space (two component hue) 510 * 511 * @see Transforms#H_TO_H1H2_2 512 */ 513 H1H2_2 { 514 @Override 515 public MBFImage convertFromRGB(final MBFImage input) { 516 return Transforms.H_TO_H1H2_2(Transforms.calculateHue(input)); 517 } 518 519 @Override 520 public MBFImage convertToRGB(final MBFImage input) { 521 throw new UnsupportedOperationException("colour transform not implemented"); 522 } 523 524 @Override 525 public int getNumBands() { 526 return 2; 527 } 528 529 @Override 530 public float computeIntensity(float[] colour) { 531 return 0; 532 } 533 }, 534 /** 535 * CIE_XYZ color space, using the same transform as in OpenCV, which in turn 536 * came from: 537 * http://www.cica.indiana.edu/cica/faq/color_spaces/color.spaces.html 538 */ 539 CIE_XYZ { 540 @Override 541 public MBFImage convertFromRGB(final MBFImage input) { 542 return Transforms.RGB_TO_CIEXYZ(input); 543 } 544 545 @Override 546 public MBFImage convertToRGB(final MBFImage input) { 547 return Transforms.CIEXYZ_TO_RGB(input); 548 } 549 550 @Override 551 public int getNumBands() { 552 return 3; 553 } 554 555 @Override 556 public float computeIntensity(float[] colour) { 557 return colour[1]; 558 } 559 }, 560 /** 561 * CIE_Lab color space, using the same transform as in OpenCV, which in turn 562 * came from: <a href= 563 * "http://www.cica.indiana.edu/cica/faq/color_spaces/color.spaces.html"> 564 * http://www.cica.indiana.edu/cica/faq/color_spaces/color.spaces.html</a> 565 * <p> 566 * The resultant L values are in the range 0-100, and the a & b values are 567 * in -127..127 inclusive. 568 * </p> 569 */ 570 CIE_Lab { 571 @Override 572 public MBFImage convertFromRGB(final MBFImage input) { 573 return Transforms.RGB_TO_CIELab(input); 574 } 575 576 @Override 577 public MBFImage convertToRGB(final MBFImage input) { 578 return Transforms.CIELab_TO_RGB(input); 579 } 580 581 @Override 582 public int getNumBands() { 583 return 3; 584 } 585 586 @Override 587 public float computeIntensity(float[] colour) { 588 return colour[0]; 589 } 590 }, 591 /** 592 * Normalised CIE_Lab color space, using the same transform as in OpenCV, 593 * which in turn came from: <a href= 594 * "http://www.cica.indiana.edu/cica/faq/color_spaces/color.spaces.html"> 595 * http://www.cica.indiana.edu/cica/faq/color_spaces/color.spaces.html</a> 596 * <p> 597 * The L, & b values are normalised to 0..1. 598 * </p> 599 */ 600 CIE_Lab_Norm { 601 @Override 602 public MBFImage convertFromRGB(final MBFImage input) { 603 return Transforms.RGB_TO_CIELabNormalised(input); 604 } 605 606 @Override 607 public MBFImage convertToRGB(final MBFImage input) { 608 return Transforms.CIELabNormalised_TO_RGB(input); 609 } 610 611 @Override 612 public int getNumBands() { 613 return 3; 614 } 615 616 @Override 617 public float computeIntensity(float[] colour) { 618 return colour[0]; 619 } 620 }, 621 /** 622 * CIE L*u*v* color space (CIE 1976). 623 * <p> 624 * The resultant L values are in the range 0-100, and the u & v values are 625 * in -100..100 inclusive. 626 * </p> 627 */ 628 CIE_Luv { 629 630 @Override 631 public MBFImage convertFromRGB(final MBFImage input) { 632 return Transforms.RGB_TO_CIELUV(input); 633 } 634 635 @Override 636 public MBFImage convertToRGB(final MBFImage input) { 637 return Transforms.CIELUV_TO_RGB(input); 638 } 639 640 @Override 641 public int getNumBands() { 642 return 3; 643 } 644 645 @Override 646 public float computeIntensity(float[] colour) { 647 return colour[0]; 648 } 649 }, 650 /** 651 * YUV 652 * <p> 653 * The resultant Y is in the range [0, 1]; U is [-0.436, 0.436] and V is 654 * [-0.615, 0.615]. 655 */ 656 YUV { 657 @Override 658 public MBFImage convertFromRGB(final MBFImage input) { 659 return Transforms.RGB_TO_YUV(input); 660 } 661 662 @Override 663 public MBFImage convertToRGB(final MBFImage input) { 664 return Transforms.YUV_TO_RGB(input); 665 } 666 667 @Override 668 public int getNumBands() { 669 return 3; 670 } 671 672 @Override 673 public float computeIntensity(float[] colour) { 674 return colour[2]; 675 } 676 }, 677 /** 678 * Normalised YUV. 679 * <p> 680 * Each of the Y, U and V values are in [0, 1]. 681 * 682 */ 683 YUV_Norm { 684 @Override 685 public MBFImage convertFromRGB(final MBFImage input) { 686 return Transforms.RGB_TO_YUVNormalised(input); 687 } 688 689 @Override 690 public MBFImage convertToRGB(final MBFImage input) { 691 return Transforms.YUVNormalised_TO_RGB(input); 692 } 693 694 @Override 695 public int getNumBands() { 696 return 3; 697 } 698 699 @Override 700 public float computeIntensity(float[] colour) { 701 return colour[2]; 702 } 703 }, 704 /** 705 * Modified Opponent colour-space as used in <code>vlfeat</code>. Intensity 706 * is computed using the NTSC conversion. The intensity is also is added 707 * back to the other two components with a small multiplier for 708 * monochromatic regions. 709 * <p> 710 * The channel order is Intensity, O1 (r-g), O2 (r + g - 2b). 711 * 712 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 713 */ 714 MODIFIED_OPPONENT { 715 @Override 716 public MBFImage convertFromRGB(MBFImage input) { 717 final FImage intensity = Transforms.calculateIntensityNTSC(input); 718 719 final float alpha = 0.01f; 720 final FImage rg = new FImage(input.getWidth(), input.getHeight()); 721 final FImage rb = new FImage(input.getWidth(), input.getHeight()); 722 723 final float[][] r = input.bands.get(0).pixels; 724 final float[][] g = input.bands.get(1).pixels; 725 final float[][] b = input.bands.get(2).pixels; 726 727 for (int y = 0; y < input.getHeight(); y++) { 728 for (int x = 0; x < input.getWidth(); x++) { 729 rg.pixels[y][x] = (float) (r[y][x] - g[y][x] / Math.sqrt(2) + alpha * intensity.pixels[y][x]); 730 rb.pixels[y][x] = (float) ((r[y][x] + g[y][x] - 2 * b[y][x]) / Math.sqrt(6) + alpha 731 * intensity.pixels[y][x]); 732 } 733 } 734 735 return new MBFImage(ColourSpace.MODIFIED_OPPONENT, intensity, rg, rb); 736 } 737 738 @Override 739 public MBFImage convertToRGB(MBFImage input) { 740 throw new UnsupportedOperationException("Not supported (yet)"); 741 } 742 743 @Override 744 public int getNumBands() { 745 return 3; 746 } 747 748 @Override 749 public float computeIntensity(float[] colour) { 750 return colour[0]; 751 } 752 }, 753 /** 754 * Basic opponent colour-space. Intensity is the mean of r, g and b. 755 * <p> 756 * The channel order is Intensity, O1 (r-g), O2 (r + g - 2b). 757 * 758 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 759 */ 760 OPPONENT { 761 @Override 762 public MBFImage convertFromRGB(MBFImage input) { 763 final FImage intensity = Transforms.calculateIntensity(input); 764 765 final FImage o1 = new FImage(input.getWidth(), input.getHeight()); 766 final FImage o2 = new FImage(input.getWidth(), input.getHeight()); 767 768 final float[][] r = input.bands.get(0).pixels; 769 final float[][] g = input.bands.get(1).pixels; 770 final float[][] b = input.bands.get(2).pixels; 771 772 for (int y = 0; y < input.getHeight(); y++) { 773 for (int x = 0; x < input.getWidth(); x++) { 774 o1.pixels[y][x] = (float) (r[y][x] - g[y][x] / Math.sqrt(2)); 775 o2.pixels[y][x] = (float) ((r[y][x] + g[y][x] - 2 * b[y][x]) / Math.sqrt(6)); 776 } 777 } 778 779 return new MBFImage(ColourSpace.MODIFIED_OPPONENT, intensity, o1, o2); 780 } 781 782 @Override 783 public MBFImage convertToRGB(MBFImage input) { 784 throw new UnsupportedOperationException("Not supported (yet)"); 785 } 786 787 @Override 788 public int getNumBands() { 789 return 3; 790 } 791 792 @Override 793 public float computeIntensity(float[] colour) { 794 return colour[0]; 795 } 796 }; 797 /** 798 * Convert the given RGB image to the current colour space 799 * 800 * @param input 801 * RGB image 802 * @return image in the current colour space 803 */ 804 public abstract MBFImage convertFromRGB(MBFImage input); 805 806 /** 807 * Convert the given RGB image to the current colour space 808 * 809 * @param input 810 * RGB image 811 * @return image in the current colour space 812 */ 813 public Float[] convertFromRGB(Float[] input){ 814 MBFImage singlePixel = new MBFImage(1,1,ColourSpace.RGB); 815 singlePixel.setPixel(0, 0, input); 816 return convertFromRGB(singlePixel).getPixel(0,0); 817 }; 818 819 /** 820 * Convert the given RGB image to the current colour space 821 * 822 * @param input 823 * RGB image 824 * @return image in the current colour space 825 */ 826 public Float[] convertToRGB(Float[] input){ 827 MBFImage singlePixel = new MBFImage(1,1,this); 828 singlePixel.setPixel(0, 0, input); 829 return convertToRGB(singlePixel).getPixel(0,0); 830 }; 831 832 /** 833 * Convert the image in this color space to RGB 834 * 835 * @param input 836 * image in this colour space 837 * @return RGB image 838 */ 839 public abstract MBFImage convertToRGB(MBFImage input); 840 841 /** 842 * Convert the image to this colour space 843 * 844 * @param input 845 * an image 846 * @return image in this colour space 847 */ 848 public MBFImage convert(final MBFImage input) { 849 return this.convertFromRGB(input.getColourSpace().convertToRGB(input)); 850 } 851 852 /** 853 * Convert the image to the given colour space 854 * 855 * @param image 856 * the image 857 * @param cs 858 * the target colour space 859 * @return the converted image 860 */ 861 public static MBFImage convert(final MBFImage image, final ColourSpace cs) { 862 return cs.convertFromRGB(image.colourSpace.convertToRGB(image)); 863 } 864 865 /** 866 * Get the number of bands required by this colour space 867 * 868 * @return the number of bands 869 */ 870 public abstract int getNumBands(); 871 872 /** 873 * Compute the intensity of the given pixel in this colourspace. In 874 * colourspaces where intensity cannot be calculated, this should just 875 * return 0. 876 * 877 * @param colour 878 * the colour to extract the intensity from 879 * 880 * @return the number of bands 881 */ 882 public abstract float computeIntensity(float[] colour); 883 884 /** 885 * Sanitise the given colour array to fit the colour space format. It uses a 886 * number of heuristics that are as follows: 887 * 888 * - if the colour has the same or more bands than the colour space, then 889 * the colour is returned unchanged. - if the colour has just one band, then 890 * it is duplicated by the same number of bands as required by the colour 891 * space - otherwise, the colour is duplicated and padded with 1s. 892 * 893 * Example: RGBA colour space, RGB colour [1.0, 0.2, 0.4] the result will be 894 * padded with 1s: [1.0, 0.2, 0.4, 1] 895 * 896 * Example: HSV colour space, single band colour [0.3] the result will be 897 * duplicated: [0.3, 0.3, 0.3] 898 * 899 * @param colour 900 * The colour to sanitise 901 * @return The sanitised colour 902 */ 903 public Float[] sanitise(final Float[] colour) 904 { 905 // If the colour is longer than the required number 906 // of bands, then we'll return as is. We needn't 907 // truncate as the extra bands will be ignored by 908 // any renderers. 909 if (colour.length >= this.getNumBands()) 910 return colour; 911 912 // If the colour is a singleton, we'll duplicate it up 913 // to the correct number of bands. 914 if (colour.length == 1) 915 { 916 final Float[] newColour = new Float[this.getNumBands()]; 917 for (int i = 0; i < newColour.length; i++) 918 newColour[i] = colour[0]; 919 return newColour; 920 } 921 922 // If it's neither of the above, then we copy the current colour 923 // into the new return colour, and pad with 1s. 924 final Float[] newColour = new Float[this.getNumBands()]; 925 926 // Copy the current colour 927 for (int i = 0; i < colour.length; i++) 928 newColour[i] = colour[i]; 929 930 // Pad with 1s 931 for (int i = colour.length; i < newColour.length; i++) 932 newColour[i] = 1f; 933 934 return newColour; 935 } 936}