{"id":60,"date":"2006-06-02T06:54:41","date_gmt":"2006-06-02T10:54:41","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=60"},"modified":"2019-10-22T12:26:39","modified_gmt":"2019-10-22T16:26:39","slug":"cell-segmentation","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2006\/06\/02\/cell-segmentation\/","title":{"rendered":"Cell segmentation"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <p>Blog reader Ramiro Massol asked for advice on segmenting his cell images, so I gave it a try.  I'm not a microscopy expert,\r\n      though, and I invite readers who have better suggestions than mine to add your comments below.\r\n   <\/p>\r\n   <p>Let's take a look first to see what we have.  I'm going to work with a cropped version of the <a href=\"https:\/\/blogs.mathworks.com\/images\/steve\/60\/nuclei.png\">original<\/a> so that the images aren't too big for the layout of this blog.\r\n   <\/p>\r\n   <p><i>Note: you can download the functions imcredit and <a title=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=10502&amp;objectType=file (link no longer works)\">imoverlay<\/a> from MATLAB Central.<\/i><\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">I = imread(<span style=\"color: #A020F0\">'https:\/\/blogs.mathworks.com\/images\/steve\/60\/nuclei.png'<\/span>);\r\nI_cropped = I(400:900, 465:965);\r\nimshow(I_cropped)\r\nimcredit(<span style=\"color: #A020F0\">'Image courtesy of Dr. Ramiro Massol'<\/span>)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/60\/cell_segmentation_01.jpg\"> <p>Strictly speaking, contrast adjustment isn't usually necessary for segmentation, but it can help the algorithm developer see\r\n      and understand the image data better.  This is a fairly low-contrast image, so I thought it might help.  You can adjust the\r\n      display contrast interactively with imtool, or you can use an automatic method such as adapthisteq. <tt>adapthisteq<\/tt> implements a technique called <i>contrast-limited adaptive histogram equalization<\/i>, or CLAHE.  (I always thought  \"CLAHE\" sounded like it must be some Klingon delicacy.)\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">I_eq = adapthisteq(I_cropped);\r\nimshow(I_eq)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/60\/cell_segmentation_02.jpg\"> <p>So what happens if we just apply a threshold now?<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">bw = im2bw(I_eq, graythresh(I_eq));\r\nimshow(bw)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/60\/cell_segmentation_03.jpg\"> <p>Let's clean that up and then overlay the perimeter on the original image.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">bw2 = imfill(bw,<span style=\"color: #A020F0\">'holes'<\/span>);\r\nbw3 = imopen(bw2, ones(5,5));\r\nbw4 = bwareaopen(bw3, 40);\r\nbw4_perim = bwperim(bw4);\r\noverlay1 = imoverlay(I_eq, bw4_perim, [.3 1 .3]);\r\nimshow(overlay1)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/60\/cell_segmentation_04.jpg\"> <p>Now, I'm not familiar with these cell images, so I don't know exactly what I'm looking at.  I assume some of these blobs need\r\n      more help to be separated properly.  One possible approach is called <i>marker-based watershed segmentation<\/i>.  There's a <i>demo<\/i> of this idea on The MathWorks web site.\r\n   <\/p>\r\n   <p>With this method, you have to find a way to \"mark\" at least a partial group of connected pixels inside each object to be segmented.\r\n      You also have to mark the background.\r\n   <\/p>\r\n   <p>Let's try to use the bright objects, which I assume are nuclei.  The <i>extended maxima<\/i> operator can be used to identify groups of pixels that are significantly higher than their immediate surrounding.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">mask_em = imextendedmax(I_eq, 30);\r\nimshow(mask_em)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/60\/cell_segmentation_05.jpg\"> <p>Let's clean that up and then overlay it.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">mask_em = imclose(mask_em, ones(5,5));\r\nmask_em = imfill(mask_em, <span style=\"color: #A020F0\">'holes'<\/span>);\r\nmask_em = bwareaopen(mask_em, 40);\r\noverlay2 = imoverlay(I_eq, bw4_perim | mask_em, [.3 1 .3]);\r\nimshow(overlay2)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/60\/cell_segmentation_06.jpg\"> <p>Next step: complement the image so that the peaks become valleys.  We do this because we are about to apply the watershed\r\n      transform, which identifies low points, not high points.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">I_eq_c = imcomplement(I_eq);<\/pre><p>Next: modify the image so that the background pixels and the extended maxima pixels are forced to be the only local minima\r\n      in the image.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">I_mod = imimposemin(I_eq_c, ~bw4 | mask_em);<\/pre><p>Now compute the watershed transform.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">L = watershed(I_mod);\r\nimshow(label2rgb(L))<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/60\/cell_segmentation_07.jpg\"> <p>I don't know if this is a good segmentation result or not, but I hope some of the methods I've shown will give Dr. Massol\r\n      some ideas to try.\r\n   <\/p>\r\n   <p>Other readers ... if you have suggestions, I invite you to post your comments here.<\/p>\r\n <script language=\"JavaScript\"> \r\n<!--\r\n    function grabCode_60() {\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='60 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 60';\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 2006 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      <\/script>\r\n<noscript>\r\n<em>A JavaScript-enabled browser is required to use the \"Get the MATLAB code\" link.<\/em>\r\n<\/noscript>\r\n<p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_60()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n            the MATLAB code<\/span><\/a><br><br>\r\n      Published with MATLAB&reg; 7.2<br><\/p>\r\n<\/div>\r\n<!--\r\n60 ##### SOURCE BEGIN #####\r\n%%\r\n% Blog reader Ramiro Massol asked for advice on segmenting his cell images, so I\r\n% gave it a try.  I'm not a microscopy expert, though, and I invite readers\r\n% who have better suggestions than mine to add your comments below.\r\n%\r\n% Let's take a look first to see what we have.  I'm going to work \r\n% with a cropped version of the <https:\/\/blogs.mathworks.com\/images\/steve\/60\/nuclei.png original> \r\n% so that the image's aren't too big\r\n% for the layout of this blog.\r\n%\r\n% _Note: you can download the functions \r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=9908&objectType=file imcredit> \r\n% and <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=10502&objectType=file imoverlay>\r\n% from MATLAB Central._\r\n\r\nI = imread('nuclei.png');\r\nI_cropped = I(400:900, 465:965);\r\nimshow(I_cropped)\r\nimcredit('Image courtesy of Dr. Ramiro Massol')\r\n\r\n%%\r\n% Strictly speaking, contrast adjustment isn't usually necessary for\r\n% segmentation, but it can help the algorithm developer see and understand\r\n% the image data better.  This is a fairly low-contrast image, so I thought\r\n% it might help.  You can adjust the display contrast interactively with\r\n% <https:\/\/www.mathworks.com\/help\/images\/index.htmlimtool.html |imtool|>, \r\n% or you can use an automatic method such as <https:\/\/www.mathworks.com\/help\/images\/index.htmladapthisteq.html |adapthisteq|>.\r\n% |adapthisteq| implements a technique called _contrast-limited adaptive\r\n% histogram equalization_, or CLAHE.  (I always thought  \"CLAHE\" sounded\r\n% like it must be some Klingon delicacy.)\r\n\r\nI_eq = adapthisteq(I_cropped);\r\nimshow(I_eq)\r\n\r\n%%\r\n% So what happens if we just apply a threshold now?\r\n\r\nbw = im2bw(I_eq, graythresh(I_eq));\r\nimshow(bw)\r\n\r\n%%\r\n% Let's clean that up and then overlay the perimeter on the original image.\r\n\r\nbw2 = imfill(bw,'holes');\r\nbw3 = imopen(bw2, ones(5,5));\r\nbw4 = bwareaopen(bw3, 40);\r\nbw4_perim = bwperim(bw4);\r\noverlay1 = imoverlay(I_eq, bw4_perim, [.3 1 .3]);\r\nimshow(overlay1)\r\n\r\n%%\r\n% Now, I'm not familiar with these cell images, so I don't know exactly\r\n% what I'm looking at.  I assume some of these blobs need more help to be\r\n% separated properly.  One possible approach is called _marker-based\r\n% watershed segmentation_.  There's a <https:\/\/www.mathworks.com\/products.htmldemos\/image\/watershed\/ipexwatershed.html _demo_> \r\n% of this idea on The MathWorks\r\n% web site.\r\n%\r\n% With this method, you have to find a way to \"mark\" at least a partial\r\n% group of connected pixels inside each object to be segmented. You also\r\n% have to mark the background.\r\n%\r\n% Let's try to use the bright objects, which I assume are nuclei.  The\r\n% _extended maxima_ operator can be used to identify groups of pixels that\r\n% are significantly higher than their immediate surrounding.\r\n\r\nmask_em = imextendedmax(I_eq, 30);\r\nimshow(mask_em)\r\n\r\n%%\r\n% Let's clean that up and then overlay it.\r\nmask_em = imclose(mask_em, ones(5,5));\r\nmask_em = imfill(mask_em, 'holes');\r\nmask_em = bwareaopen(mask_em, 40);\r\noverlay2 = imoverlay(I_eq, bw4_perim | mask_em, [.3 1 .3]);\r\nimshow(overlay2)\r\n\r\n%%\r\n% Next step: complement the image so that the peaks become valleys.  We do\r\n% this because we are about to apply the watershed transform, which\r\n% identifies low points, not high points.\r\n\r\nI_eq_c = imcomplement(I_eq);\r\n\r\n%%\r\n% Next: modify the image so that the background pixels and the extended\r\n% maxima pixels are forced to be the only local minima in the image.\r\n\r\nI_mod = imimposemin(I_eq_c, ~bw4 | mask_em);\r\n\r\n%%\r\n% Now compute the watershed transform.\r\n\r\nL = watershed(I_mod);\r\nimshow(label2rgb(L))\r\n\r\n%%\r\n% I don't know if this is a good segmentation result or not, but I hope\r\n% some of the methods I've shown will give Dr. Massol some ideas to try.\r\n%\r\n% Other readers ... if you have suggestions, I invite you to post your\r\n% comments here.\r\n##### SOURCE END ##### 60\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   Blog reader Ramiro Massol asked for advice on segmenting his cell images, so I gave it a try.  I'm not a microscopy expert,\r\n      though, and I invite readers who have better suggestions than... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2006\/06\/02\/cell-segmentation\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[134,138,140,82,84,144,146,142,136,148,108,76,36,152,150],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/60"}],"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=60"}],"version-history":[{"count":4,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/60\/revisions"}],"predecessor-version":[{"id":2394,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/60\/revisions\/2394"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=60"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=60"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=60"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}