{"id":79,"date":"2006-08-17T10:13:19","date_gmt":"2006-08-17T14:13:19","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=79"},"modified":"2019-10-22T14:45:12","modified_gmt":"2019-10-22T18:45:12","slug":"spatial-transformations-three-dimensional-rotation","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2006\/08\/17\/spatial-transformations-three-dimensional-rotation\/","title":{"rendered":"Spatial transformations: Three-dimensional rotation"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <introduction>\r\n      <p>Blog reader Stephen N., who's been following my posts about spatial transformations, asked me last week how to rotate a three-dimensional\r\n         image.\r\n      <\/p>\r\n      <p>The Image Processing Toolbox function <tt>tformarray<\/tt> is a very general multidimensional spatial transformer and can be used for three-dimensional rotation.  Here's how.\r\n      <\/p>\r\n   <\/introduction>\r\n   <h3>Contents<\/h3>\r\n   <div>\r\n      <ul>\r\n         <li><a href=\"#1\">Make a three-dimensional blob<\/a><\/li>\r\n         <li><a href=\"#3\">Make a 3-D affine tform struct<\/a><\/li>\r\n         <li><a href=\"#10\">What the tformarray inputs mean<\/a><\/li>\r\n         <li><a href=\"#16\">Call tformarray to transform the blob<\/a><\/li>\r\n         <li><a href=\"#17\">Display the rotated blob<\/a><\/li>\r\n      <\/ul>\r\n   <\/div>\r\n   <h3>Make a three-dimensional blob<a name=\"1\"><\/a><\/h3>\r\n   <p>First, let's make a three-dimensional image containing a blob that will easily show the effect of a rotation.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">[x,y,z] = ndgrid(-1:.025:1);\r\nblob = z &lt;= 0 &amp; z &gt;= -0.75 &amp; x.^2 + y.^2 &lt;= sqrt(0.25);\r\nblob = blob | (z &gt; 0 &amp; (abs(x) + abs(y) &lt;= (0.5 - z)));<\/pre><p>Display the blob using isosurface and patch.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">p = patch(isosurface(blob,0.5));\r\nset(p, <span style=\"color: #A020F0\">'FaceColor'<\/span>, <span style=\"color: #A020F0\">'red'<\/span>, <span style=\"color: #A020F0\">'EdgeColor'<\/span>, <span style=\"color: #A020F0\">'none'<\/span>);\r\ndaspect([1 1 1]);\r\nview(3)\r\ncamlight\r\nlighting <span style=\"color: #A020F0\">gouraud<\/span><\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/79\/three_dimensional_rotation_01.png\"> <h3>Make a 3-D affine tform struct<a name=\"3\"><\/a><\/h3>\r\n   <p>We want to rotate the blob about its own center.  For me, the simplest way to construct an affine transform matrix that will\r\n      do that is to use three steps:\r\n   <\/p>\r\n   <p>1. Translate the middle of the blob to the origin.<\/p>\r\n   <p>2. Rotate the blob.<\/p>\r\n   <p>3. Translate the rotated blob back to its starting location.<\/p>\r\n   <p>Here's the first translation:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">blob_center = (size(blob) + 1) \/ 2<\/pre><pre style=\"font-style:oblique\">\r\nblob_center =\r\n\r\n    41    41    41\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">T1 = [1 0 0 0\r\n    0 1 0 0\r\n    0 0 1 0\r\n    -blob_center 1]<\/pre><pre style=\"font-style:oblique\">\r\nT1 =\r\n\r\n     1     0     0     0\r\n     0     1     0     0\r\n     0     0     1     0\r\n   -41   -41   -41     1\r\n\r\n<\/pre><p>Now here's the rotation. In this example we'll rotate about the second dimension.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">theta = pi\/8;\r\nT2 = [cos(theta)  0      -sin(theta)   0\r\n    0             1              0     0\r\n    sin(theta)    0       cos(theta)   0\r\n    0             0              0     1]<\/pre><pre style=\"font-style:oblique\">\r\nT2 =\r\n\r\n    0.9239         0   -0.3827         0\r\n         0    1.0000         0         0\r\n    0.3827         0    0.9239         0\r\n         0         0         0    1.0000\r\n\r\n<\/pre><p>And here's the final translation.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">T3 = [1 0 0 0\r\n    0 1 0 0\r\n    0 0 1 0\r\n    blob_center 1]<\/pre><pre style=\"font-style:oblique\">\r\nT3 =\r\n\r\n     1     0     0     0\r\n     0     1     0     0\r\n     0     0     1     0\r\n    41    41    41     1\r\n\r\n<\/pre><p>The forward mapping is the composition of T1, T2, and T3.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">T = T1 * T2 * T3<\/pre><pre style=\"font-style:oblique\">\r\nT =\r\n\r\n    0.9239         0   -0.3827         0\r\n         0    1.0000         0         0\r\n    0.3827         0    0.9239         0\r\n  -12.5691         0   18.8110    1.0000\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">tform = maketform(<span style=\"color: #A020F0\">'affine'<\/span>, T);<\/pre><p>Let's do a quick sanity check: the tform struct should map the blob center to itself.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">tformfwd(blob_center, tform)<\/pre><pre style=\"font-style:oblique\">\r\nans =\r\n\r\n    41    41    41\r\n\r\n<\/pre><h3>What the tformarray inputs mean<a name=\"10\"><\/a><\/h3>\r\n   <p>Now let's see how to make the inputs to the <tt>tformarray<\/tt> function. The syntax of <tt>tformarray<\/tt> is <tt>B = tformarray(A, T, R, TDIMS_A, TDIMS_B, TSIZE_B, TMAP_B, F)<\/tt>.\r\n   <\/p>\r\n   <p><tt>A<\/tt> is the input array, and <tt>T<\/tt> is the tform struct.\r\n   <\/p>\r\n   <p><tt>R<\/tt> is a resampler struct produced by the <tt>makeresampler<\/tt> function.  You tell <tt>makeresampler<\/tt> the type of interpolation you want, as well as how to handle array boundaries.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">R = makeresampler(<span style=\"color: #A020F0\">'linear'<\/span>, <span style=\"color: #A020F0\">'fill'<\/span>);<\/pre><p><tt>TDIMS_A<\/tt> specifies how the dimensions of the input array correspond to the dimensions of the spatial transformation represented by\r\n      the tform struct.  Here I'll use the simplest form, in which each spatial transformation dimension corresponds to the same\r\n      input array dimension. (Don't worry about the details here.  One of these days I'll write a blog posting showing an example\r\n      of when you might want to do something different with this dimension mapping.)\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">TDIMS_A = [1 2 3];<\/pre><p><tt>TDIMS_B<\/tt> specifies how the dimensions of the output array correspond to the dimensions of the spatial transformation.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">TDIMS_B = [1 2 3];<\/pre><p><tt>TSIZE_B<\/tt> is the size of the output array.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">TSIZE_B = size(blob);<\/pre><p><tt>TMAP_B<\/tt> is unused when you have a tform struct.  Just specify it to be empty.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">TMAP_B = [];<\/pre><p><tt>F<\/tt> specifies the values to use outside the boundaries of the input array.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">F = 0;<\/pre><h3>Call tformarray to transform the blob<a name=\"16\"><\/a><\/h3><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">blob2 = tformarray(blob, tform, R, TDIMS_A, TDIMS_B, TSIZE_B, TMAP_B, F);<\/pre><h3>Display the rotated blob<a name=\"17\"><\/a><\/h3><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">clf\r\np = patch(isosurface(blob2,0.5));\r\nset(p, <span style=\"color: #A020F0\">'FaceColor'<\/span>, <span style=\"color: #A020F0\">'red'<\/span>, <span style=\"color: #A020F0\">'EdgeColor'<\/span>, <span style=\"color: #A020F0\">'none'<\/span>);\r\ndaspect([1 1 1]);\r\nview(3)\r\ncamlight\r\nlighting <span style=\"color: #A020F0\">gouraud<\/span><\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/79\/three_dimensional_rotation_02.png\"> <script language=\"JavaScript\"> \r\n<!--\r\n    function grabCode_79() {\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='79 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 79';\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_79()\"><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\n79 ##### SOURCE BEGIN #####\r\n%% Spatial transformations: Three-dimensional rotation\r\n% Blog reader Stephen N., who's been following my posts about spatial \r\n% transformations, asked me last week how to rotate a three-dimensional\r\n% image.\r\n%\r\n% The Image Processing Toolbox function \r\n% <https:\/\/www.mathworks.com\/help\/images\/index.htmltformarray.html \r\n% |tformarray|> is a very general multidimensional spatial transformer and \r\n% can be used for three-dimensional rotation.  Here's how.\r\n\r\n%% Make a three-dimensional blob\r\n% First, let's make a three-dimensional image containing a blob that will\r\n% easily show the effect of a rotation.\r\n\r\n[x,y,z] = ndgrid(-1:.025:1);\r\nblob = z <= 0 & z >= -0.75 & x.^2 + y.^2 <= sqrt(0.25);\r\nblob = blob | (z > 0 & (abs(x) + abs(y) <= (0.5 - z)));\r\n\r\n%%\r\n% Display the blob using isosurface and patch.\r\n\r\np = patch(isosurface(blob,0.5));\r\nset(p, 'FaceColor', 'red', 'EdgeColor', 'none');\r\ndaspect([1 1 1]);\r\nview(3)\r\ncamlight\r\nlighting gouraud\r\n\r\n%% Make a 3-D affine tform struct\r\n% We want to rotate the blob about its own center.  For me, the simplest\r\n% way to construct an affine transform matrix that will do that is to use\r\n% three steps:\r\n%\r\n% 1. Translate the middle of the blob to the origin.\r\n%\r\n% 2. Rotate the blob.\r\n%\r\n% 3. Translate the rotated blob back to its starting location.\r\n%\r\n% Here's the first translation:\r\n\r\nblob_center = (size(blob) + 1) \/ 2\r\n\r\n%%\r\n\r\nT1 = [1 0 0 0\r\n    0 1 0 0\r\n    0 0 1 0\r\n    -blob_center 1]\r\n\r\n%%\r\n% Now here's the rotation. In this example we'll rotate about the second\r\n% dimension.\r\n\r\ntheta = pi\/8;\r\nT2 = [cos(theta)  0      -sin(theta)   0\r\n    0             1              0     0\r\n    sin(theta)    0       cos(theta)   0\r\n    0             0              0     1]\r\n\r\n%%\r\n% And here's the final translation.\r\n\r\nT3 = [1 0 0 0\r\n    0 1 0 0\r\n    0 0 1 0\r\n    blob_center 1]\r\n\r\n%%\r\n% The forward mapping is the composition of T1, T2, and T3.\r\n\r\nT = T1 * T2 * T3\r\n\r\n%%\r\n\r\ntform = maketform('affine', T);\r\n\r\n%%\r\n% Let's do a quick sanity check: the tform struct should map the blob \r\n% center to itself.\r\n\r\ntformfwd(blob_center, tform)\r\n\r\n%% What the tformarray inputs mean\r\n% Now let's see how to make the inputs to the |tformarray| function. The \r\n% syntax of |tformarray| is |B = tformarray(A, T, R, TDIMS_A, TDIMS_B,\r\n% TSIZE_B, TMAP_B, F)|.\r\n%\r\n% |A| is the input array, and |T| is the tform struct.\r\n%\r\n% |R| is a resampler struct produced by the |makeresampler| function.  You\r\n% tell |makeresampler| the type of interpolation you want, as well as how\r\n% to handle array boundaries.\r\n\r\nR = makeresampler('linear', 'fill');\r\n\r\n%%\r\n% |TDIMS_A| specifies how the dimensions of the input array correspond to\r\n% the dimensions of the spatial transformation represented by the tform\r\n% struct.  Here I'll use the simplest form, in which each spatial\r\n% transformation dimension corresponds to the same input array dimension.\r\n% (Don't worry about the details here.  One of these days I'll write a blog\r\n% posting showing an example of when you might want to do something\r\n% different with this dimension mapping.)\r\n\r\nTDIMS_A = [1 2 3];\r\n\r\n%%\r\n% |TDIMS_B| specifies how the dimensions of the output array correspond to\r\n% the dimensions of the spatial transformation.\r\n\r\nTDIMS_B = [1 2 3];\r\n\r\n%%\r\n% |TSIZE_B| is the size of the output array.\r\n\r\nTSIZE_B = size(blob);\r\n\r\n%%\r\n% |TMAP_B| is unused when you have a tform struct.  Just specify it to be\r\n% empty.\r\n\r\nTMAP_B = [];\r\n\r\n%%\r\n% |F| specifies the values to use outside the boundaries of the input\r\n% array.\r\n\r\nF = 0;\r\n\r\n%% Call tformarray to transform the blob\r\n\r\nblob2 = tformarray(blob, tform, R, TDIMS_A, TDIMS_B, TSIZE_B, TMAP_B, F);\r\n\r\n%% Display the rotated blob\r\n\r\nclf\r\np = patch(isosurface(blob2,0.5));\r\nset(p, 'FaceColor', 'red', 'EdgeColor', 'none');\r\ndaspect([1 1 1]);\r\nview(3)\r\ncamlight\r\nlighting gouraud\r\n\r\n\r\n##### SOURCE END ##### 79\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   \r\n      Blog reader Stephen N., who's been following my posts about spatial transformations, asked me last week how to rotate a three-dimensional\r\n         image.\r\n      \r\n    ... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2006\/08\/17\/spatial-transformations-three-dimensional-rotation\/\">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":[208,218,74,214,212,220,222,62,206,210,34,190,194,64,216],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/79"}],"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=79"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/79\/revisions"}],"predecessor-version":[{"id":2201,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/79\/revisions\/2201"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=79"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=79"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=79"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}