{"id":954,"date":"2014-01-21T07:00:32","date_gmt":"2014-01-21T12:00:32","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=954"},"modified":"2019-11-01T10:58:21","modified_gmt":"2019-11-01T14:58:21","slug":"solving-mazes-with-the-watershed-transform","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2014\/01\/21\/solving-mazes-with-the-watershed-transform\/","title":{"rendered":"Solving mazes with the watershed transform"},"content":{"rendered":"\r\n<div class=\"content\"><!--introduction--><p><i>I'd like to welcome back guest blogger Spandan Tiwari for today's post. Spandan, a developer on the Image Processing Toolbox team, posted here previously about <a href=\"https:\/\/blogs.mathworks.com\/steve\/2012\/09\/04\/detecting-circular-objects-in-images\/\">circle finding<\/a> and <a href=\"https:\/\/blogs.mathworks.com\/steve\/2013\/06\/25\/homomorphic-filtering-part-1\/\">homomorphic filtering<\/a>.<\/i><\/p><p>A few weeks ago Steve wrote about <a href=\"https:\/\/blogs.mathworks.com\/steve\/2013\/11\/19\/watershed-transform-question-from-tech-support\/\">watershed transform<\/a> and how to use it for image segmentation. I thought I would continue the topic and highlight another neat application for watershed transform - Maze Solving. Steve discussed this problem in his earlier post on <a href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/12\/13\/exploring-shortest-paths-part-5\/\">Exploring Shortest Paths<\/a> and showed a solution based on Geodesic distance transform (<a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/bwdistgeodesic.html\"><tt>bwdistgeodesic<\/tt><\/a>). In this post I show another way for solving a maze using watershed transform.<\/p><!--\/introduction--><p>Here's a maze. We would like to find the <i>solution<\/i> , i.e. a path between the two points (entry and exit points) shown in red.<\/p><pre class=\"codeinput\">I = imread(<span class=\"string\">'https:\/\/blogs.mathworks.com\/images\/steve\/2011\/maze-51x51.png'<\/span>);\r\nI = logical(I);\r\nimshow(I)\r\nhold <span class=\"string\">on<\/span>;\r\nplot([6 526], [497 517], <span class=\"string\">'ro'<\/span>, <span class=\"string\">'MarkerSize'<\/span>, 15,<span class=\"string\">'LineWidth'<\/span>, 2);\r\nhold <span class=\"string\">off<\/span>;\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2014\/MazeSolving_01.png\" alt=\"\"> <p>I should mention that in my approach I am considering only 'standard' or 'perfect' mazes that have one and only one path from the entry point to the exit point, and some other nice properties such as absence of loops. I will call these mazes <i>well-behaved<\/i> mazes.<\/p><p>Here is the key idea behind using watershed transform for solving a maze. The landscape (or surface) of the image of a well-behaved maze has two catchment basins. The watershed between those catchment basins is the solution path for the maze! (For more details on the definition of <i>watershed<\/i> and <i>catchment basin<\/i> see Steve's newsletter article <a href=\"https:\/\/www.mathworks.com\/company\/newsletters\/articles\/the-watershed-transform-strategies-for-image-segmentation.html\">The Watershed Transform: Strategies for Image Segmentation<\/a>). Let's look at the catchment basins as produced by <tt>watershed<\/tt>.<\/p><pre class=\"codeinput\">L = watershed(I);\r\nimshow(L,[])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2014\/MazeSolving_02.png\" alt=\"\"> <p>The two basins are shown in grey and white. If you follow the interface (boundary) between the two basins (grey and white regions), you can see notice that it is the solution (path) to the maze.<\/p><p>The good news is that at this point we have already solved the bulk of the problem. All we need to do now is to extract the interface between the two basins. There are different ways to do this. Here's the one that I like, mainly because I find it to be robust. Another minor reason I like it is that the final path stays parallel to the maze lines which gives it a nice clean look.<\/p><p>First we create a new image retaining only the original image region from one of the catchment basins.<\/p><pre class=\"codeinput\">L1 = L == 2;\r\nI1 = L1.*I;\r\nimshow(I1)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2014\/MazeSolving_03.png\" alt=\"\"> <p>Next we use <tt>watershed<\/tt> again to get catchment basins for this new image.<\/p><pre class=\"codeinput\">L2 = watershed(I1);\r\nimshow(L2,[])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2014\/MazeSolving_04.png\" alt=\"\"> <p>Notice that the catchment basin in this new image has shrunk roughly by half the width of the maze paths as compared to the same catchment basin in the original image. This can be seen clearly using <tt>imshowpair<\/tt>. It highlights the area where the images are different using color (green, in this case)<\/p><pre class=\"codeinput\">imshowpair(L,L2)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2014\/MazeSolving_05.png\" alt=\"\"> <p>As the last step we just extract the path shown as the green region, which is the region where the two catchment basins differ.<\/p><pre class=\"codeinput\">img1 = L == 2;\r\nimg2 = L2 == 2;\r\npath = img1 - img2;\r\n<\/pre><p>We can visualize the path on the maze nicely using Steve's <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/10502-image-overlay\"><tt>imoverlay<\/tt><\/a> function.<\/p><pre class=\"codeinput\">P = imoverlay(I, path, [1 0 0]);\r\nimshow(P)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2014\/MazeSolving_06.png\" alt=\"\"> <p>Here are few other results on different mazes.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2014\/Maze2_Solution.png\" alt=\"\"> <\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2014\/Maze5_Solution.png\" alt=\"\"> <\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_89f63c87d7e44f18ba8173af552582f3() {\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='89f63c87d7e44f18ba8173af552582f3 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 89f63c87d7e44f18ba8173af552582f3';\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        copyright = 'Copyright 2014 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 copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\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     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_89f63c87d7e44f18ba8173af552582f3()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2013b<br><\/p><p class=\"footer\"><br>\r\n      Published with MATLAB&reg; R2013b<br><\/p><\/div><!--\r\n89f63c87d7e44f18ba8173af552582f3 ##### SOURCE BEGIN #####\r\n%% Solving Mazes with Watershed Transform\r\n% _I'd like to welcome back guest blogger Spandan Tiwari for today's post.\r\n% Spandan, a developer on the Image Processing Toolbox team, posted here\r\n% previously about\r\n% <https:\/\/blogs.mathworks.com\/steve\/2012\/09\/04\/detecting-circular-objects-in-images\/\r\n% circle finding> and\r\n% <https:\/\/blogs.mathworks.com\/steve\/2013\/06\/25\/homomorphic-filtering-part-1\/\r\n% homomorphic filtering>._\r\n% \r\n% A few weeks ago Steve wrote about\r\n% <https:\/\/blogs.mathworks.com\/steve\/2013\/11\/19\/watershed-transform-question-from-tech-support\/\r\n% watershed transform> and how to use it for image segmentation. I thought\r\n% I would continue the topic and highlight another neat application for\r\n% watershed transform - Maze Solving. Steve discussed this problem in his\r\n% earlier post on\r\n% <https:\/\/blogs.mathworks.com\/steve\/2011\/12\/13\/exploring-shortest-paths-part-5\/\r\n% Exploring Shortest Paths> and showed a solution based on Geodesic\r\n% distance transform\r\n% (<https:\/\/www.mathworks.com\/help\/images\/ref\/bwdistgeodesic.html\r\n% |bwdistgeodesic|>). In this post I show another way for solving a maze\r\n% using watershed transform.\r\n\r\n%%\r\n% Here's a maze. We would like to find the _solution_ , i.e. a path between\r\n% the two points (entry and exit points) shown in red.\r\nI = imread('https:\/\/blogs.mathworks.com\/images\/steve\/2011\/maze-51x51.png');\r\nI = logical(I);\r\nimshow(I)\r\nhold on;\r\nplot([6 526], [497 517], 'ro', 'MarkerSize', 15,'LineWidth', 2);\r\nhold off;\r\n%% \r\n% I should mention that in my approach I am considering only 'standard' or\r\n% 'perfect' mazes that have one and only one path from the entry point to\r\n% the exit point, and some other nice properties such as absence of loops.\r\n% I will call these mazes _well-behaved_ mazes.\r\n% \r\n% Here is the key idea behind using watershed transform for solving a maze.\r\n% The landscape (or surface) of the image of a well-behaved maze has two\r\n% catchment basins. The watershed between those catchment basins is the\r\n% solution path for the maze! (For more details on the definition of\r\n% _watershed_ and _catchment basin_ see Steve's newsletter article\r\n% <https:\/\/www.mathworks.com\/company\/newsletters\/articles\/the-watershed-transform-strategies-for-image-segmentation.html\r\n% The Watershed Transform: Strategies for Image Segmentation>). Let's look\r\n% at the catchment basins as produced by |watershed|.\r\n\r\nL = watershed(I);\r\nimshow(L,[])\r\n\r\n%%\r\n% The two basins are shown in grey and white. If you follow the interface\r\n% (boundary) between the two basins (grey and white regions), you can see\r\n% notice that it is the solution (path) to the maze.\r\n% \r\n% The good news is that at this point we have already solved the bulk of\r\n% the problem. All we need to do now is to extract the interface between\r\n% the two basins. There are different ways to do this. Here's the one that\r\n% I like, mainly because I find it to be robust. Another minor reason I\r\n% like it is that the final path stays parallel to the maze lines which\r\n% gives it a nice clean look.\r\n% \r\n% First we create a new image retaining only the original image region from\r\n% one of the catchment basins.\r\n\r\nL1 = L == 2;\r\nI1 = L1.*I;\r\nimshow(I1)\r\n\r\n%%\r\n% Next we use |watershed| again to get catchment basins for this new image.\r\nL2 = watershed(I1);\r\nimshow(L2,[])\r\n\r\n%%\r\n% Notice that the catchment basin in this new image has shrunk roughly by\r\n% half the width of the maze paths as compared to the same catchment basin\r\n% in the original image. This can be seen clearly using |imshowpair|. It\r\n% highlights the area where the images are different using color (green,\r\n% in this case)\r\nimshowpair(L,L2)\r\n\r\n%% \r\n% As the last step we just extract the path shown as the green region,\r\n% which is the region where the two catchment basins differ.\r\nimg1 = L == 2;\r\nimg2 = L2 == 2;\r\npath = img1 - img2;\r\n\r\n%%\r\n% We can visualize the path on the maze nicely using Steve's\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/10502-image-overlay\r\n% |imoverlay|> function.\r\nP = imoverlay(I, path, [1 0 0]);\r\nimshow(P)\r\n\r\n%% \r\n% Here are few other results on different mazes.\r\n% \r\n% <<https:\/\/blogs.mathworks.com\/images\/steve\/2014\/Maze2_Solution.png>>\r\n% \r\n% <<https:\/\/blogs.mathworks.com\/images\/steve\/2014\/Maze5_Solution.png>>\r\n\r\n##### SOURCE END ##### 89f63c87d7e44f18ba8173af552582f3\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img decoding=\"async\"  class=\"img-responsive\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2014\/MazeSolving_06.png\" onError=\"this.style.display ='none';\" \/><\/div><!--introduction--><p><i>I'd like to welcome back guest blogger Spandan Tiwari for today's post. Spandan, a developer on the Image Processing Toolbox team, posted here previously about <a href=\"https:\/\/blogs.mathworks.com\/steve\/2012\/09\/04\/detecting-circular-objects-in-images\/\">circle finding<\/a> and <a href=\"https:\/\/blogs.mathworks.com\/steve\/2013\/06\/25\/homomorphic-filtering-part-1\/\">homomorphic filtering<\/a>.<\/i>... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2014\/01\/21\/solving-mazes-with-the-watershed-transform\/\">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":[90,76,36,867,330,68,150],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/954"}],"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=954"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/954\/revisions"}],"predecessor-version":[{"id":955,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/954\/revisions\/955"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=954"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=954"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=954"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}