Mercurial > forge
changeset 11346:558eb88d81b1 octave-forge
new functions cp2tform, maketform, tformfwd and tforminv by Pantxo Diribarne
author | carandraug |
---|---|
date | Thu, 03 Jan 2013 14:52:10 +0000 |
parents | e75a08c7bfce |
children | 9d835921957b |
files | main/image/INDEX main/image/NEWS main/image/inst/cp2tform.m main/image/inst/maketform.m main/image/inst/tformfwd.m main/image/inst/tforminv.m |
diffstat | 6 files changed, 563 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/main/image/INDEX Wed Jan 02 22:04:17 2013 +0000 +++ b/main/image/INDEX Thu Jan 03 14:52:10 2013 +0000 @@ -105,6 +105,7 @@ poly2mask roicolor Spatial transformations + cp2tform imcrop impad imperspectivewarp @@ -114,7 +115,10 @@ imrotate_Fourier imshear imtranslate + maketform rotate_scale + tformfwd + tforminv Types and Type conversions grayslice graythresh
--- a/main/image/NEWS Wed Jan 02 22:04:17 2013 +0000 +++ b/main/image/NEWS Thu Jan 03 14:52:10 2013 +0000 @@ -3,7 +3,12 @@ ** The following functions are new: - checkerboard strel + checkerboard + cp2tform + maketform + strel + tformfwd + tforminv ** The plot produced by `imhist' is correctly scaled on the X axis so that the colorbar corresponds to the actual intensity of the stems; the given @@ -18,7 +23,7 @@ ** The function `rgbplot' accepts a style option which alows for a composite display of a colormap. - ** The option to create poisson noise to an image has been added to `imnoise'. + ** The option to create poisson noise to an image has been added to `imnoise'. ** With the addition of the strel class, the following functions are now able to handle strel objects:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/image/inst/cp2tform.m Thu Jan 03 14:52:10 2013 +0000 @@ -0,0 +1,264 @@ +## Copyright (C) 2012 Pantxo Diribarne +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} {@var{T} =} cp2tform (@var{rw_pt}, @var{ap_pt}, @var{transtype}) +## @deftypefnx {Function File} {@var{T} =} cp2tform (@var{rw_pt}, @var{ap_pt}, @var{transtype}, @var{opt}) +## Returns a transformation structure @var{T} (see "help maketform" +## for the form of the structure) that can be further used to +## transform coordinates from one space (here denoted "RW" for "real +## world") to another (here denoted "AP" for "apparent"). The transform +## is infered from two n-by-2 arrays, @var{rw_pt} and @var{ap_pt}, wich +## contain the coordinates of n control points in the two 2D spaces. +## Transform coefficients are stored +## in @var{T}.tdata. Interpretation of transform coefficients depends on the +## requested transform type @var{transtype}: +## +## @table @asis +## @item "affine" +## Return both forward (RW->AP) and inverse (AP->RW) transform +## coefficients @var{T}.tdata.T and @var{T}.tdata.Tinv. Transform +## coefficients are 3x2 matrices which can +## be used as follows: +## +## @example +## @group +## @var{rw_pt} = [@var{ap_pt} ones(rows (ap_pt,1))] * Tinv +## @var{ap_pt} = [@var{rw_pt} ones(rows (rw_pt,1))] * T +## @end group +## @end example +## This transformation is well suited when parallel lines in one space +## are still parallel in the other space (e.g. shear, translation, ...). +## +## @item "nonreflective similarity" +## Same as "affine" except that the transform matrices T and Tinv have +## the form +## @example +## @group +## Tcoefs = [a -b; +## b a; +## c d] +## @end group +## @end example +## This transformation may represent rotation, scaling and +## translation. Reflection is not included. +## +## @item "similarity" +## Same as "nonreflective similarity" except that the transform matrices T and Tinv may also have +## the form +## @example +## @group +## Tcoefs = [a b; +## b -a; +## c d] +## @end group +## @end example +## This transformation may represent reflection, rotation, scaling and +## translation. Generates a warning if the nonreflective similarity is +## better suited. +## +## @item "projective" +## Return both forward (RW->AP) and inverse (AP->RW) transform +## coefficients @var{T}.tdata.T and @var{T}.tdata.Tinv. Transform +## coefficients are 3x3 matrices which can +## be used as follows: +## +## @example +## @group +## [u v w] = [@var{ap_pt} ones(rows (ap_pt,1))] * Tinv +## @var{rw_pt} = [u./w, v./w]; +## [x y z] = [@var{rw_pt} ones(rows (rw_pt,1))] * T +## @var{ap_pt} = [x./z y./z]; +## @end group +## @end example +## This transformation is well suited when parallel lines in one space +## all converge toward a vanishing point in the other space. +## +## @item "polynomial" +## Here the @var{opt} input argument is the order of the polynomial +## fit. @var{opt} must be 2, 3 or 4 and input control points number must +## be respectively at least 6, 10 and 15. Only the inverse transform +## (AP->RW) is included in the structure @var{T}. +## Denoting x and y the apparent coordinates vector and xrw, yrw the +## the real world coordinates. Inverse transform coefficients are +## stored in a (6,10 or 15)x2 matrix which can be used as follows: +## +## @example +## @group +## Second order: +## [xrw yrw] = [1 x y x*y x^2 y^2] * Tinv +## @end group +## @group +## Third order: +## [xrw yrw] = [1 x y x*y x^2 y^2 y*x^2 x*y^2 x^3 y^3] * Tinv +## @end group +## @group +## Fourth order: +## [xrw yrw] = [1 x y x*y x^2 y^2 y*x^2 x*y^2 x^3 y^3 x^3*y x^2*y^2 x*y^3 x^4 y^4] * Tinv +## @end group +## @end example +## This transform is well suited when lines in one space become curves +## in the other space. +## @end table +## @seealso{tformfwd, tforminv, maketform} +## @end deftypefn + +## Author: Pantxo Diribarne <pantxo@dibona> +## Created: 2012-09-05 + +function trans = cp2tform (crw, cap, ttype, opt) + if (nargin < 3) + print_usage (); + endif + + if (! all (size (crw) == size (cap)) || + columns (crw) != 2) + error ("cp2tform: expect the 2 first input arguments to be (m x 2) matrices") + elseif (! ischar (ttype)) + error ("cp2tform: expect a string as third input argument") + endif + + ttype = lower (ttype); + switch ttype + case {'nonreflective similarity', 'similarity', 'affine', 'projective'} + trans = gettrans (ttype, cap, crw); + case 'polynomial' + if (nargin < 4) + error ("cp2tform: expect a fourth input argument for 'polynomial'") + elseif (! isscalar (opt)) + error ("cp2tform: expect a scalar as fourth argument") + endif + trans = gettrans (ttype, cap, crw, round (opt)); + otherwise + error ("cp2tform: expect 'nonreflective similarity', 'similarity', 'affine' or 'polynomial' as third input argument") + endswitch +endfunction + +function trans = gettrans (ttype, cap, crw, ord = 0) + if (strcmp (ttype, 'nonreflective similarity')) + x = cap(:,1); + y = cap(:,2); + u = crw(:,1); + v = crw(:,2); + tmp0 = zeros(size(x)); + tmp1 = ones(size(x)); + + A = [x y tmp1 tmp0 ; y -x tmp0 tmp1]; + B = [u; v]; + tmat = A\B; + tmat = [tmat(1) -tmat(2); + tmat(2) tmat(1); + tmat(3) tmat(4)]; + trans = maketform ("affine", tmat); + elseif (strcmp (ttype, 'similarity')) + #error ("cp2tform: similarity not implemented yet.") + x = cap(:,1); + y = cap(:,2); + u = crw(:,1); + v = crw(:,2); + tmp0 = zeros(size(x)); + tmp1 = ones(size(x)); + + #without reflection + A = [x y tmp1 tmp0 ; y -x tmp0 tmp1]; + B = [u; v]; + tmat1 = A\B; + resid = norm (A*tmat1 - B); + #with reflection + A = [x y tmp1 tmp0 ; -y x tmp0 tmp1]; + B = [u; v]; + tmat2 = A\B; + if (norm (A*tmat2 - B) < resid) + tmat = [tmat2(1) tmat2(2); + tmat2(2) -tmat2(1); + tmat2(3) tmat2(4)]; + else + tmat = [tmat1(1) -tmat1(2); + tmat1(2) tmat1(1); + tmat1(3) tmat1(4)]; + warning ("cp2tform: reflection not included.") + endif + trans = maketform ("affine", tmat); + elseif (strcmp (ttype, 'affine')) + tmat =[cap ones(rows(cap), 1)]\crw; + trans = maketform ("affine", tmat); + elseif (strcmp (ttype, "projective")) + x = cap(:,1); + y = cap(:,2); + u = crw(:,1); + v = crw(:,2); + tmp0 = zeros(size(x)); + tmp1 = ones(size(x)); + A = [-x -y -tmp1 tmp0 tmp0 tmp0 u.*x u.*y u; + tmp0 tmp0 tmp0 -x -y -tmp1 v.*x v.*y v]; + [U S V] = svd (A); + tmat = V(:,end); + tmat = reshape (tmat, 3, 3); + tmat = tmat./tmat(end,end); + trans = maketform ("projective", tmat); + elseif (strcmp (ttype, 'polynomial')) + x = cap(:,1); + y = cap(:,2); + u = crw(:,1); + v = crw(:,2); + tmp1 = ones(size(x)); + + ndims_in = 2; + ndims_out = 2; + forward_fcn = []; + inverse_fcn = @inv_polynomial; + A = [tmp1, x, y, x.*y, x.^2, y.^2]; + B = [u v]; + switch ord + case 2 + case 3 + A = [A, y.*x.^2 x.*y.^2 x.^3 y.^3]; + case 4 + A = [A, y.*x.^2 x.*y.^2 x.^3 y.^3]; + A = [A, x.^3.*y x.^2.*y.^2 x.*y.^3 x.^4 y.^4]; + otherwise + error ("cp2tform: supported polynomial orders are 2, 3 and 4.") + endswitch + tmat = A\B; + trans = maketform ("custom", ndims_in, ndims_out, ... + forward_fcn, inverse_fcn, tmat); + endif +endfunction + +function out = inv_polynomial (x, pst) + out = []; + for ii = 1:2 + p = pst.tdata(:,ii); + + if (rows (p) == 6) + ## 2nd order + out(:,ii) = p(1) + p(2)*x(:,1) + p(3)*x(:,2) + p(4)*x(:,1).*x(:,2) + \ + p(5)*x(:,1).^2 + p(6)*x(:,2).^2; + elseif (rows (p) == 10) + ## 3rd order + out(:,ii) = p(1) + p(2)*x(:,1) + p(3)*x(:,2) + p(4)*x(:,1).*x(:,2) + \ + p(5)*x(:,1).^2 + p(6)*x(:,2).^2 + p(7)*x(:,2).*x(:,1).^2 + \ + p(8)*x(:,1).*x(:,2).^2 + p(9)*x(:,1).^3 + p(10)*x(:,2).^3; + elseif (rows (p) == 15) + ## 4th order + out(:,ii) = p(1) + p(2)*x(:,1) + p(3)*x(:,2) + p(4)*x(:,1).*x(:,2) + \ + p(5)*x(:,1).^2 + p(6)*x(:,2).^2 + p(7)*x(:,2).*x(:,1).^2 + \ + p(8)*x(:,1).*x(:,2).^2 + p(9)*x(:,1).^3 + p(10)*x(:,2).^3 + \ + p(11)*x(:,2).*x(:,1).^3 + p(12)*x(:,2).^2.*x(:,1).^2+ \ + p(13)*x(:,1).*x(:,2).^3 + p(14)*x(:,1).^4 + p(15)*x(:,2).^4; + endif + endfor +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/image/inst/maketform.m Thu Jan 03 14:52:10 2013 +0000 @@ -0,0 +1,142 @@ +## Copyright (C) 2012 Pantxo Diribarne +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} {@var{T} =} maketform (@var{ttype}, @var{tmat}) +## @deftypefnx {Function File} {@var{T} =} maketform ("custom", @var{ndims_in}, @var{ndims_out}, @var{forward_fcn}, @var{inverse_fcn}, @var{tdata}) +## Returns a transform structure containing fields @var{ndims_in}, +## @var{ndims_out}, @var{forward_fcn}, @var{inverse_fcn} and @var{tdata}. The content +## of each field depends on the requested transform type @var{ttype}: +## @table @asis +## @item "projective" +## A ndims_in = N -> ndims_out = N projective transformation structure +## is returned. +## The second input argument @var{tmat} must be a (N+1)-by-(N+1) +## transformation matrix. The +## (N+1)th column must contain projection coefficients. As an example a two +## dimentionnal transform from [x y] coordinates to [u v] coordinates +## is represented by a transformation matrix defined so that: +## @example +## [xx yy zz] = [u v 1] * [a d g; +## b e h; +## c f i] +## [x y] = [xx./zz yy./zz]; +## @end example +## @item "affine" +## Affine is a subset of projective transform (see above). A ndims_in = N -> +## ndims_out = N affine transformation structure is returned. +## The second input argument @var{tmat} must be a (N+1)-by-(N+1) or +## (N+1)-by-(N) transformation matrix. If present, the (N+1)th column must +## contain [zeros(N,1); 1] so that projection is suppressed. +## @item "custom" +## For user defined transforms every field of the transform structure +## must be supplied. The prototype of the transform functions, +## @var{forward_fcn} and @var{inverse_fcn}, should be X' = +## transform_fcn (X, T). X and X' are respectively p-by-ndims_in and +## p-by-ndims_out arrays for forward_fcn and reversed for inverse_fcn. +## The argument T is the transformation structure which will contain +## the user supplied transformation matrix @var{tdata}. +## @end table +## @seealso{tformfwd, tforminv, cp2tform} +## @end deftypefn + +## Author: Pantxo Diribarne <pantxo@dibona> +## Created: 2012-09-05 + +function T = maketform (ttype, varargin) + + if (nargin < 2 || ! any (strcmp (ttype, {"affine", "projective", "custom"}))) + print_usage (); + endif + if (numel (varargin) == 1) + tmat = varargin {1}; + ndin = rows (tmat) - 1; + ndout = columns (tmat) - 1; + if (ndin < 2); + error ("maketform: expect at least 3-by-2 transform matrix") + elseif ((ndin-ndout) > 1 || (ndout > ndin)) + print_usage (); + endif + + switch ttype + case "affine" + if ((ndin - ndout) == 1) + tmat = [tmat [zeros(ndin, 1); 1]]; + ndout += 1; + elseif (!all (tmat(:,end) == [zeros(ndin, 1); 1])) + error ("maketform: \"affine\" expect [zeros(N,1); 1] as (N+1)th column"); + endif + forward_fcn = @fwd_affine; + inverse_fcn = @inv_affine; + case "projective" + if ((ndin - ndout) == 1) + print_usage (); + endif + forward_fcn = @fwd_projective; + inverse_fcn = @inv_projective; + endswitch + T.ndims_in = ndin; + T.ndims_out = ndout; + T.forward_fcn = forward_fcn; + T.inverse_fcn = inverse_fcn; + T.tdata.T = inv (tmat); + T.tdata.Tinv = tmat; + elseif (numel (varargin) == 5 && strcmp (ttype, "custom")) + if (isscalar (varargin{1}) && isscalar (varargin{2}) + && varargin{1} > 0 && varargin{2} > 0) + T.ndims_in = varargin{1}; + T.ndims_out = varargin{2}; + else + error ("maketform: expect positive scalars as ndims.") + endif + if (is_function_handle (varargin{3}) || isempty (varargin{3})) + T.forward_fcn = varargin{3}; + else + error ("maketform: expect function handle as forward_fcn.") + endif + if (is_function_handle (varargin{4}) || isempty (varargin{4})) + T.inverse_fcn = varargin{4}; + else + error ("maketform: expect function handle as inverse_fcn.") + endif + + T.tdata = varargin{5}; + else + print_usage (); + endif +endfunction + +function X = fwd_affine (U, T) + U = [U, ones(rows(U), 1)]; + X = U * T.tdata.T(:,1:end-1); +endfunction + +function U = inv_affine (X, T) + X = [X, ones(rows(X), 1)]; + U = X * T.tdata.Tinv(:,1:end-1); +endfunction + +function X = fwd_projective (U, T) + U = [U, ones(rows(U), 1)]; + XX = U * T.tdata.T; + X = [XX(:,1)./XX(:,3) XX(:,2)./XX(:,3)]; +endfunction + +function U = inv_projective (X, T) + X = [X, ones(rows(X), 1)]; + UU = X * T.tdata.Tinv; + U = [UU(:,1)./UU(:,3) UU(:,2)./UU(:,3)]; +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/image/inst/tformfwd.m Thu Jan 03 14:52:10 2013 +0000 @@ -0,0 +1,74 @@ +## Copyright (C) 2012 Pantxo Diribarne +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} {[@var{UV}] =} tformfwd (@var{T}, @var{XY}) +## @deftypefnx {Function File} {[@var{U}, @var{V}] =} tformfwd (@var{T}, @var{X}, @var{Y}) +## +## Given to dimensionnal coordinates from one space, returns two +## dimensionnal coordinates in the other space, as defined in +## the transform structure @var{T}. Input and output coordinates +## may be gigen either as a n-by-2 arrays, or as two n-by-1 vectors. +## @seealso{maketform, cp2tform, tforminv} +## @end deftypefn + +## Author: Pantxo Diribarne <pantxo@dibona> + +function varargout = tformfwd (T, varargin) + + if (nargin > 3 || nargin < 2) + print_usage (); + elseif (! istform (T)) + error ("tformfwd: expect a transform structure as first argument") + elseif (nargin == 2) + XX = varargin{1}; + if (columns (XX) != 2) + error ("tformfwd: expect n-by-2 array as second argument") + endif + else + if (!isvector (varargin{1}) || !isvector (varargin{2})) + error ("tformfwd: expect vectors as coordinates") + elseif (!all (size (varargin{1}) == size (varargin{2}))) + error ("tformfwd: expect two vectors the same size") + elseif (columns (varargin{1}) != 1) + error ("tformfwd: expect column vectors") + endif + XX = [varargin{1} varargin{2}]; + endif + UU = T.forward_fcn(XX, T); + if (nargin == 3) + varargout{1} = UU(:,1); + varargout{2} = UU(:,2); + else + varargout{1} = UU; + endif +endfunction + + +function out = istform (T) + out = true; + if (! isstruct (T)) + out = false; + else + required = {"ndims_in";"ndims_out"; ... + "forward_fcn"; "inverse_fcn"; ... + "tdata"}; + fields = fieldnames (T); + if (!all (strcmp (fields, required))) + out = false + endif + endif +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/image/inst/tforminv.m Thu Jan 03 14:52:10 2013 +0000 @@ -0,0 +1,72 @@ +## Copyright (C) 2012 Pantxo Diribarne +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} {[@var{UV}] =} tforminv (@var{T}, @var{XY}) +## @deftypefnx {Function File} {[@var{U}, @var{V}] =} tforminvfwd (@var{T}, @var{X}, @var{Y}) +## +## Given to dimensionnal coordinates from one space, returns two +## dimensionnal coordinates in the other space, as defined in +## the transform structure @var{T}. Input and output coordinates +## may be gigen either as a n-by-2 arrays, or as two n-by-1 vectors. +## @seealso{maketform, cp2tform, tformfwd} +## @end deftypefn + +## Author: Pantxo Diribarne <pantxo@dibona> + +function varargout = tforminv (T, varargin) + if (nargin > 3 || nargin < 2) + print_usage (); + elseif (! istform (T)) + error ("tforminv: expect a transform structure as first argument") + elseif (nargin == 2) + XX = varargin{1}; + if (columns (XX) != 2) + error ("tforminv: expect n-by-2 array as second argument") + endif + else + if (!isvector (varargin{1}) || !isvector (varargin{2})) + error ("tforminv: expect vectors as coordinates") + elseif (!all (size (varargin{1}) == size (varargin{2}))) + error ("tforminv: expect two vectors the same size") + elseif (columns (varargin{1}) != 1) + error ("tforminv: expect column vectors") + endif + XX = [varargin{1} varargin{2}]; + endif + UU = T.inverse_fcn(XX, T); + if (nargin == 3) + varargout{1} = UU(:,1); + varargout{2} = UU(:,2); + else + varargout{1} = UU; + endif +endfunction + +function out = istform (T) + out = true; + if (!isstruct (T)) + out = false; + else + required = {"ndims_in";"ndims_out"; ... + "forward_fcn"; "inverse_fcn"; ... + "tdata"}; + fields = fieldnames (T); + if (!all (strcmp (fields, required))) + out = false + endif + endif +endfunction