Mercurial > octave
annotate scripts/image/cmunique.m @ 31218:a33a9c87220b stable
imformats.m: Fix "isa" function in return value (bug #62974)
* imformats.m (isa_magick): Fix typo "Format" to "format" for name of field in
struct returned from __magick_ping__.
author | Rik <rik@octave.org> |
---|---|
date | Tue, 30 Aug 2022 15:24:29 -0700 |
parents | 796f54d4ddbf |
children | 597f3ee61a48 |
rev | line source |
---|---|
27923
bd51beb6205e
update formatting of copyright notices
John W. Eaton <jwe@octave.org>
parents:
27919
diff
changeset
|
1 ######################################################################## |
bd51beb6205e
update formatting of copyright notices
John W. Eaton <jwe@octave.org>
parents:
27919
diff
changeset
|
2 ## |
30564
796f54d4ddbf
update Octave Project Developers copyright for the new year
John W. Eaton <jwe@octave.org>
parents:
29359
diff
changeset
|
3 ## Copyright (C) 2004-2022 The Octave Project Developers |
27918
b442ec6dda5c
use centralized file for copyright info for individual contributors
John W. Eaton <jwe@octave.org>
parents:
26376
diff
changeset
|
4 ## |
27923
bd51beb6205e
update formatting of copyright notices
John W. Eaton <jwe@octave.org>
parents:
27919
diff
changeset
|
5 ## See the file COPYRIGHT.md in the top-level directory of this |
bd51beb6205e
update formatting of copyright notices
John W. Eaton <jwe@octave.org>
parents:
27919
diff
changeset
|
6 ## distribution or <https://octave.org/copyright/>. |
14896 | 7 ## |
8 ## This file is part of Octave. | |
9 ## | |
24534
194eb4bd202b
maint: Update punctuation for GPL v3 license text.
Rik <rik@octave.org>
parents:
23084
diff
changeset
|
10 ## Octave is free software: you can redistribute it and/or modify it |
14896 | 11 ## under the terms of the GNU General Public License as published by |
24534
194eb4bd202b
maint: Update punctuation for GPL v3 license text.
Rik <rik@octave.org>
parents:
23084
diff
changeset
|
12 ## the Free Software Foundation, either version 3 of the License, or |
22755
3a2b891d0b33
maint: Standardize Copyright formatting.
Rik <rik@octave.org>
parents:
22323
diff
changeset
|
13 ## (at your option) any later version. |
14896 | 14 ## |
15 ## Octave is distributed in the hope that it will be useful, but | |
16 ## WITHOUT ANY WARRANTY; without even the implied warranty of | |
22755
3a2b891d0b33
maint: Standardize Copyright formatting.
Rik <rik@octave.org>
parents:
22323
diff
changeset
|
17 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3a2b891d0b33
maint: Standardize Copyright formatting.
Rik <rik@octave.org>
parents:
22323
diff
changeset
|
18 ## GNU General Public License for more details. |
14896 | 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 | |
24534
194eb4bd202b
maint: Update punctuation for GPL v3 license text.
Rik <rik@octave.org>
parents:
23084
diff
changeset
|
22 ## <https://www.gnu.org/licenses/>. |
27923
bd51beb6205e
update formatting of copyright notices
John W. Eaton <jwe@octave.org>
parents:
27919
diff
changeset
|
23 ## |
bd51beb6205e
update formatting of copyright notices
John W. Eaton <jwe@octave.org>
parents:
27919
diff
changeset
|
24 ######################################################################## |
14896 | 25 |
26 ## -*- texinfo -*- | |
20852
516bb87ea72e
2015 Code Sprint: remove class of function from docstring for all m-files.
Rik <rik@octave.org>
parents:
20357
diff
changeset
|
27 ## @deftypefn {} {[@var{Y}, @var{newmap}] =} cmunique (@var{X}, @var{map}) |
516bb87ea72e
2015 Code Sprint: remove class of function from docstring for all m-files.
Rik <rik@octave.org>
parents:
20357
diff
changeset
|
28 ## @deftypefnx {} {[@var{Y}, @var{newmap}] =} cmunique (@var{RGB}) |
516bb87ea72e
2015 Code Sprint: remove class of function from docstring for all m-files.
Rik <rik@octave.org>
parents:
20357
diff
changeset
|
29 ## @deftypefnx {} {[@var{Y}, @var{newmap}] =} cmunique (@var{I}) |
27956
2310164737b3
fix many spelling errors (bug #57613)
John W. Eaton <jwe@octave.org>
parents:
26376
diff
changeset
|
30 ## Convert an input image @var{X} to an output indexed image @var{Y} which uses |
14896 | 31 ## the smallest colormap possible @var{newmap}. |
32 ## | |
19593
446c46af4b42
strip trailing whitespace from most source files
John W. Eaton <jwe@octave.org>
parents:
17744
diff
changeset
|
33 ## When the input is an indexed image (@var{X} with colormap @var{map}) the |
14896 | 34 ## output is a colormap @var{newmap} from which any repeated rows have been |
35 ## eliminated. The output image, @var{Y}, is the original input image with | |
36 ## the indices adjusted to match the new, possibly smaller, colormap. | |
37 ## | |
15040
bc61fba0e9fd
doc: Periodic spellcheck of documentation.
Rik <rik@octave.org>
parents:
14897
diff
changeset
|
38 ## When the input is an RGB image (an @nospell{MxNx3} array), the output |
21546
f7f97d7e9294
doc: Wrap m-file docstrings to 79 characters + newline (80 total).
Rik <rik@octave.org>
parents:
20852
diff
changeset
|
39 ## colormap will contain one entry for every unique color in the original |
f7f97d7e9294
doc: Wrap m-file docstrings to 79 characters + newline (80 total).
Rik <rik@octave.org>
parents:
20852
diff
changeset
|
40 ## image. In the worst case the new map could have as many rows as the |
f7f97d7e9294
doc: Wrap m-file docstrings to 79 characters + newline (80 total).
Rik <rik@octave.org>
parents:
20852
diff
changeset
|
41 ## number of pixels in the original image. |
14896 | 42 ## |
14897
8e2a6fc55787
doc: Use 'grayscale' rather than 'gray-scale' in documentation.
Rik <rik@octave.org>
parents:
14896
diff
changeset
|
43 ## When the input is a grayscale image @var{I}, the output colormap will |
14896 | 44 ## contain one entry for every unique intensity value in the original image. |
45 ## In the worst case the new map could have as many rows as the number of | |
46 ## pixels in the original image. | |
47 ## | |
48 ## Implementation Details: | |
49 ## | |
50 ## @var{newmap} is always an Mx3 matrix, even if the input image is | |
51 ## an intensity grayscale image @var{I} (all three RGB planes are | |
52 ## assigned the same value). | |
53 ## | |
54 ## The output image is of class uint8 if the size of the new colormap is | |
55 ## less than or equal to 256. Otherwise, the output image is of class double. | |
56 ## | |
57 ## @seealso{rgb2ind, gray2ind} | |
58 ## @end deftypefn | |
59 | |
60 | |
61 function [Y, newmap] = cmunique (X, map) | |
62 | |
28789
28de41192f3c
Eliminate unneeded verification of nargin, nargout in m-files.
Rik <rik@octave.org>
parents:
27978
diff
changeset
|
63 if (nargin < 1) |
14896 | 64 print_usage (); |
65 endif | |
66 | |
67 cls = class (X); | |
15719
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
68 if (! any (strcmp (cls, {"uint8", "uint16", "single", "double"}))) |
14896 | 69 error ("cmunique: X is of invalid data type '%s'", cls); |
70 endif | |
71 | |
72 if (nargin == 2) | |
73 ## (X, map) case | |
20357
a3bf503652b2
iscolormap: relax input check - specially in [0 1] range.
Carnë Draug <carandraug@octave.org>
parents:
19697
diff
changeset
|
74 if (! iscolormap (map) || min (map(:)) < 0 || max (map(:)) > 1) |
14896 | 75 error ("cmunique: MAP must be a valid colormap"); |
76 endif | |
77 [newmap,i,j] = unique (map, "rows"); # calculate unique colormap | |
15719
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
78 if (isfloat (X)) |
14896 | 79 Y = j(X); # find new indices |
80 else | |
15719
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
81 Y = j(double (X) + 1); # find new indices, switch to 1-based index |
14896 | 82 endif |
83 else | |
84 switch (size (X,3)) | |
17174
c3c1ebfaa7dc
maint: Use common indentation for switch statement.
Rik <rik@octave.org>
parents:
15719
diff
changeset
|
85 case 1 |
14896 | 86 ## I case |
87 [newmap,i,j] = unique (X); # calculate unique colormap | |
88 newmap = repmat (newmap,1,3); # get a RGB colormap | |
89 Y = reshape (j, rows (X), columns (X)); # Y is j reshaped | |
17174
c3c1ebfaa7dc
maint: Use common indentation for switch statement.
Rik <rik@octave.org>
parents:
15719
diff
changeset
|
90 case 3 |
14896 | 91 ## RGB case |
92 ## build a map with all values | |
93 map = [X(:,:,1)(:), X(:,:,2)(:), X(:,:,3)(:)]; | |
94 [newmap,i,j] = unique (map, "rows"); # calculate unique colormap | |
95 Y = reshape (j, rows (X), columns (X)); # Y is j reshaped | |
96 otherwise | |
97 error ("cmunique: X is not a valid image"); | |
98 endswitch | |
19593
446c46af4b42
strip trailing whitespace from most source files
John W. Eaton <jwe@octave.org>
parents:
17744
diff
changeset
|
99 |
14896 | 100 ## if image was uint8 or uint16 we have to convert newmap to [0,1] range |
15719
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
101 if (isinteger (X)) |
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
102 newmap = double (newmap) / double (intmax (cls)); |
14896 | 103 endif |
104 endif | |
105 | |
106 if (rows (newmap) <= 256) | |
15719
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
107 ## convert Y to uint8 and 0-based indexing |
14896 | 108 Y = uint8 (Y-1); |
109 endif | |
110 | |
111 endfunction | |
112 | |
113 | |
114 %!demo | |
115 %! [Y, newmap] = cmunique ([1:4;5:8], [hot(4);hot(4)]) | |
116 %! ## Both rows are equal since map maps colors to the same value | |
117 %! ## cmunique will give the same indices to both | |
118 | |
119 ## Check that output is uint8 in short colormaps | |
120 %!test | |
121 %! [Y, newmap] = cmunique ([1:4;5:8], [hot(4);hot(4)]); | |
122 %! assert (Y, uint8 ([0:3;0:3])); | |
123 %! assert (newmap, hot (4)); | |
124 | |
125 ## Check that output is double in bigger | |
126 %!test | |
127 %! [Y, newmap] = cmunique ([1:300;301:600], [hot(300);hot(300)]); | |
128 %! assert (Y, [1:300;1:300]); | |
129 %! assert (newmap, hot (300)); | |
130 | |
131 ## Check boundary case 256 | |
132 %!test | |
133 %! [Y, newmap] = cmunique ([1:256;257:512], [hot(256);hot(256)]); | |
134 %! assert (Y, uint8 ([0:255;0:255])); | |
135 %! assert (newmap, hot (256)); | |
136 | |
137 ## Check boundary case 257 | |
138 %!test | |
139 %! [Y, newmap] = cmunique ([1:257;258:514], [hot(257);hot(257)]); | |
140 %! assert (Y, [1:257;1:257]); | |
141 %! assert (newmap, hot (257)); | |
142 | |
143 ## Random RGB image | |
144 %!test | |
145 %! RGB = rand (10,10,3); | |
146 %! [Y, newmap] = cmunique (RGB); | |
147 %! assert (RGB(:,:,1), newmap(:,1)(Y+1)); | |
148 %! assert (RGB(:,:,2), newmap(:,2)(Y+1)); | |
149 %! assert (RGB(:,:,3), newmap(:,3)(Y+1)); | |
150 | |
151 ## Random uint8 RGB image | |
152 %!test | |
153 %! RGB = uint8 (rand (10,10,3)*255); | |
154 %! RGBd = double (RGB) / 255; | |
155 %! [Y, newmap] = cmunique (RGB); | |
156 %! assert (RGBd(:,:,1), newmap(:,1)(Y+1)); | |
157 %! assert (RGBd(:,:,2), newmap(:,2)(Y+1)); | |
158 %! assert (RGBd(:,:,3), newmap(:,3)(Y+1)); | |
159 | |
160 ## Random uint16 RGB image | |
161 %!test | |
162 %! RGB = uint16 (rand (10,10,3)*65535); | |
163 %! RGBd = double (RGB) / 65535; | |
164 %! [Y, newmap] = cmunique (RGB); | |
165 %! assert (RGBd(:,:,1), newmap(:,1)(Y+1)); | |
166 %! assert (RGBd(:,:,2), newmap(:,2)(Y+1)); | |
167 %! assert (RGBd(:,:,3), newmap(:,3)(Y+1)); | |
168 | |
169 ## Random I image | |
170 %!test | |
171 %! I = rand (10,10); | |
172 %! [Y, newmap] = cmunique (I); | |
173 %! assert (I, newmap(:,1)(Y+1)); | |
174 %! assert (I, newmap(:,2)(Y+1)); | |
175 %! assert (I, newmap(:,3)(Y+1)); | |
176 | |
177 ## Random uint8 I image | |
178 %!test | |
179 %! I = uint8 (rand (10,10)*256); | |
180 %! Id = double (I) / 255; | |
181 %! [Y, newmap] = cmunique (I); | |
182 %! assert (Id, newmap(:,1)(Y+1)); | |
183 %! assert (Id, newmap(:,2)(Y+1)); | |
184 %! assert (Id, newmap(:,3)(Y+1)); | |
185 | |
186 ## Random uint16 I image | |
187 %!test | |
188 %! I = uint16 (rand (10,10)*65535); | |
189 %! Id = double (I) / 65535; | |
15719
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
190 %! [Y, newmap] = cmunique (I); |
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
191 %! assert (Id, newmap(:,1)(Y+1)); |
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
192 %! assert (Id, newmap(:,2)(Y+1)); |
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
193 %! assert (Id, newmap(:,3)(Y+1)); |
14896 | 194 |
195 ## Test input validation | |
28896
90fea9cc9caa
test: Add expected error message <Invalid call> to BIST tests for nargin.
Rik <rik@octave.org>
parents:
28789
diff
changeset
|
196 %!error <Invalid call> cmunique () |
15719
20e9b56bbf2f
cmunique.m: Expand to accept inputs of class single.
Rik <rik@octave.org>
parents:
15714
diff
changeset
|
197 %!error <X is of invalid data type> cmunique (uint32 (magic (16))) |
14896 | 198 %!error <MAP must be a valid colormap> cmunique (1, "a") |
199 %!error <MAP must be a valid colormap> cmunique (1, i) | |
200 %!error <MAP must be a valid colormap> cmunique (1, ones (3,3,3)) | |
201 %!error <MAP must be a valid colormap> cmunique (1, ones (3,2)) | |
202 %!error <MAP must be a valid colormap> cmunique (1, [-1 1 1]) | |
203 %!error <MAP must be a valid colormap> cmunique (1, [2 1 1]) |