## Copyright (C) 2015 Motherboard ## ## 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 this program. If not, see . ## Author: Motherboard ## Created: 2015-12-1 ## -*- texinfo -*- ## affine2d takes a transpose of the affine matrix as described in standard literature ## it performs the transformation as follows: ## v = (u*T)(1:2), where u = [x y 1] and T = [a b 0; c d 0; e f 1] ## [a b; c d] is a transposed rotation\shear matrix, [e f] is the translation vector, where e = dx, f = dy. classdef affine2d properties T # Transformation matrix. Dimensionality = 2; end methods ## constructor, if no argment is given, default is the identity matrix. function obj = affine2d(tform) if (nargin == 0) obj.T = eye(3); elseif (ndims(tform) == 2 && size(tform) == [3 3] && tform(:,3) == [0; 0; 1]) ## check tform is a valid affine transform if (det(tform(1:2,1:2)) ~= 0) obj.T = tform; else error("given transform has singular matrix") endif else error("given transform is not affine! should be of the form [a b 0; c d 0; e f 1]") endif endfunction ## invert transform function obj = invert(tform) inv_T = inv(tform.T); inv_T(:,3) = [0; 0; 1]; obj = affine2d (inv_T); endfunction function ans = isRigid(tform) ## check if transform is only rotation or translation S = tform.T(1:2,1:2); ans = abs(S(1,1)*S(2,2)-S(1,2)*S(2,1)-1) < eps; endfunction function ans = isSimilarity(tform) ## check if transform is only homogeneous scaling, rotation, or translation submatrix = tform.T(1:2,1:2); s2 = submatrix*submatrix'; ans = all((abs(s2 - s2(1,1)*eye(2)) < ones(2)*eps)(:)); endfunction function ans = isTranslation(tform) ## check if transform is only a translation submatrix = tform.T(1:2,1:2); ans = all((abs(submatrix - eye(2)) < ones(2)*eps)(:)); endfunction function [limitsX limitsY] = outputLimits(tform, xlims, ylims) ## given a bounding box corner coordinated in xlims and ylims (top left, right bottom) - return the new bounding box after transformation. xlims2 = [xlims(:); xlims(:)]; ylims2 = [ylims(:); ylims(end:-1:1)(:)]; temp = [xlims2 ylims2 ones(4,1)]; ans = temp*tform.T; limitsX = [min(ans(1:4,1)), max(ans(1:4,1))]; limitsY = [min(ans(1:4,2)), max(ans(1:4,2))]; endfunction function [x,y] = transformPointsForward(tform,u,v) ## apply transformation on the set of u, v points (1xn vectors) or on U (2xn matrix) N = size(u,1); if exist("v","var") u = [u(:) v(:)]; elseif size(u) ~= [N 2] error("provided matrix to transform should be 2 by N in size"); endif U = [u ones(N,1)]; x = (U*tform.T)(:,1:2); if isargout(2) y = x(:,2); x = x(:,1); endif endfunction function [x,y] = transformPointsInverse(tform,u,v) ## apply inverse transformation on the set of u, v points (1xn vectors) or on U (2xn matrix) N = size(u,1); if exist("v","var") u = [u(:) v(:)]; elseif size(u) ~= [N 2] error("provided matrix to transform should be 2 by N in size"); endif U = [u ones(N,1)]; x = (U*inv(tform.T))(:,1:2); if isargout(2) y = x(:,2); x = x(:,1); endif endfunction end end %!test %! theta = 10; %! tform = affine2d([cosd(theta) -sind(theta) 0; sind(theta) cosd(theta) 0; 0 0 1]); %! [X,Y] = transformPointsForward(tform,5,10); %! assert(abs([X Y] - [6.6605 8.9798]) < e^-4) %! [U,V] = transformPointsInverse(tform,X,Y); %! assert(abs([U V] - [5 10]) < e^-10) %! assert(isRigid(tform)) %! assert(~isTranslation(tform)) %! assert(isSimilarity(tform)); %!test %! tform = affine2d([1 0 0; 0 1 0; 5 10 1]); %! [X Y] = transformPointsForward(tform,[1 2; 3 4; 5 6; 7 8]); %! assert(round(X) == [6;8;10;12]) %! assert(round(Y) == [12;14;16;18]) %! [U,V] = transformPointsInverse(tform,X,Y); %! assert(round(U) == [1;3;5;7]) %! assert(round(V) == [2;4;6;8]) %! assert(isRigid(tform)) %! assert(isTranslation(tform)) %! assert(isSimilarity(tform)); %!test %! tform = affine2d([1 1e-16 0; 1e-16 1 0; 5 10 1]); %! assert(isRigid(tform)) %! tform = affine2d([2 1e-16 0; 1e-16 1 0; 5 10 1]); %! assert(~isRigid(tform)) %!test %! theta = 10; %! tform = affine2d([cosd(theta) -sind(theta) 0; sind(theta) cosd(theta) 0; 0 0 1]); %! [xlim ylim] = outputLimits(tform,[1 240],[1 291]); %! assert(abs(xlim - [1.1585 286.8855]) < [1 1]*e^-4) %! assert(abs(ylim - [-40.6908 286.4054]) < [1 1]*e^-4) %!test %!error affine2d ([0 0 0; 0 0 0]) %!error affine2d ([0 0 0 0 0 0 0 0 1]) %!error affine2d ([0 0 0; 0 0 0; 0 0 0]) %!error affine2d ([0 0 0; 0 0 0; 0 0 1])