{"id":2614,"date":"2017-06-20T07:00:27","date_gmt":"2017-06-20T11:00:27","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=2614"},"modified":"2019-11-01T17:12:50","modified_gmt":"2019-11-01T21:12:50","slug":"customizing-regionprops-with-your-own-measurements","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2017\/06\/20\/customizing-regionprops-with-your-own-measurements\/","title":{"rendered":"Customizing REGIONPROPS With Your Own Measurements"},"content":{"rendered":"<div class=\"content\"><p>I saw a presentation last month that mentioned a user request to have the ability to customize <tt>regionprops<\/tt>. That is, a user wanted to be able to add their own measurement to <tt>regionprops<\/tt>.<\/p><p>Today, I'll show you how to do this yourself.<\/p><p>First, here's a brief recap on what <tt>regionprops<\/tt> does. The function computes measurements of image regions. Some of these measurements are based purely on a region's shape, while others incorporate pixel values within the regions. Here's an example using the coins.png sample image.<\/p><pre class=\"codeinput\">I = imread(<span class=\"string\">'coins.png'<\/span>);\r\nimshow(I)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/regionprops_custom_measurements_01.png\" alt=\"\"> <p>Let's convert this image to binary, using adaptive thresholding, filling holes, and removing small \"noise\" pixels.<\/p><pre class=\"codeinput\">bw = imbinarize(I,<span class=\"string\">'adaptive'<\/span>);\r\nbw = imfill(bw,<span class=\"string\">'holes'<\/span>);\r\nbw = bwareafilt(bw,[100 Inf]);\r\nimshow(bw)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/regionprops_custom_measurements_02.png\" alt=\"\"> <p>You can count the \"blobs\" (object) yourself; there are 10 of them.<\/p><p>The simplest <tt>regionprops<\/tt> call, <tt>regionprops(bw)<\/tt> computes the <tt>Area<\/tt>, <tt>Centroid<\/tt>, and <tt>BoundingBox<\/tt> for each object.<\/p><pre class=\"codeinput\">s = regionprops(bw)\r\n<\/pre><pre class=\"codeoutput\">\r\ns = \r\n\r\n  10&times;1 struct array with fields:\r\n\r\n    Area\r\n    Centroid\r\n    BoundingBox\r\n\r\n<\/pre><p>But I don't think this is the best way to call <tt>regionprops<\/tt> anymore. You can now tell <tt>regionprops<\/tt> to return the results as a table.<\/p><pre class=\"codeinput\">t = regionprops(<span class=\"string\">'table'<\/span>,bw)\r\n<\/pre><pre class=\"codeoutput\">\r\nt =\r\n\r\n  10&times;3 table\r\n\r\n    Area        Centroid        BoundingBox \r\n    ____    ________________    ____________\r\n\r\n    2635    37.133    106.85    [1x4 double]\r\n    1846    56.131    49.693    [1x4 double]\r\n    2672    96.199    146.05    [1x4 double]\r\n    1839    109.97    84.848    [1x4 double]\r\n    2744    120.37    208.73    [1x4 double]\r\n    2520    148.57    34.404    [1x4 double]\r\n    2589    174.83    120.01    [1x4 double]\r\n    2518    216.81    70.649    [1x4 double]\r\n    1857    236.03    173.36    [1x4 double]\r\n    1829    265.96    102.64    [1x4 double]\r\n\r\n<\/pre><p>The table form is a lot more convenient for many tasks. For today's topic, one especially nice thing thing about tables is how easy it is to add your own variables to the table.<\/p><p>To illustrate, let's add a measurement that I've seen called <tt>Roundness<\/tt>. One definition for roundness is:<\/p><p>$R = \\frac{4A}{\\pi L^2}$<\/p><p>where $A$ is the object area and $L$ is the major axis length of the best-fit ellipse for the object. Here's how to compute roundness and add it directly to the measurements returned by <tt>regionprops<\/tt>.<\/p><p>First, note that both <tt>Area<\/tt> and <tt>MajorAxisLength<\/tt> are supported by <tt>regionprops<\/tt>, so let's start with those.<\/p><pre class=\"codeinput\">t = regionprops(<span class=\"string\">'table'<\/span>,bw,<span class=\"string\">'Area'<\/span>,<span class=\"string\">'MajorAxisLength'<\/span>)\r\n<\/pre><pre class=\"codeoutput\">\r\nt =\r\n\r\n  10&times;2 table\r\n\r\n    Area    MajorAxisLength\r\n    ____    _______________\r\n\r\n    2635     60.08         \r\n    1846    50.178         \r\n    2672    59.792         \r\n    1839    49.674         \r\n    2744    60.374         \r\n    2520     58.08         \r\n    2589    58.676         \r\n    2518    58.162         \r\n    1857     49.77         \r\n    1829    49.564         \r\n\r\n<\/pre><p>You access table variables using dot notation, like <tt>t.Area<\/tt>. Similarly, you can create a new table variable using dot notation and assignment, like <tt>t.MyVariable = ...<\/tt>. So adding <tt>Roundness<\/tt> to the table returned by <tt>regionprops<\/tt> is this simple.<\/p><pre class=\"codeinput\">t.Roundness = 4 * t.Area .\/ (pi * t.MajorAxisLength.^2)\r\n<\/pre><pre class=\"codeoutput\">\r\nt =\r\n\r\n  10&times;3 table\r\n\r\n    Area    MajorAxisLength    Roundness\r\n    ____    _______________    _________\r\n\r\n    2635     60.08             0.92945  \r\n    1846    50.178             0.93352  \r\n    2672    59.792              0.9516  \r\n    1839    49.674             0.94893  \r\n    2744    60.374              0.9585  \r\n    2520     58.08             0.95118  \r\n    2589    58.676             0.95745  \r\n    2518    58.162             0.94772  \r\n    1857     49.77             0.95453  \r\n    1829    49.564             0.94798  \r\n\r\n<\/pre><p>Let's try this computation with an image containing objects that are not quite as round.<\/p><pre class=\"codeinput\">I2 = imread(<span class=\"string\">'rice.png'<\/span>);\r\nimshow(I2)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/regionprops_custom_measurements_03.png\" alt=\"\"> <pre class=\"codeinput\">bw2 = imbinarize(I2,<span class=\"string\">'adaptive'<\/span>);\r\nbw2 = imfill(bw2,<span class=\"string\">'holes'<\/span>);\r\nbw2 = bwareafilt(bw2,[100 Inf]);\r\nimshow(bw2)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/regionprops_custom_measurements_04.png\" alt=\"\"> <pre class=\"codeinput\">t2 = regionprops(<span class=\"string\">'table'<\/span>,bw2,<span class=\"string\">'Area'<\/span>,<span class=\"string\">'MajorAxisLength'<\/span>);\r\nt2.Roundness = 4 * t2.Area .\/ (pi * t2.MajorAxisLength.^2);\r\nhead(t2)\r\n<\/pre><pre class=\"codeoutput\">\r\nans =\r\n\r\n  8&times;3 table\r\n\r\n    Area    MajorAxisLength    Roundness\r\n    ____    _______________    _________\r\n\r\n    138     23.594             0.31562  \r\n    120     18.152              0.4637  \r\n    169     28.123             0.27207  \r\n    157     23.793              0.3531  \r\n    284     43.757             0.18885  \r\n    200     26.259             0.36929  \r\n    141     21.647             0.38311  \r\n    177     29.087             0.26636  \r\n\r\n<\/pre><p>I'm a big fan of the (relatively) new <a title=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/histogram.html (link no longer works)\"><tt>histogram<\/tt><\/a> function in MATLAB, so let's use it to compare our roundness numbers. I will follow the advice given in the <a title=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/histogram.html (link no longer works)\"><tt>histogram<\/tt> reference page<\/a> for normalizing multiple histograms so that they can be more easily compared. I'll set the y-axis limits to [0 1], which is appropriate for probability normalization, and I'll set the x-axis limits to [0 1], which is the range for <tt>Roundness<\/tt>.<\/p><pre class=\"codeinput\">h1 = histogram(t.Roundness);\r\nhold <span class=\"string\">on<\/span>\r\nh2 = histogram(t2.Roundness);\r\nhold <span class=\"string\">off<\/span>\r\nh1.Normalization = <span class=\"string\">'probability'<\/span>;\r\nh2.Normalization = <span class=\"string\">'probability'<\/span>;\r\nh1.BinWidth = 0.02;\r\nh2.BinWidth = 0.02;\r\n\r\nxlim([0 1]);\r\nylim([0 1]);\r\n\r\ntitle(<span class=\"string\">'Histogram of roundness (probability normalization)'<\/span>)\r\nlegend(<span class=\"string\">'coins'<\/span>,<span class=\"string\">'rice'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/regionprops_custom_measurements_05.png\" alt=\"\"> <p>There you have it. You can add your own object measurements to the output of <tt>regionprops<\/tt>. It's especially easy if you tell <tt>regionprops<\/tt> to return a table.<\/p><p>I'll leave you with this question, dear reader: Are there measurements you would like us to add to <tt>regionprops<\/tt>? I am aware of an enhancement request for the Feret diameter. What else would you like to see?<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_a48d6dda1b9448eb98f5c08436b13322() {\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='a48d6dda1b9448eb98f5c08436b13322 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' a48d6dda1b9448eb98f5c08436b13322';\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 2017 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_a48d6dda1b9448eb98f5c08436b13322()\"><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; R2017a<br><\/p><\/div><!--\r\na48d6dda1b9448eb98f5c08436b13322 ##### SOURCE BEGIN #####\r\n%%\r\n% I saw a presentation last month that mentioned a user request to\r\n% have the ability to customize |regionprops|. That is, a user\r\n% wanted to be able to add their own measurement to |regionprops|.\r\n%\r\n% Today, I'll show you how to do this yourself.\r\n%\r\n% First, here's a brief recap on what |regionprops| does. The\r\n% function computes measurements of image regions. Some of these\r\n% measurements are based purely on a region's shape, while others\r\n% incorporate pixel values within the regions. Here's an example\r\n% using the coins.png sample image.\r\n\r\nI = imread('coins.png');\r\nimshow(I)\r\n\r\n%%\r\n% Let's convert this image to binary, using adaptive thresholding,\r\n% filling holes, and removing small \"noise\" pixels.\r\n\r\nbw = imbinarize(I,'adaptive');\r\nbw = imfill(bw,'holes');\r\nbw = bwareafilt(bw,[100 Inf]);\r\nimshow(bw)\r\n\r\n%%\r\n% You can count the \"blobs\" (object) yourself; there are 10 of them.\r\n%\r\n% The simplest |regionprops| call, |regionprops(bw)| computes the\r\n% |Area|, |Centroid|, and |BoundingBox| for each object.\r\n\r\ns = regionprops(bw)\r\n\r\n%%\r\n% But I don't think this is the best way to call |regionprops|\r\n% anymore. You can now tell |regionprops| to return the results as a\r\n% table.\r\n\r\nt = regionprops('table',bw)\r\n\r\n%%\r\n% The table form is a lot more convenient for many tasks. For\r\n% today's topic, one especially nice thing thing about tables is how\r\n% easy it is to add your own variables to the table.\r\n%\r\n% To illustrate, let's add a measurement that I've seen called\r\n% |Roundness|. One definition for roundness is:\r\n%\r\n% $R = \\frac{4A}{\\pi L^2}$\r\n%\r\n% where $A$ is the object area and $L$ is the major axis length of\r\n% the best-fit ellipse for the object. Here's how to compute\r\n% roundness and add it directly to the measurements returned by\r\n% |regionprops|.\r\n%\r\n% First, note that both |Area| and |MajorAxisLength| are supported\r\n% by |regionprops|, so let's start with those.\r\n\r\nt = regionprops('table',bw,'Area','MajorAxisLength')\r\n\r\n%%\r\n% You access table variables using dot notation, like |t.Area|.\r\n% Similarly, you can create a new table variable using dot notation\r\n% and assignment, like |t.MyVariable = ...|. So adding |Roundness|\r\n% to the table returned by |regionprops| is this simple.\r\n\r\nt.Roundness = 4 * t.Area .\/ (pi * t.MajorAxisLength.^2)\r\n\r\n%%\r\n% Let's try this computation with an image containing objects that\r\n% are not quite as round.\r\n\r\nI2 = imread('rice.png');\r\nimshow(I2)\r\n\r\n%%\r\nbw2 = imbinarize(I2,'adaptive');\r\nbw2 = imfill(bw2,'holes');\r\nbw2 = bwareafilt(bw2,[100 Inf]);\r\nimshow(bw2)\r\n\r\n%%\r\nt2 = regionprops('table',bw2,'Area','MajorAxisLength');\r\nt2.Roundness = 4 * t2.Area .\/ (pi * t2.MajorAxisLength.^2);\r\nhead(t2)\r\n\r\n%%\r\n% I'm a big fan of the (relatively) new \r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/histogram.html |histogram|> function in\r\n% MATLAB, so let's use it to compare our roundness numbers. I will\r\n% follow the advice given in the <https:\/\/www.mathworks.com\/help\/matlab\/ref\/histogram.html \r\n% |histogram| reference page> for\r\n% normalizing multiple histograms so that they can be more easily\r\n% compared. I'll set the y-axis limits to [0 1], which is\r\n% appropriate for probability normalization, and I'll set the x-axis\r\n% limits to [0 1], which is the range for |Roundness|.\r\n\r\nh1 = histogram(t.Roundness);\r\nhold on\r\nh2 = histogram(t2.Roundness);\r\nhold off\r\nh1.Normalization = 'probability';\r\nh2.Normalization = 'probability';\r\nh1.BinWidth = 0.02;\r\nh2.BinWidth = 0.02;\r\n\r\nxlim([0 1]);\r\nylim([0 1]);\r\n\r\ntitle('Histogram of roundness (probability normalization)')\r\nlegend('coins','rice')\r\n\r\n%%\r\n% There you have it. You can add your own object measurements to the\r\n% output of |regionprops|. It's especially easy if you tell\r\n% |regionprops| to return a table.\r\n%\r\n% I'll leave you with this question, dear reader: Are there\r\n% measurements you would like us to add to |regionprops|? I am aware\r\n% of an enhancement request for the Feret diameter. What else would\r\n% you like to see?\r\n##### SOURCE END ##### a48d6dda1b9448eb98f5c08436b13322\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/steve\/files\/regionprops_custom_measurements_05.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>I saw a presentation last month that mentioned a user request to have the ability to customize regionprops. That is, a user wanted to be able to add their own measurement to regionprops.Today, I'll... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2017\/06\/20\/customizing-regionprops-with-your-own-measurements\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":2620,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[1167,1183,1169,90,1155,136,76,36,92,532,168,52,360,298],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2614"}],"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=2614"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2614\/revisions"}],"predecessor-version":[{"id":2615,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2614\/revisions\/2615"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media\/2620"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=2614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=2614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=2614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}