{"id":218,"date":"2008-07-07T09:40:02","date_gmt":"2008-07-07T13:40:02","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/2008\/07\/07\/nonlinear-operations-using-imfilter\/"},"modified":"2019-10-28T09:35:33","modified_gmt":"2019-10-28T13:35:33","slug":"nonlinear-operations-using-imfilter","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2008\/07\/07\/nonlinear-operations-using-imfilter\/","title":{"rendered":"Nonlinear operations using imfilter"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <introduction>\r\n      <p>Some nonlinear image processing operations can be expressed in terms of linear filtering.  When this is true, it often provides\r\n         a recipe for a speedy MATLAB implementation. Today I'll show two examples: Computing the local standard deviation, and computing\r\n         the local geometric mean.\r\n      <\/p>\r\n   <\/introduction>\r\n   <p>The local standard deviation operator is often used as a measure of \"busy-ness\" throughout the image.  For each pixel, the\r\n      standard deviation of that pixel's neighbors is computed:\r\n   <\/p>\r\n   <p>sqrt( sum ( (x - xbar).^2 ) )<\/p>\r\n   <p>(Scale factors omitted.)<\/p>\r\n   <p>Mathematically, the summation can be rewritten as:<\/p>\r\n   <p>sum(x.^2) - sum(x).^2\/N<\/p>\r\n   <p>Both of these local sums can be computed by using <tt>imfilter<\/tt> with an all-ones filter.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">I = im2double(imread(<span style=\"color: #A020F0\">'cameraman.tif'<\/span>));\r\nimshow(I)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2008\/nonlinear_filtering_using_imfilter_01.png\"> <p><i>Original image credit: Massachusetts Institute of Technology<\/i><\/p>\r\n   <p>To compute the sum(x.^2) term, we square (elementwise) the input to <tt>imfilter<\/tt>.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">h = ones(5,5);\r\nterm1 = imfilter(I.^2, h);\r\nimshow(term1, [])<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2008\/nonlinear_filtering_using_imfilter_02.png\"> <p>Notice the dark band around the edge.  This is because <tt>imfilter<\/tt> zero-pads by default.  We might want to use the 'symmetric' option instead.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">term1 = imfilter(I.^2, h, <span style=\"color: #A020F0\">'symmetric'<\/span>);\r\nimshow(term1, [])<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2008\/nonlinear_filtering_using_imfilter_03.png\"> <p>To compute the sum(x).^2 term, we square the output of <tt>imfilter<\/tt>.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">term2 = imfilter(I, h, <span style=\"color: #A020F0\">'symmetric'<\/span>).^2 \/ numel(h);\r\nimshow(term2, [])<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2008\/nonlinear_filtering_using_imfilter_04.png\"> <p>Then we subtract the second term from the first and take the square root.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">local_std = sqrt(term1 - term2);  <span style=\"color: #228B22\">% scale factor omitted<\/span>\r\nimshow(local_std, [])<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2008\/nonlinear_filtering_using_imfilter_05.png\"> <p><i>Cautionary notes<\/i><\/p>\r\n   <div>\r\n      <ul>\r\n         <li>The procedure shown here is not always a numerically sound way to compute the standard deviation, because it can suffer from\r\n            both overflow (squared terms) and underflow (cancellation in the subtraction of large numbers).  For typical image pixel values\r\n            and window sizes, though, it works reasonably well.\r\n         <\/li>\r\n      <\/ul>\r\n   <\/div>\r\n   <div>\r\n      <ul>\r\n         <li>Round-off error in the computation of term1 and term2 can sometimes make (term1 - term2) go slightly negative, resulting in\r\n            complex outputs from square root operator.  Avoid this problem by using this code:\r\n         <\/li>\r\n      <\/ul>\r\n   <\/div><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">local_std = sqrt(max(term1 - term2, 0));<\/pre><p>Recent releases of the Image Processing Toolbox include the function <tt>stdfilt<\/tt>, which does all this work for you.\r\n   <\/p>\r\n   <p>The local geometric mean filter multiplies together all the pixel values in the neighborhood and then takes the N-th root,\r\n      where N is the number of pixels in the neighborhood. The geometric mean filter is said to be slightly better than the arithmetic\r\n      mean at preserving image detail.\r\n   <\/p>\r\n   <p>Use the old logarithm trick to express the geometric mean in terms of a summation.  Then <tt>imfilter<\/tt> can be used to compute the neighborhood summation, like this.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">geo_mean = imfilter(log(I), h, <span style=\"color: #A020F0\">'replicate'<\/span>);\r\ngeo_mean = exp(geo_mean);\r\ngeo_mean = geo_mean .^ (1\/numel(h));\r\n\r\nimshow(geo_mean, [])<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2008\/nonlinear_filtering_using_imfilter_06.png\"> <p>Does anyone have other creative uses of <tt>imfilter<\/tt> to share?\r\n   <\/p><script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_bf9ac122c5a74882aae0188f14e914e9() {\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='bf9ac122c5a74882aae0188f14e914e9 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' bf9ac122c5a74882aae0188f14e914e9';\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 2008 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-->\r\n<\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\">Copyright 2008 The MathWorks, Inc.<br><a href=\"javascript:grabCode_bf9ac122c5a74882aae0188f14e914e9()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n            the MATLAB code \r\n            <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; 7.6<br><\/p>\r\n<\/div>\r\n<!--\r\nbf9ac122c5a74882aae0188f14e914e9 ##### SOURCE BEGIN #####\r\n%% Nonlinear filtering using imfilter\r\n% Some nonlinear image processing operations can be expressed in\r\n% terms of linear filtering.  When this is true, it often\r\n% provides a recipe for a speedy MATLAB implementation. Today I'll\r\n% show two examples: Computing the local standard deviation, and\r\n% computing the local geometric mean.\r\n\r\n%% \r\n% The local standard deviation operator is often used as a measure\r\n% of \"busy-ness\" throughout the image.  For each pixel, the standard\r\n% deviation of that pixel's neighbors is computed:\r\n%\r\n% sqrt( sum ( (x - xbar).^2 ) )\r\n%\r\n% (Scale factors omitted.)\r\n%\r\n% Mathematically, the summation can be rewritten as:\r\n%\r\n% sum(x.^2) - sum(x).^2\/N\r\n%\r\n% Both of these local sums can be computed by using \r\n% <https:\/\/www.mathworks.com\/help\/images\/index.htmlimfilter.html \r\n% |imfilter|> with\r\n% an all-ones filter.\r\n\r\nI = im2double(imread('cameraman.tif'));\r\nimshow(I)\r\n\r\n%%\r\n% _Original image credit: Massachusetts Institute of Technology_\r\n%\r\n% To compute the sum(x.^2) term, we square (elementwise) the input\r\n% to |imfilter|. \r\n\r\nh = ones(5,5);\r\nterm1 = imfilter(I.^2, h);\r\nimshow(term1, [])\r\n\r\n%%\r\n% Notice the dark band around the edge.  This is because |imfilter|\r\n% zero-pads by default.  We might want to use the 'symmetric'\r\n% option instead.\r\n\r\nterm1 = imfilter(I.^2, h, 'symmetric');\r\nimshow(term1, [])\r\n\r\n%%\r\n% To compute the sum(x).^2 term, we square the output of\r\n% |imfilter|.\r\n\r\nterm2 = imfilter(I, h, 'symmetric').^2 \/ numel(h);\r\nimshow(term2, [])\r\n\r\n%%\r\n% Then we subtract the second term from the first and take the\r\n% square root.\r\n\r\nlocal_std = sqrt(term1 - term2);  % scale factor omitted\r\nimshow(local_std, [])\r\n\r\n%%\r\n% _Cautionary notes_\r\n%\r\n% * The procedure shown here is not always a numerically sound\r\n% way to compute the standard deviation, because it can suffer\r\n% from both overflow (squared terms) and underflow (cancellation\r\n% in the subtraction of large numbers).  For typical image\r\n% pixel values and window sizes, though, it works reasonably\r\n% well.\r\n%\r\n% * Round-off error in the computation of term1 and term2 can\r\n% sometimes make (term1 - term2) go slightly negative, resulting\r\n% in complex outputs from square root operator.  Avoid this\r\n% problem by using this code:\r\n\r\nlocal_std = sqrt(max(term1 - term2, 0));\r\n\r\n%%\r\n% Recent releases of the Image Processing Toolbox include the\r\n% function \r\n% <https:\/\/www.mathworks.com\/help\/images\/index.htmlstdfilt.html \r\n% |stdfilt|>, which does all this work for you.\r\n\r\n%% \r\n% The local geometric mean filter multiplies together\r\n% all the pixel values in the neighborhood and then takes the\r\n% N-th root, where N is the number of pixels in the neighborhood.\r\n% The geometric mean filter is said to be slightly better than\r\n% the arithmetic mean at preserving image detail.\r\n%\r\n% Use the old logarithm trick to express the geometric mean in\r\n% terms of a summation.  Then |imfilter| can be used to compute the\r\n% neighborhood summation, like this.\r\n\r\ngeo_mean = imfilter(log(I), h, 'replicate');\r\ngeo_mean = exp(geo_mean);\r\ngeo_mean = geo_mean .^ (1\/numel(h));\r\n\r\nimshow(geo_mean, [])\r\n\r\n%%\r\n% Does anyone have other creative uses of |imfilter| to share?\r\n\r\n%%\r\n% Copyright 2008 The MathWorks, Inc.\r\n##### SOURCE END ##### bf9ac122c5a74882aae0188f14e914e9\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   \r\n      Some nonlinear image processing operations can be expressed in terms of linear filtering.  When this is true, it often provides\r\n         a recipe for a speedy MATLAB implementation.... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2008\/07\/07\/nonlinear-operations-using-imfilter\/\">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":[508,390,370,36,224,162,288,194,126],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/218"}],"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=218"}],"version-history":[{"count":2,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/218\/revisions"}],"predecessor-version":[{"id":2266,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/218\/revisions\/2266"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=218"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=218"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=218"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}