{"id":239,"date":"2008-11-26T20:34:52","date_gmt":"2008-11-27T01:34:52","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/2008\/11\/26\/a-structuring-element-decomposition-gotcha\/"},"modified":"2019-10-28T09:47:46","modified_gmt":"2019-10-28T13:47:46","slug":"a-structuring-element-decomposition-gotcha","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2008\/11\/26\/a-structuring-element-decomposition-gotcha\/","title":{"rendered":"A structuring element decomposition &#8220;gotcha&#8221;"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <p>In my earlier posts about <a href=\"https:\/\/blogs.mathworks.com\/steve\/category\/dilation-algorithms\/\">dilation and erosion algorithms<\/a>, I wrote about exploiting structuring element decomposition for improving speed.  Today I want to discuss a potential decomposition\r\n      <a href=\"http:\/\/catb.org\/jargon\/html\/G\/gotcha.html\">gotcha<\/a>: If you don't implement decomposed dilation and erosion carefully, you can easily end up with wrong answers near the image\r\n      boundaries.\r\n   <\/p>\r\n   <p>Here's an example to illustrate what can go wrong, starting with a single binary image containing only one foreground pixel;<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">A = false(5,5);\r\nA(5,3) = true<\/pre><pre style=\"font-style:oblique\">\r\nA =\r\n\r\n     0     0     0     0     0\r\n     0     0     0     0     0\r\n     0     0     0     0     0\r\n     0     0     0     0     0\r\n     0     0     1     0     0\r\n\r\n<\/pre><p>Here's a structuring element:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">b = [1 0 0; 1 1 0; 1 1 1; 0 1 1; 0 0 1]<\/pre><pre style=\"font-style:oblique\">\r\nb =\r\n\r\n     1     0     0\r\n     1     1     0\r\n     1     1     1\r\n     0     1     1\r\n     0     0     1\r\n\r\n<\/pre><p>Compute the dilation of <tt>A<\/tt> by <tt>b<\/tt> using <tt>imdilate<\/tt>:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">A_b = imdilate(A, b)<\/pre><pre style=\"font-style:oblique\">\r\nA_b =\r\n\r\n     0     0     0     0     0\r\n     0     0     0     0     0\r\n     0     1     0     0     0\r\n     0     1     1     0     0\r\n     0     1     1     1     0\r\n\r\n<\/pre><p>The structuring element <tt>b<\/tt> can be decomposed into two smaller structuring elements, <tt>b1<\/tt> and <tt>b2<\/tt>.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">b1 = [1; 1; 1]<\/pre><pre style=\"font-style:oblique\">\r\nb1 =\r\n\r\n     1\r\n     1\r\n     1\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">b2 = [1 0 0; 0 1 0; 0 0 1]<\/pre><pre style=\"font-style:oblique\">\r\nb2 =\r\n\r\n     1     0     0\r\n     0     1     0\r\n     0     0     1\r\n\r\n<\/pre><p>Morphology theory (actually, just the distributive property you learned back in high school) suggests that we should be able\r\n      to dilate <tt>A<\/tt> by <tt>b1<\/tt>, and then dilate the result by <tt>b2<\/tt>, to get the same result as dilating <tt>A<\/tt> by <tt>b<\/tt>. Let's try that.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">A_b1_b2 = imdilate(imdilate(A, b1), b2)<\/pre><pre style=\"font-style:oblique\">\r\nA_b1_b2 =\r\n\r\n     0     0     0     0     0\r\n     0     0     0     0     0\r\n     0     1     0     0     0\r\n     0     1     1     0     0\r\n     0     0     1     1     0\r\n\r\n<\/pre><p>Uh oh.  Compare <tt>A_b<\/tt> and <tt>A_b1_b2<\/tt> carefully to see they are not the same. So where did we go wrong?  Roughly speaking, we failed to take into consideration\r\n      the effect of dilation <i>outside the image boundaries<\/i>.  If you take the entire image plane into account, dilation by <tt>b1<\/tt> affects pixels outside the original image boundaries.  The out-of-bounds pixels can then affect in-bounds results when we\r\n      dilate by <tt>b2<\/tt>.  But in the call <tt>imdilate(imdilate(A, b1), b2)<\/tt> above, each call to <tt>imdilate<\/tt> returns a result that's only the size of the input image; affected out-of-bounds pixels get cropped out.\r\n   <\/p>\r\n   <p>So let's try that again, but this time we'll pre-pad the input image with 0s.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">A_p = padarray(A, [1 1], 0, <span style=\"color: #A020F0\">'both'<\/span>)<\/pre><pre style=\"font-style:oblique\">\r\nA_p =\r\n\r\n     0     0     0     0     0     0     0\r\n     0     0     0     0     0     0     0\r\n     0     0     0     0     0     0     0\r\n     0     0     0     0     0     0     0\r\n     0     0     0     0     0     0     0\r\n     0     0     0     1     0     0     0\r\n     0     0     0     0     0     0     0\r\n\r\n<\/pre><p>Now dilate with <tt>b1<\/tt> and <tt>b2<\/tt> in succession:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">A_p_b1_b2 = imdilate(imdilate(A_p, b1), b2)<\/pre><pre style=\"font-style:oblique\">\r\nA_p_b1_b2 =\r\n\r\n     0     0     0     0     0     0     0\r\n     0     0     0     0     0     0     0\r\n     0     0     0     0     0     0     0\r\n     0     0     1     0     0     0     0\r\n     0     0     1     1     0     0     0\r\n     0     0     1     1     1     0     0\r\n     0     0     0     1     1     0     0\r\n\r\n<\/pre><p>And trim away the padded rows and columns:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">A_p_b1_b2_cropped = A_p_b1_b2(2:end-1, 2:end-1)<\/pre><pre style=\"font-style:oblique\">\r\nA_p_b1_b2_cropped =\r\n\r\n     0     0     0     0     0\r\n     0     0     0     0     0\r\n     0     1     0     0     0\r\n     0     1     1     0     0\r\n     0     1     1     1     0\r\n\r\n<\/pre><p>Success! That result equals <tt>A_b<\/tt>.\r\n   <\/p>\r\n   <p>That means we have a trade-off to make.  We can get significant speed improvements using structural element decomposition,\r\n      but we can guarantee correct results at the image boundaries only if we use extra memory to make a padded copy of the input\r\n      image. That's what <tt>imdilate<\/tt> and <tt>imerode<\/tt> do.\r\n   <\/p>\r\n   <p>I have two more brief topics in mind for this series on dilation and erosion algorithms.  I'll try to wrap things up in December.<\/p><script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_1303531a8ef146e69fd1aa6e0504d7db() {\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='1303531a8ef146e69fd1aa6e0504d7db ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 1303531a8ef146e69fd1aa6e0504d7db';\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 2008 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-->\r\n<\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_1303531a8ef146e69fd1aa6e0504d7db()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n            the MATLAB code \r\n            <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; 7.7<br><\/p>\r\n<\/div>\r\n<!--\r\n1303531a8ef146e69fd1aa6e0504d7db ##### SOURCE BEGIN #####\r\n%%\r\n% In my earlier posts about \r\n% <https:\/\/blogs.mathworks.com\/steve\/category\/dilation-algorithms\/ \r\n% dilation and erosion algorithms>, I wrote about\r\n% exploiting structuring element decomposition for improving speed.  Today I\r\n% want to discuss a potential decomposition \r\n% <http:\/\/catb.org\/jargon\/html\/G\/gotcha.html gotcha>:  \r\n% If you don't implement decomposed dilation and erosion carefully, you can\r\n% easily end up with wrong answers near the image boundaries.\r\n%\r\n% Here's an example to illustrate what can go wrong, starting with a single\r\n% binary image containing only one foreground pixel;\r\n\r\nA = false(5,5);\r\nA(5,3) = true\r\n\r\n%%\r\n% Here's a structuring element:\r\n\r\nb = [1 0 0; 1 1 0; 1 1 1; 0 1 1; 0 0 1]\r\n\r\n%%\r\n% Compute the dilation of |A| by |b| using |imdilate|:\r\n\r\nA_b = imdilate(A, b)\r\n\r\n%%\r\n% The structuring element |b| can be decomposed into two smaller structuring\r\n% elements, |b1| and |b2|.\r\n\r\nb1 = [1; 1; 1]\r\n\r\n%%\r\n\r\nb2 = [1 0 0; 0 1 0; 0 0 1]\r\n\r\n%%\r\n% Morphology theory (actually, just the distributive property you learned back\r\n% in high school) suggests that we should be able to dilate |A| by |b1|, and \r\n% then dilate the result by |b2|, to get the same result as dilating |A| by |b|.\r\n% Let's try that.\r\n\r\nA_b1_b2 = imdilate(imdilate(A, b1), b2)\r\n\r\n%%\r\n% Uh oh.  Compare |A_b| and |A_b1_b2| carefully to see they are not the same. So\r\n% where did we go wrong?  Roughly speaking, we failed to take into consideration\r\n% the effect of dilation _outside the image boundaries_.  If you take the entire\r\n% image plane into account, dilation by |b1| affects pixels outside the original\r\n% image boundaries.  The out-of-bounds pixels can then affect in-bounds results\r\n% when we dilate by |b2|.  But in the call |imdilate(imdilate(A, b1), b2)|\r\n% above, each call to |imdilate| returns a result that's only the size of the\r\n% input image; affected out-of-bounds pixels get cropped out.\r\n%\r\n% So let's try that again, but this time we'll pre-pad the input image with 0s.\r\n\r\nA_p = padarray(A, [1 1], 0, 'both')\r\n\r\n%%\r\n% Now dilate with |b1| and |b2| in succession:\r\n\r\nA_p_b1_b2 = imdilate(imdilate(A_p, b1), b2)\r\n\r\n%%\r\n% And trim away the padded rows and columns:\r\n\r\nA_p_b1_b2_cropped = A_p_b1_b2(2:end-1, 2:end-1)\r\n\r\n%%\r\n% Success! That result equals |A_b|.\r\n%\r\n% That means we have a trade-off to make.  We can get significant speed\r\n% improvements using structural element decomposition, but we can guarantee\r\n% correct results at the image boundaries only if we use extra memory to make a\r\n% padded copy of the input image. That's what |imdilate| and |imerode| do.\r\n%\r\n% I have two more brief topics in mind for this series on dilation and erosion\r\n% algorithms.  I'll try to wrap things up in December.\r\n\r\n##### SOURCE END ##### 1303531a8ef146e69fd1aa6e0504d7db\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   In my earlier posts about dilation and erosion algorithms, I wrote about exploiting structuring element decomposition for improving speed.  Today I want to discuss a potential decomposition\r\n   ... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2008\/11\/26\/a-structuring-element-decomposition-gotcha\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[18],"tags":[102,124,344,100],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/239"}],"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=239"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/239\/revisions"}],"predecessor-version":[{"id":3610,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/239\/revisions\/3610"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=239"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}