ShapeOps.C

Go to the documentation of this file.
00001 /*!@file Image/ShapeOps.C Shape operations on Image
00002  */
00003 
00004 // //////////////////////////////////////////////////////////////////// //
00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00006 // University of Southern California (USC) and the iLab at USC.         //
00007 // See http://iLab.usc.edu for information about this project.          //
00008 // //////////////////////////////////////////////////////////////////// //
00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00011 // in Visual Environments, and Applications'' by Christof Koch and      //
00012 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00013 // pending; application number 09/912,225 filed July 23, 2001; see      //
00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00015 // //////////////////////////////////////////////////////////////////// //
00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00017 //                                                                      //
00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00019 // redistribute it and/or modify it under the terms of the GNU General  //
00020 // Public License as published by the Free Software Foundation; either  //
00021 // version 2 of the License, or (at your option) any later version.     //
00022 //                                                                      //
00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00026 // PURPOSE.  See the GNU General Public License for more details.       //
00027 //                                                                      //
00028 // You should have received a copy of the GNU General Public License    //
00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00031 // Boston, MA 02111-1307 USA.                                           //
00032 // //////////////////////////////////////////////////////////////////// //
00033 //
00034 // Primary maintainer for this file: Rob Peters <rjpeters@klab.caltech.edu>
00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/ShapeOps.C $
00036 // $Id: ShapeOps.C 14290 2010-12-01 21:44:03Z itti $
00037 //
00038 
00039 // Some of the code in this file is based on public domain code from
00040 // Graphics Gems III by Dale Schumacher and Ray Gardener, available
00041 // from http://www.graphicsgems.org/ (filter_rcg.c)
00042 
00043 #ifndef IMAGE_SHAPEOPS_C_DEFINED
00044 #define IMAGE_SHAPEOPS_C_DEFINED
00045 
00046 #include "Image/ShapeOps.H"
00047 
00048 #include "Image/CutPaste.H"
00049 #include "Image/FilterOps.H"
00050 #include "Image/Image.H"
00051 #include "Image/Pixels.H"
00052 #include "Image/vec2.h"
00053 #include "Util/Assert.H"
00054 #include "Util/StringUtil.H" // for toLowerCase()
00055 #include "Util/log.H"
00056 #include "Util/safecopy.H"
00057 #include "rutz/trace.h"
00058 
00059 #include <algorithm>
00060 #include <cmath>
00061 #include <limits>
00062 #include <vector>
00063 
00064 #define ADD_RGB(buf,Weight,bWeight,in) do { \
00065         *buf += *in * (Weight); \
00066         *bWeight += Weight; } while (0);
00067 
00068 // ######################################################################
00069 template <class T>
00070 Image<T> quickLocalAvg(const Image<T>& array, const int scale)
00071 {
00072 GVX_TRACE(__PRETTY_FUNCTION__);
00073 
00074   ASSERT(array.initialized());
00075   int lw = array.getWidth(), lh = array.getHeight();
00076   int sw = std::max(1, lw / scale), sh = std::max(1, lh / scale);
00077   int scalex = lw / sw, scaley = lh / sh; // in case the image is very small
00078   int remx = lw - 1 - (lw % sw), remy = lh - 1 - (lh % sh);
00079 
00080   typedef typename promote_trait<T, float>::TP TF;
00081   Image<TF> result(sw, sh, ZEROS);
00082 
00083   int i, j, ci = 0, cj = 0; float fac = 1.0f / float(scale * scale);
00084   typename Image<T>::const_iterator lptr = array.begin();
00085   typename Image<TF>::iterator dptr = result.beginw();
00086 
00087   for (j = 0; j < lh; ++j)
00088     {
00089       for (i = 0; i < lw; ++i)
00090         {
00091           *dptr += (*lptr++);
00092           if ((++ci) == scalex && i != remx) { ci = 0; ++dptr; }
00093         }
00094       if (ci) { ci = 0; ++dptr; } // in case the reduction is not round
00095       if ( (++cj) == scaley && j != remy) cj = 0; else dptr -= sw;
00096     }
00097 
00098   Image<T> ret = result * fac;  // normalize by pixel area
00099   return ret;
00100 }
00101 
00102 // ######################################################################
00103 template <class T>
00104 Image<T> quickLocalAvg2x2(const Image<T>& src)
00105 {
00106   const int w = src.getWidth();
00107   const int h = src.getHeight();
00108 
00109   const int nw = w / 2;
00110   const int nh = h / 2;
00111 
00112   Image<T> result(nw, nh, ZEROS);
00113 
00114   typename Image<T>::const_iterator sptr = src.begin();
00115   typename Image<T>::iterator dptr = result.beginw();
00116 
00117   for (int y = 0; y < nh; ++y)
00118     {
00119       typename Image<T>::const_iterator sptr2 = sptr + y*2*w;
00120       for (int x = 0; x < nw; ++x)
00121         {
00122           *dptr++ = T((sptr2[0] + sptr2[1] + sptr2[w] + sptr2[w+1]) * 0.25);
00123           sptr2 += 2;
00124         }
00125     }
00126 
00127   return result;
00128 }
00129 
00130 // ######################################################################
00131 
00132 namespace
00133 {
00134   template <class T>
00135   T element_wise_max(const T& t1, const T& t2)
00136   {
00137     return std::max(t1, t2);
00138   }
00139 
00140   template <class T>
00141   PixRGB<T> element_wise_max(const PixRGB<T>& t1,
00142                              const PixRGB<T>& t2)
00143   {
00144     return PixRGB<T>(std::max(t1.red(), t2.red()),
00145                      std::max(t1.green(), t2.green()),
00146                      std::max(t1.blue(), t2.blue()));
00147   }
00148 }
00149 
00150 template <class T>
00151 Image<T> quickLocalMax(const Image<T>& array, const int scale)
00152 {
00153 GVX_TRACE(__PRETTY_FUNCTION__);
00154 
00155   ASSERT(array.initialized());
00156   int lw = array.getWidth(), lh = array.getHeight();
00157   int sw = std::max(1, lw / scale), sh = std::max(1, lh / scale);
00158   int scalex = lw / sw, scaley = lh / sh;  // in case the image is very small
00159   int remx = lw - 1 - (lw % sw), remy = lh - 1 - (lh % sh);
00160   Image<T> result(sw, sh, ZEROS); // clear dest
00161   int i, j, ci = 0, cj = 0;
00162   typename Image<T>::const_iterator lptr = array.begin();
00163   typename Image<T>::iterator dptr = result.beginw();
00164 
00165   for (j = 0; j < lh; ++j)
00166     {
00167       for (i = 0; i < lw; ++i)
00168         {
00169           *dptr = element_wise_max(*dptr, *lptr);
00170           ++lptr;
00171           if ((++ci) == scalex && i != remx) { ci = 0; ++dptr; }
00172         }
00173       if (ci) { ci = 0; ++dptr; } // in case the reduction is not round
00174       if ((++cj) == scaley && j != remy) cj = 0; else dptr -= sw;
00175     }
00176   return result;
00177 }
00178 
00179 // ######################################################################
00180 
00181 namespace
00182 {
00183   template <class T>
00184   T element_wise_min(const T& t1, const T& t2)
00185   {
00186     return std::min(t1, t2);
00187   }
00188 
00189   template <class T>
00190   PixRGB<T> element_wise_min(const PixRGB<T>& t1,
00191                              const PixRGB<T>& t2)
00192   {
00193     return PixRGB<T>(std::min(t1.red(), t2.red()),
00194                      std::min(t1.green(), t2.green()),
00195                      std::min(t1.blue(), t2.blue()));
00196   }
00197 }
00198 
00199 template <class T>
00200 Image<T> quickLocalMin(const Image<T>& array, const int scale)
00201 {
00202 GVX_TRACE(__PRETTY_FUNCTION__);
00203 
00204   ASSERT(array.initialized());
00205   int lw = array.getWidth(), lh = array.getHeight();
00206   int sw = std::max(1, lw / scale), sh = std::max(1, lh / scale);
00207   int scalex = lw / sw, scaley = lh / sh;  // in case the image is very small
00208   int remx = lw - 1 - (lw % sw), remy = lh - 1 - (lh % sh);
00209   Image<T> result(sw, sh, ZEROS); // clear dest
00210   int i, j, ci = 0, cj = 0;
00211   typename Image<T>::const_iterator lptr = array.begin();
00212   typename Image<T>::iterator dptr = result.beginw();
00213 
00214   for (j = 0; j < lh; ++j)
00215     {
00216       for (i = 0; i < lw; ++i)
00217         {
00218           *dptr = element_wise_min(*dptr, *lptr);
00219           ++lptr;
00220           if ((++ci) == scalex && i != remx) { ci = 0; ++dptr; }
00221         }
00222       if (ci) { ci = 0; ++dptr; } // in case the reduction is not round
00223       if ((++cj) == scaley && j != remy) cj = 0; else dptr -= sw;
00224     }
00225   return result;
00226 }
00227 
00228 // ######################################################################
00229 template <class T>
00230 Image<T> quickInterpolate(const Image<T>& src, const int sfactor)
00231 {
00232 GVX_TRACE(__PRETTY_FUNCTION__);
00233 
00234   ASSERT(src.initialized()); ASSERT(sfactor >= 2);
00235   const int sw = src.getWidth();
00236   const int sh = src.getHeight();
00237   int lw = sw * sfactor, lh = sh * sfactor;
00238   Image<T> result(lw, lh, NO_INIT);
00239   int i, j, ci = 0, cj = 0;
00240   typename Image<T>::iterator lptr = result.beginw();
00241   typename Image<T>::const_iterator sptr = src.begin();
00242 
00243   for (j = 0; j < lh; ++j)
00244     {
00245       for (i = 0; i < lw; ++i)
00246         {
00247           *lptr++ = *sptr;
00248           if ((++ci) == sfactor) {  ci = 0; ++sptr; }
00249         }
00250       if ((++cj) == sfactor) cj = 0; else sptr -= sw;
00251     }
00252 
00253   return result;
00254 }
00255 
00256 // ######################################################################
00257 template <class T>
00258 Image<T> interpolate(const Image<T>& src)
00259 {
00260 GVX_TRACE(__PRETTY_FUNCTION__);
00261 
00262   ASSERT(src.initialized());
00263   const int sw = src.getWidth(), sh = src.getHeight();
00264   const int dw = sw * 2, dh = sh * 2;
00265   Image<T> result(dw, dh, NO_INIT);
00266 
00267   typename Image<T>::const_iterator sptr = src.begin();
00268   typename Image<T>::iterator dptr = result.beginw();
00269 
00270   // do rows 1 .. sh-1 (last row will be special)
00271   for (int j = 0; j < sh-1; j ++)
00272     {
00273       // do columns 0 .. sw-1:
00274       for (int i = 0; i < sw-1; i ++)
00275         {
00276           // top-left pixel:
00277           dptr[0] = sptr[0];
00278 
00279           // bottom-left pixel:
00280           dptr[dw] = T( (sptr[0] + sptr[sw]) * 0.5F );
00281 
00282           // top-right pixel:
00283           dptr[1] = T( (sptr[0] + sptr[1]) * 0.5F );
00284 
00285           // bottom-right pixel:
00286           dptr[dw+1] = T( (sptr[0] + sptr[1] + sptr[sw] + sptr[sw+1])*0.25F );
00287 
00288           dptr += 2; ++sptr;
00289         }
00290 
00291       // now do last column:
00292 
00293       // top-left pixel:
00294       dptr[0] = sptr[0];
00295 
00296       // bottom-left pixel:
00297       dptr[dw] = T( (sptr[0] + sptr[sw]) * 0.5F );
00298 
00299       // top-right pixel:
00300       dptr[1] = T( sptr[0] * 0.75F );
00301 
00302       // bottom-right pixel:
00303       dptr[dw+1] = T( dptr[dw] * 0.75F );
00304 
00305       dptr += 2 + dw; ++sptr;
00306     }
00307 
00308   // now the last row, except for the last pixel:
00309   for (int i = 0; i < sw-1; i ++)
00310     {
00311       // top-left pixel:
00312       dptr[0] = sptr[0];
00313 
00314       // bottom-left pixel:
00315       dptr[dw] = T( sptr[0] * 0.75F );
00316 
00317       // top-right pixel:
00318       dptr[1] = T( (sptr[0] + sptr[1]) * 0.5F );
00319 
00320       // bottom-right pixel:
00321       dptr[dw+1] = T( dptr[1] * 0.75F );
00322 
00323       dptr += 2; ++sptr;
00324     }
00325 
00326   // finally the bottom-right corner of the image:
00327 
00328   // top-left pixel:
00329   dptr[0] = sptr[0];
00330 
00331   // bottom-left pixel:
00332   dptr[dw] = T( sptr[0] * 0.75F );
00333 
00334   // top-right pixel:
00335   dptr[1] = T( sptr[0] * 0.75F );
00336 
00337   // bottom-right pixel:
00338   dptr[dw+1] = T( sptr[0] * 0.5F );
00339 
00340   return result;
00341 }
00342 
00343 // ######################################################################
00344 template <class T>
00345 Image<T> rescaleBilinear(const Image<T>& src, const Dims& dims)
00346 {
00347   return rescaleBilinear(src, dims.w(), dims.h());
00348 }
00349 
00350 // ######################################################################
00351 template <class T>
00352 Image<T> rescaleBilinear(const Image<T>& src, const int new_w, const int new_h)
00353 {
00354 GVX_TRACE(__PRETTY_FUNCTION__);
00355 
00356   ASSERT(src.initialized()); ASSERT(new_w > 0 && new_h > 0);
00357 
00358   const int orig_w = src.getWidth();
00359   const int orig_h = src.getHeight();
00360 
00361   // check if same size already
00362   if (new_w == orig_w && new_h == orig_h) return src;
00363 
00364   const float sw = float(orig_w) / float(new_w);
00365   const float sh = float(orig_h) / float(new_h);
00366 
00367   Image<T> result(new_w, new_h, NO_INIT);
00368   typename Image<T>::iterator dptr = result.beginw();
00369   typename Image<T>::const_iterator const sptr = src.begin();
00370 
00371   // code inspired from one of the Graphics Gems book:
00372   /*
00373     (1) (x,y) are the original coords corresponding to scaled coords (i,j)
00374     (2) (x0,y0) are the greatest lower bound integral coords from (x,y)
00375     (3) (x1,y1) are the least upper bound integral coords from (x,y)
00376     (4) d00, d10, d01, d11 are the values of the original image at the corners
00377         of the rect (x0,y0),(x1,y1)
00378     (5) the value in the scaled image is computed from bilinear interpolation
00379         among d00,d10,d01,d11
00380   */
00381 
00382   for (int j = 0; j < new_h; ++j)
00383     {
00384       const float y = std::max(0.0f, (j+0.5f) * sh - 0.5f);
00385 
00386       const int y0 = int(y);
00387       const int y1 = std::min(y0 + 1, orig_h - 1);
00388 
00389       const float fy = y - float(y0);
00390 
00391       const int wy0 = orig_w * y0;
00392       const int wy1 = orig_w * y1;
00393 
00394       for (int i = 0; i < new_w; ++i)
00395         {
00396           const float x = std::max(0.0f, (i+0.5f) * sw - 0.5f);
00397 
00398           const int x0 = int(x);
00399           const int x1 = std::min(x0 + 1, orig_w - 1);
00400 
00401           const float fx = x - float(x0);
00402 
00403           typename promote_trait<T, float>::TP const
00404             d00( sptr[x0 + wy0] ), d10( sptr[x1 + wy0] ),
00405             d01( sptr[x0 + wy1] ), d11( sptr[x1 + wy1] ),
00406             dx0( d00 + (d10 - d00) * fx ),
00407             dx1( d01 + (d11 - d01) * fx );
00408 
00409           *dptr++ = T( dx0 + (dx1 - dx0) * fy );  // no need to clamp
00410         }
00411     }
00412   return result;
00413 }
00414 
00415 // specialization of rescaleBilinear() for PixRGB<byte>; here we
00416 // unpack the PixRGB operations so that they can be better optimized
00417 // by the compiler for at least a 2x speedup -- unfortunately (with
00418 // gcc anyway) the overloaded PixRGB operators (i.e. PixRGB+PixRGB,
00419 // PixRGB*float, etc.) make for slow code, so here we unpack things
00420 // and do the interpolation one element at a time using builtin,
00421 // scalar arithmetic only
00422 template <>
00423 Image<PixRGB<byte> > rescaleBilinear(const Image<PixRGB<byte> >& src,
00424                                      const int new_w, const int new_h)
00425 {
00426 GVX_TRACE(__PRETTY_FUNCTION__);
00427 
00428   ASSERT(src.initialized()); ASSERT(new_w > 0 && new_h > 0);
00429 
00430   const int orig_w = src.getWidth();
00431   const int orig_h = src.getHeight();
00432 
00433   // check if same size already
00434   if (new_w == orig_w && new_h == orig_h) return src;
00435 
00436   const float sw = float(orig_w) / float(new_w);
00437   const float sh = float(orig_h) / float(new_h);
00438 
00439   Image<PixRGB<byte> > result(new_w, new_h, NO_INIT);
00440   Image<PixRGB<byte> >::iterator dptr = result.beginw();
00441   Image<PixRGB<byte> >::const_iterator const sptr = src.begin();
00442 
00443   for (int j = 0; j < new_h; ++j)
00444     {
00445       const float y = std::max(0.0f, (j+0.5f) * sh - 0.5f);
00446 
00447       const int y0 = int(y);
00448       const int y1 = std::min(y0 + 1, orig_h - 1);
00449 
00450       const float fy = y - float(y0);
00451 
00452       const int wy0 = orig_w * y0;
00453       const int wy1 = orig_w * y1;
00454 
00455       for (int i = 0; i < new_w; ++i)
00456         {
00457           const float x = std::max(0.0f, (i+0.5f) * sw - 0.5f);
00458 
00459           const int x0 = int(x);
00460           const int x1 = std::min(x0 + 1, orig_w - 1);
00461 
00462           const float fx = x - float(x0);
00463 
00464           // TODO if we need more speed, this would be a good place to
00465           // use sse2 -- could turn d00, d10, d01, d11, dx0, dx1, and
00466           // the PixRGB<float> result each into 128-bit sse registers
00467           // (with 3 32-floats, one each for r/g/b, the 4th would be
00468           // unused), then we could do all of the math in parallel
00469           // (gcc has thin C wrappers around the sse2 intrinsincs in a
00470           // header called "emmintrin.h", try "locate emmintrin.h" to
00471           // find it, on my system it is
00472           // /usr/lib/gcc/i386-redhat-linux/3.4.3/include/emmintrin.h
00473 
00474 #define RGB_BILINEAR_INTERP(EL)                                 \
00475   do {                                                          \
00476     const float                                                 \
00477       d00( sptr[x0 + wy0].p[EL] ), d10( sptr[x1 + wy0].p[EL] ), \
00478       d01( sptr[x0 + wy1].p[EL] ), d11( sptr[x1 + wy1].p[EL] ); \
00479                                                                 \
00480     const float                                                 \
00481       dx0( d00 + (d10 - d00) * fx ),                            \
00482       dx1( d01 + (d11 - d01) * fx );                            \
00483                                                                 \
00484     dptr->p[EL] = byte( dx0 + (dx1 - dx0) * fy );               \
00485   } while(0)
00486 
00487           RGB_BILINEAR_INTERP(0);
00488           RGB_BILINEAR_INTERP(1);
00489           RGB_BILINEAR_INTERP(2);
00490 
00491 #undef RGB_BILINEAR_INTERP
00492 
00493           ++dptr;
00494         }
00495     }
00496   return result;
00497 }
00498 
00499 // ######################################################################
00500 template <class T>
00501 Image<T> rescaleNI(const Image<T>& src, const Dims& dims)
00502 {
00503   return rescaleNI(src, dims.w(), dims.h());
00504 }
00505 
00506 // ######################################################################
00507 template <class T>
00508 Image<T> rescaleNI(const Image<T>& src, const int new_w, const int new_h)
00509 {
00510 GVX_TRACE(__PRETTY_FUNCTION__);
00511 
00512   ASSERT(src.initialized()); ASSERT(new_w > 0 && new_h > 0);
00513 
00514   const int orig_w = src.getWidth();
00515   const int orig_h = src.getHeight();
00516 
00517   // check if same size already
00518   if (new_w == orig_w && new_h == orig_h) return src;
00519 
00520   // are we doing an integral up-scaling? then we can use zoomXY() to
00521   // be more efficient:
00522   if (new_w >= orig_w && (new_w % orig_w) == 0
00523       &&
00524       new_h >= orig_h && (new_h % orig_h) == 0)
00525     return zoomXY(src, new_w / orig_w, new_h / orig_h);
00526 
00527   // are we doing an integral down-scaling? then we can use decXY() to
00528   // be more efficient:
00529   if (new_w <= orig_w && (orig_w % new_w) == 0
00530       &&
00531       new_h <= orig_h && (orig_h % new_h) == 0)
00532     return decXY(src, orig_w / new_w, orig_h / new_h);
00533 
00534   const float sw = float(orig_w) / float(new_w);
00535   const float sh = float(orig_h) / float(new_h);
00536 
00537   Image<T> result(new_w, new_h, NO_INIT);
00538   typename Image<T>::iterator dptr = result.beginw();
00539   typename Image<T>::const_iterator const sptr = src.begin();
00540 
00541   for (int j = 0; j < new_h; ++j)
00542     for (int i = 0; i < new_w; ++i)
00543       *dptr++ = sptr[int(j * sh) * orig_w + int(i * sw)];
00544   return result;
00545 }
00546 
00547 // ######################################################################
00548 RescaleType getRescaleTypeFromChar(char ftype)
00549 {
00550   switch (ftype)
00551     {
00552     case 'o': return RESCALE_SIMPLE_NOINTERP;
00553     case 'r': return RESCALE_SIMPLE_BILINEAR;
00554     case 'b': return RESCALE_FILTER_BOX;
00555     case 't': return RESCALE_FILTER_TRIANGLE;
00556     case 'q': return RESCALE_FILTER_BELL;
00557     case 'B': return RESCALE_FILTER_BSPLINE;
00558     case 'h': return RESCALE_FILTER_HERMITE;
00559     case 'l': return RESCALE_FILTER_LANCZOS3;
00560     case 'm': return RESCALE_FILTER_MITCHELL;
00561     default: LFATAL("invalid rescale type char '%c'", ftype);
00562     }
00563   ASSERT(0); return (RescaleType) -1;
00564 }
00565 
00566 // ######################################################################
00567 std::string convertToString(RescaleType ftype)
00568 {
00569   switch (ftype)
00570     {
00571     case RESCALE_SIMPLE_NOINTERP: return "Nointerp";
00572     case RESCALE_SIMPLE_BILINEAR: return "Bilinear";
00573     case RESCALE_FILTER_BOX     : return "Box";
00574     case RESCALE_FILTER_TRIANGLE: return "Triangle";
00575     case RESCALE_FILTER_BELL    : return "Bell";
00576     case RESCALE_FILTER_BSPLINE : return "Bspline";
00577     case RESCALE_FILTER_HERMITE : return "Hermite";
00578     case RESCALE_FILTER_LANCZOS3: return "Lanczos3";
00579     case RESCALE_FILTER_MITCHELL: return "Mitchell";
00580     default: LFATAL("invalid rescale type %d", int(ftype));
00581     }
00582   ASSERT(0); return std::string();
00583 }
00584 
00585 // ######################################################################
00586 void convertFromString(const std::string& str1, RescaleType& ftype)
00587 {
00588   const std::string str = toLowerCase(str1);
00589 
00590   if      (str.compare("nointerp") == 0) ftype = RESCALE_SIMPLE_NOINTERP;
00591   else if (str.compare("bilinear") == 0) ftype = RESCALE_SIMPLE_BILINEAR;
00592   else if (str.compare("box") == 0)      ftype = RESCALE_FILTER_BOX     ;
00593   else if (str.compare("triangle") == 0) ftype = RESCALE_FILTER_TRIANGLE;
00594   else if (str.compare("bell") == 0)     ftype = RESCALE_FILTER_BELL    ;
00595   else if (str.compare("bspline") == 0)  ftype = RESCALE_FILTER_BSPLINE ;
00596   else if (str.compare("hermite") == 0)  ftype = RESCALE_FILTER_HERMITE ;
00597   else if (str.compare("lanczos3") == 0) ftype = RESCALE_FILTER_LANCZOS3;
00598   else if (str.compare("mitchell") == 0) ftype = RESCALE_FILTER_MITCHELL;
00599   else
00600     LFATAL("invalid rescale type '%s'", str1.c_str());
00601 }
00602 
00603 // ######################################################################
00604 namespace
00605 {
00606   // helper structs and functions for generic image rescaling
00607 
00608   struct HermiteFilter
00609   {
00610     static double support() { return 1.0; }
00611 
00612     static double filter(double t)
00613     {
00614       /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */
00615       if (t < 0.0) t = -t;
00616       if (t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0);
00617       return(0.0);
00618     }
00619   };
00620 
00621   struct BoxFilter
00622   {
00623     static double support() { return 0.5; }
00624 
00625     static double filter(double t)
00626     {
00627       if ((t > -0.5) && (t <= 0.5)) return(1.0);
00628       return(0.0);
00629     }
00630   };
00631 
00632   struct TriangleFilter
00633   {
00634     static double support() { return 1.0; }
00635 
00636     static double filter(double t)
00637     {
00638       if (t < 0.0) t = -t;
00639       if (t < 1.0) return(1.0 - t);
00640       return(0.0);
00641     }
00642   };
00643 
00644   struct BellFilter
00645   {
00646     static double support() { return 1.5; }
00647 
00648     static double filter(double t)          /* box (*) box (*) box */
00649     {
00650       if (t < 0) t = -t;
00651       if (t < .5) return(.75 - (t * t));
00652       if (t < 1.5) {
00653         t = (t - 1.5);
00654         return(.5 * (t * t));
00655       }
00656       return(0.0);
00657     }
00658   };
00659 
00660   struct BsplineFilter
00661   {
00662     static double support() { return 2.0; }
00663 
00664     static double filter(double t)
00665     {
00666       double tt;
00667 
00668       if (t < 0) t = -t;
00669       if (t < 1) {
00670         tt = t * t;
00671         return((.5 * tt * t) - tt + (2.0 / 3.0));
00672       } else if (t < 2) {
00673         t = 2 - t;
00674         return((1.0 / 6.0) * (t * t * t));
00675       }
00676       return(0.0);
00677     }
00678   };
00679 
00680   inline double sinc(double x)
00681   {
00682     x *= M_PI;
00683     if (x != 0) return(sin(x) / x);
00684     return(1.0);
00685   }
00686 
00687   struct Lanczos3Filter
00688   {
00689     static double support() { return 3.0; }
00690 
00691     static double filter(double t)
00692     {
00693       if (t < 0) t = -t;
00694       if (t < 3.0) return(sinc(t) * sinc(t/3.0));
00695       return(0.0);
00696     }
00697   };
00698 
00699   struct MitchellFilter
00700   {
00701     static double support() { return 2.0; }
00702 
00703     static double filter(double t)
00704     {
00705 #define B       (1.0 / 3.0)
00706 #define C       (1.0 / 3.0)
00707       double tt;
00708 
00709       tt = t * t;
00710       if (t < 0) t = -t;
00711       if (t < 1.0) {
00712         t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt))
00713              + ((-18.0 + 12.0 * B + 6.0 * C) * tt)
00714              + (6.0 - 2 * B));
00715         return(t / 6.0);
00716       } else if (t < 2.0) {
00717         t = (((-1.0 * B - 6.0 * C) * (t * tt))
00718              + ((6.0 * B + 30.0 * C) * tt)
00719              + ((-12.0 * B - 48.0 * C) * t)
00720              + (8.0 * B + 24 * C));
00721         return(t / 6.0);
00722       }
00723       return(0.0);
00724 #undef B
00725 #undef C
00726     }
00727   };
00728 
00729   /*
00730    *      image rescaling routine
00731    */
00732 
00733   struct CONTRIB {
00734     CONTRIB() : pixel(0), weight(0.0) {}
00735 
00736     CONTRIB(int p, double w) : pixel(p), weight(w) {}
00737 
00738     int     pixel;
00739     double  weight;
00740   };
00741 
00742   struct CLIST {
00743     std::vector<CONTRIB> p;
00744 
00745     void normalize()
00746     {
00747       double sum = 0.0;
00748       for (size_t i = 0; i < p.size(); ++i)
00749         sum += p[i].weight;
00750       for (size_t i = 0; i < p.size(); ++i)
00751         p[i].weight /= sum;
00752     }
00753   };
00754 
00755 
00756 
00757   /*
00758     calc_x_contrib()
00759 
00760     Calculates the filter weights for a single target column.
00761     contribX->p must be freed afterwards.
00762 
00763     Returns -1 if error, 0 otherwise.
00764   */
00765   template <class F>
00766   void calc_x_contrib(CLIST* contribX, /* Receiver of contrib info */
00767                       double xscale,   /* Horizontal zooming scale */
00768                       int dstwidth,    /* Target bitmap width */
00769                       int srcwidth,    /* Source bitmap width */
00770                       int i)           /* Pixel column in source bitmap being processed */
00771   {
00772     if (xscale < 1.0)
00773       {
00774         /* Shrinking image */
00775         const double width = F::support() / xscale;
00776 
00777         contribX->p.resize(0);
00778         contribX->p.reserve(int(width * 2 + 1));
00779 
00780         const double center = (double) i / xscale;
00781         const int left = int(ceil(center - width)) - 1;
00782         const int right = int(floor(center + width)) + 1;
00783         for (int j = left; j <= right; ++j)
00784           {
00785             double weight = i - (double) j * xscale;
00786             weight = F::filter(weight) * xscale;
00787             const int n = clampValue(j, 0, srcwidth - 1);
00788             if (weight != 0.0)
00789               contribX->p.push_back(CONTRIB(n, weight));
00790           }
00791         contribX->normalize();
00792       }
00793     else
00794       {
00795         /* Expanding image */
00796         contribX->p.resize(0);
00797         contribX->p.reserve(int(F::support() * 2 + 1));
00798 
00799         const double center = (double) i / xscale;
00800         const int left = int(ceil(center - F::support())) - 1;
00801         const int right = int(floor(center + F::support())) + 1;
00802 
00803         for (int j = left; j <= right; ++j)
00804           {
00805             double weight = (double) i / xscale - (double) j;
00806             weight = F::filter(weight);
00807             const int n = clampValue(j, 0, srcwidth - 1);
00808             if (weight != 0.0)
00809               contribX->p.push_back(CONTRIB(n, weight));
00810           }
00811         contribX->normalize();
00812       }
00813   } /* calc_x_contrib */
00814 
00815 
00816   /*
00817     genericRescale()
00818 
00819     Resizes bitmaps while resampling them.
00820   */
00821   template <class F, class T>
00822   Image<T> genericRescale(const Image<T>& src, const Dims& newdims)
00823   {
00824     typedef typename promote_trait<T, double>::TP TF;
00825 
00826     Image<T> dst(newdims, ZEROS);
00827 
00828     const int src_w = src.getWidth();
00829     const int src_h = src.getHeight();
00830     const int dst_w = dst.getWidth();
00831     const int dst_h = dst.getHeight();
00832 
00833     /* create intermediate column to hold horizontal dst column zoom */
00834     std::vector<TF> tmp(src_h);
00835 
00836     const double xscale = (double) dst_w / (double) src_w;
00837 
00838     /* Build y weights */
00839     /* pre-calculate filter contributions for a column */
00840     std::vector<CLIST> contribY(dst_h);
00841 
00842     const double yscale = (double) dst_h / (double) src_h;
00843 
00844     if (yscale < 1.0)
00845       {
00846         const double width = F::support() / yscale;
00847 
00848         for (int i = 0; i < dst_h; ++i)
00849           {
00850             contribY[i].p.resize(0);
00851             contribY[i].p.reserve(int(width * 2 + 1));
00852 
00853             const double center = (double) i / yscale;
00854             const int left = int(ceil(center - width)) - 1;
00855             const int right = int(floor(center + width)) + 1;
00856             for (int j = left; j <= right; ++j) {
00857               double weight = i - (double) j * yscale;
00858               weight = F::filter(weight) * yscale;
00859               const int n = clampValue(j, 0, src_h -1);
00860               if (weight != 0.0)
00861                 contribY[i].p.push_back(CONTRIB(n, weight));
00862             }
00863             contribY[i].normalize();
00864           }
00865       }
00866     else
00867       {
00868         for (int i = 0; i < dst_h; ++i) {
00869           contribY[i].p.resize(0);
00870           contribY[i].p.reserve(int(F::support() * 2 + 1));
00871 
00872           const double center = (double) i / yscale;
00873           const int left = int(ceil(center - F::support())) - 1;
00874           const int right = int(floor(center + F::support())) + 1;
00875           for (int j = left; j <= right; ++j) {
00876             double weight = (double) i / yscale - (double) j;
00877             weight = F::filter(weight);
00878             const int n = clampValue(j, 0, src_h -1);
00879             if (weight != 0.0)
00880               contribY[i].p.push_back(CONTRIB(n, weight));
00881           }
00882           contribY[i].normalize();
00883         }
00884       }
00885 
00886 
00887     for (int xx = 0; xx < dst_w; ++xx)
00888       {
00889         CLIST   contribX;
00890         calc_x_contrib<F>(&contribX, xscale,
00891                           dst_w, src_w, xx);
00892 
00893         /* Apply horz filter to make dst column in tmp. */
00894         for (int k = 0; k < src_h; ++k)
00895           {
00896             TF weight = TF();
00897             bool bPelDelta = false;
00898 
00899             const T* const srcrowptr =
00900               src.getArrayPtr() + k * src_w;
00901 
00902             ASSERT((contribX.p[0].pixel >= 0)
00903                    && (contribX.p[0].pixel < src_w));
00904 
00905             const T pel = srcrowptr[contribX.p[0].pixel];
00906 
00907             for (size_t j = 0; j < contribX.p.size(); ++j)
00908               {
00909                 ASSERT((contribX.p[j].pixel >= 0)
00910                        && (contribX.p[j].pixel < src_w));
00911 
00912                 const T pel2 = srcrowptr[contribX.p[j].pixel];
00913                 if (pel2 != pel)
00914                   bPelDelta = true;
00915                 weight += TF(pel2) * contribX.p[j].weight;
00916               }
00917             tmp[k] = bPelDelta ? weight : TF(pel);
00918           } /* next row in temp column */
00919 
00920         /* The temp column has been built. Now stretch it
00921            vertically into dst column. */
00922         for (int i = 0; i < dst_h; ++i)
00923           {
00924             TF weight = TF();
00925             bool bPelDelta = false;
00926             const TF pel = tmp[contribY[i].p[0].pixel];
00927 
00928             for (size_t j = 0; j < contribY[i].p.size(); ++j)
00929               {
00930                 const TF pel2 = tmp[contribY[i].p[j].pixel];
00931                 if (pel2 != pel)
00932                   bPelDelta = true;
00933                 weight += pel2 * contribY[i].p[j].weight;
00934               }
00935 
00936             ASSERT(xx >= 0 && xx < dst_w);
00937             ASSERT(i >= 0 && i < dst_h);
00938 
00939             dst.getArrayPtr()[xx + i * dst_w] =
00940               bPelDelta
00941               ? clamped_rounded_convert<T>(weight)
00942               : clamped_rounded_convert<T>(pel);
00943           } /* next dst row */
00944       } /* next dst column */
00945 
00946     return dst;
00947   } /* genericRescale */
00948 }
00949 
00950 // ######################################################################
00951 template <class T>
00952 Image<T> rescale(const Image<T>& src, const Dims& newdims,
00953                  RescaleType ftype)
00954 {
00955   switch (ftype)
00956     {
00957     case RESCALE_SIMPLE_NOINTERP: return rescaleNI(src, newdims);
00958     case RESCALE_SIMPLE_BILINEAR: return rescaleBilinear(src, newdims);
00959     case RESCALE_FILTER_BOX     : return genericRescale<BoxFilter>(src, newdims);
00960     case RESCALE_FILTER_TRIANGLE: return genericRescale<TriangleFilter>(src, newdims);
00961     case RESCALE_FILTER_BELL    : return genericRescale<BellFilter>(src, newdims);
00962     case RESCALE_FILTER_BSPLINE : return genericRescale<BsplineFilter>(src, newdims);
00963     case RESCALE_FILTER_HERMITE : return genericRescale<HermiteFilter>(src, newdims);
00964     case RESCALE_FILTER_LANCZOS3: return genericRescale<Lanczos3Filter>(src, newdims);
00965     case RESCALE_FILTER_MITCHELL: return genericRescale<MitchellFilter>(src, newdims);
00966     default: LFATAL("invalid ftype '%c'", ftype);
00967     }
00968   ASSERT(0); return Image<T>();
00969 }
00970 
00971 // ######################################################################
00972 template <class T>
00973 Image<T> rescale(const Image<T>& src, const int width, const int height,
00974                  RescaleType ftype)
00975 {
00976   return rescale(src, Dims(width, height), ftype);
00977 }
00978 
00979 // ######################################################################
00980 template <class T>
00981 Image<T> rescaleOpt(const Image<T>& src, const Dims& dims, const bool interp)
00982 {
00983   if (interp) return rescaleBilinear(src, dims);
00984   else return rescaleNI(src, dims);
00985 }
00986 
00987 // ######################################################################
00988 template <class T>
00989 Image<T> rescaleOpt(const Image<T>& src, const int new_w, const int new_h,
00990                  const bool interp)
00991 {
00992   if (interp) return rescaleBilinear(src, new_w, new_h);
00993   else return rescaleNI(src, new_w, new_h);
00994 }
00995 
00996 // ######################################################################
00997 // rescale an image using fancy widgets like anti-aliasing, resampling
00998 template <class T>
00999 Image<PixRGB<T> > downscaleFancy(const Image<PixRGB<T> >& src,
01000                                  int width, int height, int weighting_slope,
01001                                  bool no_weight_black)
01002 
01003 {
01004 GVX_TRACE(__PRETTY_FUNCTION__);
01005 
01006   PixRGB<T> pix(0);
01007   Image<PixRGB<T> > buffer;
01008   Image<PixRGB<T> > out;
01009   Image<T>          bufferWeight;
01010 
01011   buffer.resize(width,height,true);
01012   out.resize(width,height,true);
01013   bufferWeight.resize(width,height,true);
01014 
01015   T scalex = (T)width  / (T)src.getWidth();
01016   T scaley = (T)height / (T)src.getHeight();
01017   T dx, dy, weight, xweight, yweight, xfrac, yfrac;
01018   typename Image<PixRGB<T> >::iterator bufelem;
01019   typename Image<T>::iterator bufw;
01020   dx = (T)0.0;
01021   dy = (T)0.0;
01022 
01023   for (int sy = (int)0; sy < (int)src.getHeight();
01024        sy++, dy += scaley)
01025   {
01026     // outer loop for Y axis
01027     yfrac = dy - (T)floor(dy);
01028     switch (weighting_slope)
01029     {
01030     case 5: yweight = yfrac < 0.5 ? 0 : 1;
01031       break;
01032     case 4: yweight = (T)(0.5 + 0.5*tanh(15*(yfrac - 0.5)));
01033       break;
01034     case 3: yweight = (T)(0.5 + 0.5*tanh(8*(yfrac - 0.5)));
01035       break;
01036     case 2: yweight = (T)(0.5 + 0.5*tanh(5*(yfrac - 0.5)));
01037       break;
01038     case 1: yweight = (T)(0.5 - 0.5*cos(yfrac*M_PI));
01039       break;
01040     case 0: yweight = yfrac;
01041       break;
01042     default : LERROR("illegal weighting slope");
01043       yweight = yfrac;
01044     }
01045     // inner loop for X axis
01046     dx = (T)0;
01047     for (int sx = (int)0; sx < (int)src.getWidth();
01048          sx++, dx += scalex)
01049     {
01050       //LINFO("X %d",sx);
01051       xfrac = dx - (T)floor(dx);
01052       switch (weighting_slope)
01053       {
01054       case 5: xweight = xfrac < 0.5 ? 0 : 1;
01055         break;
01056       case 4: xweight = (T)(0.5 + 0.5*tanh(15*(xfrac - 0.5)));
01057         break;
01058       case 3: xweight = (T)(0.5 + 0.5*tanh(8*(xfrac - 0.5)));
01059         break;
01060       case 2: xweight = (T)(0.5 + 0.5*tanh(5*(xfrac - 0.5)));
01061         break;
01062       case 1: xweight = (T)(0.5 - 0.5*cos(xfrac*M_PI));
01063         /*almost same as tanh(4*x)*/
01064         break;
01065       case 0: xweight = xfrac;
01066         break;
01067       default : LINFO("illegal weighting slope");
01068         xweight = xfrac;
01069       }
01070       //LINFO("XWEIGHT %f",xweight);
01071       int floordx = (int)floor((T)dx);
01072       int floordy = (int)floor((T)dy);
01073       const PixRGB<T> *in_sy_sx = &src.getVal(sx,sy);
01074 
01075       if (no_weight_black)
01076         if (in_sy_sx->red()   == 0 &&
01077             in_sy_sx->green() == 0 &&
01078             in_sy_sx->blue()  == 0)
01079           continue;
01080 
01081       bufelem = buffer.beginw()
01082         + buffer.getWidth()*floordy + floordx;
01083 
01084       bufw    = bufferWeight.beginw()
01085         + bufferWeight.getWidth()*floordy + floordx;
01086 
01087       ADD_RGB(bufelem, ((T)1.0-xweight)*((T)1.0-yweight), bufw, in_sy_sx);
01088 
01089       if (dx < width - 1)
01090       {
01091         bufelem++; bufw++;
01092         ADD_RGB(bufelem, xweight*((T)1.0-yweight), bufw, in_sy_sx);
01093       }
01094 
01095       if (dy < height - 1)
01096       {
01097         bufelem = buffer.beginw()
01098           + buffer.getWidth()*(floordy+1) + floordx;
01099         bufw    = bufferWeight.beginw()
01100           + bufferWeight.getWidth()*(floordy+1) + floordx;
01101 
01102         ADD_RGB(bufelem, ((T)1.0-xweight)*yweight, bufw, in_sy_sx);
01103         if (dx < width - 1) {
01104           bufelem++;
01105           bufw++;
01106           //bufelem = &(buf[BUFIDX(floordx+1,floordy+1)]);
01107           ADD_RGB(bufelem, xweight*yweight, bufw, in_sy_sx);
01108         }
01109       }
01110     }
01111     if (floorf(dy + scaley) > floorf(dy))
01112     {
01113       /* line finished -> write to out */
01114       int dsty = (int)floor(dy);
01115       for (int dstx = 0; dstx < width; dstx++)
01116       {
01117         weight = bufferWeight.getVal(dstx,dsty);
01118         if(weight != 0.0)
01119         {
01120           pix = buffer.getVal(dstx,dsty);
01121           pix /= weight;
01122           out.setVal(dstx,dsty,pix);
01123         }
01124         PixRGB<T> zero(0);
01125         buffer.setVal(dstx,dsty,zero);
01126         bufferWeight.setVal(dstx,dsty,0);
01127       }
01128     }
01129   }
01130   return out;
01131 }
01132 
01133 
01134 // ######################################################################
01135 template <class T>
01136 Image<T> downSize(const Image<T>& src, const Dims& dims,
01137                   const int filterWidth)
01138 {
01139   return downSize(src, dims.w(), dims.h(), filterWidth);
01140 }
01141 
01142 // ######################################################################
01143 template <class T>
01144 Image<T> downSize(const Image<T>& src, const int new_w, const int new_h,
01145                   const int filterWidth)
01146 {
01147 GVX_TRACE(__PRETTY_FUNCTION__);
01148 
01149   if (src.getWidth() == new_w && src.getHeight() == new_h) return src;
01150 
01151   ASSERT(src.getWidth() / new_w > 1 && src.getHeight() / new_h > 1);
01152 
01153   const int wdepth = int(0.5+log(double(src.getWidth() / new_w)) / M_LN2);
01154   const int hdepth = int(0.5+log(double(src.getHeight() / new_h)) / M_LN2);
01155 
01156   if (wdepth != hdepth)
01157     LFATAL("arrays must have same proportions");
01158 
01159   Image<T> result = src;
01160 
01161   for (int i = 0; i < wdepth; ++i)
01162     {
01163       result = decX(lowPassX(filterWidth, result));
01164       result = decY(lowPassY(filterWidth, result));
01165     }
01166 
01167   return result;
01168 }
01169 
01170 // ######################################################################
01171 Image<float> downSizeClean(const Image<float>& src, const Dims& new_dims,
01172                            const int filterWidth)
01173 {
01174 GVX_TRACE(__PRETTY_FUNCTION__);
01175 
01176   if (src.getDims() == new_dims) return src;
01177 
01178   ASSERT(new_dims.isNonEmpty());
01179   ASSERT(filterWidth >= 1);
01180 
01181   Image<float> result = src;
01182 
01183   while (result.getWidth() > new_dims.w() * 2 &&
01184          result.getHeight() > new_dims.h() * 2)
01185     {
01186       if (filterWidth == 1)
01187         {
01188           result = decX(result);
01189           result = decY(result);
01190         }
01191       else if (filterWidth == 2)
01192         {
01193           result = quickLocalAvg2x2(result);
01194         }
01195       else
01196         {
01197           result = decX(lowPassX(filterWidth, result));
01198           result = decY(lowPassY(filterWidth, result));
01199         }
01200     }
01201 
01202   return rescaleBilinear(result, new_dims);
01203 }
01204 
01205 // ######################################################################
01206 template <class T>
01207 Image<T> concatArray(const Image<T> arr[], const int nbarr, const int Nx,
01208                      const int destX, const int destY)
01209 {
01210 GVX_TRACE(__PRETTY_FUNCTION__);
01211 
01212   int width, height;
01213   // check if we are going to reshape:
01214   if (destX <= 0 || destY <= 0)
01215     {
01216       width = arr[0].getWidth(); height = arr[0].getHeight();
01217     }
01218   else { width = destX; height = destY; }
01219   int tw = width * Nx;
01220   int th = height * (int)ceil(((double)nbarr) / ((double)Nx));
01221   Image<T> result(tw, th, NO_INIT);
01222   T zeros[width]; for (int i = 0; i < width; ++i) zeros[i] = T();
01223 
01224   int arrnb = 0;
01225   for (int j = 0; j < th; j += height)
01226     for (int i = 0; i < tw; i += width)
01227       {
01228         if (destX <= 0 || destY <= 0)  // no reshaping
01229           {
01230             if (arrnb < nbarr)  // copy image
01231               {
01232                 ASSERT(arr[arrnb].getWidth() == width &&
01233                        arr[arrnb].getHeight() == height);
01234                 for (int jj = 0; jj < height; jj ++)
01235                   safecopy(result.beginw() + (i + (j + jj) * tw),
01236                            arr[arrnb].begin() + (jj * width),
01237                            width);
01238               }
01239             else                // put blanks at the end
01240               for (int jj = 0; jj < height; jj ++)
01241                 safecopy(result.beginw() + (i + (j + jj) * tw),
01242                          zeros,
01243                          width);
01244           }
01245         else  // reshape first
01246           {
01247             if (arrnb < nbarr)  // copy image
01248               {
01249                 Image<T> tmp = rescaleBilinear(arr[arrnb], width, height);
01250                 for (int jj = 0; jj < height; jj ++)
01251                   safecopy(result.beginw() + (i + (j + jj) * tw),
01252                            tmp.begin() + (jj * width),
01253                            width);
01254               }
01255             else                // put blanks at the end
01256               for (int jj = 0; jj < height; jj ++)
01257                 safecopy(result.beginw() + (i + (j + jj) * tw),
01258                          zeros, width);
01259           }
01260         arrnb ++;
01261       }
01262 
01263   return result;
01264 }
01265 
01266 // ######################################################################
01267 template <class T>
01268 Image<T> decXY(const Image<T>& src,
01269                const int xfactor, const int yfactor_raw)
01270 {
01271 GVX_TRACE(__PRETTY_FUNCTION__);
01272 
01273   const int yfactor = yfactor_raw >= 0 ? yfactor_raw : xfactor;
01274 
01275   // do not go smaller than 1x1:
01276   if (src.getWidth() <= 1 && src.getHeight() <= 1) return src;
01277 
01278   if (src.getWidth() == 1)  // only thinout vertic
01279     {
01280       int h2 = src.getHeight() / yfactor;
01281 
01282       Image<T> result(1, h2, NO_INIT);
01283 
01284       for (int j = 0; j < h2; ++j)
01285         result.setVal(j, src.getVal(j * yfactor));
01286 
01287       return result;
01288     }
01289   if (src.getHeight() == 1)
01290     {
01291       int w2 = src.getWidth() / xfactor;
01292 
01293       Image<T> result(w2, 1, NO_INIT);
01294 
01295       for (int i = 0; i < w2; ++i)
01296         result.setVal(i, src.getVal(i * xfactor));
01297 
01298       return result;
01299     }
01300 
01301   const int w2 = src.getWidth() / xfactor;
01302   const int h2 = src.getHeight() / yfactor;
01303 
01304   Image<T> result(w2, h2, NO_INIT);
01305 
01306   typename Image<T>::const_iterator sptr = src.begin();
01307   typename Image<T>::iterator dptr = result.beginw();
01308   const int skip = src.getWidth() % xfactor + src.getWidth() * (yfactor - 1);
01309 
01310   for (int j = 0; j < h2; ++j)
01311     {
01312       for (int i = 0; i < w2; ++i)
01313         {
01314           *dptr++ = *sptr;   // copy one pixel
01315           sptr += xfactor;   // skip some pixels
01316         }
01317       sptr += skip;          // skip to start of next line
01318     }
01319 
01320   return result;
01321 }
01322 
01323 // ######################################################################
01324 template <class T>
01325 Image<T> decX(const Image<T>& src, const int factor)
01326 {
01327 GVX_TRACE(__PRETTY_FUNCTION__);
01328 
01329   ASSERT(factor > 0);
01330 
01331   if (src.getWidth() <= 1) return src;  // do not go smaller than 1 pixel wide
01332 
01333   const int h = src.getHeight();
01334   int w2 = src.getWidth() / factor;
01335   int skip = src.getWidth() % factor;
01336   if (w2 == 0)
01337     {
01338       w2 = 1;
01339       skip = src.getWidth() - factor;
01340     }
01341 
01342   Image<T> result(w2, h, NO_INIT);
01343 
01344   typename Image<T>::const_iterator sptr = src.begin();
01345   typename Image<T>::iterator dptr = result.beginw();
01346 
01347   for (int j = 0; j < h; ++j)
01348     {
01349       for (int i = 0; i < w2; ++i)
01350         {
01351           *dptr++ = *sptr;   // copy one point
01352           sptr += factor;    // skip a few points
01353         }
01354       sptr += skip;
01355     }
01356 
01357   return result;
01358 }
01359 
01360 // ######################################################################
01361 template <class T>
01362 Image<T> decY(const Image<T>& src, const int factor)
01363 {
01364 GVX_TRACE(__PRETTY_FUNCTION__);
01365 
01366   ASSERT(factor > 0);
01367 
01368   if (src.getHeight() <= 1) return src;  // do not go smaller than 1 pixel high
01369 
01370   const int w = src.getWidth();
01371   int h2 = src.getHeight() / factor;
01372   if (h2 == 0) h2 = 1;
01373 
01374   Image<T> result(w, h2, NO_INIT);
01375 
01376   typename Image<T>::const_iterator sptr = src.begin();
01377   typename Image<T>::iterator dptr = result.beginw();
01378   int skip = w * factor;
01379 
01380   for (int j = 0; j < h2; ++j)
01381     {
01382       safecopy(dptr, sptr, w);
01383       dptr += w; sptr += skip;
01384     }
01385 
01386   return result;
01387 }
01388 
01389 // ######################################################################
01390 template <class T>
01391 Image<T> blurAndDecY(const Image<T>& src, const int factor)
01392 {
01393   GVX_TRACE(__PRETTY_FUNCTION__);
01394 
01395   ASSERT(factor > 0);
01396 
01397   if (src.getHeight() <= 1) return src;  // do not go smaller than 1 pixel high
01398 
01399   const int w = src.getWidth();
01400   const int h1 = src.getHeight();
01401   int h2 = h1 / factor;
01402   if (h2 == 0) h2 = 1;
01403 
01404   Image<T> result(w, h2, ZEROS);
01405 
01406   typename Image<T>::const_iterator const sptr = src.begin();
01407   typename Image<T>::iterator dptr = result.beginw();
01408 
01409   for (int j2 = 0; j2 < h2; ++j2)
01410     {
01411       int j1start = j2 * factor - (factor/2);
01412       int j1end = j1start + factor;
01413       if (j1start < 0) j1start = 0;
01414       if (j1end > h1) j1end = h1;
01415 
01416       const int nj = j1end-j1start;
01417 
01418       ASSERT(nj > 0);
01419 
01420       const double factor = 1.0/nj;
01421 
01422       for (int j1 = j1start; j1 < j1end; ++j1)
01423         for (int i = 0; i < w; ++i)
01424           dptr[i] += T(factor * sptr[i + j1*w]);
01425 
01426       dptr += w;
01427     }
01428 
01429   return result;
01430 }
01431 
01432 // ######################################################################
01433 template <class T>
01434 Image<T> intXY(const Image<T>& src, const bool dupli)
01435 {
01436 GVX_TRACE(__PRETTY_FUNCTION__);
01437 
01438   ASSERT(src.initialized());   // works only if there is an image
01439 
01440   const int w = src.getWidth();
01441   const int h = src.getHeight();
01442 
01443   const int w2 = w * 2;
01444   const int h2 = h * 2;
01445 
01446   Image<T> result(w2, h2, NO_INIT);
01447 
01448   typename Image<T>::const_iterator sptr = src.begin();
01449   typename Image<T>::iterator dptr = result.beginw();
01450   if (dupli)
01451     for (int j = 0; j < h; ++j)
01452       {
01453         for (int i = 0; i < w; ++i)
01454           {
01455             *dptr++ = *sptr;     // copy one point
01456             *dptr++ = *sptr++;   // again
01457           }
01458         // duplicate line we just wrote:
01459         safecopy(dptr, dptr - w2, w2);
01460         dptr += w2;
01461       }
01462   else
01463     {
01464       T zeros[w2];
01465       for (int i = 0; i < w2; ++i) zeros[i] = T();  // needed??
01466       for (int j = 0; j < h; ++j)
01467         {
01468           for (int i = 0; i < w; ++i)
01469             {
01470               *dptr++ = *sptr++;        // copy one point
01471               *dptr++ = zeros[0];   // put a blank
01472             }
01473           safecopy(dptr, zeros, w2);
01474           dptr += w2;
01475         }
01476     }
01477 
01478   return result;
01479 }
01480 
01481 // ######################################################################
01482 template <class T>
01483 Image<T> intX(const Image<T>& src, const bool dupli)
01484 {
01485 GVX_TRACE(__PRETTY_FUNCTION__);
01486 
01487   ASSERT(src.initialized());
01488 
01489   const int w2 = src.getWidth() * 2;
01490 
01491   Image<T> result(w2, src.getHeight(), NO_INIT);
01492 
01493   typename Image<T>::const_iterator sptr = src.begin();
01494   typename Image<T>::iterator dptr = result.beginw();
01495   T zero = T();
01496 
01497   const int siz = src.getSize();
01498 
01499   if (dupli)
01500     for (int i = 0; i < siz; ++i)
01501       {
01502         *dptr++ = *sptr;     // copy one point
01503         *dptr++ = *sptr++;   // again
01504       }
01505   else
01506     for (int i = 0; i < siz; ++i)
01507       {
01508         *dptr++ = *sptr++;  // copy one point
01509         *dptr++ = zero;     // put a zero
01510       }
01511 
01512   return result;
01513 }
01514 
01515 // ######################################################################
01516 template <class T>
01517 Image<T> intY(const Image<T>& src, const bool dupli)
01518 {
01519 GVX_TRACE(__PRETTY_FUNCTION__);
01520 
01521   ASSERT(src.initialized());
01522 
01523   const int w = src.getWidth();
01524   const int h = src.getHeight();
01525   const int h2 = h * 2;
01526 
01527   Image<T> result(w, h2, NO_INIT);
01528 
01529   typename Image<T>::const_iterator sptr = src.begin();
01530   typename Image<T>::iterator dptr = result.beginw();
01531 
01532   if (dupli)
01533     for (int j = 0; j < h; ++j)
01534       {
01535         safecopy(dptr, sptr, w);
01536         dptr += w;
01537         safecopy(dptr, sptr, w);
01538         dptr += w;
01539         sptr += w;
01540       }
01541   else
01542     {
01543       T zeros[w];
01544       for (int i = 0; i < w; ++i) zeros[i] = T(); // needed??
01545       for (int j = 0; j < h; ++j)
01546         {
01547           safecopy(dptr, sptr, w);
01548           dptr += w;
01549           safecopy(dptr, zeros, w);
01550           dptr += w;
01551           sptr += w;
01552         }
01553     }
01554 
01555   return result;
01556 }
01557 
01558 // ######################################################################
01559 template <class T>
01560 Image<T> zoomXY(const Image<T>& src, int xzoom, int yzoom)
01561 {
01562 GVX_TRACE(__PRETTY_FUNCTION__);
01563 
01564   if (yzoom < 0) yzoom = xzoom;
01565 
01566   if (xzoom == 0 || yzoom == 0) return Image<T>();
01567   if (xzoom == 1 && yzoom == 1) return src;
01568 
01569   Image<T> result(src.getWidth()*xzoom, src.getHeight()*yzoom, NO_INIT);
01570 
01571   typename Image<T>::iterator dptr = result.beginw();
01572 
01573   for (int y = 0; y < src.getHeight(); ++y)
01574     {
01575       for (int yz = 0; yz < yzoom; ++yz)
01576         {
01577           typename Image<T>::const_iterator sptr =
01578             src.begin() + y*src.getWidth();
01579           for (int x = 0; x < src.getWidth(); ++x)
01580             {
01581               for (int xz = 0; xz < xzoom; ++xz)
01582                 {
01583                   *dptr = *sptr;
01584                   ++dptr;
01585                 }
01586               ++sptr;
01587             }
01588         }
01589     }
01590 
01591   return result;
01592 }
01593 
01594 // ######################################################################
01595 template <class T>
01596 Image<T> rotate(const Image<T>& srcImg, const int x, const int y,
01597                 const float ang)
01598 {
01599 GVX_TRACE(__PRETTY_FUNCTION__);
01600 
01601   // make sure the source image is valid
01602   ASSERT(srcImg.initialized());
01603 
01604   // create and clear the return image
01605   int w = srcImg.getWidth(), h = srcImg.getHeight();
01606   Image<T> retImg(w, h, ZEROS);
01607 
01608   // create temporary working image, put srcImg in the middle
01609   // put some padding (values are 0) around the image
01610   // so we won't need to check out of bounds when rotating
01611   int pad = int(ceil(sqrt(w * w + h * h)));
01612   int wR  = 2 * pad + w;
01613   int hR  = 2 * pad + h;
01614   Image<T> tempImg(wR, hR, ZEROS);
01615   inplacePaste(tempImg, srcImg, Point2D<int>(pad,pad));
01616 
01617   typename Image<T>::iterator rBPtr = retImg.beginw();
01618   float cAng = cos(ang), sAng = sin(ang);
01619 
01620   // fill in the return image with the appropriate values
01621   float xR = 0.0; float yR = 0.0;
01622   int dx = pad + x, dy = pad + y;
01623   for(int j = -y; j < h-y; j++)
01624     for(int i = -x; i < w-x; i++)
01625       {
01626         xR = dx + i*cAng + j*sAng;
01627         yR = dy - i*sAng + j*cAng;
01628         *rBPtr++ = T(tempImg.getValInterp(xR,yR));
01629       }
01630 
01631   return retImg;
01632 }
01633 
01634 // Include the explicit instantiations (color instantiations are now
01635 // requested by using "T_or_RGB" for the template formal parameter name in
01636 // the declarations in the .H file).
01637 #include "inst/Image/ShapeOps.I"
01638 
01639 template Image<double> quickLocalAvg2x2(const Image<double>&);
01640 template Image<int> decX(const Image<int>&, int);
01641 template Image<int> decY(const Image<int>&, int);
01642 template Image<int> decXY(const Image<int>&, int, int);
01643 template Image<double> intXY(const Image<double>&, bool);
01644 template Image<PixH2SV2<float> > decXY(const Image<PixH2SV2<float> >&, int, int);
01645 template Image<PixH2SV2<float> > rescaleBilinear(const Image<PixH2SV2<float> >& src, int, int);
01646 template Image<geom::vec2f> rotate(const Image<geom::vec2f>&, int, int,float);
01647 template Image<uint16> zoomXY(const Image<uint16>&, int, int);
01648 template Image<uint16> rescale(const Image<uint16>&, const Dims&, RescaleType);
01649 
01650 // ######################################################################
01651 /* So things look consistent in everyone's emacs... */
01652 /* Local Variables: */
01653 /* indent-tabs-mode: nil */
01654 /* End: */
01655 
01656 #endif // !IMAGE_SHAPEOPS_C_DEFINED
Generated on Sun May 8 08:40:57 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3