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