{"id":2473,"date":"2017-01-16T11:29:19","date_gmt":"2017-01-16T16:29:19","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=2473"},"modified":"2019-11-01T16:52:22","modified_gmt":"2019-11-01T20:52:22","slug":"aliasing-and-image-resizing-part-3","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2017\/01\/16\/aliasing-and-image-resizing-part-3\/","title":{"rendered":"Aliasing and image resizing &#8211; part 3"},"content":{"rendered":"<div class=\"content\"><p>Today I'll try to wrap up my discussion about how aliasing affects image resizing and about how the <tt>imresize<\/tt> function tries to prevent it. (This is called <i>antialiasing<\/i>.) Let me show you where we are going. Here is the zone plate image I showed last time.<\/p><pre class=\"codeinput\">Z = imzoneplate(501);\r\nimshow(Z)\r\ntitle(<span class=\"string\">'Z'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/antialiasing_3_01.png\" alt=\"\"> <p>Here's what happens when we shrink <tt>Z<\/tt> by throwing away samples.<\/p><pre class=\"codeinput\">Z4 = Z(1:4:end,1:4:end);\r\nimshow(Z4)\r\ntitle(<span class=\"string\">'Z4'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/antialiasing_3_02.png\" alt=\"\"> <p>And here's what <tt>imresize<\/tt> does (with the default behavior):<\/p><pre class=\"codeinput\">Z4_imresize = imresize(Z,0.25);\r\nimshow(Z4_imresize)\r\ntitle(<span class=\"string\">'imresize(Z,0.25)'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/antialiasing_3_03.png\" alt=\"\"> <p>Before I get started describing the <tt>imresize<\/tt> approach to antialiasing, I should say that there are several algorithm approaches to antialiasing in image resizing. This is just one. It is based on classical ideas from digital signal processing.<\/p><p>I'll use the notation that $f[n]$ is a function of a discrete variable $n$, and $f(x)$ is a function of a continuous variable $x$. Here's a way to think about the image resizing problem that is theoretically useful. To simplify the discussion for a bit, I'll talk in terms of 1-D signals. (Warning: people with a graduate-level background in digital signal processing will probably find this explanation too hand-wavy. Everybody else will find it too jargon-y. I'm doing my best.)<\/p><p>Suppose we want to convert a 1-D signal, $f[n]$, to another 1-D signal that has a different sampling rate.<\/p><div><ol><li>Convert the discrete-domain signal, $f[n]$, to a continuous-domain signal, $f(x)$. Theoretically, this step involves the use of a continuous-domain filter, $h(x)$. You can think of this step as a form of <b>interpolation<\/b> (for some choices of $h(x)$, anyway).<\/li><li>Convert the continuous-domain signal, $f(x)$, to a new discrete-domain signal, $g[n]$, by sampling $f(x)$ at the desired rate: $g[n] = f(nT)$.<\/li><\/ol><\/div><p>There's a problem with the two-step procedure above. If the sampling rate for $g[n]$ is reduced from the original sampling rate of $f[n]$, then the procedure is susceptible to aliasing distortion if $f[n]$ contains high frequencies that cannot be represented at the lower sampling rate. To address this problem, let's insert another step in the procedure.<\/p><div><ol><li>Convert the discrete-domain signal, $f[n]$, to a continuous-domain signal, $f(x)$.<\/li><li>Pass $f(x)$ through a lowpass filter to remove high-frequency components that would cause aliasing distortion at the desired sampling rate. Let's call this filter $h_a(x)$.<\/li><li>Convert the continuous-domain signal, $f(x)$, to a new discrete-domain signal, $g[n]$, by sampling $f(x)$ at the desired rate: $g[n] = f(nT)$.<\/li><\/ol><\/div><p>One key to understanding the <tt>imresize<\/tt> approach is to recognize that filtering with two filters in succession, in this case $h(x)$ and $h_a(x)$, can be replaced by one filter whose frequency response is the product of the original two filters. The second key to understanding the <tt>imresize<\/tt> algorithm is that the desired frequency response for the antialiasing filter, $h_a(x)$, depends how much we are changing the sampling rate. If we shrink the original signal more, then we have to remove more of the high frequencies with $h_a(x)$.<\/p><p>To state that more compactly (while waving my hands fairly vigorously), you can use just <b>one<\/b> continuous-domain filter to simultaneously accomplish interpolation and antialiasing.<\/p><p>Let me show you how this works for the cubic interpolation kernel that <tt>imresize<\/tt> uses by default. Here is the code for the cubic interpolation kernel:<\/p><pre>function f = cubic(x)\r\n% See Keys, \"Cubic Convolution Interpolation for Digital Image\r\n% Processing,\" IEEE Transactions on Acoustics, Speech, and Signal\r\n% Processing, Vol. ASSP-29, No. 6, December 1981, p. 1155.<\/pre><pre>absx = abs(x);\r\nabsx2 = absx.^2;\r\nabsx3 = absx.^3;<\/pre><pre>f = (1.5*absx3 - 2.5*absx2 + 1) .* (absx &lt;= 1) + ...\r\n                (-0.5*absx3 + 2.5*absx2 - 4*absx + 2) .* ...\r\n                ((1 &lt; absx) &amp; (absx &lt;= 2));<\/pre><p>(This code is inside the file imresize.m.) And here's what the interpolation kernel looks like:<\/p><pre class=\"codeinput\">figure\r\nfplot(@cubic,[-2.5 2.5],<span class=\"string\">'LineWidth'<\/span>,2.0)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/antialiasing_3_04.png\" alt=\"\"> <p>The function <tt>imresize<\/tt> then modifies the interpolation by stretching it out. The stretch factor depends directly how much we are shrinking the original signal. Here's the <tt>imresize<\/tt> code that modifies the interpolation kernel:<\/p><pre>if (scale &lt; 1) &amp;&amp; (antialiasing)\r\n    % Use a modified kernel to simultaneously interpolate and\r\n    % antialias.\r\n    h = @(x) scale * kernel(scale * x);\r\n    kernel_width = kernel_width \/ scale;\r\nelse\r\n    % No antialiasing; use unmodified kernel.\r\n    h = kernel;\r\nend<\/pre><p>(I don't always comment my code well, but I think I did OK here.)<\/p><p>Notice that we don't modify the kernel at all when we are <b>growing<\/b> a signal (<tt>scale &gt; 1<\/tt>) instead of shrinking it (<tt>scale &lt; 1<\/tt>).<\/p><p>Here are three versions of the cubic interpolation kernel: the original one, one modified for shrinking by a factor of 2, and one modified by shrinking by a factor of 4.<\/p><pre class=\"codeinput\">h1 = @cubic;\r\nh2 = @(x) 0.5 * h1(0.5*x);\r\nh4 = @(x) 0.25 * h1(0.25*x);\r\n\r\nfplot(h1,[-10 10],<span class=\"string\">'LineWidth'<\/span>,2)\r\nhold <span class=\"string\">on<\/span>\r\nfplot(h2,[-10 10],<span class=\"string\">'LineWidth'<\/span>,2)\r\nfplot(h4,[-10 10],<span class=\"string\">'LineWidth'<\/span>,2)\r\nhold <span class=\"string\">off<\/span>\r\n\r\nlegend(<span class=\"string\">'h_1(x)'<\/span>,<span class=\"string\">'h_2(x)'<\/span>,<span class=\"string\">'h_4(x)'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/antialiasing_3_05.png\" alt=\"\"> <p>When we spread out the interpolation kernel in this way, the interpolation computation averages pixel values from a wider neighborhood to produce each output pixel. That is additional smoothing, which gives us the desired antialiasing effect.<\/p><p>Here is the <tt>imresize<\/tt> output for the zone plate image again.<\/p><pre class=\"codeinput\">imshow(Z4_imresize)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/antialiasing_3_06.png\" alt=\"\"> <p>Only the rings near the center are visible. That's because those rings had lower spatial frequency in the original, and they can be successfully represented using only one-fourth the sampling rate. The higher-frequency rings further away from the center have been smoothed away into a solid gray.<\/p><p>You can experiment yourself with <tt>imresize<\/tt> by disabling antialiasing. Here's how to shrink an image with antialiasing turned off:<\/p><pre class=\"codeinput\">Z4_imresize_noaa = imresize(Z,0.25,<span class=\"string\">'Antialiasing'<\/span>,false);\r\nimshow(Z4_imresize_noaa)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/antialiasing_3_07.png\" alt=\"\"> <p>To give credit where credit is due, the algorithm used by <tt>imresize<\/tt> was inspired by the article \"General Filtered Image Rescaling,\" by Dale Schumacher, in <i>Graphics Gems III<\/i>, Morgan Kaufmann, 1994.<\/p><p>Do you have other questions about <tt>imresize<\/tt>? Let me know by leaving a comment.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_b15b81f80bae4208bbaf292a3ba87035() {\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='b15b81f80bae4208bbaf292a3ba87035 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' b15b81f80bae4208bbaf292a3ba87035';\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_b15b81f80bae4208bbaf292a3ba87035()\"><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; R2016b<br><\/p><\/div><!--\r\nb15b81f80bae4208bbaf292a3ba87035 ##### SOURCE BEGIN #####\r\n%%\r\n% Today I'll try to wrap up my discussion about how aliasing affects image\r\n% resizing and about how the |imresize| function tries to prevent it. (This is called\r\n% _antialiasing_.) Let me show you\r\n% where we are going. Here is the zone plate image I showed last time.\r\n\r\nZ = imzoneplate(501);\r\nimshow(Z)\r\ntitle('Z')\r\n\r\n%%\r\n% Here's what happens when we shrink |Z| by throwing away samples.\r\n\r\nZ4 = Z(1:4:end,1:4:end);\r\nimshow(Z4)\r\ntitle('Z4')\r\n\r\n%%\r\n% And here's what |imresize| does (with the default behavior):\r\n\r\nZ4_imresize = imresize(Z,0.25);\r\nimshow(Z4_imresize)\r\ntitle('imresize(Z,0.25)')\r\n\r\n%%\r\n% Before I get started describing the |imresize| approach to antialiasing,\r\n% I should say that there are several algorithm\r\n% approaches to antialiasing in image resizing. This is just one. It is\r\n% based on classical ideas from digital signal processing.\r\n%\r\n% I'll use the notation that $f[n]$ is a function of a discrete variable\r\n% $n$, and $f(x)$ is a function of a continuous variable $x$. Here's a way\r\n% to think about the image resizing problem that is theoretically useful.\r\n% To simplify the discussion for a bit, I'll talk in terms of 1-D signals.\r\n% (Warning: people with a graduate-level background in digital signal\r\n% processing will probably find this explanation too hand-wavy. Everybody\r\n% else will find it too jargon-y. I'm doing my best.)\r\n%\r\n% Suppose we want to convert a 1-D signal, $f[n]$, to another 1-D signal\r\n% that has a different sampling rate.\r\n%\r\n% # Convert the discrete-domain signal, $f[n]$, to a continuous-domain\r\n% signal, $f(x)$. Theoretically, this step involves the use of a\r\n% continuous-domain filter, $h(x)$. You can think of this step as\r\n% a form of *interpolation* (for some choices of $h(x)$, anyway).\r\n% # Convert the continuous-domain signal, $f(x)$, to a new discrete-domain\r\n% signal, $g[n]$, by sampling $f(x)$ at the desired rate: $g[n] = f(nT)$.\r\n%\r\n% There's a problem with the two-step procedure above. If the sampling rate\r\n% for $g[n]$ is reduced from the original sampling rate of $f[n]$, then the\r\n% procedure is susceptible to aliasing distortion if $f[n]$ contains high\r\n% frequencies that cannot be represented at the lower sampling rate. To\r\n% address this problem, let's insert another step in the procedure.\r\n%\r\n% # Convert the discrete-domain signal, $f[n]$, to a continuous-domain\r\n% signal, $f(x)$. \r\n% # Pass $f(x)$ through a lowpass filter to remove high-frequency\r\n% components that would cause aliasing distortion at the desired sampling\r\n% rate. Let's call this filter $h_a(x)$.\r\n% # Convert the continuous-domain signal, $f(x)$, to a new discrete-domain\r\n% signal, $g[n]$, by sampling $f(x)$ at the desired rate: $g[n] = f(nT)$.\r\n%\r\n% One key to understanding the |imresize| approach is to recognize that\r\n% filtering with two filters in succession, in this case $h(x)$ and\r\n% $h_a(x)$, can be replaced by one filter whose frequency response is the\r\n% product of the original two filters. The second key to understanding the\r\n% |imresize| algorithm is that the desired frequency response for the\r\n% antialiasing filter, $h_a(x)$, depends how much we are changing the\r\n% sampling rate. If we shrink the original signal more, then we have to\r\n% remove more of the high frequencies with $h_a(x)$.\r\n%\r\n% To state that more compactly (while waving my hands fairly vigorously),\r\n% you can use just *one* continuous-domain filter to simultaneously\r\n% accomplish interpolation and antialiasing.\r\n%\r\n% Let me show you how this works for the cubic interpolation kernel that\r\n% |imresize| uses by default. Here is the code for the cubic interpolation\r\n% kernel:\r\n%\r\n%  function f = cubic(x)\r\n%  % See Keys, \"Cubic Convolution Interpolation for Digital Image\r\n%  % Processing,\" IEEE Transactions on Acoustics, Speech, and Signal\r\n%  % Processing, Vol. ASSP-29, No. 6, December 1981, p. 1155.\r\n%  \r\n%  absx = abs(x);\r\n%  absx2 = absx.^2;\r\n%  absx3 = absx.^3;\r\n%  \r\n%  f = (1.5*absx3 - 2.5*absx2 + 1) .* (absx <= 1) + ...\r\n%                  (-0.5*absx3 + 2.5*absx2 - 4*absx + 2) .* ...\r\n%                  ((1 < absx) & (absx <= 2));\r\n%\r\n% (This code is inside the file imresize.m.) And here's what the\r\n% interpolation kernel looks like:\r\n\r\nfigure\r\nfplot(@cubic,[-2.5 2.5],'LineWidth',2.0)\r\n\r\n%%\r\n% The function |imresize| then modifies the interpolation by stretching it\r\n% out. The stretch factor depends directly how much we are shrinking the\r\n% original signal. Here's the |imresize| code that modifies the\r\n% interpolation kernel:\r\n%\r\n%  if (scale < 1) && (antialiasing)\r\n%      % Use a modified kernel to simultaneously interpolate and\r\n%      % antialias.\r\n%      h = @(x) scale * kernel(scale * x);\r\n%      kernel_width = kernel_width \/ scale;\r\n%  else\r\n%      % No antialiasing; use unmodified kernel.\r\n%      h = kernel;\r\n%  end\r\n%\r\n% (I don't always comment my code well, but I think I did OK here.)\r\n%\r\n% Notice that we don't modify the kernel at all when we are *growing* a\r\n% signal (|scale > 1|) instead of shrinking it (|scale < 1|).\r\n%\r\n% Here are three versions of the cubic interpolation kernel: the original one, \r\n% one modified for shrinking by a factor of 2, and one modified by\r\n% shrinking by a factor of 4.\r\n\r\nh1 = @cubic;\r\nh2 = @(x) 0.5 * h1(0.5*x);\r\nh4 = @(x) 0.25 * h1(0.25*x);\r\n\r\nfplot(h1,[-10 10],'LineWidth',2)\r\nhold on\r\nfplot(h2,[-10 10],'LineWidth',2)\r\nfplot(h4,[-10 10],'LineWidth',2)\r\nhold off\r\n\r\nlegend('h_1(x)','h_2(x)','h_4(x)')\r\n\r\n%%\r\n% When we spread out the interpolation kernel in this way, the\r\n% interpolation computation averages pixel values from a wider neighborhood\r\n% to produce each output pixel. That is additional smoothing, which\r\n% gives us the desired antialiasing effect.\r\n%\r\n% Here is the |imresize| output for the zone plate image again.\r\n\r\nimshow(Z4_imresize)\r\n\r\n%%\r\n% Only the rings near the center are visible. That's because those rings\r\n% had lower spatial frequency in the original, and they can be successfully\r\n% represented using only one-fourth the sampling rate. The higher-frequency\r\n% rings further away from the center have been smoothed away into a solid\r\n% gray.\r\n%\r\n% You can experiment yourself with |imresize| by disabling\r\n% antialiasing. Here's how to shrink an image with antialiasing turned off:\r\n\r\nZ4_imresize_noaa = imresize(Z,0.25,'Antialiasing',false);\r\nimshow(Z4_imresize_noaa)\r\n\r\n%%\r\n% To give credit where credit is due, the algorithm used by |imresize| was\r\n% inspired by the article \"General Filtered Image Rescaling,\" by Dale\r\n% Schumacher, in _Graphics Gems III_, Morgan Kaufmann, 1994.\r\n%\r\n% Do you have other questions about |imresize|? Let me know by leaving a\r\n% comment.\r\n\r\n##### SOURCE END ##### b15b81f80bae4208bbaf292a3ba87035\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/steve\/files\/antialiasing_3_05.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>Today I'll try to wrap up my discussion about how aliasing affects image resizing and about how the imresize function tries to prevent it. (This is called antialiasing.) Let me show you where we are... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2017\/01\/16\/aliasing-and-image-resizing-part-3\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":2479,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[208,725,1179,90,156,36,1177,92,52],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2473"}],"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=2473"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2473\/revisions"}],"predecessor-version":[{"id":2474,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2473\/revisions\/2474"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media\/2479"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=2473"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=2473"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=2473"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}