{"id":274,"date":"2009-12-27T11:18:15","date_gmt":"2009-12-27T18:18:15","guid":{"rendered":"http:\/\/www.giassa.net\/?page_id=274"},"modified":"2025-08-24T08:03:52","modified_gmt":"2025-08-24T15:03:52","slug":"iii-bicubic-interpolation","status":"publish","type":"page","link":"https:\/\/www.giassa.net\/?page_id=274","title":{"rendered":"III &#8211; Bicubic Spline Interpolation"},"content":{"rendered":"<p>Get ready for a long read on this one (it will be worth it though).<\/p>\n<p>So, before proceeding with bicubic interpolation, we will examine the 1D case first. In the case of of nearest neighbour or bilinear interpolation, we get a piecewise function that is continuous over small intervals. For nearest neighbour, we get constant, flat &#8220;spikes&#8221;. With bilinear interpolation, we get sections that are linear over a certain range, but have sharp discontinuities between these sections. We would like a piecewise interpolation function that is continuous over the entire interpolation interval.<\/p>\n<p>First, I will generate a discrete sinusoid, shifted up by 1, as shown below:<\/p>\n<p><div id=\"attachment_295\" style=\"width: 351px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/0-sine-wave.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-295\" class=\"size-full wp-image-295 \" title=\"0-sine-wave\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/0-sine-wave.png\" alt=\"\" width=\"341\" height=\"301\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/0-sine-wave.png 568w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/0-sine-wave-300x265.png 300w\" sizes=\"auto, (max-width: 341px) 100vw, 341px\" \/><\/a><p id=\"caption-attachment-295\" class=\"wp-caption-text\">Discrete Sinusoid [y=f(x)=sin(x)+1<\/p><\/div>Next, I will decimate values so that we have a lower resolution sinusoid (fewer samples available).<\/p>\n<div id=\"attachment_296\" style=\"width: 351px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/1-sine-wave-ds.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-296\" class=\"size-full wp-image-296 \" title=\"1-sine-wave-ds\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/1-sine-wave-ds.png\" alt=\"\" width=\"341\" height=\"301\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/1-sine-wave-ds.png 568w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/1-sine-wave-ds-300x265.png 300w\" sizes=\"auto, (max-width: 341px) 100vw, 341px\" \/><\/a><p id=\"caption-attachment-296\" class=\"wp-caption-text\">Decimated Sinusoid<\/p><\/div>\n<p>Now, I will first demonstrate, as shown previously, nearest neighbour 1D interpolation.<\/p>\n<div id=\"attachment_297\" style=\"width: 351px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/2-sine-wave-nn.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-297\" class=\"size-full wp-image-297 \" title=\"2-sine-wave-nn\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/2-sine-wave-nn.png\" alt=\"\" width=\"341\" height=\"301\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/2-sine-wave-nn.png 568w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/2-sine-wave-nn-300x265.png 300w\" sizes=\"auto, (max-width: 341px) 100vw, 341px\" \/><\/a><p id=\"caption-attachment-297\" class=\"wp-caption-text\">Reconstructed Sinusoid &#8211; Nearest Neighbour Interpolation<\/p><\/div>\n<p>Notice how the interpolated values are constant (flat) over this piecewise function. There are sharp discontinuities between each &#8220;piece&#8221;, and the above plot barely resembles a sinusoid. Now, I will repeat this interpolation on the decimated sinusoid, using linear interpolation.<\/p>\n<div id=\"attachment_298\" style=\"width: 351px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/3-sine-wave-linear.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-298\" class=\"size-full wp-image-298 \" title=\"3-sine-wave-linear\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/3-sine-wave-linear.png\" alt=\"\" width=\"341\" height=\"301\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/3-sine-wave-linear.png 568w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/3-sine-wave-linear-300x265.png 300w\" sizes=\"auto, (max-width: 341px) 100vw, 341px\" \/><\/a><p id=\"caption-attachment-298\" class=\"wp-caption-text\">Reconstructed Sinusoid &#8211; Linear Interpolation<\/p><\/div>\n<p>This is better than when we used nearest neighbour interpolation, as the above plot at least looks like a sinusoid to some extent. Also, each &#8220;piece&#8221; is linear, rather than constant, so it does a better job of approximating the original sinusoid. We still have one problem &#8211; each &#8220;piece&#8221; has a sharp discontinuity. We will correct this with cubic spline interpolation, as shown below:<\/p>\n<div id=\"attachment_299\" style=\"width: 351px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/4-sine-wave-spline.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-299\" class=\"size-full wp-image-299 \" title=\"4-sine-wave-spline\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/4-sine-wave-spline.png\" alt=\"\" width=\"341\" height=\"301\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/4-sine-wave-spline.png 568w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/4-sine-wave-spline-300x265.png 300w\" sizes=\"auto, (max-width: 341px) 100vw, 341px\" \/><\/a><p id=\"caption-attachment-299\" class=\"wp-caption-text\">Reconstructed Sinusoid &#8211; Cubic Spline Interpolation<\/p><\/div>\n<p>Note how the above plot closely resembles the original sinusoid quite well. More importantly, note how is does not have any sharp discontinuities as was the case with the two previous interpolation methods. Cubic spline interpolation is essentially another form of curve fitting, somewhat similar to linear interpolation. The difference is that instead of fitting all of the data points along a line, we fit them along a third order polynomial of the form:<\/p>\n<ul>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=y%3Df%28x%29%3Da%2Bbx%2Bcx%5E2%2Bdx%5E3&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='y=f(x)=a+bx+cx^2+dx^3' title='y=f(x)=a+bx+cx^2+dx^3' class='latex' \/><\/li>\n<\/ul>\n<p>What we need to remember is that we can&#8217;t simply have a single third-order polynomial equation that fits ALL of the data points in our original data set, it&#8217;s just not possible. What we can do, however, is to create a set of these equations for each &#8220;piece&#8221; of our interpolated data set so that they all connect together smoothly. We can define a cubic spline, according to [1], as follows:<\/p>\n<p>For a set of <img src='https:\/\/s0.wp.com\/latex.php?latex=n%2B1&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='n+1' title='n+1' class='latex' \/> data points on <img src='https:\/\/s0.wp.com\/latex.php?latex=%5Bj%2C+k%5D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='[j, k]' title='[j, k]' class='latex' \/>, with a set of data points defined by <img src='https:\/\/s0.wp.com\/latex.php?latex=j%3Dx_0%2C+k%3Dx_n&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='j=x_0, k=x_n' title='j=x_0, k=x_n' class='latex' \/> and <img src='https:\/\/s0.wp.com\/latex.php?latex=x_0+%3C+x_1+%3C+x_2+%5Cldots+%3C+x_%28n-1%29+%3C+x_n&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x_0 &lt; x_1 &lt; x_2 \\ldots &lt; x_(n-1) &lt; x_n' title='x_0 &lt; x_1 &lt; x_2 \\ldots &lt; x_(n-1) &lt; x_n' class='latex' \/>, with y-values defined as <img src='https:\/\/s0.wp.com\/latex.php?latex=y_i+%3D+f%28x_i%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='y_i = f(x_i)' title='y_i = f(x_i)' class='latex' \/>, we can define a cubic spline interpolant <img src='https:\/\/s0.wp.com\/latex.php?latex=S&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S' title='S' class='latex' \/> for the function <img src='https:\/\/s0.wp.com\/latex.php?latex=f%28x%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='f(x)' title='f(x)' class='latex' \/> such that:<\/p>\n<ol>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=S%28x%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S(x)' title='S(x)' class='latex' \/> is a cubic (third order) polynomial, denoted <img src='https:\/\/s0.wp.com\/latex.php?latex=S_i%28x%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S_i(x)' title='S_i(x)' class='latex' \/>, on the subinterval <img src='https:\/\/s0.wp.com\/latex.php?latex=%5Bx_i%2Cx_%7Bi%2B1%7D%5D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='[x_i,x_{i+1}]' title='[x_i,x_{i+1}]' class='latex' \/> for each <img src='https:\/\/s0.wp.com\/latex.php?latex=i%3D0%2C1%2C%5Cldots+%2C+n-1&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='i=0,1,\\ldots , n-1' title='i=0,1,\\ldots , n-1' class='latex' \/> (<em><strong>This is a function that provides a value for any point between the extreme data points <img src='https:\/\/s0.wp.com\/latex.php?latex=j&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='j' title='j' class='latex' \/> and <img src='https:\/\/s0.wp.com\/latex.php?latex=k&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='k' title='k' class='latex' \/><\/strong><\/em>.)<\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=S_i%28x_i%29+%3D+f%28x_i%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S_i(x_i) = f(x_i)' title='S_i(x_i) = f(x_i)' class='latex' \/> and <img src='https:\/\/s0.wp.com\/latex.php?latex=S_i%28x_%7Bi%2B1%7D%29%3Df%28x_%7Bi%2B1%7D%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S_i(x_{i+1})=f(x_{i+1})' title='S_i(x_{i+1})=f(x_{i+1})' class='latex' \/> for each <img src='https:\/\/s0.wp.com\/latex.php?latex=i%3D0%2C1%2C%5Cldots+%2Cn-1&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='i=0,1,\\ldots ,n-1' title='i=0,1,\\ldots ,n-1' class='latex' \/> (<em><strong>The spline MUST pass through each of the original data points, <img src='https:\/\/s0.wp.com\/latex.php?latex=f%28x_i%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='f(x_i)' title='f(x_i)' class='latex' \/><\/strong><\/em>.)<\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=S_%7Bi%2B1%7D%28x_%7Bi%2B1%7D%29+%3D+S_i%28x_%7Bi%2B1%7D%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S_{i+1}(x_{i+1}) = S_i(x_{i+1})' title='S_{i+1}(x_{i+1}) = S_i(x_{i+1})' class='latex' \/> for each <img src='https:\/\/s0.wp.com\/latex.php?latex=i%3D0%2C1%2C%5Cldots+%2Cn-2&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='i=0,1,\\ldots ,n-2' title='i=0,1,\\ldots ,n-2' class='latex' \/> (<em><strong>We are creating a piecewise-continuous function over several sub-intervals of <img src='https:\/\/s0.wp.com\/latex.php?latex=%5Bj%2C+k%5D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='[j, k]' title='[j, k]' class='latex' \/>. This means that each &#8220;piece&#8221; of our interpolated data set must connect to its neighbouring &#8220;pieces&#8221;<\/strong><\/em>.)<\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=S%27_%7Bi%2B1%7D%28x_%7Bi%2B1%7D%29+%3D+S%27_i%28x_%7Bi%2B1%7D%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S&#039;_{i+1}(x_{i+1}) = S&#039;_i(x_{i+1})' title='S&#039;_{i+1}(x_{i+1}) = S&#039;_i(x_{i+1})' class='latex' \/> for each <img src='https:\/\/s0.wp.com\/latex.php?latex=i%3D0%2C1%2C%5Cldots+%2Cn-2&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='i=0,1,\\ldots ,n-2' title='i=0,1,\\ldots ,n-2' class='latex' \/> (<em><strong>At each data point, the first derivative (ie: the slope) must be the same on both sides, even if only on an infinitesimally small range. This makes sure that in addition to the function being continuous (from rules <img src='https:\/\/s0.wp.com\/latex.php?latex=%281%29%2C+%282%29%2C%283%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='(1), (2),(3)' title='(1), (2),(3)' class='latex' \/> above), that the spline is differentiable over the entire interval <img src='https:\/\/s0.wp.com\/latex.php?latex=%5Bj%2C+k%5D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='[j, k]' title='[j, k]' class='latex' \/> as well. Without this contraint, we would still have the sharp discontinuities present in the previous interpolation methods.<\/strong><\/em>)<\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=S%27%27_%7Bi%2B1%7D%28x_%7Bi%2B1%7D%29+%3D+S%27%27_i%28x_%7Bi%2B1%7D%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S&#039;&#039;_{i+1}(x_{i+1}) = S&#039;&#039;_i(x_{i+1})' title='S&#039;&#039;_{i+1}(x_{i+1}) = S&#039;&#039;_i(x_{i+1})' class='latex' \/> for each <img src='https:\/\/s0.wp.com\/latex.php?latex=i%3D0%2C1%2C%5Cldots+%2Cn-2&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='i=0,1,\\ldots ,n-2' title='i=0,1,\\ldots ,n-2' class='latex' \/> (<em><strong>Similar to rule <img src='https:\/\/s0.wp.com\/latex.php?latex=%284%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='(4)' title='(4)' class='latex' \/> above, this makes sure that the curvature (second derivative) is continuous as well.<\/strong><\/em>)<\/li>\n<li>One of the two following sets of boundary conditions is satisfied:\n<ol>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=S%27%27%28x_0%29%3DS%27%27%28x_n%29%3D0&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S&#039;&#039;(x_0)=S&#039;&#039;(x_n)=0' title='S&#039;&#039;(x_0)=S&#039;&#039;(x_n)=0' class='latex' \/> (<em><strong>This is referred to as a &#8220;free\/natural&#8221; boundary. Imagine a flexible, straight piece of string being pulled to pass through each and every data point.<\/strong><\/em>)<\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=S%27%28x_0%29%3Df%27%28x_0%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S&#039;(x_0)=f&#039;(x_0)' title='S&#039;(x_0)=f&#039;(x_0)' class='latex' \/> and <img src='https:\/\/s0.wp.com\/latex.php?latex=S%27%28x_n%29%3Df%27%28x_n%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S&#039;(x_n)=f&#039;(x_n)' title='S&#039;(x_n)=f&#039;(x_n)' class='latex' \/> (<em><strong>This is referred to as a &#8220;clamped&#8221; boundary. It typically produces a spline that is more accurate than one generated using a &#8220;natural&#8221; boundary, but requires that we know the value of the derivatives at the end points. Since cubic spline interpolation does not yield the best results if we attempt to extrapolate data for the end points, and since we do not have this information on-hand, we will use &#8220;natural&#8221; boundaries in the rest of this tutorial<\/strong><\/em>.)<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p>So, we now have a concrete definition of a spline. Now, how do we calculate one that fits our data points according to the criteria above? Without going into <strong>too<\/strong> much detail on the mathematics involved (a complete proof is available in [1]), for a natural cubic spline, we want, for each subinterval, <img src='https:\/\/s0.wp.com\/latex.php?latex=%5Bx_%7Bi-1%7D%2Cx_i%5D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='[x_{i-1},x_i]' title='[x_{i-1},x_i]' class='latex' \/>, the coefficients for the following equation:<\/p>\n<ul>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=S%28x_i%29+%3D+a_i%2Bb_i+%28x-x_i%29+%2B+c_i+%28x-x_i%29%5E2%2Bd_i+%28x-x_i%29%5E3&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='S(x_i) = a_i+b_i (x-x_i) + c_i (x-x_i)^2+d_i (x-x_i)^3' title='S(x_i) = a_i+b_i (x-x_i) + c_i (x-x_i)^2+d_i (x-x_i)^3' class='latex' \/><\/li>\n<\/ul>\n<p>Applying the constraints previously listed to the above equation, we have the following set of equations:<\/p>\n<ul>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=m_i+%3D+S%28x_i%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='m_i = S(x_i)' title='m_i = S(x_i)' class='latex' \/><\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=h_i+%3D+x_i-x_%7Bi-1%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='h_i = x_i-x_{i-1}' title='h_i = x_i-x_{i-1}' class='latex' \/><\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=%5Calpha_i%3D2%28h_i%2Bh_%7Bi%2B1%7D%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\\alpha_i=2(h_i+h_{i+1})' title='\\alpha_i=2(h_i+h_{i+1})' class='latex' \/> (note that this is the symbol for &#8220;alpha&#8221;, not a lowercase &#8216;a&#8217;; this criteria only applies to natural splines)<\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=a_i+%3D+f%28x_i%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a_i = f(x_i)' title='a_i = f(x_i)' class='latex' \/><\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=b_i+%3D+%5Cdfrac%7Bf%28x_i%29-f%28x_%7Bx-1%7D%29%7D%7Bh_i%7D+%2B+%5Cdfrac%7Bh_i%282m_i%2Bm_%7Bi-1%7D%29%7D%7B6%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='b_i = \\dfrac{f(x_i)-f(x_{x-1})}{h_i} + \\dfrac{h_i(2m_i+m_{i-1})}{6}' title='b_i = \\dfrac{f(x_i)-f(x_{x-1})}{h_i} + \\dfrac{h_i(2m_i+m_{i-1})}{6}' class='latex' \/><\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=c_i+%3D+%5Cdfrac%7Bm_i%7D%7B2%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='c_i = \\dfrac{m_i}{2}' title='c_i = \\dfrac{m_i}{2}' class='latex' \/><\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=d_i+%3D+%5Cdfrac%7Bm_i-m_%7Bi-1%7D%7D%7B6h_i%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='d_i = \\dfrac{m_i-m_{i-1}}{6h_i}' title='d_i = \\dfrac{m_i-m_{i-1}}{6h_i}' class='latex' \/><\/li>\n<\/ul>\n<p>Now, applying the criteria from rule (6.1), we get the following equation:<\/p>\n<ul>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=h_%7Bi-1%7Dc_%7Bi-1%7D%2B2%28h_%7Bi-1%7D%2Bh_i%29c_i%2Bh_ic_%7Bi%2B1%7D+%3D+%5Cdfrac%7B3%7D%7Bh_i%7D%28a_%7Bi%2B1%7D-a_i%29-%5Cdfrac%7B3%7D%7Bh_i%7D%28a_%7Bi%7D-a_%7Bi-1%7D%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='h_{i-1}c_{i-1}+2(h_{i-1}+h_i)c_i+h_ic_{i+1} = \\dfrac{3}{h_i}(a_{i+1}-a_i)-\\dfrac{3}{h_i}(a_{i}-a_{i-1})' title='h_{i-1}c_{i-1}+2(h_{i-1}+h_i)c_i+h_ic_{i+1} = \\dfrac{3}{h_i}(a_{i+1}-a_i)-\\dfrac{3}{h_i}(a_{i}-a_{i-1})' class='latex' \/><\/li>\n<\/ul>\n<p>This equation provides with a set of equations we must now solve in the form of Ax=b, with the vectors\/matrices defined as:<\/p>\n<p><code><img src='https:\/\/s0.wp.com\/latex.php?latex=A%3D+%5Cleft%28%5Cbegin%7Barray%7D%7Bccccccc%7D%5Calpha_1%26h_2%260%26%5Ccdots%260%260%260%5C%5Ch_2%26%5Calpha_2%26h_3%26%5Ccdots%260%260%260%5C%5C0%26h_3%26%5Calpha_4%26%5Ccdots%260%260%260%5C%5C%5Cvdots%26%5Cvdots%26%5Cvdots%26%5Cddots%26%5Cvdots%26%5Cvdots%26%5Cvdots%5C%5C0%260%260%26%5Ccdots%26%5Calpha_%7Bn-3%7D%26h_%7Bn-2%7D%260%5C%5C0%260%260%26%5Ccdots%26h_%7Bn-2%7D%26%5Calpha_%7Bn-1%7D%26h_%7Bn-1%7D%5C%5C0%260%260%26%5Ccdots%260%26h_%7Bn-1%7D%26%5Calpha_%7Bn%7D%5C%5C%5Cend%7Barray%7D%5Cright%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='A= \\left(\\begin{array}{ccccccc}\\alpha_1&amp;h_2&amp;0&amp;\\cdots&amp;0&amp;0&amp;0\\\\h_2&amp;\\alpha_2&amp;h_3&amp;\\cdots&amp;0&amp;0&amp;0\\\\0&amp;h_3&amp;\\alpha_4&amp;\\cdots&amp;0&amp;0&amp;0\\\\\\vdots&amp;\\vdots&amp;\\vdots&amp;\\ddots&amp;\\vdots&amp;\\vdots&amp;\\vdots\\\\0&amp;0&amp;0&amp;\\cdots&amp;\\alpha_{n-3}&amp;h_{n-2}&amp;0\\\\0&amp;0&amp;0&amp;\\cdots&amp;h_{n-2}&amp;\\alpha_{n-1}&amp;h_{n-1}\\\\0&amp;0&amp;0&amp;\\cdots&amp;0&amp;h_{n-1}&amp;\\alpha_{n}\\\\\\end{array}\\right)' title='A= \\left(\\begin{array}{ccccccc}\\alpha_1&amp;h_2&amp;0&amp;\\cdots&amp;0&amp;0&amp;0\\\\h_2&amp;\\alpha_2&amp;h_3&amp;\\cdots&amp;0&amp;0&amp;0\\\\0&amp;h_3&amp;\\alpha_4&amp;\\cdots&amp;0&amp;0&amp;0\\\\\\vdots&amp;\\vdots&amp;\\vdots&amp;\\ddots&amp;\\vdots&amp;\\vdots&amp;\\vdots\\\\0&amp;0&amp;0&amp;\\cdots&amp;\\alpha_{n-3}&amp;h_{n-2}&amp;0\\\\0&amp;0&amp;0&amp;\\cdots&amp;h_{n-2}&amp;\\alpha_{n-1}&amp;h_{n-1}\\\\0&amp;0&amp;0&amp;\\cdots&amp;0&amp;h_{n-1}&amp;\\alpha_{n}\\\\\\end{array}\\right)' class='latex' \/><\/code><\/p>\n<img src='https:\/\/s0.wp.com\/latex.php?latex=x%3D+%5Cleft%28%5Cbegin%7Barray%7D%7Bc%7Dm_1%5C%5Cm_2%5C%5Cm_3%5C%5C%5Cvdots%5C%5Cm_%7Bn-3%7D%5C%5Cm_%7Bn-2%7D%5C%5Cm_%7Bn-1%7D%5Cend%7Barray%7D%5Cright%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x= \\left(\\begin{array}{c}m_1\\\\m_2\\\\m_3\\\\\\vdots\\\\m_{n-3}\\\\m_{n-2}\\\\m_{n-1}\\end{array}\\right)' title='x= \\left(\\begin{array}{c}m_1\\\\m_2\\\\m_3\\\\\\vdots\\\\m_{n-3}\\\\m_{n-2}\\\\m_{n-1}\\end{array}\\right)' class='latex' \/>\n<img src='https:\/\/s0.wp.com\/latex.php?latex=b%3D+6%5Cleft%28%5Cbegin%7Barray%7D%7Bc%7D%5Cdfrac%7Bf%28x_2%29-f%28x_1%29%7D%7Bh_2%7D-%5Cdfrac%7Bf%28x_1%29-f%28x_0%29%7D%7Bh_1%7D%5C%5C%5Cdfrac%7Bf%28x_3%29-f%28x_2%29%7D%7Bh_3%7D-%5Cdfrac%7Bf%28x_2%29-f%28x_1%29%7D%7Bh_2%7D%5C%5C%5Cvdots%5C%5C%5Cdfrac%7Bf%28x_%7Bn-1%7D%29-f%28x_%7Bn-2%7D%29%7D%7Bh_%7Bn-1%7D%7D-%5Cdfrac%7Bf%28x_%7Bn-2%7D%29-f%28x_%7Bn-3%7D%29%7D%7Bh_%7Bn-2%7D%7D%5C%5C%5Cdfrac%7Bf%28x_%7Bn%7D%29-f%28x_%7Bn-1%7D%29%7D%7Bh_%7Bn%7D%7D-%5Cdfrac%7Bf%28x_%7Bn-1%7D%29-f%28x_%7Bn-2%7D%29%7D%7Bh_%7Bn-1%7D%7D%5Cend%7Barray%7D%5Cright%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='b= 6\\left(\\begin{array}{c}\\dfrac{f(x_2)-f(x_1)}{h_2}-\\dfrac{f(x_1)-f(x_0)}{h_1}\\\\\\dfrac{f(x_3)-f(x_2)}{h_3}-\\dfrac{f(x_2)-f(x_1)}{h_2}\\\\\\vdots\\\\\\dfrac{f(x_{n-1})-f(x_{n-2})}{h_{n-1}}-\\dfrac{f(x_{n-2})-f(x_{n-3})}{h_{n-2}}\\\\\\dfrac{f(x_{n})-f(x_{n-1})}{h_{n}}-\\dfrac{f(x_{n-1})-f(x_{n-2})}{h_{n-1}}\\end{array}\\right)' title='b= 6\\left(\\begin{array}{c}\\dfrac{f(x_2)-f(x_1)}{h_2}-\\dfrac{f(x_1)-f(x_0)}{h_1}\\\\\\dfrac{f(x_3)-f(x_2)}{h_3}-\\dfrac{f(x_2)-f(x_1)}{h_2}\\\\\\vdots\\\\\\dfrac{f(x_{n-1})-f(x_{n-2})}{h_{n-1}}-\\dfrac{f(x_{n-2})-f(x_{n-3})}{h_{n-2}}\\\\\\dfrac{f(x_{n})-f(x_{n-1})}{h_{n}}-\\dfrac{f(x_{n-1})-f(x_{n-2})}{h_{n-1}}\\end{array}\\right)' class='latex' \/>\n<p>We already know the values for all <img src='https:\/\/s0.wp.com\/latex.php?latex=x_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x_i' title='x_i' class='latex' \/> and <img src='https:\/\/s0.wp.com\/latex.php?latex=f%28x_i%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='f(x_i)' title='f(x_i)' class='latex' \/>, and from these, we can calculate the values for <img src='https:\/\/s0.wp.com\/latex.php?latex=a_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a_i' title='a_i' class='latex' \/>,<img src='https:\/\/s0.wp.com\/latex.php?latex=h_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='h_i' title='h_i' class='latex' \/>, and <img src='https:\/\/s0.wp.com\/latex.php?latex=%5Calpha_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\\alpha_i' title='\\alpha_i' class='latex' \/>. Once we&#8217;ve done this, we solve the matrix equation Ax=b for the x-vector containing the values <img src='https:\/\/s0.wp.com\/latex.php?latex=m_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='m_i' title='m_i' class='latex' \/>. Once this is done, we can calculate the values <img src='https:\/\/s0.wp.com\/latex.php?latex=b_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='b_i' title='b_i' class='latex' \/>,<img src='https:\/\/s0.wp.com\/latex.php?latex=c_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='c_i' title='c_i' class='latex' \/>, and <img src='https:\/\/s0.wp.com\/latex.php?latex=d_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='d_i' title='d_i' class='latex' \/>. Once all these values are calculated, we now have the coefficients for each &#8220;piece&#8221; of the entire cubic spline interpolating polynomial over <img src='https:\/\/s0.wp.com\/latex.php?latex=%5Bj%2C+k%5D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='[j, k]' title='[j, k]' class='latex' \/>. The sample MATLAB code below calculates a 1D cublic spline interpolant for an arbitrary 1D data set. As with all of my code, I will attempt to cover all the low-level details. This algorithm is based directly on the one provided in [1]. For a complete version of this algorithm written entirely in C, including code to solve a tridiagonal matrix, please refer to &#8220;ALGO34.C&#8221; at the web page for <a title=\"Numerical Analysis (8th) - Burden and Faires\" href=\"http:\/\/www.as.ysu.edu\/~faires\/Numerical-Analysis\/DiskMaterial\/programs\/C\/\" target=\"_blank\">Burden and Faires (8th)<\/a>.<\/p>\n<p>The following MATLAB M-functions, csi.M, and csiplot.M below, can be tested by declaring a line space, x, and a function of x, y, and then running the csi and csiplot routines.<\/p>\n<p><strong>Cubic Spline Interpolation Function:<\/strong><\/p>\n<pre class=\"lang:matlab decode:true \">%=========================================================================\r\n\r\nfunction output_vector = csi( input_x,input_y )\r\n\r\n%       CSI - calculates a cubic spline interpolant\r\n\r\n%       input_vector - a 1-by-n vector of y-values\r\n\r\n%       output_vector - a 1-by-n' vector of interpolated y-values\r\n\r\n%=========================================================================\r\n\r\n%Initialize variables\r\n\r\nx=input_x;a=input_y;\r\n\r\nn=length(a)-1;\r\n\r\nb=zeros(1,n+1); c=zeros(1,n+1); d=zeros(1,n+1);\r\n\r\nalpha=zeros(1,n+1);\r\n\r\nl=zeros(1,n+1); mu=zeros(1,n+1); z=zeros(1,n+1); h=zeros(1,n);\r\n\r\n%Step 1 - Set h-values\r\n\r\nfor i = 0:n-1\r\n\r\n    j=i+1;\r\n\r\n    h(j)=x(j+1)-x(j);\r\n\r\nend\r\n\r\n%Step 2 - Set alpha-values\r\n\r\nfor i = 1:n-1\r\n\r\n    j=i+1;\r\n\r\n    alpha(j)=3.*((a(j+1)-a(j)).\/h(j) - (a(j)-a(j-1)).\/h(j-1));\r\n\r\nend\r\n\r\n%=========================================================================\r\n\r\n%Reduce tridiagonal matrix using method outlined in Burden and Faires (8th)\r\n\r\n%=========================================================================\r\n\r\n%Step 3 - Initialize indexes and specific values\r\n\r\ni=0;\r\n\r\nj=i+1;\r\n\r\nl(j)=1;\r\n\r\nmu(j)=0;\r\n\r\nz(j)=0;\r\n\r\n%Step 4 - Matrix reduction\r\n\r\nfor i = 1:n-1\r\n\r\n    j=i+1;\r\n\r\n    l(j)=2.*(x(j+1)-x(j-1))-h(j-1).*mu(j-1);\r\n\r\n    mu(j)=h(j).\/l(j);\r\n\r\n    z(j)=(alpha(j)-h(j-1).*z(j-1)).\/l(j);\r\n\r\nend\r\n\r\n%Step 5 - Set remaining values\r\n\r\nl(n+1)=1;\r\n\r\nz(n+1)=0;\r\n\r\nc(n+1)=0;\r\n\r\n%Step 6 - Finish matrix reduction\r\n\r\nfor i = 0:n-1\r\n\r\n    j=n-i;\r\n\r\n    c(j)=z(j)-mu(j).*c(j+1);\r\n\r\n    b(j)=(a(j+1)-a(j)).\/h(j)-h(j).*(c(j+1)+2.*c(j)).\/3;\r\n\r\n    d(j)=(c(j+1)-c(j)).\/(3.*h(j));\r\n\r\nend\r\n\r\n%=========================================================================\r\n\r\n%Finished with matrix calculations\r\n\r\n%=========================================================================\r\n\r\noutput_vector = [a(1,1:length(a)-1);b(1,1:length(b)-1);c(1,1:length(c)-1);d(1,1:length(d)-1)];<\/pre>\n<p>&nbsp;<\/p>\n<p><strong>Plot(able) Vector Generation Code:<\/strong><\/p>\n<pre class=\"lang:matlab decode:true \">%=========================================================================\r\nfunction output_matrix = csiplot( coef_matrix,x_matrix,y_matrix,n_insert )\r\n%=========================================================================\r\n%Initialize variables\r\na=coef_matrix(1,:);\r\nb=coef_matrix(2,:);\r\nc=coef_matrix(3,:);\r\nd=coef_matrix(4,:);\r\n\r\n%Determine size\r\n[n1 n2]=size(x_matrix);\r\nn_more_samples = n_insert;\r\nnew_total_n_samples = (n_more_samples+1).*(n2-1)+1;\r\noutput_x = zeros(1,new_total_n_samples);\r\noutput_y = 666.*ones(1,new_total_n_samples);\r\n\r\n%Copy old x and y-values first\r\nfor i = 1:n2\r\n    index = 1+(i-1).*(n_more_samples+1);\r\n    output_x(index)=x_matrix(i);\r\n    output_y(index)=y_matrix(i);\r\nend\r\n\r\n%Calculate new x-values\r\nfor i = 1:n2-1\r\n    index = 1+(i-1).*(n_more_samples+1);\r\n\r\n    start = x_matrix(i);\r\n    stop = x_matrix(i+1);\r\n    step = (x_matrix(i+1)-x_matrix(i)).\/(n_more_samples+1);\r\n    %Space the x-values for the output evenly between input x-values\r\n    for j = index+1:index+n_more_samples+1\r\n        output_x(j)=output_x(j-1)+step;\r\n    end\r\n    %Calculate new (interpolated) y-values\r\n    for j = index:index+n_more_samples+1\r\n        output_y(j)=d(i).*(output_x(j)-output_x(index))^3+c(i).*(output_x(j)-output_x(index))^2+b(i).*(output_x(j)-output_x(index))+a(i);\r\n    end\r\n\r\nend\r\n\r\noutput_matrix = [output_x;output_y];<\/pre>\n<p>&nbsp;<\/p>\n<p><strong>Code for a Sample Run:<\/strong><\/p>\n<pre class=\"lang:matlab decode:true \">x1=1:1:20;\r\n\r\ny1=sin(x1)+1;\r\n\r\nxx=csi(x1,y1);\r\n\r\nyy=csiplot(xx,x1,y1,10); %The 10 is the number of interpolated data points we want inserted between our existing datapoints. Increasing this number leads to a smoother curve.\r\n\r\n subplot(1,2,1);plot(x1,y1); subplot(1,2,2);plot(yy(1,:),yy(2,:));<\/pre>\n<p>&nbsp;<\/p>\n<p>So, we now have a means to generate accurate interpolations for any arbitray data set which will allow for reasonably sharp edges while still maintaining a smooth and continuous transition between data points. This comes at the expense of a much more complicated algorithm. Hopefully, at the least you have learned the constraints required to understand how a cubic spline is continuous and smooth on the entire interval [j,k]. If you want to really understand the entire concept down to the finest detail, I highly recommend reading [1], section 3.4. Now, let&#8217;s extend this concept to image processing!<\/p>\n<p>In order to carry out bicubic spline interpolation, we first need to define our coordinate systems as we did in the previous subsections, as shown below:<\/p>\n<div id=\"attachment_408\" style=\"width: 317px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/grid-nn1.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-408\" class=\"size-full wp-image-408\" title=\"grid-nn\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/grid-nn1.png\" alt=\"\" width=\"307\" height=\"259\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/grid-nn1.png 307w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/grid-nn1-300x253.png 300w\" sizes=\"auto, (max-width: 307px) 100vw, 307px\" \/><\/a><p id=\"caption-attachment-408\" class=\"wp-caption-text\">Coordinate System<\/p><\/div>\n<p>Here we calculate an interpolated data point based on a function of the nearest 4 neighbouring pixels from the source data. The twist here is that we need to generate a continuous, curved region from this data, rather than a linear one. To understand this better, consider a grayscale image: every point has an x-coordinate, y-coordinate, and an intensity value. The intensity value can be represented by a color, such as a grayscale value, which is proportional to the intensity value. Shown below is a 2D grayscale representation of a simple checkerboard (4&#215;4 pixel) image upsampled using bicubic spline interpolation (we need at least a 3&#215;3 pixel image to use bicubic spline interpolation).<\/p>\n<div id=\"attachment_356\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/2d-csi.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-356\" class=\"size-medium wp-image-356\" title=\"2d-csi\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/2d-csi-300x212.png\" alt=\"\" width=\"300\" height=\"212\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/2d-csi-300x212.png 300w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/2d-csi.png 674w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-356\" class=\"wp-caption-text\">2D Bicubic Resampling<\/p><\/div>\n<p>This still doesn&#8217;t provide much insight into how bicubic interpolation generates a curved, interpolated surface. So, we&#8217;ll display these two images again, but instead of the intensity value representing a shade of gray, it will represent a height in 3D space, similar to a 3D elevation map.<\/p>\n<div id=\"attachment_355\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/3d-csi.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-355\" class=\"size-medium wp-image-355\" title=\"3d-csi\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/3d-csi-300x208.png\" alt=\"\" width=\"300\" height=\"208\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/3d-csi-300x208.png 300w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/3d-csi-1024x711.png 1024w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/3d-csi.png 1036w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-355\" class=\"wp-caption-text\">3D Representation of Bicubic Resampling<\/p><\/div>\n<p><em>So, we can see that we have taken the original data set, and interpolated values in both the x and y dimensions to create a smooth surface. <strong>An important note<\/strong>: Notice how the interpolated dataset is smooth in the x-direction, y-direction, and the xy-direction (ie: along the diagonals). This means that for any point in our interpolated data set, we should be able to compute not only the intensity (ie: height\/grayscale) value, but also it&#8217;s derivative in ANY direction along x, y, or xy. This is equivalent to us having the following data available to us (<\/em><strong>assume <img src='https:\/\/s0.wp.com\/latex.php?latex=f%28a%2Cb%29%3D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='f(a,b)=' title='f(a,b)=' class='latex' \/>the grayscale intensity value from the source image, <img src='https:\/\/s0.wp.com\/latex.php?latex=X&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='X' title='X' class='latex' \/>, at <img src='https:\/\/s0.wp.com\/latex.php?latex=X%28a%2Cb%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='X(a,b)' title='X(a,b)' class='latex' \/>, and that we are trying to calculate the interpolated grayscale value in the target\/output image, <img src='https:\/\/s0.wp.com\/latex.php?latex=Y&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='Y' title='Y' class='latex' \/>, at <img src='https:\/\/s0.wp.com\/latex.php?latex=Y%28J%2CK%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='Y(J,K)' title='Y(J,K)' class='latex' \/><\/strong>):<\/p>\n<ul>\n<li>The original intensity values of the surrounding pixels (4 values)\n<ul>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=f%28A%2CB%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='f(A,B)' title='f(A,B)' class='latex' \/><\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=f%28A%2B1%2CB%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='f(A+1,B)' title='f(A+1,B)' class='latex' \/><\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=f%28A%2CB%2B1%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='f(A,B+1)' title='f(A,B+1)' class='latex' \/><\/li>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=f%28A%2B1%2CB%2B1%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='f(A+1,B+1)' title='f(A+1,B+1)' class='latex' \/><\/li>\n<\/ul>\n<\/li>\n<li>The partial derivatives along the x-axis for each of these points (4 values)\n<ul>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=%5Cdfrac%7B%5Cdelta+f%7D%7B%5Cdelta+x%7D%3Df_x&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\\dfrac{\\delta f}{\\delta x}=f_x' title='\\dfrac{\\delta f}{\\delta x}=f_x' class='latex' \/><\/li>\n<\/ul>\n<\/li>\n<li>The partial derivatives along the y-axis for each of these points (4 values)\n<ul>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=%5Cdfrac%7B%5Cdelta+f%7D%7B%5Cdelta+y%7D%3Df_y&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\\dfrac{\\delta f}{\\delta y}=f_y' title='\\dfrac{\\delta f}{\\delta y}=f_y' class='latex' \/><\/li>\n<\/ul>\n<\/li>\n<li>The cross-derivatives at each of these points (4 values)\n<ul>\n<li><img src='https:\/\/s0.wp.com\/latex.php?latex=%5Cdfrac%7B%5Cdelta%5E2+f%7D%7B%5Cdelta+y%5Cdelta+x%7D%3Df_%7Bxy%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\\dfrac{\\delta^2 f}{\\delta y\\delta x}=f_{xy}' title='\\dfrac{\\delta^2 f}{\\delta y\\delta x}=f_{xy}' class='latex' \/><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>This leaves us with a total of 16 values to solve for in the case of generalized bicubic interpolation, as opposed to the 4 needed with 1D cubic spline interpolation (<img src='https:\/\/s0.wp.com\/latex.php?latex=a_i%2Cb_i%2Cc_i%2C&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a_i,b_i,c_i,' title='a_i,b_i,c_i,' class='latex' \/> and <img src='https:\/\/s0.wp.com\/latex.php?latex=d_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='d_i' title='d_i' class='latex' \/>).<\/p>\n<p>In image processing, we usually do not have most of these values readily available, and must frequently approximate them though numerical methods. Fortunately, in the case of bicubic spline interpolation, there is a very easy way to approximate the solution to this problem using what we&#8217;ve already learned from the 1D case of cubic spline interpolation! (<em><strong>Note that &#8220;bicubic spline interpolation&#8221; is basically a special case of a more general technique referred to as &#8220;bicubic interpolation&#8221; Don&#8217;t confuse the two! Bicubic spline interpolation does not make use of the cross-derivative values, and therefore is not as accurate as generalized cubic interpolation, which will be covered in the <a title=\"IV \u2013 Generalized Bicubic Interpolation\" href=\"https:\/\/www.giassa.net\/?page_id=371\" target=\"_self\">next subsection<\/a><\/strong><\/em>.)<\/p>\n<p>To generate our approximate solution, we simply do the following:<\/p>\n<ol>\n<li>Take our input (source) image, and caclulate the number of columns and rows<\/li>\n<li>For each column (from top to bottom), calculate a linear 1D spline using the rows indexes as x-values and the intensity (grayscale) values as the y-values. Save these results to an ouput table.<\/li>\n<li>Now, perform the same operation on all the rows (from left to right) of the previously generated output table.<\/li>\n<li>Done!<\/li>\n<\/ol>\n<p>The following code uses the 1D cubic spline interpolation function to create the following upsampled images:<\/p>\n<pre class=\"lang:matlab decode:true \">clear all;\r\nnew_size=10;\r\n%src=[0 0 1 1; 0 0 1 1; 1 1 0.5 0.5];\r\nsrc=rgb2gray(imread('trees_small.jpg','jpg'));\r\n[a b] = size(src);\r\nfor c = 1:b\r\n    x1 = 1:a;\r\n    y1 = src(:,c)';\r\n    xx=csi(x1,y1);\r\n    temp=csiplot(xx,x1,y1,new_size);\r\n    temp=temp(2,:);\r\n    output(:,c)=temp;\r\nend\r\n[a b] = size(output);\r\nfor c = 1:a\r\n    x1 = 1:b;\r\n    y1 = output(c,:);\r\n    xx=csi(x1,y1);\r\n    temp=csiplot(xx,x1,y1,new_size);\r\n    temp=temp(2,:);\r\n    output2(c,:)=temp;\r\nend\r\nimagesc(output2);colormap gray;\r\n%subplot(1,2,1);imagesc(output2);colormap gray;\r\n%subplot(1,2,2);mesh(output2);\r\n%subplot(1,2,1);imagesc(output2);colormap default;\r\n%subplot(1,2,2);meshc(output2);shading interp;<\/pre>\n<p>&nbsp;<\/p>\n<div id=\"attachment_364\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/gs-2d-3d.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-364\" class=\"size-medium wp-image-364\" title=\"gs-2d-3d\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/gs-2d-3d-300x172.png\" alt=\"\" width=\"300\" height=\"172\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/gs-2d-3d-300x172.png 300w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/gs-2d-3d.png 793w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-364\" class=\"wp-caption-text\">Grayscale &#8211; Checkered Pattern Upsampled with Bicubic Spline Interpolation<\/p><\/div>\n<div id=\"attachment_362\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/c-2d-3d.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-362\" class=\"size-medium wp-image-362\" title=\"c-2d-3d\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/c-2d-3d-300x185.png\" alt=\"\" width=\"300\" height=\"185\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/c-2d-3d-300x185.png 300w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/c-2d-3d.png 764w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-362\" class=\"wp-caption-text\">False Color &#8211; Checkered Pattern Upsampled with Bicubic Spline Interpolation<\/p><\/div>\n<div id=\"attachment_363\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/cool.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-363\" class=\"size-medium wp-image-363\" title=\"cool\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/cool-300x196.png\" alt=\"\" width=\"300\" height=\"196\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/cool-300x196.png 300w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/cool.png 897w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-363\" class=\"wp-caption-text\">False Color &#8211; Checkered Pattern Upsampled with Bicubic Spline Interpolation (Higher Resolution)<\/p><\/div>\n<p>See how this interpolation method has resampled my &#8220;trees&#8221; picture from this:<\/p>\n<p><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/Copy-of-trees_small.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-367\" title=\"Copy of trees_small\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/Copy-of-trees_small.jpg\" alt=\"\" width=\"100\" height=\"75\" \/><\/a><\/p>\n<p>To this:<\/p>\n<div id=\"attachment_365\" style=\"width: 633px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/trees_up_bcsi.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-365\" class=\"size-full wp-image-365\" title=\"trees_up_bcsi\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/trees_up_bcsi.png\" alt=\"\" width=\"623\" height=\"583\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/trees_up_bcsi.png 623w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/trees_up_bcsi-300x280.png 300w\" sizes=\"auto, (max-width: 623px) 100vw, 623px\" \/><\/a><p id=\"caption-attachment-365\" class=\"wp-caption-text\">Upsampled Trees using Bicubic Spline Interpolation<\/p><\/div>\n<p>The image above looks better than when we used the two previous interpolation methods. Before we conclude this subsection, we should note a potential artifact that this interpolation method can introduce &#8211; halation. Just like how nearest neighbour interpolation introduces aliasing, or bilinear interpolation introduces blurring, this method introduces artifacts too. This essentially causes &#8220;halos&#8221; around parts of an image, or an overbrightening of select pixels near the borders of shapes. <strong>It can also cause values in the output image to be less than zero due to overshoot, so make sure to either normalize the final output image or adjust your code to check for this<\/strong>. More on this will be covered in the <a title=\"3 \u2013 Histograms &amp; Statistics \" href=\"https:\/\/www.giassa.net\/?page_id=411\" target=\"_self\">next section on histograms and statistics.<\/a><\/p>\n<div id=\"attachment_369\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/smile-halo.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-369\" class=\"size-medium wp-image-369\" title=\"smile-halo\" src=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/smile-halo-300x209.png\" alt=\"\" width=\"300\" height=\"209\" srcset=\"https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/smile-halo-300x209.png 300w, https:\/\/www.giassa.net\/wp-content\/uploads\/2009\/12\/smile-halo.png 656w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-369\" class=\"wp-caption-text\">Halation<\/p><\/div>\n<p>As shown above, notice how the image on the right has a bright &#8220;halo&#8221; around the outer edges of the smiley face.<\/p>\n<p><strong>Please note<\/strong>: in the code I used to generate 1D cubic splines, you may notice that I designed the function to allow a user-selectable number of data points to be inserted between existing data points rather than to allow the user to specify the final number of data points in the result. With a little work, this should be easy to change. No point in making this <strong>too<\/strong> easy. Now, with bicubic spline interpolation covered, we will move on to the final subsection for resampling and interpolation: <a title=\"IV \u2013 Generalized Bicubic Interpolation\" href=\"https:\/\/www.giassa.net\/?page_id=371\" target=\"_self\">generalized bicubic interpolation<\/a>.<\/p>\n<h1>References<\/h1>\n<p>[1] Burden, Richard L., and J. Douglas Faires. <em>Numerical Analysis<\/em>. Belmont: Brooks Cole, 2004. Print.<\/p>\n<p>[2] <em>Numerical recipes in C the art of scientific computing<\/em>. Cambridge: Cambridge UP, 1992. Print.<\/p>\n<div id=\"_mcePaste\" style=\"overflow: hidden; position: absolute; left: -10000px; top: 3698px; width: 1px; height: 1px;\">(<strong>We are creating a piecewise-continuous function over several sub-intervals of <img src='https:\/\/s0.wp.com\/latex.php?latex=%5Bj%2Ck%5D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='[j,k]' title='[j,k]' class='latex' \/>. This means that each &#8220;piece&#8221; of our interpolated data set must connect to its neighbours<\/strong>)<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Get ready for a long read on this one (it will be worth it though). So, before proceeding with bicubic interpolation, we will examine the 1D case first. In the case of of nearest neighbour or bilinear interpolation, we get &hellip; <a href=\"https:\/\/www.giassa.net\/?page_id=274\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":200,"menu_order":2,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-274","page","type-page","status-publish","hentry"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.giassa.net\/index.php?rest_route=\/wp\/v2\/pages\/274","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.giassa.net\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.giassa.net\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.giassa.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.giassa.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=274"}],"version-history":[{"count":80,"href":"https:\/\/www.giassa.net\/index.php?rest_route=\/wp\/v2\/pages\/274\/revisions"}],"predecessor-version":[{"id":1094,"href":"https:\/\/www.giassa.net\/index.php?rest_route=\/wp\/v2\/pages\/274\/revisions\/1094"}],"up":[{"embeddable":true,"href":"https:\/\/www.giassa.net\/index.php?rest_route=\/wp\/v2\/pages\/200"}],"wp:attachment":[{"href":"https:\/\/www.giassa.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=274"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}