{"id":2230,"date":"2008-05-23T14:30:49","date_gmt":"2008-05-23T19:30:49","guid":{"rendered":"https:\/\/blogs.mathworks.com\/pick\/2008\/05\/23\/detecting-circles-in-an-image\/"},"modified":"2016-05-11T10:08:16","modified_gmt":"2016-05-11T14:08:16","slug":"detecting-circles-in-an-image","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/pick\/2008\/05\/23\/detecting-circles-in-an-image\/","title":{"rendered":"Detecting Circles in an Image"},"content":{"rendered":"<div class=\"content\">\r\n\r\nA customer recently provided me with an image of cells that were roughly circular, but not very well defined, and often overlapping.\r\nHe asked how we might use MATLAB and the <a href=\"https:\/\/www.mathworks.com\/products\/image\/\">Image Processing Toolbox<\/a> to segment the cells in the presence of noise.\r\n\r\nMany of us know the Hough transform functionality in the Image Processing Toolbox, and the ability of that function to detect\r\nlines in an image. With some modifications, the Hough transform can be used to find other shapes as well. In this case, I\r\nwanted to find circles; a quick search for \"detect circles\" on the MATLAB Central File Exchange revealed Tao Peng's implementation of the <a title=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=9168&amp;objectType=FILE (link no longer works)\">Circular Hough Transform<\/a>. After an easy download, I was on my way to solving the problem. Tao's file is Brett's choice for this week's Pick of the Week.\r\n\r\n(Thanks to Cem Girit at Biopticon Corp. for permission to use his image!)\r\n\r\n&nbsp;\r\n<h3>Contents<\/h3>\r\n<div>\r\n<ul>\r\n\t<li><a href=\"#1\">Read, Display image<\/a><\/li>\r\n\t<li><a href=\"#2\">How would you segment this?<\/a><\/li>\r\n\t<li><a href=\"#3\">Discarding color information<\/a><\/li>\r\n\t<li><a href=\"#4\">Parameters for the segmentation<\/a><\/li>\r\n\t<li><a href=\"#5\">Now for the segmentation...<\/a><\/li>\r\n\t<li><a href=\"#6\">...and a bit of post-processing<\/a><\/li>\r\n\t<li><a href=\"#8\">View the results<\/a><\/li>\r\n\t<li><a href=\"#10\">Final comments<\/a><\/li>\r\n<\/ul>\r\n<\/div>\r\n<h3>Read, Display image<a name=\"1\"><\/a><\/h3>\r\n<pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid #c8c8c8;\">togglefig <span style=\"color: #a020f0;\">Original<\/span>\r\n<span style=\"color: #228b22;\">% Note: togglefig is a utility function I wrote for managing figure<\/span>\r\n<span style=\"color: #228b22;\">% windows, and is available<\/span>\r\n<span style=\"color: #228b22;\">% &lt;https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=18220&amp;objectType=file here&gt;.<\/span>\r\nimg = imread(<span style=\"color: #a020f0;\">'NB1ln1.jpg'<\/span>);\r\nimshow(img);<\/pre>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/pick\/circular_hough_potw_01.png\" alt=\"\" hspace=\"5\" vspace=\"5\" \/>\r\n<h3>How would you segment this?<a name=\"2\"><\/a><\/h3>\r\nSegmentation is often a very challenging task, particularly with noisy images like this one. One might try simple threshold\r\nor edge detection routines, or more sophisticated watershed approaches, for instance. Unfortunately, none of these approaches\r\nwas giving me the results I wanted for this particular image. I was at a bit of a loss--until I found CircularHough_Grd. Tao\r\nsuggests some filtering operations in his very helpful comments. For this demonstration, I'm going to see how the algorithm\r\ncan perform with just some very minor pre-processing.\r\n<h3>Discarding color information<a name=\"3\"><\/a><\/h3>\r\nTao's function works directly on grayscale images. Rather than converting the color image to grayscale with the Image Processing\r\nToolbox's RGB2GRAY function, I elected to simply use the first (red) color plane, and to use adaptive histogram equalization:\r\n<pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid #c8c8c8;\">togglefig(<span style=\"color: #a020f0;\">'Red Plane, Adjusted'<\/span>)\r\nred = img(:,:,2);\r\nred = adapthisteq(red);\r\nimshow(red)<\/pre>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/pick\/circular_hough_potw_02.png\" alt=\"\" hspace=\"5\" vspace=\"5\" \/>\r\n<h3>Parameters for the segmentation<a name=\"4\"><\/a><\/h3>\r\nBefore I segment, I needed to know how big the cells were in the image; CircularHough_Grd takes as an input a range of radii\r\nto search for. Using the IMDISTLINE function in the Image Processing Toolbox's IMTOOL, I estimated that the radii of interest\r\nrange from about 5 to 25 pixels.\r\n\r\nYou can play around with the other input arguments to modify the function's sensitivity to less-than-perfect circles, for\r\ninstance, or to change the way it deals with concentric circles. This makes the program pretty flexible!\r\n<h3>Now for the segmentation...<a name=\"5\"><\/a><\/h3>\r\n<pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid #c8c8c8;\">tic;\r\n[accum, circen, cirrad] = <span style=\"color: #0000ff;\">...<\/span>\r\n    CircularHough_Grd(red, [5 25],<span style=\"color: #0000ff;\">...<\/span>\r\n    20, 13, 1);\r\ntoc<\/pre>\r\n<pre style=\"font-style: oblique;\">Elapsed time is 4.169272 seconds.\r\n<\/pre>\r\n<h3>...and a bit of post-processing<a name=\"6\"><\/a><\/h3>\r\nNote to Tao: occasionally, your algorithm returns zero-radii \"hits\":\r\n<pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid #c8c8c8;\">any(cirrad &lt;= 0)<\/pre>\r\n<pre style=\"font-style: oblique;\">ans =\r\n     1\r\n<\/pre>\r\nThis is easy to address (for instance, to keep the RECTANGLE command below from erroring), but might be an opportunity for\r\nenhancement.\r\n<pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid #c8c8c8;\"><span style=\"color: #0000ff;\">if<\/span> any(cirrad &lt;= 0)\r\n    inds = find(cirrad&gt;0);\r\n    cirrad = cirrad(inds);\r\n    circen = circen(inds,:);\r\n<span style=\"color: #0000ff;\">end<\/span><\/pre>\r\n<h3>View the results<a name=\"8\"><\/a><\/h3>\r\nNow let's see how well the algorithm performed:\r\n<pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid #c8c8c8;\">togglefig <span style=\"color: #a020f0;\">Results<\/span>\r\nimshow(img);\r\nhold <span style=\"color: #a020f0;\">on<\/span>;\r\nplot(circen(:,1), circen(:,2), <span style=\"color: #a020f0;\">'r+'<\/span>);\r\n<span style=\"color: #0000ff;\">for<\/span> ii = 1 : size(circen, 1)\r\n    rectangle(<span style=\"color: #a020f0;\">'Position'<\/span>,[circen(ii,1) - cirrad(ii), circen(ii,2) - cirrad(ii), 2*cirrad(ii), 2*cirrad(ii)],<span style=\"color: #0000ff;\">...<\/span>\r\n        <span style=\"color: #a020f0;\">'Curvature'<\/span>, [1,1], <span style=\"color: #a020f0;\">'edgecolor'<\/span>, <span style=\"color: #a020f0;\">'b'<\/span>, <span style=\"color: #a020f0;\">'linewidth'<\/span>, 1.5);\r\n<span style=\"color: #0000ff;\">end<\/span>\r\nhold <span style=\"color: #a020f0;\">off<\/span>;<\/pre>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/pick\/circular_hough_potw_03.png\" alt=\"\" hspace=\"5\" vspace=\"5\" \/>\r\n\r\nThat's pretty remarkable, especially given the simplicity of my pre-processing. (Adaptive histogram equilization helped a\r\nlot; Tao's suggested filters improve the performance even more.)\r\n<h3>Final comments<a name=\"10\"><\/a><\/h3>\r\nThe time it takes to run this algorithm varies markedly, depending on the user settings. In this case, it took my computer\r\napproximately 4 seconds--but did a pretty amazing job of segmentating the image. Note how well it handled overlapping cells\r\n(circles), for instance:\r\n<pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid #c8c8c8;\">togglefig <span style=\"color: #a020f0;\">Blowup<\/span>\r\nimshow(img)\r\nxlims = [406 520];\r\nylims = [52 143];\r\nset(gca,<span style=\"color: #a020f0;\">'xlim'<\/span>,xlims,<span style=\"color: #a020f0;\">'ylim'<\/span>,ylims)\r\ninImageCircles = find(inpolygon(circen(:,1), circen(:,2), xlims([1 2 2 1]), ylims([1 1 2 2])));\r\n<span style=\"color: #0000ff;\">for<\/span> ii = 1 : numel(inImageCircles)\r\n    rectangle(<span style=\"color: #a020f0;\">'Position'<\/span>,<span style=\"color: #0000ff;\">...<\/span>\r\n        [circen(inImageCircles(ii),1) - cirrad(inImageCircles(ii)),<span style=\"color: #0000ff;\">...<\/span>\r\n        circen(inImageCircles(ii),2) - cirrad(inImageCircles(ii)),<span style=\"color: #0000ff;\">...<\/span>\r\n        2*cirrad(inImageCircles(ii)),<span style=\"color: #0000ff;\">...<\/span>\r\n        2*cirrad(inImageCircles(ii))],<span style=\"color: #0000ff;\">...<\/span>\r\n        <span style=\"color: #a020f0;\">'Curvature'<\/span>, [1,1], <span style=\"color: #a020f0;\">'edgecolor'<\/span>, <span style=\"color: #a020f0;\">'b'<\/span>, <span style=\"color: #a020f0;\">'linewidth'<\/span>, 1.5);\r\n<span style=\"color: #0000ff;\">end<\/span><\/pre>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/pick\/circular_hough_potw_04.png\" alt=\"\" hspace=\"5\" vspace=\"5\" \/>\r\n\r\nIf you do any image processing, you might recognize how Tao's function made a very challenging problem pretty manageable.\r\nIf you're not an image processing expert, this might seem a bit arcane to you...but hopefully you'll find some value in it\r\nanyway. I'd love to hear your comments!\r\n\r\n<script>\/\/ <![CDATA[\r\nfunction grabCode_42be364c7cc34e2fb4d07c78f0f731bd() {\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='42be364c7cc34e2fb4d07c78f0f731bd ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 42be364c7cc34e2fb4d07c78f0f731bd';\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 = 'Brett Shoelson';\r\n        copyright = 'Copyright 2008 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('\r\n\r\n<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>\r\n\r\n\r\n\\n');\r\n      \r\n      d.title = title + ' (MATLAB code)';\r\n      d.close();\r\n      }\r\n\/\/ ]]><\/script>\r\n<p style=\"text-align: right; font-size: xx-small; font-weight: lighter; font-style: italic; color: gray;\">\r\n<a><span style=\"font-size: x-small; font-style: italic;\">Get\r\nthe MATLAB code\r\n<noscript>(requires JavaScript)<\/noscript><\/span><\/a>\r\n\r\nPublished with MATLAB\u00ae 7.6<\/p>\r\n\r\n<\/div>\r\n<!--\r\n42be364c7cc34e2fb4d07c78f0f731bd ##### SOURCE BEGIN #####\r\n%% Segmment cells in an image?\r\n% A customer recently provided me with an image of cells that were roughly\r\n% circular, but not very well defined, and often overlapping. He asked how\r\n% we might use MATLAB and the <https:\/\/www.mathworks.com\/products\/image\/ Image Processing Toolbox>\r\n% to segment the cells in the presence of noise.\r\n%\r\n% Many of us know the Hough transform functionality in the Image Processing\r\n% Toolbox, and the ability of that function to detect lines in an image.\r\n% With some modifications, the Hough transform can be used to find other\r\n% shapes as well. In this case, I wanted to find circles; a quick search\r\n% for \"detect circles\" on the MATLAB Central File Exchange revealed\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadAuthor.do?objectId=1094843&objectType=author Tao Peng>'s\r\n% implementation of the\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=9168&objectType=FILE Circular Hough Transform>.\r\n% After an easy download, I was on my way to solving the problem. Tao's file is <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadAuthor.do?objectType=author&objectId=1093599 Brett>'s\r\n% choice for this week's Pick of the Week.\r\n%\r\n% (Thanks to Cem Girit at Biopticon Corp. for permission to use his image!)\r\n\r\n%% Read, Display image\r\ntogglefig Original\r\n% Note: togglefig is a utility function I wrote for managing figure\r\n% windows, and is available\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=18220&objectType=file here>.\r\nimg = imread('NB1ln1.jpg');\r\nimshow(img);\r\n\r\n%% How would you segment this?\r\n% Segmentation is often a very challenging task, particularly with noisy\r\n% images like this one. One might try simple threshold or edge detection\r\n% routines, or more sophisticated watershed approaches, for instance.\r\n% Unfortunately, none of these approaches was giving me the results I\r\n% wanted for this particular image. I was at a bit of a lossREPLACE_WITH_DASH_DASHuntil I found\r\n% CircularHough_Grd. Tao suggests some filtering operations in his very\r\n% helpful comments. For this demonstration, I'm going to see how the\r\n% algorithm can perform with just some very minor pre-processing.\r\n\r\n%% Discarding color information\r\n% Tao's function works directly on grayscale images. Rather than converting\r\n% the color image to grayscale with the Image Processing Toolbox's RGB2GRAY\r\n% function, I elected to  simply use the first (red) color plane, and to\r\n% use adaptive histogram equalization:\r\ntogglefig('Red Plane, Adjusted')\r\nred = img(:,:,2);\r\nred = adapthisteq(red);\r\nimshow(red)\r\n\r\n%% Parameters for the segmentation\r\n% Before I segment, I needed to know how big the cells were in the image;\r\n% CircularHough_Grd takes as an input a range of radii to search for. Using\r\n% the IMDISTLINE function in the Image Processing Toolbox's IMTOOL, I\r\n% estimated that the radii of interest range from about 5 to 25 pixels.\r\n%\r\n% You can play around with the other input arguments to modify the\r\n% function's sensitivity to less-than-perfect circles, for instance, or to\r\n% change the way it deals with concentric circles. This makes the program\r\n% pretty flexible!\r\n\r\n%% Now for the segmentation...\r\ntic;\r\n[accum, circen, cirrad] = ...\r\nCircularHough_Grd(red, [5 25],...\r\n20, 13, 1);\r\ntoc\r\n\r\n%% ...and a bit of post-processing\r\n% Note to Tao: occasionally, your algorithm returns zero-radii \"hits\":\r\nany(cirrad <= 0)\r\n%%\r\n% This is easy to address (for instance, to keep the RECTANGLE command below\r\n% from erroring), but might be an opportunity for enhancement.\r\nif any(cirrad <= 0) inds = find(cirrad>0);\r\ncirrad = cirrad(inds);\r\ncircen = circen(inds,:);\r\nend\r\n\r\n%% View the results\r\n% Now let's see how well the algorithm performed:\r\ntogglefig Results\r\nimshow(img);\r\nhold on;\r\nplot(circen(:,1), circen(:,2), 'r+');\r\nfor ii = 1 : size(circen, 1)\r\nrectangle('Position',[circen(ii,1) - cirrad(ii), circen(ii,2) - cirrad(ii), 2*cirrad(ii), 2*cirrad(ii)],...\r\n'Curvature', [1,1], 'edgecolor', 'b', 'linewidth', 1.5);\r\nend\r\nhold off;\r\n%%\r\n% That's pretty remarkable, especially given the simplicity of my\r\n% pre-processing. (Adaptive histogram equilization helped a lot; Tao's\r\n% suggested filters improve the performance even more.)\r\n\r\n%% Final comments\r\n% The time it takes to run this algorithm varies markedly, depending on the\r\n% user settings. In this case, it took my computer approximately 4\r\n% secondsREPLACE_WITH_DASH_DASHbut did a pretty amazing job of segmentating the image. Note how\r\n% well it handled overlapping cells (circles), for instance:\r\ntogglefig Blowup\r\nimshow(img)\r\nxlims = [406 520];\r\nylims = [52 143];\r\nset(gca,'xlim',xlims,'ylim',ylims)\r\ninImageCircles = find(inpolygon(circen(:,1), circen(:,2), xlims([1 2 2 1]), ylims([1 1 2 2])));\r\nfor ii = 1 : numel(inImageCircles)\r\nrectangle('Position',...\r\n[circen(inImageCircles(ii),1) - cirrad(inImageCircles(ii)),...\r\ncircen(inImageCircles(ii),2) - cirrad(inImageCircles(ii)),...\r\n2*cirrad(inImageCircles(ii)),...\r\n2*cirrad(inImageCircles(ii))],...\r\n'Curvature', [1,1], 'edgecolor', 'b', 'linewidth', 1.5);\r\nend\r\n%%\r\n% If you do any image processing, you might recognize how Tao's function\r\n% made a very challenging problem pretty manageable. If you're not an image\r\n% processing expert, this might seem a bit arcane to you...but hopefully\r\n% you'll find some value in it anyway. I'd love to hear your comments!\r\n##### SOURCE END ##### 42be364c7cc34e2fb4d07c78f0f731bd\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n\r\nA customer recently provided me with an image of cells that were roughly circular, but not very well defined, and often overlapping.\r\nHe asked how we might use MATLAB and the Image Processing... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/pick\/2008\/05\/23\/detecting-circles-in-an-image\/\">read more >><\/a><\/p>","protected":false},"author":34,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[16],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts\/2230"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/users\/34"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/comments?post=2230"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts\/2230\/revisions"}],"predecessor-version":[{"id":7134,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts\/2230\/revisions\/7134"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/media?parent=2230"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/categories?post=2230"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/tags?post=2230"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}