Mercurial > forge
comparison main/image/inst/imtransform.m @ 11662:fbd81057dab0 octave-forge
image: new spatial transformation functions
* maketform: accept input/output control points as second and third argument
* imtransform: new function
* findsbounds: new function
* private/istform: add comment on what function does
* NEWS/INDEX: update list of new functions
author | carandraug |
---|---|
date | Sun, 28 Apr 2013 02:31:13 +0000 |
parents | |
children | 0f16ee5611b8 |
comparison
equal
deleted
inserted
replaced
11661:1b4e81051b66 | 11662:fbd81057dab0 |
---|---|
1 ## Copyright (C) 2012 Pantxo Diribarne | |
2 ## | |
3 ## This program is free software; you can redistribute it and/or modify | |
4 ## it under the terms of the GNU General Public License as published by | |
5 ## the Free Software Foundation; either version 3 of the License, or | |
6 ## (at your option) any later version. | |
7 ## | |
8 ## This program is distributed in the hope that it will be useful, | |
9 ## but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 ## GNU General Public License for more details. | |
12 ## | |
13 ## You should have received a copy of the GNU General Public License | |
14 ## along with Octave; see the file COPYING. If not, see | |
15 ## <http://www.gnu.org/licenses/>. | |
16 | |
17 ## -*- texinfo -*- | |
18 ## @deftypefn {Function File} {@var{B} =} imtransform (@var{A}, @var{T}) | |
19 ## @deftypefnx {Function File} {@var{B} =} imtransform (@var{A}, @var{T}, @var{interp}) | |
20 ## @deftypefnx {Function File} {@var{B} =} imtransform (@dots{}, @var{prop}, @var{val}) | |
21 ## Transform image. | |
22 ## | |
23 ## Given an image @var{A} in one space, returns an image @var{B} | |
24 ## resulting from the forward transform defined in the transformation | |
25 ## structure @var{T}. An additionnal input argument @var{interp}, | |
26 ## 'bicubic', 'bilinear' (default) or 'nearest', | |
27 ## specifies the interpolation method to be used. Finally, the | |
28 ## transformation can be tuned using @var{prop}/@var{val} pairs. The | |
29 ## following properties are supported: | |
30 ## | |
31 ## @table @asis | |
32 ## @item "udata" | |
33 ## Specifies the input space horizontal limits. Value must be a two | |
34 ## elements vector [minval maxval]. Default: [1 columns(@var{A})] | |
35 ## | |
36 ## @item "vdata" | |
37 ## Specifies the input space vertical limits. Value must be a two | |
38 ## elements vector [minval maxval]. Default: [1 rows(@var{A})] | |
39 ## | |
40 ## @item "xdata" | |
41 ## Specifies the requiered output space horizontal limits. Value must | |
42 ## be a two elements vector [minval maxval]. Default: estimated using | |
43 ## udata, vdata and findbounds function. | |
44 ## | |
45 ## @item "ydata" | |
46 ## Specifies the requiered output space vertical limits. Value must | |
47 ## be a two elements vector [minval maxval]. Default: estimated using | |
48 ## udata, vdata and findbounds function. | |
49 ## | |
50 ## @item "xyscale" | |
51 ## Specifies the output scale in outputspace_units/pixel. If a scalar | |
52 ## is provided, both vertical and horizontal dimensions are scaled the | |
53 ## same way. If @var{val} is a two element vector, it must indicate | |
54 ## consecutively horizontal and vertical scales. Default value is | |
55 ## computed using the input space scale provided | |
56 ## that the number of pixel of any dimension of the output image does | |
57 ## not exceed 20000. | |
58 ## | |
59 ## @item "size" | |
60 ## Size of the output image (1-by-2 vector). Overrides the effect of | |
61 ## "xyscale" property. | |
62 ## | |
63 ## @item "fillvalues" | |
64 ## Color of the areas where no interpolation where possible, e.g. when | |
65 ## coorfinates of points in the output space are out of the limits of | |
66 ## the input space. @var{val} must be coherent with the input image | |
67 ## format: for grayscale and indexed images (2D) @var{val} must be | |
68 ## scalar, for RGB (n-by-m-by-3) @var{val} must be a 3 element vector. | |
69 ## e.g.: | |
70 ## | |
71 ## @end table | |
72 ## | |
73 ## @seealso{maketform, cp2tform, tforminv, tformfwd, findbounds} | |
74 ## @end deftypefn | |
75 | |
76 ## Author: Pantxo Diribarne <pantxo@dibona> | |
77 | |
78 function [varargout] = imtransform (im, T, varargin) | |
79 | |
80 if (nargin < 2) | |
81 print_usage (); | |
82 elseif (! istform (T)) | |
83 error ("imtransform: T must be a transformation structure (see `maketform')"); | |
84 endif | |
85 | |
86 ## Parameters | |
87 interp = "linear"; | |
88 imdepth = size (im, 3); | |
89 maximsize = [20000 20000]; | |
90 | |
91 udata = [1; columns(im)]; | |
92 vdata = [1; rows(im)]; | |
93 xdata = ydata = []; | |
94 xyscale = []; | |
95 imsize = []; | |
96 fillvalues = ones (1, imdepth) * NaN; | |
97 | |
98 if (isempty (varargin)) | |
99 xydata = findbounds (T, [udata vdata]); | |
100 xdata = xydata(:,1); | |
101 ydata = xydata(:,2); | |
102 else | |
103 ## interp | |
104 if (floor (numel (varargin)/2) != numel (varargin)/2) | |
105 allowed = {"bicubic", "bilinear", "nearest"}; | |
106 tst = strcmp (varargin{1}, allowed); | |
107 if (!any (tst)) | |
108 error ("imtransform: expect one of %s as interp method", disp (allowed)); | |
109 else | |
110 interp = {"pchip", "linear", "nearest"}{find (tst)}; | |
111 endif | |
112 varargin = varargin(2:end); | |
113 endif | |
114 | |
115 ## options | |
116 allowed = {"udata", "vdata", "xdata", "ydata", ... | |
117 "xyscale", "size", "fillvalues"}; | |
118 props = varargin(1:2:end); | |
119 vals = varargin(2:2:end); | |
120 np = numel (props); | |
121 if (!all (cellfun (@ischar, props))) | |
122 error ("imtransform: expect property/value pairs."); | |
123 endif | |
124 | |
125 props = tolower (props); | |
126 tst = cellfun (@(x) any (strcmp (x, allowed)), props); | |
127 if (!all (tst)) | |
128 error ("imtransform: unknown property %s", disp (props{!tst})); | |
129 endif | |
130 | |
131 ## u(vxy)data | |
132 iolims = allowed(1:4); | |
133 for ii = 1:numel (iolims) | |
134 tst = cellfun (@(x) any (strcmp (x, iolims{ii})), props); | |
135 if (any (tst)) | |
136 prop = props{find (tst)(1)}; | |
137 val = vals{find (tst)(1)}; | |
138 if (isnumeric (val) && numel (val) == 2) | |
139 if (isrow (val)) | |
140 val = val'; | |
141 endif | |
142 eval (sprintf ("%s = val;", prop), | |
143 "error (\"imtransform: %s\n\", lasterr ());"); | |
144 else | |
145 error ("imtransform: expect 2 elements real vector for %s", prop) | |
146 endif | |
147 endif | |
148 endfor | |
149 if (isempty (xdata) && isempty (ydata)) | |
150 xydata = findbounds (T, [udata vdata]); | |
151 xdata = xydata(:,1); | |
152 ydata = xydata(:,2); | |
153 elseif (isempty (xdata)) | |
154 xydata = findbounds (T, [udata vdata]); | |
155 xdata = xydata(:,1); | |
156 elseif (isempty (ydata)) | |
157 xydata = findbounds (T, [udata vdata]); | |
158 ydata = xydata(:,2); | |
159 endif | |
160 | |
161 ## size and xyscale | |
162 tst = strcmp ("size", props); | |
163 if (any (tst)) | |
164 val = vals{find (tst)(1)}; | |
165 if (isnumeric (val) && numel (val) == 2 && | |
166 all (val > 0)) | |
167 imsize = val; | |
168 else | |
169 error ("imtransform: expect 2 elements real vector for size"); | |
170 endif | |
171 elseif (any (tst = strcmp ("xyscale", props))) | |
172 val = vals{find (tst)(1)}; | |
173 if (isnumeric (val) && all (val > 0)) | |
174 if (numel (val) == 1) | |
175 xyscale(1:2) = val; | |
176 elseif (numel (val) == 2) | |
177 xyscale = val; | |
178 else | |
179 error ("imtransform: expect 1 or 2 element(s) real vector for xyscale"); | |
180 endif | |
181 else | |
182 error ("imtransform: expect 1 or 2 elements real vector for xyscale"); | |
183 endif | |
184 else | |
185 xyscale = [(diff (udata) / columns (im)) (diff (vdata) / rows (im))]; | |
186 endif | |
187 | |
188 ## Fillvalues | |
189 tst = strcmp ("fillvalues", props); | |
190 if (any (tst)) | |
191 val = vals{find (tst)(1)}; | |
192 if (isnumeric (val) && numel (val) == 1) | |
193 fillvalues(1:end) = val; | |
194 elseif (isnumeric (val) && numel (val) == 3) | |
195 fillvalues = val; | |
196 else | |
197 error ("imtransform: expect 1 or 3 elements real vector for `fillvalues'"); | |
198 endif | |
199 endif | |
200 endif | |
201 | |
202 ## Ouput/Input pixels | |
203 if (isempty (imsize)) | |
204 if (isempty (xyscale)) | |
205 xyscale = [(diff (udata) / columns (im)) (diff (vdata) / rows (im))]; | |
206 endif | |
207 xscale = xyscale(1); | |
208 yscale = xyscale(2); | |
209 xsize = floor (diff (xdata) / xscale); | |
210 ysize = floor (diff (ydata) / yscale); | |
211 if (xsize > maximsize(2) || ysize > maximsize(1)) | |
212 if (xsize >= ysize) | |
213 scalefactor = (diff (xdata) / maximsize(2)) / xscale; | |
214 else | |
215 scalefactor = (diff (ydata) / maximsize(1)) / yscale; | |
216 endif | |
217 xscale *= scalefactor | |
218 yscale *= scalefactor | |
219 xsize = floor (diff (xdata) / xscale); | |
220 ysize = floor (diff (ydata) / yscale); | |
221 warning ("imtransform: output image two large, adjusting the largest dimension to %d", maximsize); | |
222 endif | |
223 imsize = [ysize xsize]; | |
224 endif | |
225 [xx yy] = meshgrid (linspace (xdata(1), xdata(2), imsize(2)), | |
226 linspace (ydata(1), ydata(2), imsize(1))); | |
227 | |
228 [uu vv] = meshgrid (linspace (udata(1), udata(2), size(im)(2)), | |
229 linspace (vdata(1), vdata(2), size(im)(1))); | |
230 | |
231 ## Input coordinates | |
232 [uui, vvi] = tforminv (T, reshape (xx, numel (xx), 1), | |
233 reshape (yy, numel (yy), 1)); | |
234 uui = reshape (uui, size (xx)); | |
235 vvi = reshape (vvi, size (yy)); | |
236 ## Interpolation | |
237 for layer = 1:imdepth | |
238 imout(:,:,layer) = interp2 (uu, vv, im(:,:,layer), ... | |
239 uui, vvi, interp, fillvalues(layer)); | |
240 endfor | |
241 if (nargout == 1) | |
242 varargout{1} = imout; | |
243 else | |
244 varargout = {imout, xdata, ydata}; | |
245 endif | |
246 endfunction | |
247 | |
248 %!demo | |
249 %! ## Various linear transforms | |
250 %! figure (); | |
251 %! im = [checkerboard(20, 2, 4); checkerboard(40, 1, 2)]; | |
252 %! %input space corners | |
253 %! incp = [1 1; 160 1; 160 160; 1 160]; | |
254 %! udata = [min(incp(:,1)) max(incp(:,1))]; | |
255 %! vdata = [min(incp(:,2)) max(incp(:,2))]; | |
256 %! subplot (2,3,1); | |
257 %! imshow (im) | |
258 %! hold on | |
259 %! plot (incp(:,1), incp(:,2), 'ob') | |
260 %! axis on | |
261 %! xlabel ('Original') | |
262 %! | |
263 %! % Translation and scaling | |
264 %! outcp = incp * 2; | |
265 %! outcp(:,1) += 200; | |
266 %! outcp(:,2) += 500; | |
267 %! T = maketform ('affine', incp(1:3,:), outcp(1:3,:)); | |
268 %! subplot (2,3,2); | |
269 %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, | |
270 %! 'vdata', vdata, 'fillvalues', 1); | |
271 %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) | |
272 %! set (gca, 'xlim', xdata, 'ylim', ydata) | |
273 %! axis on, hold on, xlabel ('Translation / Scaling'); | |
274 %! plot (outcp(:,1), outcp(:,2), 'or') | |
275 %! | |
276 %! % Shear | |
277 %! outcp = [1 1; 160 1; 140 160; -19 160]; % affine only needs 3 control points | |
278 %! T = maketform ('affine', incp(1:3,:), outcp(1:3,:)); | |
279 %! subplot (2,3,3); | |
280 %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, | |
281 %! 'vdata', vdata, 'fillvalues', 1); | |
282 %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) | |
283 %! set (gca, 'xlim', xdata, 'ylim', ydata) | |
284 %! axis on, hold on, xlabel ('Shear'); | |
285 %! plot (outcp(:,1), outcp(:,2), 'or') | |
286 %! | |
287 %! % Rotation | |
288 %! theta = pi/4; | |
289 %! T = maketform ('affine', [cos(theta) -sin(theta); ... | |
290 %! sin(theta) cos(theta); 0 0]); | |
291 %! outcp = tformfwd (T, incp); | |
292 %! subplot (2,3,4); | |
293 %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, | |
294 %! 'vdata', vdata, 'fillvalues', 1 ); | |
295 %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) | |
296 %! set (gca, 'xlim', xdata, 'ylim', ydata) | |
297 %! axis on, hold on, xlabel ('Rotation'); | |
298 %! plot (outcp(:,1), outcp(:,2), 'or') | |
299 %! | |
300 %! % Reflection around x axis | |
301 %! outcp = incp; | |
302 %! outcp(:,2) *= -1; | |
303 %! T = cp2tform (incp, outcp, 'similarity'); | |
304 %! subplot (2,3,5); | |
305 %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, | |
306 %! 'vdata', vdata, 'fillvalues', 1 ); | |
307 %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) | |
308 %! set (gca, 'xlim', xdata, 'ylim', ydata) | |
309 %! axis on, hold on, xlabel ('Reflection'); | |
310 %! plot (outcp(:,1), outcp(:,2), 'or') | |
311 %! | |
312 %! % Projection | |
313 %! outcp = [1 1; 160 -40; 220 220; 12 140]; | |
314 %! T = maketform ('projective', incp, outcp); | |
315 %! subplot (2,3,6); | |
316 %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, | |
317 %! 'vdata', vdata, 'fillvalues', 1 ); | |
318 %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) | |
319 %! set (gca, 'xlim', xdata, 'ylim', ydata) | |
320 %! axis on, hold on, xlabel ('Projection'); | |
321 %! plot (outcp(:,1), outcp(:,2), 'or') | |
322 | |
323 %!demo | |
324 %! ## Streched image | |
325 %! rad = 2; % minimum value: 4/pi | |
326 %! [uu vv] = meshgrid ((-2:2)/rad, (-2:2)/rad); | |
327 %! rescfactor = sin ((uu.^2 + vv.^2).^.5); | |
328 %! inpts = [(reshape (uu, numel (uu), 1)), (reshape (vv, numel (uu), 1))]; | |
329 %! xx = rescfactor .* sign(uu); | |
330 %! yy = rescfactor .* sign(vv); | |
331 %! outpts = [reshape(xx, numel (xx), 1) reshape(yy, numel (yy), 1)]; | |
332 %! | |
333 %! T = cp2tform (inpts, outpts, "polynomial", 4); | |
334 %! figure; | |
335 %! subplot (1,2,1) | |
336 %! im = zeros (800, 800, 3); | |
337 %! im(:,:,1) = checkerboard (100) > 0.2; | |
338 %! im(:,:,3) = checkerboard (100) < 0.2; | |
339 %! [im2 xdata ydata] = imtransform (im, T, 'udata', [-2 2], | |
340 %! 'vdata', [-2 2], 'fillvalues', | |
341 %! [0 1 0]); | |
342 %! imh = imshow (im2); | |
343 %! set (imh, 'xdata', xdata, 'ydata', ydata) | |
344 %! set (gca, 'xlim', xdata, 'ylim', ydata) | |
345 %! [im cmap] = imread ('default.img'); | |
346 %! subplot (1,2,2) | |
347 %! [im2 xdata ydata] = imtransform (im, T, 'udata', [-1 1], | |
348 %! 'vdata', [-1 1], 'fillvalues', | |
349 %! round (length (cmap) / 2)); | |
350 %! imh = imshow (im2, cmap); |