{"id":125,"date":"2007-03-23T07:00:24","date_gmt":"2007-03-23T11:00:24","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/2007\/03\/23\/pad-values-in-dilation-and-erosion\/"},"modified":"2019-10-23T08:47:52","modified_gmt":"2019-10-23T12:47:52","slug":"pad-values-in-dilation-and-erosion","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2007\/03\/23\/pad-values-in-dilation-and-erosion\/","title":{"rendered":"Pad values in dilation and erosion"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <p>Blog reader DKS <a href=\"https:\/\/blogs.mathworks.com\/steve\/2006\/10\/23\/nonflat-grayscale-dilation-and-erosion\/#comment-6544\">asked recently<\/a> why values outside the image are assumed to be <tt>-Inf<\/tt> when computing dilation.  I thought this issue was worth exploring further because it has practical implications for certain\r\n      computations.\r\n   <\/p>\r\n   <p>Suppose we start with a simple 4-by-4 matrix:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">f = [22 23 15 16; 24 25 14 15; 20 18 17 23; 19 16 15 20]<\/pre><pre style=\"font-style:oblique\">\r\nf =\r\n\r\n    22    23    15    16\r\n    24    25    14    15\r\n    20    18    17    23\r\n    19    16    15    20\r\n\r\n<\/pre><p>Now think about computing the erosion with a 3-by-3 flat structuring element.  What's the output at the upper left corner?\r\n      It's the minimum of these 9 values:\r\n   <\/p><pre>?? ?? ??\r\n?? 22 23\r\n?? 24 25<\/pre><p>So what do we use for the unknown values outside the image boundary? Suppose we zero pad:<\/p><pre> 0  0  0\r\n 0 22 23\r\n 0 24 25<\/pre><p>Then the (1,1) output value is 0. In fact, if the image pixels are nonnegative, which is common, there will be a zero-valued\r\n      one-pixel-wide border all the way around the edge of the output image. This is called a <i>boundary artifact<\/i> and is undesirable.  We could avoid this problem by simply excluding external values in our computation of the minimum. A\r\n      mathematical equivalent for erosion is to assume external values all equal some constant that is guaranteed to be greater than or equal\r\n      to all image pixels - like Inf:\r\n   <\/p><pre>Inf Inf Inf\r\nInf  22  23\r\nInf  24  25<\/pre><p>To avoid boundary artifacts when performing dilation, pad with <tt>-Inf<\/tt>:\r\n   <\/p><pre>-Inf -Inf -Inf\r\n-Inf   22   23\r\n-Inf   24   25<\/pre><p>Most of the time you don't need to explicitly use the <tt>Inf<\/tt> values.  One type of exception occurs when the structuring element doesn't include the center element.  Then sometimes the\r\n      <tt>Inf<\/tt> values can appear in the output.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">imdilate(f, [1 0 0])<\/pre><pre style=\"font-style:oblique\">\r\nans =\r\n\r\n    23    15    16  -Inf\r\n    25    14    15  -Inf\r\n    18    17    23  -Inf\r\n    16    15    20  -Inf\r\n\r\n<\/pre><p>Similarly, most of the time you don't actually need to explicitly pad the image.  The exception is when you are computing\r\n      a sequence of dilations (or erosions).  In the Image Processing Toolbox this frequently occurs because <tt>imdilate<\/tt> and <tt>imerode<\/tt> exploit ''structuring element decomposition.''   That is, a large structuring element is decomposed into two or more smaller\r\n      structuring elements that are mathematically equivalent to the original.\r\n   <\/p>\r\n   <p>Here's a contrived example to demonstrate why you need to pad explicitly to make the mathematical equivalence work out.<\/p>\r\n   <p>Structuring element 1 translates an image to the left by 2 pixels.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">se1 = [1 0 0 0 0];<\/pre><p>Structuring element 2 translates an image to the right by 1 pixel.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">se2 = [0 0 1];<\/pre><p>You'd expect the composition of dilation with these two structuring elements to be equivalent to a single dilation with a\r\n      structuring element that translates an image left by 1 pixel:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">se3 = [1 0 0];<\/pre><p>Let's try that sequence with no padding:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">f1 = imdilate(f, se1)<\/pre><pre style=\"font-style:oblique\">\r\nf1 =\r\n\r\n    15    16  -Inf  -Inf\r\n    14    15  -Inf  -Inf\r\n    17    23  -Inf  -Inf\r\n    15    20  -Inf  -Inf\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">f2 = imdilate(f1, se2)<\/pre><pre style=\"font-style:oblique\">\r\nf2 =\r\n\r\n  -Inf    15    16  -Inf\r\n  -Inf    14    15  -Inf\r\n  -Inf    17    23  -Inf\r\n  -Inf    15    20  -Inf\r\n\r\n<\/pre><p>Now dilate the original image with se3 and compare:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">f3 = imdilate(f, se3)<\/pre><pre style=\"font-style:oblique\">\r\nf3 =\r\n\r\n    23    15    16  -Inf\r\n    25    14    15  -Inf\r\n    18    17    23  -Inf\r\n    16    15    20  -Inf\r\n\r\n<\/pre><p>They aren't the same.  To make the results equivalent, you have to pad the original image, perform the dilation sequence,\r\n      and the crop the result.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">fp = padarray(f, [2 2], -Inf)<\/pre><pre style=\"font-style:oblique\">\r\nfp =\r\n\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n  -Inf  -Inf    22    23    15    16  -Inf  -Inf\r\n  -Inf  -Inf    24    25    14    15  -Inf  -Inf\r\n  -Inf  -Inf    20    18    17    23  -Inf  -Inf\r\n  -Inf  -Inf    19    16    15    20  -Inf  -Inf\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">fp1 = imdilate(fp, se1)<\/pre><pre style=\"font-style:oblique\">\r\nfp1 =\r\n\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n    22    23    15    16  -Inf  -Inf  -Inf  -Inf\r\n    24    25    14    15  -Inf  -Inf  -Inf  -Inf\r\n    20    18    17    23  -Inf  -Inf  -Inf  -Inf\r\n    19    16    15    20  -Inf  -Inf  -Inf  -Inf\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">fp2 = imdilate(fp1, se2)<\/pre><pre style=\"font-style:oblique\">\r\nfp2 =\r\n\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n  -Inf    22    23    15    16  -Inf  -Inf  -Inf\r\n  -Inf    24    25    14    15  -Inf  -Inf  -Inf\r\n  -Inf    20    18    17    23  -Inf  -Inf  -Inf\r\n  -Inf    19    16    15    20  -Inf  -Inf  -Inf\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf  -Inf\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">fp2_cropped = fp2(3:end-2, 3:end-2)<\/pre><pre style=\"font-style:oblique\">\r\nfp2_cropped =\r\n\r\n    23    15    16  -Inf\r\n    25    14    15  -Inf\r\n    18    17    23  -Inf\r\n    16    15    20  -Inf\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">isequal(f3, fp2_cropped)<\/pre><pre style=\"font-style:oblique\">\r\nans =\r\n\r\n     1\r\n\r\n<\/pre><p>Whenever <tt>imdilate<\/tt> or <tt>imerode<\/tt> is called with a decomposed structuring element, the functions compute the minimum padding necessary to avoid boundary artifacts\r\n      and pad with either <tt>-Inf<\/tt> or <tt>Inf<\/tt>, respectively.\r\n   <\/p>\r\n   <p>It's a classic speed vs. memory tradeoff.  Exploiting structuring element decomposition is faster, but storing the intermediate\r\n      padded arrays takes more memory.\r\n   <\/p><script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_34b60c9aaca9459c90759cf1138f42a6() {\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='34b60c9aaca9459c90759cf1138f42a6 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 34b60c9aaca9459c90759cf1138f42a6';\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 2007 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_34b60c9aaca9459c90759cf1138f42a6()\"><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.4<br><\/p>\r\n<\/div>\r\n<!--\r\n34b60c9aaca9459c90759cf1138f42a6 ##### SOURCE BEGIN #####\r\n%% External values in dilation, erosion\r\n% Blog reader DKS \r\n% <https:\/\/blogs.mathworks.com\/steve\/2006\/10\/23\/nonflat-grayscale-dilation-and-erosion\/#comment-6544\r\n% asked recently> why values outside the image are\r\n% assumed to be |-Inf| when computing dilation.  I thought this issue was\r\n% worth exploring further because it has practical implications for certain\r\n% computations.\r\n%\r\n% Suppose we start with a simple 4-by-4 matrix:\r\n\r\nf = [22 23 15 16; 24 25 14 15; 20 18 17 23; 19 16 15 20]\r\n\r\n%%\r\n% Now think about computing the erosion with a 3-by-3 flat structuring \r\n% element.  What's the output at the upper left corner? It's the minimum of\r\n% these 9 values:\r\n%\r\n%  ?? ?? ??\r\n%  ?? 22 23\r\n%  ?? 24 25\r\n%\r\n% So what do we use for the unknown values outside the image boundary?\r\n% Suppose we zero pad:\r\n%\r\n%   0  0  0\r\n%   0 22 23\r\n%   0 24 25\r\n%\r\n% Then the (1,1) output value is 0. In fact, if the image pixels are\r\n% nonnegative, which is common, there will be a zero-valued one-pixel-wide \r\n% border all the way around the edge of the output image. This is called a\r\n% _boundary artifact_ and is undesirable.  We could avoid this problem by\r\n% simply excluding external values in our computation of the minimum.\r\n% A mathematical equivalent is to assume external values all equal some\r\n% constant that is guaranteed to be greater than or equal to all image\r\n% pixels - like Inf:\r\n%\r\n%  Inf Inf Inf\r\n%  Inf  22  23\r\n%  Inf  24  25\r\n%\r\n% To avoid boundary artifacts when performing dilation, pad with |-Inf|:\r\n%\r\n%  -Inf -Inf -Inf\r\n%  -Inf   22   23\r\n%  -Inf   24   25\r\n%\r\n% Most of the time you don't need to explicitly use the |Inf| values.  One \r\n% type of exception occurs when the structuring element doesn't include the\r\n% center element.  Then sometimes the |Inf| values can appear in the\r\n% output.\r\n\r\nimdilate(f, [1 0 0])\r\n\r\n%%\r\n% Similarly, most of the time you don't actually need to explicitly pad the\r\n% image.  The exception is when you are computing a sequence of dilations\r\n% (or erosions).  In the Image Processing Toolbox this frequently occurs\r\n% because |imdilate| and |imerode| exploit ''structuring element\r\n% decomposition.''   That is, a large structuring element is decomposed\r\n% into two or more smaller structuring elements that are mathematically\r\n% equivalent to the original.\r\n%\r\n% Here's a contrived example to demonstrate why you need to pad explicitly\r\n% to make the mathematical equivalence work out.\r\n%\r\n% Structuring element 1 translates an image to the left by 2 pixels.\r\n\r\nse1 = [1 0 0 0 0];\r\n\r\n%%\r\n% Structuring element 2 translates an image to the right by 1 pixel.\r\n\r\nse2 = [0 0 1];\r\n\r\n%%\r\n% You'd expect the composition of dilation with these two structuring\r\n% elements to be equivalent to a single dilation with a structuring element\r\n% that translates an image left by 1 pixel:\r\n\r\nse3 = [1 0 0];\r\n\r\n%%\r\n% Let's try that sequence with no padding:\r\n\r\nf1 = imdilate(f, se1)\r\n\r\n%%\r\n\r\nf2 = imdilate(f1, se2)\r\n\r\n%%\r\n% Now dilate the original image with se3 and compare:\r\n\r\nf3 = imdilate(f, se3)\r\n\r\n%%\r\n% They aren't the same.  To make the results equivalent, you have to pad\r\n% the original image, perform the dilation sequence, and the crop the\r\n% result.\r\n\r\nfp = padarray(f, [2 2], -Inf)\r\n\r\n%%\r\n\r\nfp1 = imdilate(fp, se1)\r\n\r\n%%\r\n\r\nfp2 = imdilate(fp1, se2)\r\n\r\n%%\r\n\r\nfp2_cropped = fp2(3:end-2, 3:end-2)\r\n\r\n%%\r\n\r\nisequal(f3, fp2_cropped)\r\n\r\n%%\r\n% Whenever |imdilate| or |imerode| is called with a decomposed structuring\r\n% element, the functions compute the minimum padding necessary to avoid\r\n% boundary artifacts and pad with either |-Inf| or |Inf|, respectively.\r\n%\r\n% It's a classic speed vs. memory tradeoff.  Exploiting structuring element\r\n% decomposition is faster, but storing the intermediate padded arrays takes\r\n% more memory.\r\n\r\n##### SOURCE END ##### 34b60c9aaca9459c90759cf1138f42a6\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   Blog reader DKS asked recently why values outside the image are assumed to be -Inf when computing dilation.  I thought this issue was worth exploring further because it has practical... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2007\/03\/23\/pad-values-in-dilation-and-erosion\/\">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":[124,346,344],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/125"}],"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=125"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/125\/revisions"}],"predecessor-version":[{"id":3522,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/125\/revisions\/3522"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=125"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=125"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=125"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}