{"id":75,"date":"2006-08-04T07:00:43","date_gmt":"2006-08-04T11:00:43","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=75"},"modified":"2019-10-22T14:03:52","modified_gmt":"2019-10-22T18:03:52","slug":"spatial-transformations-defining-and-applying-custom-transforms","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2006\/08\/04\/spatial-transformations-defining-and-applying-custom-transforms\/","title":{"rendered":"Spatial transformations: Defining and applying custom transforms"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <p>Blog reader David A. asked me a while back about how to transform an image based on some mathematical function.  For example,\r\n      the online paper \"Visualizing complex analytic functions using domain coloring,\" by Hans Lundmark, has an example of defining a spatial transformation by assuming that the input and output spaces are complex\r\n      planes, and that the inverse mapping is given by:\r\n   <\/p>\r\n   <p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/74\/custom_tforms_eq7703.png\"> <\/p>\r\n   <p>Check out Figure 3 in the paper to see what this does to an image.<\/p>\r\n   <p>You can use <tt>maketform<\/tt> to create a custom spatial transformation by supplying your own function handle to perform the inverse mapping.  Your function\r\n      handle has to take two input arguments.  The first input argument is a P-by-ndims matrix of points, one per row. (<tt>imtransform<\/tt> applies two-dimensional spatial transformations, so I'll be using P-by-2 matrices here.)  The second argument, called <tt>tdata<\/tt>, can be used to pass auxiliary information to your function handle.  My examples will just ignore this second argument.\r\n   <\/p>\r\n   <p>Let's start with a very simple example just to illustrate the mechanics. Make an inverse mapping that just swaps the horizontal\r\n      and vertical coordinates:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #228B22\">% inverse mapping function<\/span>\r\nf = @(x, unused) fliplr(x);\r\n\r\n<span style=\"color: #228B22\">% maketform arguments<\/span>\r\nndims_in = 2;\r\nndims_out = 2;\r\nforward_mapping = [];\r\ninverse_mapping = f;\r\ntdata = [];\r\ntform = maketform(<span style=\"color: #A020F0\">'custom'<\/span>, ndims_in, ndims_out, <span style=\"color: #0000FF\">...<\/span>\r\n    forward_mapping, inverse_mapping, tdata);\r\n\r\nbody = imread(<span style=\"color: #A020F0\">'liftingbody.png'<\/span>);\r\nbody2 = imtransform(body, tform);\r\n\r\nsubplot(1,2,1)\r\nimshow(body)\r\nsubplot(1,2,2)\r\nimshow(body2)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/74\/custom_tforms_01.jpg\"> <p>As we might have guessed, this custom transform just transposes the image.<\/p>\r\n   <p>For my other examples I'll use a picture of someone I know well:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">face = imread(<span style=\"color: #A020F0\">'https:\/\/blogs.mathworks.com\/images\/steve\/74\/face.jpg'<\/span>);\r\nclf\r\nimshow(face)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/74\/custom_tforms_02.jpg\"> <p>There is a fun little book called <a href=\"http:\/\/www.amazon.com\/gp\/product\/0130744107\/sr=8-1\/qid=1154044914\/ref=sr_1_1\/104-1319363-5457505?ie=UTF8\"><i>Beyond Photography: The Digital Darkroom<\/i><\/a>, by Gerard Holzmann.  This book has lots examples of spatial transformations based on simple mathematical expressions.  Here's\r\n      one that uses the square root of the polar radial component.\r\n   <\/p>\r\n   <p>Note that the call to imtransform below sets up the input image to be located in the square from -1 to 1 in both directions.\r\n       The output image grid is set up to be the same square.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">r = @(x) sqrt(x(:,1).^2 + x(:,2).^2);\r\nw = @(x) atan2(x(:,2), x(:,1));\r\nf = @(x) [sqrt(r(x)) .* cos(w(x)), sqrt(r(x)) .* sin(w(x))];\r\ng = @(x, unused) f(x);\r\n\r\ntform2 = maketform(<span style=\"color: #A020F0\">'custom'<\/span>, 2, 2, [], g, []);\r\nface2 = imtransform(face, tform2, <span style=\"color: #A020F0\">'UData'<\/span>, [-1 1], <span style=\"color: #A020F0\">'VData'<\/span>, [-1 1], <span style=\"color: #0000FF\">...<\/span>\r\n    <span style=\"color: #A020F0\">'XData'<\/span>, [-1 1], <span style=\"color: #A020F0\">'YData'<\/span>, [-1 1]);\r\nimshow(face2)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/74\/custom_tforms_03.jpg\"> <p>This example uses the square of polar radial component.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">f = @(x) [r(x).^2 .* cos(w(x)), r(x).^2 .* sin(w(x))];\r\ng = @(x, unused) f(x);\r\n\r\ntform3 = maketform(<span style=\"color: #A020F0\">'custom'<\/span>, 2, 2, [], g, []);\r\nface3 = imtransform(face, tform3, <span style=\"color: #A020F0\">'UData'<\/span>, [-1 1], <span style=\"color: #A020F0\">'VData'<\/span>, [-1 1], <span style=\"color: #0000FF\">...<\/span>\r\n    <span style=\"color: #A020F0\">'XData'<\/span>, [-1 1], <span style=\"color: #A020F0\">'YData'<\/span>, [-1 1]);\r\nimshow(face3)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/74\/custom_tforms_04.jpg\"> <p>Finally, let's try the complex-plane function used in Lundmark's article. I'll construct the inverse mapping function in several\r\n      steps: First, convert output-space Cartesian coordinates to complex values; square the complex values; and then produce new\r\n      input-space Cartesian coordinates from the squared complex values.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">f = @(x) complex(x(:,1), x(:,2));\r\ng = @(z) z.^2;\r\nh = @(w) [real(w), imag(w)];\r\nq = @(x, unused) h(g(f(x)));\r\n\r\ntform4 = maketform(<span style=\"color: #A020F0\">'custom'<\/span>, 2, 2, [], q, []);\r\nface4 = imtransform(face, tform4, <span style=\"color: #A020F0\">'UData'<\/span>, [-1 1], <span style=\"color: #A020F0\">'VData'<\/span>, [-1 1], <span style=\"color: #0000FF\">...<\/span>\r\n    <span style=\"color: #A020F0\">'XData'<\/span>, [-1 1], <span style=\"color: #A020F0\">'YData'<\/span>, [-1 1]);\r\nimshow(face4)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/74\/custom_tforms_05.jpg\"> <p>If you want see to more examples of this kind of mathematical image fun, take a look at \"Exploring a Conformal Mapping\" in the Image Processing Toolbox product examples area.\r\n   <\/p>\r\n <script language=\"JavaScript\"> \r\n<!--\r\n    function grabCode_75() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='75 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 75';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        author = 'Steve Eddins';\r\n        copyright = 'Copyright 2006 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add author and copyright lines at the bottom if specified.\r\n        if ((author.length > 0) || (copyright.length > 0)) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (author.length > 0) {\r\n                d.writeln('% _' + author + '_');\r\n            }\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n      \r\n      d.title = title + ' (MATLAB code)';\r\n      d.close();\r\n      }   \r\n-->\r\n      <\/script>\r\n<noscript>\r\n<em>A JavaScript-enabled browser is required to use the \"Get the MATLAB code\" link.<\/em>\r\n<\/noscript>\r\n<p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_75()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n            the MATLAB code<\/span><\/a><br><br>\r\n      Published with MATLAB&reg; 7.2<br><\/p>\r\n<\/div>\r\n<!--\r\n75 ##### SOURCE BEGIN #####\r\n%% Spatial transformations: Defining and applying custom transforms\r\n%\r\n% Blog reader David A. asked me a while back about how to transform an\r\n% image based on some mathematical function.  For example, the online paper\r\n% <http:\/\/www.mai.liu.se\/~halun\/complex\/domain_coloring-unicode.html \r\n% \"Visualizing complex analytic functions using domain coloring,\"> by Hans\r\n% Lundmark, has an example of defining a spatial transformation by assuming\r\n% that the input and output spaces are complex planes, and that the inverse\r\n% mapping is given by:\r\n%\r\n% $$w = T^{-1}\\{z\\} = z^2$$\r\n% \r\n% Check out Figure 3 in the paper to see what this does to an image.\r\n%\r\n% You can use \r\n% <https:\/\/www.mathworks.com\/help\/images\/index.htmlmaketform.html \r\n% |maketform|> to create a custom spatial transformation by\r\n% supplying your own function handle to perform the inverse mapping.  Your\r\n% function handle has to take two input arguments.  The first input\r\n% argument is a P-by-ndims matrix of points, one per row.  \r\n% (<https:\/\/www.mathworks.com\/help\/images\/index.htmlimtransform.html \r\n% |imtransform|>\r\n% applies two-dimensional spatial transformations, so I'll be using P-by-2\r\n% matrices here.  The second argument, called |tdata|, can be used to pass\r\n% auxiliary information to your function handle.  My examples will just\r\n% ignore this second argument.\r\n%\r\n% Let's start with a very simple example just to illustrate the mechanics.\r\n% Make an inverse mapping that just swaps the horizontal and vertical\r\n% coordinates.\r\n\r\n% inverse mapping function\r\nf = @(x, unused) fliplr(x);\r\n\r\n% maketform arguments\r\nndims_in = 2;\r\nndims_out = 2;\r\nforward_mapping = [];\r\ninverse_mapping = f;\r\ntdata = [];\r\ntform = maketform('custom', ndims_in, ndims_out, ...\r\n    forward_mapping, inverse_mapping, tdata);\r\n\r\nbody = imread('liftingbody.png');\r\nbody2 = imtransform(body, tform);\r\n\r\nsubplot(1,2,1)\r\nimshow(body)\r\nsubplot(1,2,2)\r\nimshow(body2)\r\n\r\n%%\r\n% As we might have guessed, this custom transform just transposes the \r\n% image.\r\n%\r\n% For my other examples I'll use a picture of someone I know well:\r\n\r\nface = imread('https:\/\/blogs.mathworks.com\/images\/steve\/74\/face.jpg');\r\nclf\r\nimshow(face)\r\n\r\n%%\r\n% There is a fun little book called \r\n% <http:\/\/www.amazon.com\/gp\/product\/0130744107\/sr=8-1\/qid=1154044914\/ref=sr_1_1\/104-1319363-5457505?ie=UTF8\r\n% _Beyond Photography: The Digital\r\n% Darkroom_>, by Gerard Holzmann.  This book has lots examples of spatial\r\n% transformations based on simple mathematical expressions.  Here's one\r\n% that uses the square root of the polar radial component.\r\n%\r\n% Note that the call to imtransform below sets up the input image to be\r\n% located in the square from -1 to 1 in both directions.  The output image\r\n% grid is set up to be the same square.\r\n\r\nr = @(x) sqrt(x(:,1).^2 + x(:,2).^2);\r\nw = @(x) atan2(x(:,2), x(:,1));\r\nf = @(x) [sqrt(r(x)) .* cos(w(x)), sqrt(r(x)) .* sin(w(x))];\r\ng = @(x, unused) f(x);\r\n\r\ntform2 = maketform('custom', 2, 2, [], g, []);\r\nface2 = imtransform(face, tform2, 'UData', [-1 1], 'VData', [-1 1], ...\r\n    'XData', [-1 1], 'YData', [-1 1]);\r\nimshow(face2)\r\n\r\n%%\r\n% This example uses the square of polar radial component.\r\n\r\nf = @(x) [r(x).^2 .* cos(w(x)), r(x).^2 .* sin(w(x))];\r\ng = @(x, unused) f(x);\r\n\r\ntform3 = maketform('custom', 2, 2, [], g, []);\r\nface3 = imtransform(face, tform3, 'UData', [-1 1], 'VData', [-1 1], ...\r\n    'XData', [-1 1], 'YData', [-1 1]);\r\nimshow(face3)\r\n\r\n%%\r\n% Finally, let's try the complex-plane function used in Lundmark's article.\r\n% I'll construct the inverse mapping function in several steps: First,\r\n% convert output-space Cartesian coordinates to complex values; square the\r\n% complex values; and then produce new input-space Cartesian coordinates\r\n% from the squared complex values.\r\n\r\nf = @(x) complex(x(:,1), x(:,2));\r\ng = @(z) z.^2;\r\nh = @(w) [real(w), imag(w)];\r\nq = @(x, unused) h(g(f(x)));\r\n\r\ntform4 = maketform('custom', 2, 2, [], q, []);\r\nface4 = imtransform(face, tform4, 'UData', [-1 1], 'VData', [-1 1], ...\r\n    'XData', [-1 1], 'YData', [-1 1]);\r\nimshow(face4)\r\n\r\n%%\r\n% If you want see to more examples of this kind of mathematical image fun, take a\r\n% look at <https:\/\/www.mathworks.com\/products.htmldemos\/image\/conformal_mapping\/tform4.html \r\n% \"Exploring a Conformal Mapping\"> in the \r\n% <https:\/\/www.mathworks.com\/products\/image\/demos.html Image Processing Toolbox\r\n% product demos area>.\r\n##### SOURCE END ##### 75\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   Blog reader David A. asked me a while back about how to transform an image based on some mathematical function.  For example,\r\n      the online paper \"Visualizing complex analytic functions... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2006\/08\/04\/spatial-transformations-defining-and-applying-custom-transforms\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[10],"tags":[196,178,198,74,192,202,76,36,44,62,200,194,72],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/75"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/comments?post=75"}],"version-history":[{"count":3,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/75\/revisions"}],"predecessor-version":[{"id":2392,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/75\/revisions\/2392"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=75"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=75"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=75"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}