{"id":399,"date":"2011-12-13T07:00:45","date_gmt":"2011-12-13T12:00:45","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=399"},"modified":"2019-10-29T17:03:35","modified_gmt":"2019-10-29T21:03:35","slug":"exploring-shortest-paths-part-5","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2011\/12\/13\/exploring-shortest-paths-part-5\/","title":{"rendered":"Exploring shortest paths &#8211; part 5"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <p>In this post in the <i>Exploring shortest paths<\/i> series, I make things a little more complicated (and interesting) by adding constraints to the shortest path problem. Last\r\n      time, I showed this example of finding the shortest paths between the \"T\" and the sideways \"s\" in this image:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">url = <span style=\"color: #A020F0\">'https:\/\/blogs.mathworks.com\/images\/steve\/2011\/two-letters-cropped.png'<\/span>;\r\nbw = imread(url);<\/pre><p>And this was the result:<\/p>\r\n   <p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_d_05.png\"> <\/p>\r\n   <p>What if we complicate the problem by adding other objects to the image and looking for the shortest path that avoids these\r\n      other objects?\r\n   <\/p>\r\n   <p>For example, what if we want to find a shortest path between the \"T\" and the sideways \"s\" without going through any of other\r\n      letters between them in the image below?\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">url = <span style=\"color: #A020F0\">'https:\/\/blogs.mathworks.com\/images\/steve\/2011\/text-cropped.png'<\/span>;\r\ntext = imread(url);\r\nimshow(text, <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_e_01.png\"> <p>We can do this by using <tt>bwdistgeodesic<\/tt> instead of <tt>bwdist<\/tt> in our algorithm. <tt>bwdistgeodesic<\/tt> is a new function in the R2011b release of the Image Processing Toolbox. In addition to taking a binary image input that\r\n      <tt>bwdist<\/tt> takes, it takes another argument that specifies which pixels the paths are allowed to traverse.\r\n   <\/p>\r\n   <p>Let's use our text image to make a mask of allowed path pixels.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">mask = ~text | bw;\r\nimshow(mask, <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_e_02.png\"> <p>Next, we make our two binary object images as before.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">L = bwlabel(bw);\r\nbw1 = (L == 1);\r\nbw2 = (L == 2);<\/pre><p>Next, instead of using <tt>bwdist<\/tt>, we use <tt>bwdistgeodesic<\/tt>.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D1 = bwdistgeodesic(mask, bw1, <span style=\"color: #A020F0\">'quasi-euclidean'<\/span>);\r\nD2 = bwdistgeodesic(mask, bw2, <span style=\"color: #A020F0\">'quasi-euclidean'<\/span>);\r\n\r\nD = D1 + D2;\r\nD = round(D * 8) \/ 8;\r\n\r\nD(isnan(D)) = inf;\r\npaths = imregionalmin(D);\r\n\r\npaths_thinned_many = bwmorph(paths, <span style=\"color: #A020F0\">'thin'<\/span>, inf);\r\nP = false(size(bw));\r\nP = imoverlay(P, ~mask, [1 0 0]);\r\nP = imoverlay(P, paths, [.5 .5 .5]);\r\nP = imoverlay(P, paths_thinned_many, [1 1 0]);\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_e_03.png\"> <p>In the image above, the white characters are the starting and ending points for the path. The path is not allowed to pass\r\n      through the red characters. The gray pixels are all the pixels on any shortest path, and the yellow pixels lie along one particular\r\n      shortest path.\r\n   <\/p>\r\n   <p>Let's have a little fun by using this technique to solve a maze. Here's a test maze (created by <a href=\"http:\/\/www.billsgames.com\/mazegenerator\/\">The Maze Generator<\/a>).\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">url = <span style=\"color: #A020F0\">'https:\/\/blogs.mathworks.com\/images\/steve\/2011\/maze-51x51.png'<\/span>;\r\nI = imread(url);\r\nimshow(I)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_e_04.png\"> <p>Make an image of walls by thresholding the maze image. Dilate the walls (make them a little thicker) in order to keep the\r\n      solution path a little bit away from them.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">walls = imdilate(I &lt; 128, ones(7,7));\r\nimshow(walls)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_e_05.png\"> <p>The function <tt>bwgeodesic<\/tt> will take seed locations (as column and row coordinates) in addition to binary images. Below I specify the seed locations\r\n      as the two entry points into the maze.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D1 = bwdistgeodesic(~walls, 1, 497, <span style=\"color: #A020F0\">'quasi-euclidean'<\/span>);\r\nD2 = bwdistgeodesic(~walls, 533, 517, <span style=\"color: #A020F0\">'quasi-euclidean'<\/span>);<\/pre><p>The rest of the procedure is the same as above.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D = D1 + D2;\r\nD = round(D * 8) \/ 8;\r\n\r\nD(isnan(D)) = inf;\r\npaths = imregionalmin(D);\r\n\r\nsolution_path = bwmorph(paths, <span style=\"color: #A020F0\">'thin'<\/span>, inf);\r\nthick_solution_path = imdilate(solution_path, ones(3,3));\r\nP = imoverlay(I, thick_solution_path, [1 0 0]);\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_e_06.png\"> <p>(If you're interested in other maze-solving techniques using image processing, take a look at the File Exchange contribution\r\n      <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/27175-maze-solution\/content\/Maze_Solution\/maze_solution.m\">maze_solution<\/a> by <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/31862\">Image Analyst<\/a>.)\r\n   <\/p>\r\n   <p>In a <a href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/11\/01\/exploring-shortest-paths-part-1\/#comment-24596\">comment on part 1 of this series<\/a>, Sepideh asked this question:\r\n   <\/p>\r\n   <p>\"I have a binary image which contains one pixel width skeleton of an image. I have got the skeleton using the bwmorph function.\r\n      As you may know basically in this binary image, I have ones on the skeleton and zeros anywhere else. how can I find the shortest\r\n      &#8216;quasi-euclidean&#8217; distance between two nonzero elements which are on the skeleton? The path should be on the skeleton itself\r\n      too.\"\r\n   <\/p>\r\n   <p>The techniques described above can be applied to this path-along-a-skeleton problem. Let's try an example.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">bw = imread(<span style=\"color: #A020F0\">'circles.png'<\/span>);\r\nimshow(bw, <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, 200)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_e_07.png\"> <pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">skel = bwmorph(bw, <span style=\"color: #A020F0\">'skel'<\/span>, Inf);\r\nimshow(skel, <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, 200)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_e_08.png\"> <p>Let's find the skeleton-constrained path distance between these two points.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">r1 = 38;\r\nc1 = 53;\r\n\r\nr2 = 208;\r\nc2 = 147;\r\n\r\nhold <span style=\"color: #A020F0\">on<\/span>\r\nplot(c1, r1, <span style=\"color: #A020F0\">'g*'<\/span>, <span style=\"color: #A020F0\">'MarkerSize'<\/span>, 15)\r\nplot(c2, r2, <span style=\"color: #A020F0\">'g*'<\/span>, <span style=\"color: #A020F0\">'MarkerSize'<\/span>, 15)\r\nhold <span style=\"color: #A020F0\">off<\/span><\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_e_09.png\"> <pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">D1 = bwdistgeodesic(skel, c1, r1, <span style=\"color: #A020F0\">'quasi-euclidean'<\/span>);\r\nD2 = bwdistgeodesic(skel, c2, r2, <span style=\"color: #A020F0\">'quasi-euclidean'<\/span>);\r\n\r\nD = D1 + D2;\r\nD = round(D * 8) \/ 8;\r\n\r\nD(isnan(D)) = inf;\r\nskeleton_path = imregionalmin(D);\r\nP = imoverlay(skel, imdilate(skeleton_path, ones(3,3)), [1 0 0]);\r\nimshow(P, <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, 200)\r\nhold <span style=\"color: #A020F0\">on<\/span>\r\nplot(c1, r1, <span style=\"color: #A020F0\">'g*'<\/span>, <span style=\"color: #A020F0\">'MarkerSize'<\/span>, 15)\r\nplot(c2, r2, <span style=\"color: #A020F0\">'g*'<\/span>, <span style=\"color: #A020F0\">'MarkerSize'<\/span>, 15)\r\nhold <span style=\"color: #A020F0\">off<\/span><\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_e_10.png\"> <p>Finally, the skeleton path length is given by the value of <tt>D<\/tt> at any point along the path.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">path_length = D(skeleton_path);\r\npath_length = path_length(1)<\/pre><pre style=\"font-style:oblique\">\r\npath_length =\r\n\r\n  239.2500\r\n\r\n<\/pre>\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_6c3df5ae559f4e679ee76128ce614074() {\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='6c3df5ae559f4e679ee76128ce614074 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 6c3df5ae559f4e679ee76128ce614074';\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_6c3df5ae559f4e679ee76128ce614074()\"><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\n6c3df5ae559f4e679ee76128ce614074 ##### SOURCE BEGIN #####\r\n%%\r\n% In this post in the _Exploring shortest paths_ series, I make things a\r\n% little more complicated (and interesting) by adding constraints to the\r\n% shortest path problem. Last time, I showed this example of finding the\r\n% shortest paths between the \"T\" and the sideways \"s\" in this image:\r\n\r\nurl = 'https:\/\/blogs.mathworks.com\/images\/steve\/2011\/two-letters-cropped.png';\r\nbw = imread(url);\r\n\r\n%%\r\n% And this was the result:\r\n%\r\n% <<https:\/\/blogs.mathworks.com\/images\/steve\/2011\/shortest_path_d_05.png>>\r\n%\r\n% What if we complicate the problem by adding other objects to the image\r\n% and looking for the shortest path that avoids these other objects?\r\n%\r\n% For example, what if we want to find a shortest path between the \"T\" and\r\n% the sideways \"s\" without going through any of other letters between them\r\n% in the image below?\r\n\r\nurl = 'https:\/\/blogs.mathworks.com\/images\/steve\/2011\/text-cropped.png';\r\ntext = imread(url);\r\nimshow(text, 'InitialMagnification', 'fit')\r\n\r\n%%\r\n% We can do this by using |bwdistgeodesic| instead of |bwdist| in our\r\n% algorithm. |bwdistgeodesic| is a new function in the R2011b release of\r\n% the Image Processing Toolbox. In addition to taking a binary image input\r\n% that |bwdist| takes, it takes another argument that specifies which\r\n% pixels the paths are allowed to traverse.\r\n%\r\n% Let's use our text image to make a mask of allowed path pixels.\r\n\r\nmask = ~text | bw;\r\nimshow(mask, 'InitialMagnification', 'fit')\r\n\r\n%%\r\n% Next, we make our two binary object images as before.\r\n\r\nL = bwlabel(bw);\r\nbw1 = (L == 1);\r\nbw2 = (L == 2);\r\n\r\n%%\r\n% Next, instead of using |bwdist|, we use |bwdistgeodesic|.\r\nD1 = bwdistgeodesic(mask, bw1, 'quasi-euclidean');\r\nD2 = bwdistgeodesic(mask, bw2, 'quasi-euclidean');\r\n\r\nD = D1 + D2;\r\nD = round(D * 8) \/ 8;\r\n\r\nD(isnan(D)) = inf;\r\npaths = imregionalmin(D);\r\n\r\npaths_thinned_many = bwmorph(paths, 'thin', inf);\r\nP = false(size(bw));\r\nP = imoverlay(P, ~mask, [1 0 0]);\r\nP = imoverlay(P, paths, [.5 .5 .5]);\r\nP = imoverlay(P, paths_thinned_many, [1 1 0]);\r\nP = imoverlay(P, bw, [1 1 1]);\r\nimshow(P, 'InitialMagnification', 'fit')\r\n\r\n%%\r\n% In the image above, the white characters are the starting and ending\r\n% points for the path. The path is not allowed to pass through the red\r\n% characters. The gray pixels are all the pixels on any shortest path, and\r\n% the yellow pixels lie along one particular shortest path.\r\n%\r\n% Let's have a little fun by using this technique to solve a maze. Here's a\r\n% test maze (created by <http:\/\/www.billsgames.com\/mazegenerator\/ The Maze\r\n% Generator>).\r\n\r\n%%\r\nurl = 'https:\/\/blogs.mathworks.com\/images\/steve\/2011\/maze-51x51.png';\r\nI = imread(url);\r\nimshow(I)\r\n\r\n%%\r\n% Make an image of walls by thresholding the maze image. Dilate the walls\r\n% (make them a little thicker) in order to keep the solution path a little\r\n% bit away from them.\r\n\r\nwalls = imdilate(I < 128, ones(7,7));\r\nimshow(walls)\r\n\r\n%%\r\n% The function |bwgeodesic| will take seed locations (as column and row\r\n% coordinates) in addition to binary images. Below I specify the seed\r\n% locations as the two entry points into the maze. \r\n\r\nD1 = bwdistgeodesic(~walls, 1, 497, 'quasi-euclidean');\r\nD2 = bwdistgeodesic(~walls, 533, 517, 'quasi-euclidean');\r\n\r\n%%\r\n% The rest of the procedure is the same as above.\r\n\r\nD = D1 + D2;\r\nD = round(D * 8) \/ 8;\r\n\r\nD(isnan(D)) = inf;\r\npaths = imregionalmin(D);\r\n\r\nsolution_path = bwmorph(paths, 'thin', inf);\r\nthick_solution_path = imdilate(solution_path, ones(3,3));\r\nP = imoverlay(I, thick_solution_path, [1 0 0]);\r\nimshow(P, 'InitialMagnification', 'fit')\r\n\r\n%%\r\n% (If you're interested in other maze-solving techniques using image\r\n% processing, take a look at the File Exchange contribution\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/27175-maze-solution\/content\/Maze_Solution\/maze_solution.m\r\n% maze_solution> by\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/31862 Image\r\n% Analyst>.)\r\n%\r\n% In a\r\n% <https:\/\/blogs.mathworks.com\/steve\/2011\/11\/01\/exploring-shortest-paths-part-1\/#comment-24596\r\n% comment on part 1 of this series>, Sepideh asked this question:\r\n%\r\n% \"I have a binary image which contains one pixel width skeleton of an\r\n% image. I have got the skeleton using the bwmorph function. As you may\r\n% know basically in this binary image, I have ones on the skeleton and\r\n% zeros anywhere else. how can I find the shortest \u00e2\u20ac\u02dcquasi-euclidean\u00e2\u20ac&#x2122;\r\n% distance between two nonzero elements which are on the skeleton? The path\r\n% should be on the skeleton itself too.\"\r\n%\r\n% The techniques described above can be applied to this\r\n% path-along-a-skeleton problem. Let's try an example.\r\n\r\nbw = imread('circles.png');\r\nimshow(bw, 'InitialMagnification', 200)\r\n\r\n%%\r\nskel = bwmorph(bw, 'skel', Inf);\r\nimshow(skel, 'InitialMagnification', 200)\r\n\r\n%%\r\n% Let's find the skeleton-constrained path distance between these two\r\n% points.\r\n\r\nr1 = 38;\r\nc1 = 53;\r\n\r\nr2 = 208;\r\nc2 = 147;\r\n\r\nhold on\r\nplot(c1, r1, 'g*', 'MarkerSize', 15)\r\nplot(c2, r2, 'g*', 'MarkerSize', 15)\r\nhold off\r\n\r\n%%\r\nD1 = bwdistgeodesic(skel, c1, r1, 'quasi-euclidean');\r\nD2 = bwdistgeodesic(skel, c2, r2, 'quasi-euclidean');\r\n\r\nD = D1 + D2;\r\nD = round(D * 8) \/ 8;\r\n\r\nD(isnan(D)) = inf;\r\nskeleton_path = imregionalmin(D);\r\nP = imoverlay(skel, imdilate(skeleton_path, ones(3,3)), [1 0 0]);\r\nimshow(P, 'InitialMagnification', 200)\r\nhold on\r\nplot(c1, r1, 'g*', 'MarkerSize', 15)\r\nplot(c2, r2, 'g*', 'MarkerSize', 15)\r\nhold off\r\n\r\n%%\r\n% Finally, the skeleton path length is given by the value of |D| at any\r\n% point along the path.\r\npath_length = D(skeleton_path);\r\npath_length = path_length(1)\r\n\r\n##### SOURCE END ##### 6c3df5ae559f4e679ee76128ce614074\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   In this post in the Exploring shortest paths series, I make things a little more complicated (and interesting) by adding constraints to the shortest path problem. Last\r\n      time, I showed this... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/12\/13\/exploring-shortest-paths-part-5\/\">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":[839,166,86,102,90,124,737,76,366,36,573,376,68,188,190],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/399"}],"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=399"}],"version-history":[{"count":3,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/399\/revisions"}],"predecessor-version":[{"id":3771,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/399\/revisions\/3771"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=399"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=399"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=399"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}