{"id":917,"date":"2013-11-19T18:52:59","date_gmt":"2013-11-19T23:52:59","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=917"},"modified":"2019-11-01T09:35:16","modified_gmt":"2019-11-01T13:35:16","slug":"watershed-transform-question-from-tech-support","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2013\/11\/19\/watershed-transform-question-from-tech-support\/","title":{"rendered":"Watershed transform question from tech support"},"content":{"rendered":"\r\n<div class=\"content\"><p>A support call came in this week from a customer trying to use <tt>watershed<\/tt> to segment this image:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/blobs.png\" alt=\"\"> <\/p><p>The complaint was that calling <tt>watershed<\/tt> did not produce a good segmentation.<\/p><p>Today I want to show how to use <tt>watershed<\/tt> to segment this image. Along the way I'll explain the difference between the <i>watershed transform<\/i> and <i>watershed segmentation<\/i>.<\/p><p>First, let's just try calling <tt>watershed<\/tt> and see what happens.<\/p><pre class=\"codeinput\">url = <span class=\"string\">'https:\/\/blogs.mathworks.com\/images\/steve\/2013\/blobs.png'<\/span>;\r\nbw = imread(url);\r\nL = watershed(bw);\r\nLrgb = label2rgb(L);\r\nimshow(Lrgb)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/watershed_edg_case_01.png\" alt=\"\"> <p>When I saw that result, I was puzzled at first. Then I realized what was going on. Let me use <tt>imfuse<\/tt> to show these two images together, zooming in on one particular blob.<\/p><pre class=\"codeinput\">imshow(imfuse(bw,Lrgb))\r\naxis([10 175 15 155])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/watershed_edg_case_02.png\" alt=\"\"> <p>You see, the watershed transform always gives you one watershed region for every local minimum (or regional minimum) in the image. These little black \"noise spots\" are local minima, and so there's a watershed region around each one.<\/p><p>Even if we fill these holes, though, just using the watershed transform by itself is never going to produce the segmentation that the customer was seeking. That brings me to my point about the distinction between watershed segmentation and the watershed transform. <i>Watershed segmentation<\/i> refers to a family of algorithms that are <b>based<\/b> on the watershed transform. Except for very specific cases, the watershed transform isn't a full segmentation method on its own.<\/p><p>Some years ago, I wrote a MathWorks newsletter article called <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>. It's worth reviewing in order to brush up on the basics. An central concept from the article is this:<\/p><p>The key behind using the watershed transform for segmentation is this: <b>Change your image into another image whose catchment basins are the objects you want to identify.<\/b><\/p><p>For an image such as this, consisting of roughly circular, touching blobs. the distance transform can be useful for producing an image whose \"catchment basins are the objects you want to identify.\"<\/p><p>Before go to the distance transform, though, let's clean up the noise a bit. The function <tt>bwareaopen<\/tt> can be used to remove very small dots. It removes them in the foreground, though, so we complement the image before and after calling <tt>bwareaopen<\/tt>.<\/p><pre class=\"codeinput\">bw2 = ~bwareaopen(~bw, 10);\r\nimshow(bw2)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/watershed_edg_case_03.png\" alt=\"\"> <pre class=\"codeinput\">D = -bwdist(~bw);\r\nimshow(D,[])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/watershed_edg_case_04.png\" alt=\"\"> <p>Now we're starting to get somewhere. Next, compute the watershed transform of <tt>D<\/tt>.<\/p><pre class=\"codeinput\">Ld = watershed(D);\r\nimshow(label2rgb(Ld))\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/watershed_edg_case_05.png\" alt=\"\"> <p>The watershed ridge lines, in white, correspond to <tt>Ld == 0<\/tt>. Let's use these ridge lines to segment the binary image by changing the corresponding pixels into background.<\/p><pre class=\"codeinput\">bw2 = bw;\r\nbw2(Ld == 0) = 0;\r\nimshow(bw2)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/watershed_edg_case_06.png\" alt=\"\"> <p>The \"raw\" watershed transform is known for its tendency to \"oversegment\" an image. The reason is something I mentioned above: each local minimum, no matter how small, becomes a catchment basin. A common trick, then, in watershed-based segmentation methods is to filter out tiny local minima using <tt>imextendedmin<\/tt> and then modify the distance transform so that no minima occur at the filtered-out locations. This is called \"minima imposition\" and is implemented via the function <tt>imimposemin<\/tt>.<\/p><p>The following call to <tt>imextendedmin<\/tt> should ideally just produce small spots that are roughly in the middle of the cells to be segmented. I'll use <tt>imshowpair<\/tt> to superimpose the mask on the original image.<\/p><pre class=\"codeinput\">mask = imextendedmin(D,2);\r\nimshowpair(bw,mask,<span class=\"string\">'blend'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/watershed_edg_case_07.png\" alt=\"\"> <p>Home stretch, now. Modify the distance transform so it only has minima at the desired locations, and then repeat the watershed steps above.<\/p><pre class=\"codeinput\">D2 = imimposemin(D,mask);\r\nLd2 = watershed(D2);\r\nbw3 = bw;\r\nbw3(Ld2 == 0) = 0;\r\nimshow(bw3)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/watershed_edg_case_08.png\" alt=\"\"> <p>That's it! Read my <a href=\"https:\/\/www.mathworks.com\/company\/newsletters\/articles\/the-watershed-transform-strategies-for-image-segmentation.html\">newsletter article<\/a> for other thoughts on image segmentation using the watershed transformation.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_e49ced1c45704909be15558821e0a9c8() {\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='e49ced1c45704909be15558821e0a9c8 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' e49ced1c45704909be15558821e0a9c8';\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 2013 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_e49ced1c45704909be15558821e0a9c8()\"><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\ne49ced1c45704909be15558821e0a9c8 ##### SOURCE BEGIN #####\r\n%%\r\n% A support call came in this week from a customer trying to use\r\n% |watershed| to segment this image:\r\n%\r\n% <<https:\/\/blogs.mathworks.com\/images\/steve\/2013\/blobs.png>>\r\n%\r\n% The complaint was that calling |watershed| did not produce a good\r\n% segmentation.\r\n%\r\n% Today I want to show how to use |watershed| to segment this image. Along\r\n% the way I'll explain the difference between the _watershed transform_ and\r\n% _watershed segmentation_.\r\n%\r\n% First, let's just try calling |watershed| and see what happens.\r\n\r\nurl = 'https:\/\/blogs.mathworks.com\/images\/steve\/2013\/blobs.png';\r\nbw = imread(url);\r\nL = watershed(bw);\r\nLrgb = label2rgb(L);\r\nimshow(Lrgb)\r\n\r\n%%\r\n% When I saw that result, I was puzzled at first. Then I realized what was\r\n% going on. Let me use |imfuse| to show these two images together, zooming\r\n% in on one particular blob.\r\n\r\nimshow(imfuse(bw,Lrgb))\r\naxis([10 175 15 155])\r\n\r\n%%\r\n% You see, the watershed transform always gives you one watershed region\r\n% for every local minimum (or regional minimum) in the image. These little\r\n% black \"noise spots\" are local minima, and so there's a watershed region\r\n% around each one.\r\n%\r\n% Even if we fill these holes, though, just using the watershed transform\r\n% by itself is never going to produce the segmentation that the customer\r\n% was seeking. That brings me to my point about the distinction between\r\n% watershed segmentation and the watershed transform. _Watershed\r\n% segmentation_ refers to a family of algorithms that are *based* on the\r\n% watershed transform. Except for very specific cases, the watershed\r\n% transform isn't a full segmentation method on its own.\r\n%\r\n% Some years ago, I wrote a MathWorks newsletter article called\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>. It's worth\r\n% reviewing in order to brush up on the basics. An central concept from the\r\n% article is this:\r\n%\r\n% The key behind using the watershed transform for segmentation is this:\r\n% *Change your image into another image whose catchment basins are the\r\n% objects you want to identify.*\r\n%\r\n% For an image such as this, consisting of roughly circular, touching\r\n% blobs. the distance transform can be useful for producing an image whose\r\n% \"catchment basins are the objects you want to identify.\"\r\n%\r\n% Before go to the distance transform, though, let's clean up the noise a\r\n% bit. The function |bwareaopen| can be used to remove very small dots. It\r\n% removes them in the foreground, though, so we complement the image before\r\n% and after calling |bwareaopen|.\r\n\r\nbw2 = ~bwareaopen(~bw, 10);\r\nimshow(bw2)\r\n\r\n%%\r\nD = -bwdist(~bw);\r\nimshow(D,[])\r\n\r\n%%\r\n% Now we're starting to get somewhere. Next, compute the watershed\r\n% transform of |D|.\r\n\r\nLd = watershed(D);\r\nimshow(label2rgb(Ld))\r\n\r\n%%\r\n% The watershed ridge lines, in white, correspond to |Ld == 0|. Let's use\r\n% these ridge lines to segment the binary image by changing the\r\n% corresponding pixels into background.\r\n\r\nbw2 = bw;\r\nbw2(Ld == 0) = 0;\r\nimshow(bw2)\r\n\r\n%%\r\n% The \"raw\" watershed transform is known for its tendency to \"oversegment\"\r\n% an image. The reason is something I mentioned above: each local minimum,\r\n% no matter how small, becomes a catchment basin. A common trick, then, in\r\n% watershed-based segmentation methods is to filter out tiny local minima\r\n% using |imextendedmin| and then modify the distance transform so that no\r\n% minima occur at the filtered-out locations. This is called \"minima\r\n% imposition\" and is implemented via the function |imimposemin|.\r\n%\r\n% The following call to |imextendedmin| should ideally just produce small\r\n% spots that are roughly in the middle of the cells to be segmented. I'll\r\n% use |imshowpair| to superimpose the mask on the original image.\r\n\r\nmask = imextendedmin(D,2);\r\nimshowpair(bw,mask,'blend')\r\n\r\n%%\r\n% Home stretch, now. Modify the distance transform so it only has minima at\r\n% the desired locations, and then repeat the watershed steps above.\r\n\r\nD2 = imimposemin(D,mask);\r\nLd2 = watershed(D2);\r\nbw3 = bw;\r\nbw3(Ld2 == 0) = 0;\r\nimshow(bw3)\r\n\r\n%%\r\n% That's it! Read my\r\n% <https:\/\/www.mathworks.com\/company\/newsletters\/articles\/the-watershed-transform-strategies-for-image-segmentation.html\r\n% newsletter article> for other thoughts on image \r\n% segmentation using the watershed transformation.\r\n##### SOURCE END ##### e49ced1c45704909be15558821e0a9c8\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img decoding=\"async\"  class=\"img-responsive\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/watershed_edg_case_08.png\" onError=\"this.style.display ='none';\" \/><\/div><p>\r\nA support call came in this week from a customer trying to use watershed to segment this image: The complaint was that calling watershed did not produce a good segmentation.Today I want to show how... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2013\/11\/19\/watershed-transform-question-from-tech-support\/\">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":[50,138,456,446,869,148,76,36,867,152,150],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/917"}],"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=917"}],"version-history":[{"count":4,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/917\/revisions"}],"predecessor-version":[{"id":921,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/917\/revisions\/921"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=917"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=917"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=917"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}