1########################################################################
2##
3## Copyright (C) 2018-2021 The Octave Project Developers
4##
5## See the file COPYRIGHT.md in the top-level directory of this
6## distribution or <https://octave.org/copyright/>.
7##
8## This file is part of Octave.
9##
10## Octave is free software: you can redistribute it and/or modify it
11## under the terms of the GNU General Public License as published by
12## the Free Software Foundation, either version 3 of the License, or
13## (at your option) any later version.
14##
15## Octave is distributed in the hope that it will be useful, but
16## WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18## GNU General Public License for more details.
19##
20## You should have received a copy of the GNU General Public License
21## along with Octave; see the file COPYING.  If not, see
22## <https://www.gnu.org/licenses/>.
23##
24########################################################################
25
26## -*- texinfo -*-
27## @deftypefn  {} {@var{I} =} rgb2gray (@var{rgb_img})
28## @deftypefnx {} {@var{gray_map} =} rgb2gray (@var{rgb_map})
29## Transform an image or colormap from red-green-blue (RGB) color space to
30## a grayscale intensity image.
31##
32## The input may be of class uint8, int8, uint16, int16, single, or double.
33## The output is of the same class as the input.
34##
35## Implementation Note:
36## The grayscale intensity is calculated as
37##
38## @example
39## @group
40## @var{I} = 0.298936*@var{R} + 0.587043*@var{G} + 0.114021*@var{B}
41## @end group
42## @end example
43##
44## @noindent
45## which corresponds to the luminance channel when RGB is translated to
46## @nospell{YIQ} as documented in @url{https://en.wikipedia.org/wiki/YIQ}.
47## @seealso{rgb2hsv, rgb2ind}
48## @end deftypefn
49
50function I = rgb2gray (rgb)
51
52  if (nargin != 1)
53    print_usage ();
54  endif
55
56  is_int = isinteger (rgb);
57  if (is_int)
58    cls = class (rgb);
59  endif
60  [rgb, sz, is_im, is_nd] ...
61    = colorspace_conversion_input_check ("rgb2gray", "RGB", rgb);
62
63  ## Reference matrix for transform from http://en.wikipedia.org/wiki/YIQ.
64  ## Matlab uses this matrix for their conversion with oddly more precision.
65  xform = [0.298936; 0.587043; 0.114021];
66  I = rgb * xform;
67
68  sz(3) = 1; # grayscale images have 3rd dimension of length 1
69  I = colorspace_conversion_revert (I, sz, is_im, is_nd);
70
71  ## Restore integer class if necessary
72  if (is_int)
73    if (cls(end) == "8")  # uint8 or int8
74      I *= 255;
75      if (cls(1) == "i")  # int8
76        I -= 128;
77      endif
78    else                  # uint16 or int16
79      I *= 65535;
80      if (cls(1) == "i")  # int16
81        I -= 32768;
82      endif
83    endif
84    I = feval (cls, I);
85  endif
86
87endfunction
88
89
90## Test pure RED, GREEN, BLUE colors
91%!assert (rgb2gray ([1 0 0]), 0.298936)
92%!assert (rgb2gray ([0 1 0]), 0.587043)
93%!assert (rgb2gray ([0 0 1]), 0.114021)
94
95## test tolerance input checking on floats
96%! assert (rgb2gray ([1.5 1 1]), 1.149468, -1.6e-3);
97
98## Test ND input
99%!test
100%! rgb = rand (16, 16, 3, 5);
101%! I = zeros (16, 16, 1, 5);
102%! for i = 1:5
103%!   I(:,:,1,i) = rgb2gray (rgb(:,:,:,i));
104%! endfor
105%! assert (rgb2gray (rgb), I);
106
107## Test output class and size for input images.
108## Most of the tests only test for colormap input.
109
110%!test
111%! I = rgb2gray (rand (10, 10, 3));
112%! assert (class (I), "double");
113%! assert (size (I), [10 10]);
114
115%!test
116%! I = rgb2gray (rand (10, 10, 3, "single"));
117%! assert (class (I), "single");
118%! assert (size (I), [10 10]);
119
120%!test
121%! rgb = (rand (10, 10, 3) * 3 ) - 0.5; # values outside range [0 1]
122%! I = rgb2gray (rgb);
123%! assert (class (I), "double");
124%! assert (size (I), [10 10]);
125
126%!test
127%! rgb = (rand (10, 10, 3, "single") * 3 ) - 0.5; # values outside range [0 1]
128%! I = rgb2gray (rgb);
129%! assert (class (I), "single");
130%! assert (size (I), [10 10]);
131
132%!test
133%! I = rgb2gray (randi ([0 255], 10, 10, 3, "uint8"));
134%! assert (class (I), "uint8");
135%! assert (size (I), [10 10]);
136
137%!test
138%! I = rgb2gray (randi ([0 65535], 10, 10, 3, "uint16"));
139%! assert (class (I), "uint16");
140%! assert (size (I), [10 10]);
141
142%!test
143%! I = rgb2gray (randi ([-128 127], 10, 10, 3, "int8"));
144%! assert (class (I), "int8");
145%! assert (size (I), [10 10]);
146
147%!test
148%! I = rgb2gray (randi ([-32768 32767], 10, 10, 3, "int16"));
149%! assert (class (I), "int16");
150%! assert (size (I), [10 10]);
151
152%!test
153%! rgb_double = reshape ([1 0 0 0 0 1 0 0 0 0 1 0], [2 2 3]);
154%! rgb_uint8  = reshape (uint8 ([255 0 0 0 0 255 0 0 0 0 255 0]),
155%!                       [2 2 3]);
156%! rgb_int16 = int16 (double (rgb_double * uint16 (65535)) - 32768);
157%! expected = [0.298936, 0.114021; 0.587043, 0.0];
158%!
159%! assert (rgb2gray (rgb_double), expected);
160%! assert (rgb2gray (rgb_uint8), uint8 (expected*255));
161%! assert (rgb2gray (single (rgb_double)), single (expected));
162
163## Test input validation
164%!error rgb2gray ()
165%!error rgb2gray (1,2)
166%!error <invalid data type 'cell'> rgb2gray ({1})
167%!error <RGB must be a colormap or RGB image> rgb2gray (ones (2,2))
168