11662
|
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); |