core/vil/vil_bicub_interp.txx
Go to the documentation of this file.
00001 // core/vil/vil_bicub_interp.txx
00002 #ifndef vil_bicub_interp_txx_
00003 #define vil_bicub_interp_txx_
00004 //:
00005 // \file
00006 // \brief Bicubic interpolation functions for 2D images
00007 //
00008 // The vil bicub source files were derived from the corresponding
00009 // vil bilin files, thus the vil bilin/bicub source files are very
00010 // similar.  If you modify something in this file, there is a
00011 // corresponding bilin file that would likely also benefit from
00012 // the same change.
00013 //
00014 // In this particular case, there is no corresponding
00015 // vil_bilin_interp.txx file, see vil_bilin_interp.h instead.
00016 
00017 #include "vil_bicub_interp.h"
00018 #include <vcl_compiler.h>
00019 
00020 // vil_bilin_interp.h defines only inline functions, but some of the
00021 // corresponding vil_bicub_interp functions are a little big to be
00022 // inline.  Plus, on one platform, msvc 6.0 with /O2 optimization
00023 // compiled the vil_bicub_interp functions without a peep but gave
00024 // incorrect numerical results when these functions were inline and
00025 // defined in vil_bicub_interp.h.
00026 
00027 template<class T>
00028 double vil_bicub_interp_unsafe(double x, double y, const T* data,
00029                                vcl_ptrdiff_t xstep, vcl_ptrdiff_t ystep)
00030 {
00031     int p1x=int(x);
00032     double normx = x-p1x;
00033     int p1y=int(y);
00034     double normy = y-p1y;
00035 
00036     const T* pix1 = data + p1y*ystep + p1x*xstep;
00037 
00038     // like bilinear interpolation, use separability.
00039     // the s's are for the x-direction and the t's for the y-direction.
00040     double s0 = ((2-normx)*normx-1)*normx;    // -1
00041     double s1 = (3*normx-5)*normx*normx+2;    //  0
00042     double s2 = ((4-3*normx)*normx+1)*normx;  // +1
00043     double s3 = (normx-1)*normx*normx;        // +2
00044 
00045     double t0 = ((2-normy)*normy-1)*normy;
00046     double t1 = (3*normy-5)*normy*normy+2;
00047     double t2 = ((4-3*normy)*normy+1)*normy;
00048     double t3 = (normy-1)*normy*normy;
00049 
00050 #define vil_I(dx,dy) (pix1[(dx)*xstep+(dy)*ystep])
00051 
00052     double xi0 = s0*vil_I(-1,-1) + s1*vil_I(+0,-1) + s2*vil_I(+1,-1) + s3*vil_I(+2,-1);
00053     double xi1 = s0*vil_I(-1,+0) + s1*vil_I(+0,+0) + s2*vil_I(+1,+0) + s3*vil_I(+2,+0);
00054     double xi2 = s0*vil_I(-1,+1) + s1*vil_I(+0,+1) + s2*vil_I(+1,+1) + s3*vil_I(+2,+1);
00055     double xi3 = s0*vil_I(-1,+2) + s1*vil_I(+0,+2) + s2*vil_I(+1,+2) + s3*vil_I(+2,+2);
00056 
00057 #undef vil_I
00058 
00059     double val = 0.25 * ( xi0*t0 + xi1*t1 + xi2*t2 + xi3*t3 );
00060 
00061     return val;
00062 }
00063 
00064 // See the comments where this variable is used below.  If it is
00065 // necessary to get rid of this static variable we can try using a
00066 // volatile automatic variable defined in vil_bicub_interp_raw()
00067 // instead.  That should have the same effect.
00068 #ifdef VCL_VC_6
00069 static double vil_bicub_interp_raw_temp_hack = 0.0;
00070 #endif
00071 
00072 template<class T>
00073 double vil_bicub_interp_raw(double x, double y, const T* data,
00074                             vcl_ptrdiff_t xstep, vcl_ptrdiff_t ystep)
00075 {
00076     int p1x=int(x);
00077     double normx = x-p1x;
00078     int p1y=int(y);
00079     double normy = y-p1y;
00080 
00081     const T* pix1 = data + p1y*ystep + p1x*xstep;
00082 
00083     // special boundary cases can be handled more quickly first; also
00084     // avoids accessing an invalid pix1[t] which is going to have
00085     // weight 0.
00086 
00087     if (normx == 0.0 && normy == 0.0) return pix1[0];
00088 
00089     // coefficients for interpolation
00090     double s0=-1.0, s1=-1.0, s2=-1.0, s3=-1.0;      // in the x-direction
00091     double t0=-1.0, t1=-1.0, t2=-1.0, t3=-1.0;      // in the y-direction
00092 
00093     if (normx != 0.0) {
00094         s0 = ((2-normx)*normx-1)*normx;    // -1
00095         s1 = (3*normx-5)*normx*normx+2;    //  0
00096         s2 = ((4-3*normx)*normx+1)*normx;  // +1
00097         s3 = (normx-1)*normx*normx;        // +2
00098     }
00099 
00100     if (normy != 0.0) {
00101         t0 = ((2-normy)*normy-1)*normy;    // -1
00102         t1 = (3*normy-5)*normy*normy+2;    //  0
00103         t2 = ((4-3*normy)*normy+1)*normy;  // +1
00104         t3 = (normy-1)*normy*normy;        // +2
00105     }
00106 
00107 #define vil_I(dx,dy) (pix1[(dx)*xstep+(dy)*ystep])
00108 
00109     if (normy == 0.0) {
00110         double val = 0.0;
00111         val += s0*vil_I(-1,+0);
00112         val += s1*vil_I(+0,+0);
00113         val += s2*vil_I(+1,+0);
00114 
00115 #ifdef VCL_VC60
00116         // On some hardware platforms, with optimization, MSVC 6.0
00117         // miscompiles the computation of 'val' in this section of
00118         // code.  It appears that the computation of 'val' is lumped
00119         // into one large operation that is not handled properly,
00120         // resulting in incorrect arithmetic.  The very similar
00121         // section of code below, for the normx==0.0 case works fine.
00122         // After a lot of experimentation I have found that if we
00123         // force the compiler to split the computation of 'val' here
00124         // into two parts by assigning 'val' to a static variable
00125         // here, then the compilation and tests are OK.  In the past
00126         // this bug was dealt with by turning optimization off for
00127         // this file under MSVC, but this is a much better
00128         // solution. --Fred Wheeler
00129         vil_bicub_interp_raw_temp_hack = val;
00130 #endif
00131 
00132         val += s3*vil_I(+2,+0);
00133         val *= 0.5;
00134         return val;
00135     }
00136 
00137     if (normx == 0.0) {
00138         // The computation of 'val' in this section seems to compile
00139         // fine, even when the very similar computation just above has
00140         // trouble.
00141         double val = t0*vil_I(+0,-1) + t1*vil_I(+0,+0) + t2*vil_I(+0,+1) + t3*vil_I(+0,+2);
00142         val *= 0.5;
00143         return val;
00144     }
00145 
00146     double xi0 = s0*vil_I(-1,-1) + s1*vil_I(+0,-1) + s2*vil_I(+1,-1) + s3*vil_I(+2,-1);
00147     double xi1 = s0*vil_I(-1,+0) + s1*vil_I(+0,+0) + s2*vil_I(+1,+0) + s3*vil_I(+2,+0);
00148     double xi2 = s0*vil_I(-1,+1) + s1*vil_I(+0,+1) + s2*vil_I(+1,+1) + s3*vil_I(+2,+1);
00149     double xi3 = s0*vil_I(-1,+2) + s1*vil_I(+0,+2) + s2*vil_I(+1,+2) + s3*vil_I(+2,+2);
00150 
00151 #undef vil_I
00152 
00153     double val = 0.25 * ( xi0*t0 + xi1*t1 + xi2*t2 + xi3*t3 );
00154 
00155     return val;
00156 }
00157 
00158 #define VIL_BICUB_INTERP_INSTANTIATE(T) \
00159 template double \
00160 vil_bicub_interp_unsafe (double x, double y, const T* data, \
00161                          vcl_ptrdiff_t xstep, vcl_ptrdiff_t ystep); \
00162 template double \
00163 vil_bicub_interp_raw (double x, double y, const T* data, \
00164                       vcl_ptrdiff_t xstep, vcl_ptrdiff_t ystep)
00165 
00166 #endif // vil_bicub_interp_txx_