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 */ 569 CIE_Lab { 570 @Override 571 public MBFImage convertFromRGB(final MBFImage input) { 572 return Transforms.RGB_TO_CIELab(input); 573 } 574 575 @Override 576 public MBFImage convertToRGB(final MBFImage input) { 577 return Transforms.CIELab_TO_RGB(input); 578 } 579 580 @Override 581 public int getNumBands() { 582 return 3; 583 } 584 585 @Override 586 public float computeIntensity(float[] colour) { 587 return colour[0]; 588 } 589 }, 590 /** 591 * Normalised CIE_Lab color space, using the same transform as in OpenCV, 592 * which in turn came from: <a href= 593 * "http://www.cica.indiana.edu/cica/faq/color_spaces/color.spaces.html"> 594 * http://www.cica.indiana.edu/cica/faq/color_spaces/color.spaces.html</a> 595 * <p> 596 * The L, a & b values are normalised to 0..1. 597 */ 598 CIE_Lab_Norm { 599 @Override 600 public MBFImage convertFromRGB(final MBFImage input) { 601 return Transforms.RGB_TO_CIELabNormalised(input); 602 } 603 604 @Override 605 public MBFImage convertToRGB(final MBFImage input) { 606 return Transforms.CIELabNormalised_TO_RGB(input); 607 } 608 609 @Override 610 public int getNumBands() { 611 return 3; 612 } 613 614 @Override 615 public float computeIntensity(float[] colour) { 616 return colour[0]; 617 } 618 }, 619 /** 620 * CIE L*u*v* color space (CIE 1976). 621 * <p> 622 * The resultant L values are in the range 0-100, and the u & v values are 623 * in -100..100 inclusive. 624 */ 625 CIE_Luv { 626 627 @Override 628 public MBFImage convertFromRGB(final MBFImage input) { 629 return Transforms.RGB_TO_CIELUV(input); 630 } 631 632 @Override 633 public MBFImage convertToRGB(final MBFImage input) { 634 return Transforms.CIELUV_TO_RGB(input); 635 } 636 637 @Override 638 public int getNumBands() { 639 return 3; 640 } 641 642 @Override 643 public float computeIntensity(float[] colour) { 644 return colour[0]; 645 } 646 }, 647 /** 648 * YUV 649 * <p> 650 * The resultant Y is in the range [0, 1]; U is [-0.436, 0.436] and V is 651 * [-0.615, 0.615]. 652 */ 653 YUV { 654 @Override 655 public MBFImage convertFromRGB(final MBFImage input) { 656 return Transforms.RGB_TO_YUV(input); 657 } 658 659 @Override 660 public MBFImage convertToRGB(final MBFImage input) { 661 return Transforms.YUV_TO_RGB(input); 662 } 663 664 @Override 665 public int getNumBands() { 666 return 3; 667 } 668 669 @Override 670 public float computeIntensity(float[] colour) { 671 return colour[2]; 672 } 673 }, 674 /** 675 * Normalised YUV. 676 * <p> 677 * Each of the Y, U and V values are in [0, 1]. 678 * 679 */ 680 YUV_Norm { 681 @Override 682 public MBFImage convertFromRGB(final MBFImage input) { 683 return Transforms.RGB_TO_YUVNormalised(input); 684 } 685 686 @Override 687 public MBFImage convertToRGB(final MBFImage input) { 688 return Transforms.YUVNormalised_TO_RGB(input); 689 } 690 691 @Override 692 public int getNumBands() { 693 return 3; 694 } 695 696 @Override 697 public float computeIntensity(float[] colour) { 698 return colour[2]; 699 } 700 }, 701 /** 702 * Modified Opponent colour-space as used in <code>vlfeat</code>. Intensity 703 * is computed using the NTSC conversion. The intensity is also is added 704 * back to the other two components with a small multiplier for 705 * monochromatic regions. 706 * <p> 707 * The channel order is Intensity, O1 (r-g), O2 (r + g - 2b). 708 * 709 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 710 */ 711 MODIFIED_OPPONENT { 712 @Override 713 public MBFImage convertFromRGB(MBFImage input) { 714 final FImage intensity = Transforms.calculateIntensityNTSC(input); 715 716 final float alpha = 0.01f; 717 final FImage rg = new FImage(input.getWidth(), input.getHeight()); 718 final FImage rb = new FImage(input.getWidth(), input.getHeight()); 719 720 final float[][] r = input.bands.get(0).pixels; 721 final float[][] g = input.bands.get(1).pixels; 722 final float[][] b = input.bands.get(2).pixels; 723 724 for (int y = 0; y < input.getHeight(); y++) { 725 for (int x = 0; x < input.getWidth(); x++) { 726 rg.pixels[y][x] = (float) (r[y][x] - g[y][x] / Math.sqrt(2) + alpha * intensity.pixels[y][x]); 727 rb.pixels[y][x] = (float) ((r[y][x] + g[y][x] - 2 * b[y][x]) / Math.sqrt(6) + alpha 728 * intensity.pixels[y][x]); 729 } 730 } 731 732 return new MBFImage(ColourSpace.MODIFIED_OPPONENT, intensity, rg, rb); 733 } 734 735 @Override 736 public MBFImage convertToRGB(MBFImage input) { 737 throw new UnsupportedOperationException("Not supported (yet)"); 738 } 739 740 @Override 741 public int getNumBands() { 742 return 3; 743 } 744 745 @Override 746 public float computeIntensity(float[] colour) { 747 return colour[0]; 748 } 749 }, 750 /** 751 * Basic opponent colour-space. Intensity is the mean of r, g and b. 752 * <p> 753 * The channel order is Intensity, O1 (r-g), O2 (r + g - 2b). 754 * 755 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 756 */ 757 OPPONENT { 758 @Override 759 public MBFImage convertFromRGB(MBFImage input) { 760 final FImage intensity = Transforms.calculateIntensity(input); 761 762 final FImage o1 = new FImage(input.getWidth(), input.getHeight()); 763 final FImage o2 = new FImage(input.getWidth(), input.getHeight()); 764 765 final float[][] r = input.bands.get(0).pixels; 766 final float[][] g = input.bands.get(1).pixels; 767 final float[][] b = input.bands.get(2).pixels; 768 769 for (int y = 0; y < input.getHeight(); y++) { 770 for (int x = 0; x < input.getWidth(); x++) { 771 o1.pixels[y][x] = (float) (r[y][x] - g[y][x] / Math.sqrt(2)); 772 o2.pixels[y][x] = (float) ((r[y][x] + g[y][x] - 2 * b[y][x]) / Math.sqrt(6)); 773 } 774 } 775 776 return new MBFImage(ColourSpace.MODIFIED_OPPONENT, intensity, o1, o2); 777 } 778 779 @Override 780 public MBFImage convertToRGB(MBFImage input) { 781 throw new UnsupportedOperationException("Not supported (yet)"); 782 } 783 784 @Override 785 public int getNumBands() { 786 return 3; 787 } 788 789 @Override 790 public float computeIntensity(float[] colour) { 791 return colour[0]; 792 } 793 }; 794 /** 795 * Convert the given RGB image to the current colour space 796 * 797 * @param input 798 * RGB image 799 * @return image in the current colour space 800 */ 801 public abstract MBFImage convertFromRGB(MBFImage input); 802 803 /** 804 * Convert the given RGB image to the current colour space 805 * 806 * @param input 807 * RGB image 808 * @return image in the current colour space 809 */ 810 public Float[] convertFromRGB(Float[] input){ 811 MBFImage singlePixel = new MBFImage(1,1,ColourSpace.RGB); 812 singlePixel.setPixel(0, 0, input); 813 return convertFromRGB(singlePixel).getPixel(0,0); 814 }; 815 816 /** 817 * Convert the given RGB image to the current colour space 818 * 819 * @param input 820 * RGB image 821 * @return image in the current colour space 822 */ 823 public Float[] convertToRGB(Float[] input){ 824 MBFImage singlePixel = new MBFImage(1,1,this); 825 singlePixel.setPixel(0, 0, input); 826 return convertToRGB(singlePixel).getPixel(0,0); 827 }; 828 829 /** 830 * Convert the image in this color space to RGB 831 * 832 * @param input 833 * image in this colour space 834 * @return RGB image 835 */ 836 public abstract MBFImage convertToRGB(MBFImage input); 837 838 /** 839 * Convert the image to this colour space 840 * 841 * @param input 842 * an image 843 * @return image in this colour space 844 */ 845 public MBFImage convert(final MBFImage input) { 846 return this.convertFromRGB(input.getColourSpace().convertToRGB(input)); 847 } 848 849 /** 850 * Convert the image to the given colour space 851 * 852 * @param image 853 * the image 854 * @param cs 855 * the target colour space 856 * @return the converted image 857 */ 858 public static MBFImage convert(final MBFImage image, final ColourSpace cs) { 859 return cs.convertFromRGB(image.colourSpace.convertToRGB(image)); 860 } 861 862 /** 863 * Get the number of bands required by this colour space 864 * 865 * @return the number of bands 866 */ 867 public abstract int getNumBands(); 868 869 /** 870 * Compute the intensity of the given pixel in this colourspace. In 871 * colourspaces where intensity cannot be calculated, this should just 872 * return 0. 873 * 874 * @param colour 875 * the colour to extract the intensity from 876 * 877 * @return the number of bands 878 */ 879 public abstract float computeIntensity(float[] colour); 880 881 /** 882 * Sanitise the given colour array to fit the colour space format. It uses a 883 * number of heuristics that are as follows: 884 * 885 * - if the colour has the same or more bands than the colour space, then 886 * the colour is returned unchanged. - if the colour has just one band, then 887 * it is duplicated by the same number of bands as required by the colour 888 * space - otherwise, the colour is duplicated and padded with 1s. 889 * 890 * Example: RGBA colour space, RGB colour [1.0, 0.2, 0.4] the result will be 891 * padded with 1s: [1.0, 0.2, 0.4, 1] 892 * 893 * Example: HSV colour space, single band colour [0.3] the result will be 894 * duplicated: [0.3, 0.3, 0.3] 895 * 896 * @param colour 897 * The colour to sanitise 898 * @return The sanitised colour 899 */ 900 public Float[] sanitise(final Float[] colour) 901 { 902 // If the colour is longer than the required number 903 // of bands, then we'll return as is. We needn't 904 // truncate as the extra bands will be ignored by 905 // any renderers. 906 if (colour.length >= this.getNumBands()) 907 return colour; 908 909 // If the colour is a singleton, we'll duplicate it up 910 // to the correct number of bands. 911 if (colour.length == 1) 912 { 913 final Float[] newColour = new Float[this.getNumBands()]; 914 for (int i = 0; i < newColour.length; i++) 915 newColour[i] = colour[0]; 916 return newColour; 917 } 918 919 // If it's neither of the above, then we copy the current colour 920 // into the new return colour, and pad with 1s. 921 final Float[] newColour = new Float[this.getNumBands()]; 922 923 // Copy the current colour 924 for (int i = 0; i < colour.length; i++) 925 newColour[i] = colour[i]; 926 927 // Pad with 1s 928 for (int i = colour.length; i < newColour.length; i++) 929 newColour[i] = 1f; 930 931 return newColour; 932 } 933}