{"id":397,"date":"2011-12-02T13:00:55","date_gmt":"2011-12-02T18:00:55","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=397"},"modified":"2019-10-29T17:01:01","modified_gmt":"2019-10-29T21:01:01","slug":"exploring-shortest-paths-part-3","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2011\/12\/02\/exploring-shortest-paths-part-3\/","title":{"rendered":"Exploring shortest paths &#8211; part 3"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <p>In <a href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/11\/26\/exploring-shortest-paths-part-2\/\">part 2 of <i>Exploring shortest paths<\/i><\/a>, I noted a problem with using the 'quasi-euclidean' distance transform to find the shortest paths between two objects in\r\n      a binary image. Specifically, our algorithm resulted in a big, unfilled gap between the two objects.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">bw = logical([ <span style=\"color: #0000FF\">...<\/span>\r\n    0 0 0 0 0 0 0\r\n    0 1 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 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 1 0\r\n    0 0 0 0 0 0 0  ]);\r\nL = bwlabel(bw);\r\nbw1 = (L == 1);\r\nbw2 = (L == 2);\r\n\r\nD1 = bwdist(bw1, <span style=\"color: #A020F0\">'quasi-euclidean'<\/span>);\r\nD2 = bwdist(bw2, <span style=\"color: #A020F0\">'quasi-euclidean'<\/span>);\r\nD = D1 + D2;\r\n\r\npaths = imregionalmin(D);\r\nP = false(size(bw));\r\nP = imoverlay(P, paths, [.5 .5 .5]);\r\nP = imoverlay(P, bw, [1 1 1]);\r\nimshow(P, <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, <span style=\"color: #A020F0\">'fit'<\/span>)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_c_01.png\"> <p>The pixels marked in gray should be the set of pixels that lie along a shortest path from the two objects in the original\r\n      image. You can plainly, see, however, that there's a large gap.\r\n   <\/p>\r\n   <p>To see why, let's examine more closely the values of <tt>D<\/tt>.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D<\/pre><pre style=\"font-style:oblique\">\r\nD =\r\n\r\n   12.4853   11.6569   11.6569   12.2426   12.8284   13.4142   14.8284\r\n   11.0711    9.6569   10.2426   10.8284   11.4142   12.0000   13.4142\r\n   10.4853    9.6569    9.6569   10.2426   10.8284   11.4142   12.8284\r\n   10.4853    9.6569    9.6569    9.6569   10.2426   10.8284   12.2426\r\n   10.4853    9.6569    9.6569    9.6569    9.6569   10.2426   11.6569\r\n   11.0711    9.6569    9.6569    9.6569    9.6569    9.6569   11.0711\r\n   11.6569   10.2426    9.6569    9.6569    9.6569    9.6569   10.4853\r\n   12.2426   10.8284   10.2426    9.6569    9.6569    9.6569   10.4853\r\n   12.8284   11.4142   10.8284   10.2426    9.6569    9.6569   10.4853\r\n   13.4142   12.0000   11.4142   10.8284   10.2426    9.6569   11.0711\r\n   14.8284   13.4142   12.8284   12.2426   11.6569   11.6569   12.4853\r\n\r\n<\/pre><p>There appears to be an unbroken set of pixels between the two objects with value 9.6569. Actually, it turns out that the pixels\r\n      do not all have the same value. For example, <tt>D(3,3)<\/tt> appear to have the same value <tt>D(4,4)<\/tt> but are actually slightly different.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D(3,3)<\/pre><pre style=\"font-style:oblique\">\r\nans =\r\n\r\n    9.6569\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D(4,4)<\/pre><pre style=\"font-style:oblique\">\r\nans =\r\n\r\n    9.6569\r\n\r\n<\/pre><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D(3,3) - D(4,4)<\/pre><pre style=\"font-style:oblique\">\r\nans =\r\n\r\n -9.5367e-007\r\n\r\n<\/pre><p>The difference between the two is the corresponding single-precision relative floating-point precision:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">eps(D(3,3))<\/pre><pre style=\"font-style:oblique\">\r\nans =\r\n\r\n  9.5367e-007\r\n\r\n<\/pre><p>This floating-point round-off error comes into play because of the way that multiples of sqrt(2) are being added in different\r\n      orders.\r\n   <\/p>\r\n   <p>So we need to adjust our algorithm to account for floating-point round-off error. One way to do that is to round the distance\r\n      transform values to some lower precision. For example, the code below rounds the distance transform to be a multiple of (1\/32).\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D = round(D*32)\/32;<\/pre><p>Now <tt>D(3,3)<\/tt> and <tt>D(4,4)<\/tt> are equal:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D(3,3) - D(4,4)<\/pre><pre style=\"font-style:oblique\">\r\nans =\r\n\r\n     0\r\n\r\n<\/pre><p>And <tt>imregionalmin<\/tt> works as expected to extract the set of pixels belonging to shortest paths. (I'm using <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/10502-image-overlay\">imoverlay<\/a> from the MATLAB Central File Exchange.)\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">paths = imregionalmin(D);\r\nP = false(size(bw));\r\nP = imoverlay(P, paths, [.5 .5 .5]);\r\nP = imoverlay(P, bw, [1 1 1]);\r\nimshow(P, <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, <span style=\"color: #A020F0\">'fit'<\/span>)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_c_02.png\"> <p>Here's our revised algorithm, including the new rounding step:<\/p>\r\n   <div>\r\n      <ol>\r\n         <li>Compute the distance transform for just the upper left block of foreground pixels.<\/li>\r\n         <li>Compute the distance transform for just the lower right block of foreground pixels.<\/li>\r\n         <li>Add the two distance transforms together.<\/li>\r\n         <li>Round to lower precision.<\/li>\r\n         <li>The pixels in the regional minimum of the result lie along one or more of the shortest paths from one block to the other.<\/li>\r\n      <\/ol>\r\n   <\/div>\r\n   <p>Next time I'll look into how to pick a particular path among the many shortest-path choices.<\/p>\r\n<h4>All the posts in this series<\/h4>\r\n      <ul>\r\n         <li>the basic idea of finding shortest paths by adding two distance transforms together (<a href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/11\/01\/exploring-shortest-paths-part-1\/\">part 1<\/a>)\r\n         <\/li>\r\n         <li>the nonuniqueness of the shortest paths (<a href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/11\/26\/exploring-shortest-paths-part-2\/\">part 2<\/a>)\r\n         <\/li>\r\n         <li>handling floating-point round-off effects (<a href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/12\/02\/exploring-shortest-paths-part-3\/\">part 3<\/a>)\r\n         <\/li>\r\n         <li>using thinning to pick out a single path (<a href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/12\/06\/exploring-shortest-paths-part-4\/\">part 4<\/a>)\r\n         <\/li>\r\n         <li>using <tt>bwdistgeodesic<\/tt> to find shortest paths subject to constraint (<a href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/12\/13\/exploring-shortest-paths-part-5\/\">part 5<\/a>)\r\n         <\/li>\r\n      <\/ul>\r\n<script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_fb9cda11c3d94bb4b0f7316b9abcbf01() {\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='fb9cda11c3d94bb4b0f7316b9abcbf01 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' fb9cda11c3d94bb4b0f7316b9abcbf01';\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 2011 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_fb9cda11c3d94bb4b0f7316b9abcbf01()\"><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.13<br><\/p>\r\n<\/div>\r\n<!--\r\nfb9cda11c3d94bb4b0f7316b9abcbf01 ##### SOURCE BEGIN #####\r\n%%\r\n% In <https:\/\/blogs.mathworks.com\/steve\/2011\/11\/26\/exploring-shortest-paths-part-2\/ \r\n% part 2 of _Exploring shortest paths_>, I noted a problem with using the\r\n% 'quasi-euclidean' distance transform to find the shortest paths between\r\n% two objects in a binary image. Specifically, our algorithm resulted in a\r\n% big, unfilled gap between the two objects.\r\n\r\nbw = logical([ ...\r\n    0 0 0 0 0 0 0\r\n    0 1 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 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 1 0 \r\n    0 0 0 0 0 0 0  ]);\r\nL = bwlabel(bw);\r\nbw1 = (L == 1);\r\nbw2 = (L == 2);\r\n\r\nD1 = bwdist(bw1, 'quasi-euclidean');\r\nD2 = bwdist(bw2, 'quasi-euclidean');\r\nD = D1 + D2;\r\n\r\npaths = imregionalmin(D);\r\nP = false(size(bw));\r\nP = imoverlay(P, paths, [.5 .5 .5]);\r\nP = imoverlay(P, bw, [1 1 1]);\r\nimshow(P, 'InitialMagnification', 'fit')\r\n\r\n%%\r\n% The pixels marked in gray should be the set of pixels that lie along a\r\n% shortest path from the two objects in the original image. You can\r\n% plainly, see, however, that there's a large gap.\r\n%\r\n% To see why, let's examine more closely the values of |D|.\r\n\r\nD\r\n\r\n%%\r\n% There appears to be an unbroken set of pixels between the two objects\r\n% with value 9.6569. Actually, it turns out that the pixels do not all have\r\n% the same value. For example, |D(3,3)| appear to have the same value\r\n% |D(4,4)| but are actually slightly different.\r\n\r\nD(3,3)\r\n\r\n%%\r\n\r\nD(4,4)\r\n\r\n%%\r\n\r\nD(3,3) - D(4,4)\r\n\r\n%%\r\n% The difference between the two is the corresponding single-precision\r\n% relative floating-point precision:\r\n\r\neps(D(3,3))\r\n\r\n%%\r\n% This floating-point round-off error comes into play because of the way\r\n% that multiples of sqrt(2) are being added in different orders.\r\n%\r\n% So we need to adjust our algorithm to account for floating-point\r\n% round-off error. One way to do that is to round the distance transform\r\n% values to some lower precision. For example, the code below rounds the\r\n% distance transform to be a multiple of (1\/32).\r\n\r\nD = round(D*32)\/32;\r\n\r\n%%\r\n% Now |D(3,3)| and |D(4,4)| are equal:\r\n\r\nD(3,3) - D(4,4)\r\n\r\n%%\r\n% And |imregionalmin| works as expected to extract the set of pixels\r\n% belonging to shortest paths. (I'm using\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/10502-image-overlay\r\n% imoverlay> from the MATLAB Central File Exchange.)\r\n\r\npaths = imregionalmin(D);\r\nP = false(size(bw));\r\nP = imoverlay(P, paths, [.5 .5 .5]);\r\nP = imoverlay(P, bw, [1 1 1]);\r\nimshow(P, 'InitialMagnification', 'fit')\r\n\r\n%%\r\n% Here's our revised algorithm, including the new rounding step:\r\n%\r\n% # Compute the distance transform for just the upper left block of\r\n% foreground pixels.\r\n% # Compute the distance transform for just the lower right block of\r\n% foreground pixels.\r\n% # Add the two distance transforms together.\r\n% # Round to lower precision.\r\n% # The pixels in the regional minimum of the result lie along one or more\r\n% of the shortest paths from one block to the other.\r\n%\r\n% Next time I'll look into how to pick a particular path among the many\r\n% shortest-path choices.\r\n##### SOURCE END ##### fb9cda11c3d94bb4b0f7316b9abcbf01\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   In part 2 of Exploring shortest paths, I noted a problem with using the 'quasi-euclidean' distance transform to find the shortest paths between two objects in\r\n      a binary image.... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/12\/02\/exploring-shortest-paths-part-3\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[456,166,286,102,737,366,36,330,188,190],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/397"}],"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=397"}],"version-history":[{"count":12,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/397\/revisions"}],"predecessor-version":[{"id":3769,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/397\/revisions\/3769"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=397"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=397"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=397"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}