View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.openimaj.image.colour;
31  
32  import org.openimaj.image.FImage;
33  import org.openimaj.image.MBFImage;
34  
35  /**
36   * Different colour space types with conversion methods.
37   * 
38   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
39   */
40  public enum ColourSpace {
41  	/**
42  	 * RGB colour space
43  	 */
44  	RGB {
45  		@Override
46  		public MBFImage convertFromRGB(final MBFImage input) {
47  			return input;
48  		}
49  
50  		@Override
51  		public int getNumBands() {
52  			return 3;
53  		}
54  
55  		@Override
56  		public MBFImage convertToRGB(final MBFImage input) {
57  			return input;
58  		}
59  
60  		@Override
61  		public float computeIntensity(float[] colour) {
62  			return (colour[0] + colour[1] + colour[2]) / 3f;
63  		}
64  	},
65  	/**
66  	 * HSV colour space
67  	 */
68  	HSV {
69  		@Override
70  		public MBFImage convertFromRGB(final MBFImage input) {
71  			return Transforms.RGB_TO_HSV(input);
72  		}
73  
74  		@Override
75  		public int getNumBands() {
76  			return 3;
77  		}
78  
79  		@Override
80  		public MBFImage convertToRGB(final MBFImage input) {
81  			return Transforms.HSV_TO_RGB(input);
82  		}
83  
84  		@Override
85  		public float computeIntensity(float[] colour) {
86  			return colour[2];
87  		}
88  	},
89  	/**
90  	 * HSI colour space
91  	 */
92  	HSI {
93  		@Override
94  		public MBFImage convertFromRGB(final MBFImage input) {
95  			return Transforms.RGB_TO_HSI(input);
96  		}
97  
98  		@Override
99  		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 &amp; 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, &amp; 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 &amp; 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 }