{"id":1832,"date":"2016-05-16T14:20:29","date_gmt":"2016-05-16T18:20:29","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=1832"},"modified":"2019-11-01T16:41:30","modified_gmt":"2019-11-01T20:41:30","slug":"image-binarization-new-r2016a-functions","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2016\/05\/16\/image-binarization-new-r2016a-functions\/","title":{"rendered":"Image binarization &#8211; new R2016a functions"},"content":{"rendered":"<div class=\"content\"><p>In my <a href=\"https:\/\/blogs.mathworks.com\/steve\/2016\/05\/09\/image-binarization-im2bw-and-graythresh\/\">09-May-2016 post<\/a>, I described the Image Processing Toolbox functions <tt>im2bw<\/tt> and <tt>graythresh<\/tt>, which have been in the product for a long time. I also identified a few weaknesses in the functional designs:<\/p><div><ul><li>The function <tt>im2bw<\/tt> uses a fixed threshold value (<tt>LEVEL<\/tt>) of 0.5 by default. Using <tt>graythresh<\/tt> to determine the threshold value automatically would be a more useful behavior most of the time.<\/li><li>If you don't need to save the value of LEVEL, then you end up calling the functions in a slightly awkward way, passing the input image to each of the two functions: bw = im2bw(I,graythresh(I))<\/li><li>Although Otsu's method really only needs to know the image histogram, you have to pass in the image itself to the graythresh function. This is awkward for some use cases, such as using the collective histogram of multiple images in a dataset to compute a single threshold.<\/li><li>Some users wanted to control the number of histogram bins used by <tt>graythresh<\/tt>, which does not have that as an option. (I forgot to mention this item in my previous post.)<\/li><li>There was no locally adaptive thresholding method in the toolbox.<\/li><\/ul><\/div><p>For all of these reasons, the Image Processing Toolbox development undertook a redesign of binarization functionality for the R2016a release. The functional designs are different and the capabilities have been extended. We now encourage the use of a new family of functions:<\/p><div><ul><li><a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/imbinarize.html\"><tt>imbinarize<\/tt><\/a><\/li><li><a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/otsuthresh.html\"><tt>otsuthresh<\/tt><\/a><\/li><li><a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/adaptthresh.html\"><tt>adaptthresh<\/tt><\/a><\/li><\/ul><\/div><p>Binarization using an automatically computed threshold value is now simpler. Instead of two function calls, <tt>im2bw(I,graythresh(I))<\/tt>, you can do it with one, <tt>imbinarize(I)<\/tt>.<\/p><pre class=\"codeinput\">I = imread(<span class=\"string\">'cameraman.tif'<\/span>);\r\nimshow(I)\r\nxlabel(<span class=\"string\">'Cameraman image courtesy of MIT'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/binarization_part2_01.jpg\" alt=\"\"> <pre class=\"codeinput\">bw = imbinarize(I);\r\nimshowpair(I,bw,<span class=\"string\">'montage'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/binarization_part2_02.jpg\" alt=\"\"> <p>In addition to global thresholding, <tt>imbinarize<\/tt> can also do locally adaptive thresholding. Here is an example using an image with a mild illumination gradient from top to bottom.<\/p><pre class=\"codeinput\">I = imread(<span class=\"string\">'rice.png'<\/span>);\r\nbw = imbinarize(I);\r\nimshowpair(I,bw,<span class=\"string\">'montage'<\/span>)\r\ntitle(<span class=\"string\">'Original and global threshold'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/binarization_part2_03.jpg\" alt=\"\"> <p>You can see that the rice grains at the bottom of the image are imperfectly segmented because they are in a darker portion of the image. Now switch to an adaptive threshold.<\/p><pre class=\"codeinput\">bw = imbinarize(I,<span class=\"string\">'adaptive'<\/span>);\r\nimshowpair(I,bw,<span class=\"string\">'montage'<\/span>)\r\ntitle(<span class=\"string\">'Original and adaptive threshold'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/binarization_part2_04.jpg\" alt=\"\"> <p>Here is a more extreme example of nonuniform illumination.<\/p><pre class=\"codeinput\">I = imread(<span class=\"string\">'printedtext.png'<\/span>);\r\nimshow(I)\r\ntitle(<span class=\"string\">'Original image'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/binarization_part2_05.jpg\" alt=\"\"> <pre class=\"codeinput\">bw = imbinarize(I);\r\nimshow(bw)\r\ntitle(<span class=\"string\">'Global threshold'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/binarization_part2_06.jpg\" alt=\"\"> <p>Let's see how using an adaptive threshold can improve the results. Before jumping into it, though, notice that the foreground pixels in this image are darker than the background, which is the opposite of the rice grains image above. The adaptive method works better if it knows whether to look for foreground pixels that are brighter or darker than the background. The optional parameter <tt>'ForegroundPolarity'<\/tt> lets is specify that.<\/p><pre class=\"codeinput\">bw = imbinarize(I,<span class=\"string\">'adaptive'<\/span>,<span class=\"string\">'ForegroundPolarity'<\/span>,<span class=\"string\">'dark'<\/span>);\r\nimshow(bw)\r\ntitle(<span class=\"string\">'Adaptive threshold'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/binarization_part2_07.jpg\" alt=\"\"> <p>The new functions <tt>otsuthresh<\/tt> and <tt>adaptthresh<\/tt> are for those who want to have more fine-grained control over the algorithms underlying the global and adaptive thresholding behavior of <tt>imbinarize<\/tt>. I'll talk about them next time.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_6c4054d20ae2423ea15cffeaedaf60da() {\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='6c4054d20ae2423ea15cffeaedaf60da ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 6c4054d20ae2423ea15cffeaedaf60da';\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 2016 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_6c4054d20ae2423ea15cffeaedaf60da()\"><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; R2016a<br><\/p><\/div><!--\r\n6c4054d20ae2423ea15cffeaedaf60da ##### SOURCE BEGIN #####\r\n%%\r\n% In my\r\n% <https:\/\/blogs.mathworks.com\/steve\/2016\/05\/09\/image-binarization-im2bw-and-graythresh\/\r\n% 09-May-2016 post>, I described the Image Processing Toolbox functions\r\n% |im2bw| and |graythresh|, which have been in the product for a long time.\r\n% I also identified a few weaknesses in the functional designs:\r\n% \r\n% * The function |im2bw| uses a fixed threshold value (|LEVEL|) of 0.5 by\r\n% default. Using |graythresh| to determine the threshold value automatically\r\n% would be a more useful behavior most of the time.\r\n% * If you don't need to save the value of LEVEL, then you end up calling\r\n% the functions in a slightly awkward way, passing the input image to each\r\n% of the two functions: bw = im2bw(I,graythresh(I)) \r\n% * Although Otsu's method really only needs to know the image histogram,\r\n% you have to pass in the image itself to the graythresh function. This is\r\n% awkward for some use cases, such as using the collective histogram of\r\n% multiple images in a dataset to compute a single threshold.\r\n% * Some users wanted to control the number of histogram bins used by\r\n% |graythresh|, which does not have that as an option. (I forgot to mention\r\n% this item in my previous post.)\r\n% * There was no locally adaptive thresholding method in the toolbox.\r\n%\r\n% For all of these reasons, the Image Processing Toolbox development\r\n% undertook a redesign of binarization functionality for the R2016a release.\r\n% The functional designs are different and the capabilities have been\r\n% extended. We now encourage the use of a new family of functions:\r\n%\r\n% * <https:\/\/www.mathworks.com\/help\/images\/ref\/imbinarize.html |imbinarize|>\r\n% * <https:\/\/www.mathworks.com\/help\/images\/ref\/otsuthresh.html |otsuthresh|>\r\n% * <https:\/\/www.mathworks.com\/help\/images\/ref\/adaptthresh.html\r\n% |adaptthresh|>\r\n%\r\n% Binarization using an automatically computed threshold value is now\r\n% simpler. Instead of two function calls, |im2bw(I,graythresh(I))|, you can\r\n% do it with one, |imbinarize(I)|.\r\n\r\nI = imread('cameraman.tif');\r\nimshow(I)\r\nxlabel('Cameraman image courtesy of MIT')\r\n\r\n%%\r\nbw = imbinarize(I);\r\nimshowpair(I,bw,'montage')\r\n\r\n%%\r\n% In addition to global thresholding, |imbinarize| can also do locally\r\n% adaptive thresholding. Here is an example using an image with a mild\r\n% illumination gradient from top to bottom.\r\n\r\nI = imread('rice.png');\r\nbw = imbinarize(I);\r\nimshowpair(I,bw,'montage')\r\ntitle('Original and global threshold')\r\n\r\n%%\r\n% You can see that the rice grains at the bottom of the image are\r\n% imperfectly segmented because they are in a darker portion of the image.\r\n% Now switch to an adaptive threshold.\r\n\r\nbw = imbinarize(I,'adaptive');\r\nimshowpair(I,bw,'montage')\r\ntitle('Original and adaptive threshold')\r\n\r\n%%\r\n% Here is a more extreme example of nonuniform illumination.\r\n\r\nI = imread('printedtext.png');\r\nimshow(I)\r\ntitle('Original image')\r\n\r\n%%\r\nbw = imbinarize(I);\r\nimshow(bw)\r\ntitle('Global threshold')\r\n\r\n%%\r\n% Let's see how using an adaptive threshold can improve the results. Before\r\n% jumping into it, though, notice that the foreground pixels in this image\r\n% are darker than the background, which is the opposite of the rice grains\r\n% image above. The adaptive method works better if it knows whether to look\r\n% for foreground pixels that are brighter or darker than the background. The\r\n% optional parameter |'ForegroundPolarity'| lets is specify that.\r\n\r\nbw = imbinarize(I,'adaptive','ForegroundPolarity','dark');\r\nimshow(bw)\r\ntitle('Adaptive threshold')\r\n\r\n%%\r\n% The new functions |otsuthresh| and |adaptthresh| are for those who want to\r\n% have more fine-grained control over the algorithms underlying the global\r\n% and adaptive thresholding behavior of |imbinarize|. I'll talk about them\r\n% next time.\r\n##### SOURCE END ##### 6c4054d20ae2423ea15cffeaedaf60da\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/steve\/files\/binarization_part2_06.jpg\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>In my 09-May-2016 post, I described the Image Processing Toolbox functions im2bw and graythresh, which have been in the product for a long time. I also identified a few weaknesses in the functional... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2016\/05\/16\/image-binarization-new-r2016a-functions\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":1839,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[1159,82,84,1155,76,36,867,1157,52,94],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1832"}],"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=1832"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1832\/revisions"}],"predecessor-version":[{"id":1833,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1832\/revisions\/1833"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media\/1839"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=1832"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=1832"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=1832"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}