Mercurial > octave-nkf
annotate scripts/image/cmunique.m @ 15714:b1cd65881592
Clean up scripts in image directory.
Use Octave coding conventions. Redo docstrings. Add %!tests.
* brighten.m: Put input validation first. Use iscolormap to simplify
input checking.
* cmunique.m: Use faster method of validating input class.
* colormap.m: Tweak docstring. Improve input validation.
* contrast.m: Tweak docstring. Use cmap instead of map as variable
name for clarity.
* gray2ind.m: Wrap long lines. Use faster method of validating input class.
Delete unreachable code for n>65536.
* hsv2rgb.m: Use faster method of validating input class.
* imwrite.m: Tweak FIXME notes.
* ind2gray.m: Use correct caller name for ind2x. Update %!tests
with new 2-input calling convention.
* ind2rgb.m: Tweak docstring. Update %!tests with new 2-input
calling convention.
* iscolormap.m: Tweak docstring. Re-order validation tests.
* ntsc2rgb.m: Use faster method of validating input class. Better input
validation. Add %!tests.
* private/ind2x.m: Use more descriptive variable names.
* rgb2hsv.m: Tweak docstring. Use faster method of validating input class.
* rgb2ind.m: Tweak docstring. Wrap long lines.
* rgb2ntsc.m: Use faster method of validating input class. Improve input
validation. Add %!tests.
* rgbplot.m: Match variable names in docstring to those in function
prototype.
author | Rik <rik@octave.org> |
---|---|
date | Sun, 02 Dec 2012 10:02:57 -0800 |
parents | 4beb3a4bd440 |
children | 20e9b56bbf2f |
rev | line source |
---|---|
14896 | 1 ## Copyright (C) 2004 Josep Mones i Teixidor |
2 ## Copyright (C) 2012 Rik Wehbring | |
3 ## | |
4 ## This file is part of Octave. | |
5 ## | |
6 ## Octave is free software; you can redistribute it and/or modify it | |
7 ## under the terms of the GNU General Public License as published by | |
8 ## the Free Software Foundation; either version 3 of the License, or (at | |
9 ## your option) any later version. | |
10 ## | |
11 ## Octave is distributed in the hope that it will be useful, but | |
12 ## WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 ## General Public License for more details. | |
15 ## | |
16 ## You should have received a copy of the GNU General Public License | |
17 ## along with Octave; see the file COPYING. If not, see | |
18 ## <http://www.gnu.org/licenses/>. | |
19 | |
20 ## -*- texinfo -*- | |
21 ## @deftypefn {Function File} {[@var{Y}, @var{newmap}] =} cmunique (@var{X}, @var{map}) | |
22 ## @deftypefnx {Function File} {[@var{Y}, @var{newmap}] =} cmunique (@var{RGB}) | |
23 ## @deftypefnx {Function File} {[@var{Y}, @var{newmap}] =} cmunique (@var{I}) | |
24 ## Convert an input image @var{X} to an ouput indexed image @var{Y} which uses | |
25 ## the smallest colormap possible @var{newmap}. | |
26 ## | |
27 ## When the input is an indexed image (@var{X} with colormap @var{map}) the | |
28 ## output is a colormap @var{newmap} from which any repeated rows have been | |
29 ## eliminated. The output image, @var{Y}, is the original input image with | |
30 ## the indices adjusted to match the new, possibly smaller, colormap. | |
31 ## | |
15040
bc61fba0e9fd
doc: Periodic spellcheck of documentation.
Rik <rik@octave.org>
parents:
14897
diff
changeset
|
32 ## When the input is an RGB image (an @nospell{MxNx3} array), the output |
bc61fba0e9fd
doc: Periodic spellcheck of documentation.
Rik <rik@octave.org>
parents:
14897
diff
changeset
|
33 ## colormap will contain one entry for every unique color in the original image. |
14896 | 34 ## In the worst case the new map could have as many rows as the number of |
35 ## pixels in the original image. | |
36 ## | |
14897
8e2a6fc55787
doc: Use 'grayscale' rather than 'gray-scale' in documentation.
Rik <rik@octave.org>
parents:
14896
diff
changeset
|
37 ## When the input is a grayscale image @var{I}, the output colormap will |
14896 | 38 ## contain one entry for every unique intensity value in the original image. |
39 ## In the worst case the new map could have as many rows as the number of | |
40 ## pixels in the original image. | |
41 ## | |
42 ## Implementation Details: | |
43 ## | |
44 ## @var{newmap} is always an Mx3 matrix, even if the input image is | |
45 ## an intensity grayscale image @var{I} (all three RGB planes are | |
46 ## assigned the same value). | |
47 ## | |
48 ## The output image is of class uint8 if the size of the new colormap is | |
49 ## less than or equal to 256. Otherwise, the output image is of class double. | |
50 ## | |
51 ## @seealso{rgb2ind, gray2ind} | |
52 ## @end deftypefn | |
53 | |
54 | |
55 ## Author: Josep Mones i Teixidor <jmones@puntbarra.com> | |
56 | |
57 function [Y, newmap] = cmunique (X, map) | |
58 | |
59 if (nargin < 1 || nargin > 2) | |
60 print_usage (); | |
61 endif | |
62 | |
63 cls = class (X); | |
64 ## FIXME: Documentation accepts only 3 classes. Could easily add 'single'. | |
15714
b1cd65881592
Clean up scripts in image directory.
Rik <rik@octave.org>
parents:
15515
diff
changeset
|
65 if (! any (strcmp (cls, {"uint8", "uint16", "double"}))) |
14896 | 66 error ("cmunique: X is of invalid data type '%s'", cls); |
67 endif | |
68 | |
69 if (nargin == 2) | |
70 ## (X, map) case | |
15515
4beb3a4bd440
rgbplot.m, cmpermute.m, cmunique.m, ind2rgb.m, imwrite.m: use core iscolormap function
Carnë Draug <carandraug+dev@gmail.com>
parents:
15040
diff
changeset
|
71 if (! iscolormap (map)) |
14896 | 72 error ("cmunique: MAP must be a valid colormap"); |
73 endif | |
74 [newmap,i,j] = unique (map, "rows"); # calculate unique colormap | |
75 if (isa (X, "double")) | |
76 Y = j(X); # find new indices | |
77 else | |
78 Y = j(double (X) + 1); # find new indices | |
79 endif | |
80 else | |
81 switch (size (X,3)) | |
82 case (1) | |
83 ## I case | |
84 [newmap,i,j] = unique (X); # calculate unique colormap | |
85 newmap = repmat (newmap,1,3); # get a RGB colormap | |
86 Y = reshape (j, rows (X), columns (X)); # Y is j reshaped | |
87 case (3) | |
88 ## RGB case | |
89 ## build a map with all values | |
90 map = [X(:,:,1)(:), X(:,:,2)(:), X(:,:,3)(:)]; | |
91 [newmap,i,j] = unique (map, "rows"); # calculate unique colormap | |
92 Y = reshape (j, rows (X), columns (X)); # Y is j reshaped | |
93 otherwise | |
94 error ("cmunique: X is not a valid image"); | |
95 endswitch | |
96 | |
97 ## if image was uint8 or uint16 we have to convert newmap to [0,1] range | |
98 if (! isa (X, "double")) | |
99 newmap = double (newmap) / double (intmax (class (X))); | |
100 endif | |
101 endif | |
102 | |
103 if (rows (newmap) <= 256) | |
104 ## convert Y to uint8 (0-based indices then) | |
105 Y = uint8 (Y-1); | |
106 endif | |
107 | |
108 endfunction | |
109 | |
110 | |
111 %!demo | |
112 %! [Y, newmap] = cmunique ([1:4;5:8], [hot(4);hot(4)]) | |
113 %! ## Both rows are equal since map maps colors to the same value | |
114 %! ## cmunique will give the same indices to both | |
115 | |
116 ## Check that output is uint8 in short colormaps | |
117 %!test | |
118 %! [Y, newmap] = cmunique ([1:4;5:8], [hot(4);hot(4)]); | |
119 %! assert (Y, uint8 ([0:3;0:3])); | |
120 %! assert (newmap, hot (4)); | |
121 | |
122 ## Check that output is double in bigger | |
123 %!test | |
124 %! [Y, newmap] = cmunique ([1:300;301:600], [hot(300);hot(300)]); | |
125 %! assert (Y, [1:300;1:300]); | |
126 %! assert (newmap, hot (300)); | |
127 | |
128 ## Check boundary case 256 | |
129 %!test | |
130 %! [Y, newmap] = cmunique ([1:256;257:512], [hot(256);hot(256)]); | |
131 %! assert (Y, uint8 ([0:255;0:255])); | |
132 %! assert (newmap, hot (256)); | |
133 | |
134 ## Check boundary case 257 | |
135 %!test | |
136 %! [Y, newmap] = cmunique ([1:257;258:514], [hot(257);hot(257)]); | |
137 %! assert (Y, [1:257;1:257]); | |
138 %! assert (newmap, hot (257)); | |
139 | |
140 ## Random RGB image | |
141 %!test | |
142 %! RGB = rand (10,10,3); | |
143 %! [Y, newmap] = cmunique (RGB); | |
144 %! assert (RGB(:,:,1), newmap(:,1)(Y+1)); | |
145 %! assert (RGB(:,:,2), newmap(:,2)(Y+1)); | |
146 %! assert (RGB(:,:,3), newmap(:,3)(Y+1)); | |
147 | |
148 ## Random uint8 RGB image | |
149 %!test | |
150 %! RGB = uint8 (rand (10,10,3)*255); | |
151 %! RGBd = double (RGB) / 255; | |
152 %! [Y, newmap] = cmunique (RGB); | |
153 %! assert (RGBd(:,:,1), newmap(:,1)(Y+1)); | |
154 %! assert (RGBd(:,:,2), newmap(:,2)(Y+1)); | |
155 %! assert (RGBd(:,:,3), newmap(:,3)(Y+1)); | |
156 | |
157 ## Random uint16 RGB image | |
158 %!test | |
159 %! RGB = uint16 (rand (10,10,3)*65535); | |
160 %! RGBd = double (RGB) / 65535; | |
161 %! [Y, newmap] = cmunique (RGB); | |
162 %! assert (RGBd(:,:,1), newmap(:,1)(Y+1)); | |
163 %! assert (RGBd(:,:,2), newmap(:,2)(Y+1)); | |
164 %! assert (RGBd(:,:,3), newmap(:,3)(Y+1)); | |
165 | |
166 ## Random I image | |
167 %!test | |
168 %! I = rand (10,10); | |
169 %! [Y, newmap] = cmunique (I); | |
170 %! assert (I, newmap(:,1)(Y+1)); | |
171 %! assert (I, newmap(:,2)(Y+1)); | |
172 %! assert (I, newmap(:,3)(Y+1)); | |
173 | |
174 ## Random uint8 I image | |
175 %!test | |
176 %! I = uint8 (rand (10,10)*256); | |
177 %! Id = double (I) / 255; | |
178 %! [Y, newmap] = cmunique (I); | |
179 %! assert (Id, newmap(:,1)(Y+1)); | |
180 %! assert (Id, newmap(:,2)(Y+1)); | |
181 %! assert (Id, newmap(:,3)(Y+1)); | |
182 | |
183 ## Random uint16 I image | |
184 %!test | |
185 %! I = uint16 (rand (10,10)*65535); | |
186 %! Id = double (I) / 65535; | |
187 %! [Y,newmap] = cmunique (I); | |
188 %! assert (Id,newmap (:,1)(Y+1)); | |
189 %! assert (Id,newmap (:,2)(Y+1)); | |
190 %! assert (Id,newmap (:,3)(Y+1)); | |
191 | |
192 ## Test input validation | |
193 %!error cmpermute () | |
194 %!error cmpermute (1,2,3) | |
195 %!error <X is of invalid data type> cmunique (single (magic (16))) | |
196 %!error <MAP must be a valid colormap> cmunique (1, "a") | |
197 %!error <MAP must be a valid colormap> cmunique (1, i) | |
198 %!error <MAP must be a valid colormap> cmunique (1, ones (3,3,3)) | |
199 %!error <MAP must be a valid colormap> cmunique (1, ones (3,2)) | |
200 %!error <MAP must be a valid colormap> cmunique (1, [-1 1 1]) | |
201 %!error <MAP must be a valid colormap> cmunique (1, [2 1 1]) | |
202 |