00001 /*!@file Image/Convolutions.C basic 1-D and 2-D filtering operations */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Rob Peters <rjpeters at usc dot edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/Convolutions.C $ 00035 // $Id: Convolutions.C 10532 2008-12-16 20:02:46Z dparks $ 00036 // 00037 00038 #ifndef IMAGE_CONVOLUTIONS_C_DEFINED 00039 #define IMAGE_CONVOLUTIONS_C_DEFINED 00040 00041 #include "Image/Convolutions.H" 00042 00043 #include "Image/Image.H" 00044 #include "Image/Pixels.H" 00045 #include "rutz/trace.h" 00046 00047 // ###################################################################### 00048 template <class T> static 00049 Image<typename promote_trait<T, float>::TP> 00050 convolveZeroHelper(const Image<T>& src, const float* filter, 00051 const int Nx, const int Ny) 00052 { 00053 GVX_TRACE(__PRETTY_FUNCTION__); 00054 ASSERT(src.initialized()); //ASSERT((Nx & 1) && (Ny & 1)); 00055 const int w = src.getWidth(), h = src.getHeight(); 00056 // promote the source image to float if necessary, so that we do the 00057 // promotion only once for all, rather than many times as we access 00058 // the pixels of the image; if no promotion is necessary, "source" 00059 // will just point to the original data of "src" through the 00060 // copy-on-write/ref-counting behavior of Image: 00061 typedef typename promote_trait<T, float>::TP TF; 00062 const Image<TF> source = src; 00063 Image<TF> result(w, h, NO_INIT); 00064 typename Image<TF>::const_iterator sptr = source.begin(); 00065 typename Image<TF>::iterator dptr = result.beginw(); 00066 00067 const int kkk = Nx * Ny - 1; 00068 const int Nx2 = (Nx - 1) / 2; 00069 const int Ny2 = (Ny - 1) / 2; 00070 00071 // very inefficient implementation; one has to be crazy to use non 00072 // separable filters anyway... 00073 for (int j = 0; j < h; ++j) // LOOP rows of src 00074 { 00075 const int j_offset = j-Ny2; 00076 00077 for (int i = 0; i < w; ++i) // LOOP columns of src 00078 { 00079 TF sum = TF(); 00080 const int i_offset = i-Nx2; 00081 const int kj_bgn = std::max(- j_offset, 0); 00082 const int kj_end = std::min(h - j_offset, Ny); 00083 00084 for (int kj = kj_bgn; kj < kj_end; ++kj) // LOOP rows of filter 00085 { 00086 const int kjj = kj + j_offset; // row of src 00087 const int src_offset = w * kjj + i_offset; 00088 const int filt_offset = kkk - Nx*kj; 00089 00090 const int ki_bgn = std::max(-i_offset, 0); 00091 const int ki_end = std::min(w-i_offset, Nx); 00092 00093 for (int ki = ki_bgn; ki < ki_end; ++ki) // LOOP cols of filt 00094 sum += sptr[ki + src_offset] * filter[filt_offset - ki]; 00095 } 00096 *dptr++ = sum; 00097 } 00098 } 00099 return result; 00100 } 00101 00102 // ###################################################################### 00103 template <class T> static 00104 Image<typename promote_trait<T, float>::TP> 00105 convolveCleanHelper(const Image<T>& src, const float* filter, 00106 const int Nx, const int Ny) 00107 { 00108 GVX_TRACE(__PRETTY_FUNCTION__); 00109 ASSERT(src.initialized()); //ASSERT((Nx & 1) && (Ny & 1)); 00110 const int w = src.getWidth(), h = src.getHeight(); 00111 // promote the source image to float if necessary, so that we do the 00112 // promotion only once for all, rather than many times as we access 00113 // the pixels of the image; if no promotion is necessary, "source" 00114 // will just point to the original data of "src" through the 00115 // copy-on-write/ref-counting behavior of Image: 00116 typedef typename promote_trait<T, float>::TP TF; 00117 const Image<TF> source = src; 00118 Image<TF> result(w, h, NO_INIT); 00119 typename Image<TF>::const_iterator sptr = source.begin(); 00120 typename Image<TF>::iterator dptr = result.beginw(); 00121 00122 const int kkk = Nx * Ny - 1; 00123 const int Nx2 = (Nx - 1) / 2; 00124 const int Ny2 = (Ny - 1) / 2; 00125 00126 float sumf = 0.0f; 00127 for (int i = 0; i < Nx*Ny; ++i) 00128 sumf += filter[i]; 00129 00130 // very inefficient implementation; one has to be crazy to use non 00131 // separable filters anyway... 00132 for (int j = 0; j < h; ++j) // LOOP rows of src 00133 { 00134 const int j_offset = j-Ny2; 00135 00136 for (int i = 0; i < w; ++i) // LOOP columns of src 00137 { 00138 TF sum = TF(); 00139 float sumw = 0.0f; 00140 const int i_offset = i-Nx2; 00141 const int kj_bgn = std::max(- j_offset, 0); 00142 const int kj_end = std::min(h - j_offset, Ny); 00143 00144 for (int kj = kj_bgn; kj < kj_end; ++kj) // LOOP rows of filter 00145 { 00146 const int kjj = kj + j_offset; // row of src 00147 const int src_offset = w * kjj + i_offset; 00148 const int filt_offset = kkk - Nx*kj; 00149 00150 const int ki_bgn = std::max(-i_offset, 0); 00151 const int ki_end = std::min(w-i_offset, Nx); 00152 00153 for (int ki = ki_bgn; ki < ki_end; ++ki) // LOOP cols of filt 00154 { 00155 sum += sptr[ki + src_offset] * filter[filt_offset - ki]; 00156 sumw += filter[filt_offset - ki]; 00157 } 00158 } 00159 *dptr++ = sum * sumf / sumw; 00160 } 00161 } 00162 return result; 00163 } 00164 00165 // ###################################################################### 00166 template <class T> static 00167 Image<typename promote_trait<T, float>::TP> 00168 convolve(const Image<T>& src, const float* filter, 00169 const int Nx, const int Ny, 00170 ConvolutionBoundaryStrategy boundary) 00171 { 00172 switch (boundary) 00173 { 00174 case CONV_BOUNDARY_ZERO: 00175 return convolveZeroHelper(src, filter, Nx, Ny); 00176 case CONV_BOUNDARY_CLEAN: 00177 return convolveCleanHelper(src, filter, Nx, Ny); 00178 case CONV_BOUNDARY_REPLICATE: 00179 // not implemented yet 00180 break; 00181 } 00182 00183 LFATAL("convolution boundary strategy %d not supported", 00184 (int) boundary); 00185 /* can't happen */ return Image<typename promote_trait<T, float>::TP>(); 00186 } 00187 00188 // ###################################################################### 00189 template <class T> 00190 Image<typename promote_trait<T, float>::TP> 00191 optConvolve(const Image<T>& src, const Image<float>& f) 00192 { 00193 GVX_TRACE(__PRETTY_FUNCTION__); 00194 ASSERT(src.initialized()); 00195 00196 const int src_w = src.getWidth(); 00197 const int src_h = src.getHeight(); 00198 00199 Image<float>::const_iterator filter = f.begin(); 00200 const int fil_w = f.getWidth(); 00201 const int fil_h = f.getHeight(); 00202 00203 ASSERT((fil_w & 1) && (fil_h & 1)); 00204 00205 typedef typename promote_trait<T, float>::TP TF; 00206 const Image<TF> source = src; 00207 Image<TF> result(src_w, src_h, NO_INIT); 00208 typename Image<TF>::const_iterator sptr = source.begin(); 00209 typename Image<TF>::iterator dptr = result.beginw(); 00210 00211 const int fil_end = fil_w * fil_h - 1; 00212 const int Nx2 = (fil_w - 1) / 2; 00213 const int Ny2 = (fil_h - 1) / 2; 00214 00215 const int srow_skip = src_w-fil_w; 00216 00217 for (int dst_y = 0; dst_y < src_h; ++dst_y) 00218 { 00219 // Determine if we're safely inside the image in the y-direction: 00220 const bool y_clean = dst_y >= Ny2 && dst_y < (src_h - Nx2); 00221 00222 for (int dst_x = 0; dst_x < src_w; ++dst_x, ++dptr) 00223 { 00224 // Determine if we're safely inside the image in the x-direction: 00225 const bool x_clean = dst_x >= Nx2 && dst_x < (src_w - Nx2); 00226 00227 // Here is where we pick whether we can use the optimized inner 00228 // loop (in cases where the filter and image patch overlap 00229 // completely) or whether we must use the inner loop that can 00230 // handle boundary conditions. 00231 00232 if (x_clean && y_clean) 00233 { 00234 float dst_val = 0.0f; 00235 00236 Image<float>::const_iterator fptr = filter+fil_end; 00237 00238 Image<float>::const_iterator srow_ptr = 00239 sptr + src_w*(dst_y-Nx2) + dst_x - Nx2; 00240 00241 for (int f_y = 0; f_y < fil_h; ++f_y) 00242 { 00243 for (int f_x = 0; f_x < fil_w; ++f_x) 00244 dst_val += (*srow_ptr++) * (*fptr--); 00245 00246 srow_ptr += srow_skip; 00247 } 00248 *dptr = dst_val; 00249 continue; 00250 } 00251 else 00252 { 00253 // OK, we're at an image boundary, so what do we do to make 00254 // up for the missing pixels? The approach here is to 00255 // compute the average value of the non-missing pixels, and 00256 // proceed as if the missing pixels had that value. This 00257 // minimizes the introduction of edge artifacts e.g. when 00258 // convolving with an oriented filter. 00259 00260 float dst_val = 0.0f; 00261 float src_sum = 0.0f; 00262 int src_cnt = 0; 00263 float fil_sum_skipped = 0.0f; 00264 00265 for (int f_y = 0; f_y < fil_h; ++f_y) 00266 { 00267 const int src_y = f_y + dst_y - Ny2; 00268 if (src_y >= 0 && src_y < src_h) 00269 { 00270 for (int f_x = 0; f_x < fil_w; ++f_x) 00271 { 00272 const float fil = filter[fil_end - f_x - fil_w*f_y]; 00273 00274 const int src_x = f_x + dst_x - Nx2; 00275 if (src_x >= 0 && src_x < src_w) 00276 { 00277 const float src_val = sptr[src_x + src_w * src_y]; 00278 dst_val += src_val * fil; 00279 src_sum += src_val; 00280 ++src_cnt; 00281 } 00282 else 00283 { 00284 fil_sum_skipped += fil; 00285 } 00286 } 00287 } 00288 else 00289 { 00290 for (int f_x = 0; f_x < fil_w; ++f_x) 00291 fil_sum_skipped += filter[fil_end - f_x - fil_w*f_y]; 00292 } 00293 } 00294 const float src_avg = src_sum / src_cnt; 00295 *dptr = dst_val + (fil_sum_skipped * src_avg); 00296 } 00297 } 00298 } 00299 return result; 00300 } 00301 00302 // ###################################################################### 00303 template <class T> 00304 Image<typename promote_trait<T, float>::TP> 00305 convolveHmax(const Image<T>& src, const Image<float>& filter) 00306 { 00307 GVX_TRACE(__PRETTY_FUNCTION__); 00308 ASSERT(src.initialized()); 00309 const int Nx = filter.getWidth(), Ny = filter.getHeight(); 00310 ASSERT((Nx & 1) && (Ny & 1)); 00311 00312 const int w = src.getWidth(), h = src.getHeight(); 00313 // promote the source image to float if necessary, so that we do the 00314 // promotion only once for all, rather than many times as we access 00315 // the pixels of the image; if no promotion is necessary, "source" 00316 // will just point to the original data of "src" through the 00317 // copy-on-write/ref-counting behavior of Image: 00318 typedef typename promote_trait<T, float>::TP TF; 00319 const Image<TF> source = src; 00320 Image<TF> result(w, h, NO_INIT); 00321 typename Image<TF>::const_iterator sptr = source.begin(); 00322 typename Image<TF>::iterator dptr = result.beginw(); 00323 typename Image<float>::const_iterator const filt = filter.begin(); 00324 00325 int kkk = Nx * Ny - 1; 00326 int Nx2 = (Nx - 1) / 2, Ny2 = (Ny - 1) / 2; 00327 // very inefficient implementation; one has to be crazy to use non 00328 // separable filters anyway... 00329 for (int j = 0; j < h; ++j) 00330 for (int i = 0; i < w; ++i) 00331 { 00332 TF sum = TF(), sumw = TF(); 00333 for (int kj = 0; kj < Ny; ++kj) 00334 { 00335 int kjj = kj + j - Ny2; 00336 if (kjj >= 0 && kjj < h) 00337 for (int ki = 0; ki < Nx; ++ki) 00338 { 00339 int kii = ki + i - Nx2; 00340 if (kii >= 0 && kii < w) 00341 { 00342 float fil = filt[kkk - ki - Nx*kj]; 00343 TF img = sptr[kii + w * kjj]; 00344 sum += img * fil; sumw += img * img; 00345 } 00346 } 00347 } 00348 //if (sumw > 0.0F) sum /= fastSqrt(sumw); 00349 if (sumw > 0.0F) sum = fabs(sum)/sqrt(sumw); 00350 *dptr++ = sum; 00351 } 00352 return result; 00353 } 00354 00355 // ###################################################################### 00356 template <class T> 00357 static Image<typename promote_trait<T, float>::TP> 00358 xFilter(const Image<T>& src, const float* hFilt, const int hfs, 00359 ConvolutionBoundaryStrategy boundary) 00360 { 00361 GVX_TRACE(__PRETTY_FUNCTION__); 00362 const int w = src.getWidth(), h = src.getHeight(); 00363 // promote the source image to float if necessary, so that we do the 00364 // promotion only once for all, rather than many times as we access 00365 // the pixels of the image; if no promotion is necessary, "source" 00366 // will just point to the original data of "src" through the 00367 // copy-on-write/ref-counting behavior of Image: 00368 typedef typename promote_trait<T, float>::TP TF; 00369 const Image<TF> source = src; 00370 if (hfs == 0) return source; // no filter 00371 Image<TF> result(w, h, NO_INIT); 00372 typename Image<TF>::const_iterator sptr = source.begin(); 00373 typename Image<TF>::iterator dptr = result.beginw(); 00374 00375 const int hfs2 = (hfs - 1) / 2; 00376 00377 // flip the filter to accelerate convolution: 00378 float *hFilter = new float[hfs]; float sumh = 0.0f; 00379 for (int x = 0; x < hfs; x ++) 00380 { sumh += hFilt[x]; hFilter[hfs-1-x] = hFilt[x]; } 00381 00382 // *** horizontal pass *** 00383 if (w < hfs) // special function for very small images 00384 { 00385 switch (boundary) 00386 { 00387 case CONV_BOUNDARY_ZERO: 00388 { 00389 for (int j = 0; j < h; j ++) 00390 for (int i = 0; i < w; i ++) 00391 { 00392 TF val = TF(); 00393 for (int k = 0; k < hfs; k ++) 00394 if (i + k - hfs2 >= 0 && i + k - hfs2 < w) 00395 { 00396 val += sptr[k - hfs2] * hFilter[k]; 00397 } 00398 *dptr++ = val; sptr ++; 00399 } 00400 } 00401 break; 00402 case CONV_BOUNDARY_CLEAN: 00403 { 00404 for (int j = 0; j < h; j ++) 00405 for (int i = 0; i < w; i ++) 00406 { 00407 TF val = TF(); float sum = 0.0F; 00408 for (int k = 0; k < hfs; k ++) 00409 if (i + k - hfs2 >= 0 && i + k - hfs2 < w) 00410 { 00411 val += sptr[k - hfs2] * hFilter[k]; 00412 sum += hFilter[k]; 00413 } 00414 *dptr++ = val * sumh / sum; sptr ++; 00415 } 00416 } 00417 break; 00418 case CONV_BOUNDARY_REPLICATE: 00419 { 00420 for (int j = 0; j < h; j ++) 00421 for (int i = 0; i < w; i ++) 00422 { 00423 TF val = TF(); 00424 for (int k = 0; k < hfs; k ++) 00425 if (i + k - hfs2 < 0) 00426 { 00427 val += sptr[-i] * hFilter[k]; 00428 } 00429 else if (i + k - hfs2 >= w) 00430 { 00431 val += sptr[w-1-i] * hFilter[k]; 00432 } 00433 else 00434 { 00435 val += sptr[k - hfs2] * hFilter[k]; 00436 } 00437 *dptr++ = val; sptr ++; 00438 } 00439 } 00440 break; 00441 default: 00442 LFATAL("invalid convolution boundary strategy %d", 00443 (int) boundary); 00444 } 00445 } 00446 else // function for reasonably large images 00447 for (int jj = 0; jj < h; jj ++) 00448 { 00449 // leftmost points: 00450 switch (boundary) 00451 { 00452 case CONV_BOUNDARY_ZERO: 00453 { 00454 for (int j = hfs2; j > 0; --j) 00455 { 00456 TF val = TF(); 00457 for (int k = j; k < hfs; ++k) 00458 { 00459 val += sptr[k - j] * hFilter[k]; 00460 } 00461 *dptr++ = val; 00462 } 00463 } 00464 break; 00465 case CONV_BOUNDARY_CLEAN: 00466 { 00467 for (int j = hfs2; j > 0; --j) 00468 { 00469 TF val = TF(); float sum = 0.0F; 00470 for (int k = j; k < hfs; ++k) 00471 { 00472 val += sptr[k - j] * hFilter[k]; 00473 sum += hFilter[k]; 00474 } 00475 *dptr++ = val * sumh / sum; 00476 } 00477 } 00478 break; 00479 case CONV_BOUNDARY_REPLICATE: 00480 { 00481 for (int j = hfs2; j > 0; --j) 00482 { 00483 TF val = TF(); 00484 for (int k = 0; k < j; ++k) 00485 { 00486 val += sptr[0] * hFilter[k]; 00487 } 00488 for (int k = j; k < hfs; ++k) 00489 { 00490 val += sptr[k - j] * hFilter[k]; 00491 } 00492 *dptr++ = val; 00493 } 00494 } 00495 break; 00496 // case CONV_BOUNDARY_REFLECT: 00497 // { 00498 // for (int j = hfs2; j > 0; --j) 00499 // { 00500 // TF val = TF(); 00501 // for (int k = 0; k < j; ++k) 00502 // { 00503 // val += sptr[j - k] * hFilter[k]; 00504 // } 00505 // for (int k = j; k < hfs; ++k) 00506 // { 00507 // val += sptr[k - j] * hFilter[k]; 00508 // } 00509 // *dptr++ = val; 00510 // } 00511 // } 00512 // break; 00513 default: 00514 LFATAL("invalid convolution boundary strategy %d", 00515 (int) boundary); 00516 } 00517 // bulk (far from the borders): 00518 for (int i = 0; i < w - hfs + 1; i ++) 00519 { 00520 TF val = TF(); 00521 for (int k = 0; k < hfs; k ++) val += sptr[k] * hFilter[k]; 00522 *dptr++ = val; sptr++; 00523 } 00524 // rightmost points: 00525 switch (boundary) 00526 { 00527 case CONV_BOUNDARY_ZERO: 00528 { 00529 for (int j = hfs - 1; j > hfs2; --j) 00530 { 00531 TF val = TF(); 00532 for (int k = 0; k < j; ++k) 00533 { 00534 val += sptr[k] * hFilter[k]; 00535 } 00536 *dptr++ = val; 00537 sptr++; 00538 } 00539 } 00540 break; 00541 case CONV_BOUNDARY_CLEAN: 00542 { 00543 for (int j = hfs - 1; j > hfs2; --j) 00544 { 00545 TF val = TF(); float sum = 0.0F; 00546 for (int k = 0; k < j; ++k) 00547 { 00548 val += sptr[k] * hFilter[k]; 00549 sum += hFilter[k]; 00550 } 00551 *dptr++ = val * sumh / sum; 00552 sptr++; 00553 } 00554 } 00555 break; 00556 case CONV_BOUNDARY_REPLICATE: 00557 { 00558 for (int j = hfs - 1; j > hfs2; --j) 00559 { 00560 TF val = TF(); 00561 for (int k = 0; k < j; ++k) 00562 { 00563 val += sptr[k] * hFilter[k]; 00564 } 00565 for (int k = j; k < hfs; ++k) 00566 { 00567 val += sptr[j-1] * hFilter[k]; 00568 } 00569 *dptr++ = val; 00570 sptr++; 00571 } 00572 } 00573 break; 00574 // case CONV_BOUNDARY_REFLECT: 00575 // { 00576 // for (int j = hfs - 1; j > hfs2; --j) 00577 // { 00578 // TF val = TF(); 00579 // for (int k = 0; k < j; ++k) 00580 // { 00581 // val += sptr[k] * hFilter[k]; 00582 // } 00583 // for (int k = j; k < hfs; ++k) 00584 // { 00585 // val += sptr[2*j-k-1] * hFilter[k]; 00586 // } 00587 // *dptr++ = val; 00588 // sptr++; 00589 // } 00590 // } 00591 // break; 00592 default: 00593 LFATAL("invalid convolution boundary strategy %d", 00594 (int) boundary); 00595 } 00596 sptr += hfs2; // sptr back to same as dptr (start of next line) 00597 } 00598 delete [] hFilter; 00599 return result; 00600 } 00601 00602 // ###################################################################### 00603 template <class T> 00604 static Image<typename promote_trait<T, float>::TP> 00605 yFilter(const Image<T>& src, const float* vFilt, const int vfs, 00606 ConvolutionBoundaryStrategy boundary) 00607 { 00608 GVX_TRACE(__PRETTY_FUNCTION__); 00609 const int w = src.getWidth(), h = src.getHeight(); 00610 // promote the source image to float if necessary, so that we do the 00611 // promotion only once for all, rather than many times as we access 00612 // the pixels of the image; if no promotion is necessary, "source" 00613 // will just point to the original data of "src" through the 00614 // copy-on-write/ref-counting behavior of Image: 00615 typedef typename promote_trait<T, float>::TP TF; 00616 const Image<TF> source = src; 00617 if (vfs == 0) return source; // no filter 00618 Image<TF> result(w, h, NO_INIT); 00619 typename Image<TF>::const_iterator sptr = source.begin(); 00620 typename Image<TF>::iterator dptr = result.beginw(); 00621 00622 const int vfs2 = (vfs - 1) / 2; 00623 00624 // flip the filter to accelerate convolution: 00625 float *vFilter = new float[vfs]; float sumv = 0.0f; 00626 for (int x = 0; x < vfs; x ++) 00627 { sumv += vFilt[x]; vFilter[vfs-1-x] = vFilt[x]; } 00628 00629 int ww[vfs]; 00630 for (int i = 0; i < vfs; i ++) ww[i] = w * i; // speedup precompute 00631 00632 if (h < vfs) // special function for very small images 00633 { 00634 switch (boundary) 00635 { 00636 case CONV_BOUNDARY_ZERO: 00637 { 00638 for (int j = 0; j < h; j ++) 00639 for (int i = 0; i < w; i ++) 00640 { 00641 TF val = TF(); 00642 for (int k = 0; k < vfs; k ++) 00643 if (j + k - vfs2 >= 0 && j + k - vfs2 < h) 00644 { 00645 val += sptr[w * (k - vfs2)] * vFilter[k]; 00646 } 00647 *dptr++ = val; sptr ++; 00648 } 00649 } 00650 break; 00651 case CONV_BOUNDARY_CLEAN: 00652 { 00653 for (int j = 0; j < h; j ++) 00654 for (int i = 0; i < w; i ++) 00655 { 00656 TF val = TF(); float sum = 0.0; 00657 for (int k = 0; k < vfs; k ++) 00658 if (j + k - vfs2 >= 0 && j + k - vfs2 < h) 00659 { 00660 val += sptr[w * (k - vfs2)] * vFilter[k]; 00661 sum += vFilter[k]; 00662 } 00663 *dptr++ = val * sumv / sum; sptr ++; 00664 } 00665 } 00666 break; 00667 case CONV_BOUNDARY_REPLICATE: 00668 { 00669 for (int j = 0; j < h; j ++) 00670 for (int i = 0; i < w; i ++) 00671 { 00672 TF val = TF(); 00673 for (int k = 0; k < vfs; k ++) 00674 if (j + k - vfs2 < 0) 00675 { 00676 val += sptr[w * (-j)] * vFilter[k]; 00677 } 00678 else if (j + k - vfs2 >= h) 00679 { 00680 val += sptr[w * (h-1-j)] * vFilter[k]; 00681 } 00682 else 00683 { 00684 val += sptr[w * (k - vfs2)] * vFilter[k]; 00685 } 00686 *dptr++ = val; sptr ++; 00687 } 00688 } 00689 break; 00690 default: 00691 LFATAL("invalid convolution boundary strategy %d", 00692 (int) boundary); 00693 } 00694 } 00695 else // function for reasonably large images 00696 { 00697 // top points: 00698 switch (boundary) 00699 { 00700 case CONV_BOUNDARY_ZERO: 00701 { 00702 for (int j = vfs2; j > 0; --j) 00703 { 00704 for (int i = 0; i < w; ++i) // scan all points of given horiz 00705 { 00706 TF val = TF(); 00707 for (int k = j; k < vfs; ++k) 00708 { 00709 val += sptr[ww[k - j]] * vFilter[k]; 00710 } 00711 *dptr++ = val; 00712 sptr++; 00713 } 00714 sptr -= w; // back to top-left corner 00715 } 00716 } 00717 break; 00718 case CONV_BOUNDARY_CLEAN: 00719 { 00720 for (int j = vfs2; j > 0; --j) 00721 { 00722 for (int i = 0; i < w; ++i) // scan all points of given horiz 00723 { 00724 TF val = TF(); float sum = 0.0; 00725 for (int k = j; k < vfs; ++k) 00726 { 00727 val += sptr[ww[k - j]] * vFilter[k]; 00728 sum += vFilter[k]; 00729 } 00730 *dptr++ = val * sumv / sum; 00731 sptr++; 00732 } 00733 sptr -= w; // back to top-left corner 00734 } 00735 } 00736 break; 00737 case CONV_BOUNDARY_REPLICATE: 00738 { 00739 for (int j = vfs2; j > 0; --j) 00740 { 00741 for (int i = 0; i < w; ++i) // scan all points of given horiz 00742 { 00743 TF val = TF(); 00744 for (int k = 0; k < j; ++k) 00745 { 00746 val += sptr[ww[0]] * vFilter[k]; 00747 } 00748 for (int k = j; k < vfs; ++k) 00749 { 00750 val += sptr[ww[k - j]] * vFilter[k]; 00751 } 00752 *dptr++ = val; 00753 sptr++; 00754 } 00755 sptr -= w; // back to top-left corner 00756 } 00757 } 00758 break; 00759 default: 00760 LFATAL("invalid convolution boundary strategy %d", 00761 (int) boundary); 00762 } 00763 // bulk (far from edges): 00764 for (int j = 0; j < h - vfs + 1; j ++) 00765 for (int i = 0; i < w; i ++) 00766 { 00767 TF val = TF(); 00768 for (int k = 0; k < vfs; k ++) val += sptr[ww[k]] * vFilter[k]; 00769 *dptr++ = val; sptr ++; 00770 } 00771 // bottommost points: 00772 switch (boundary) 00773 { 00774 case CONV_BOUNDARY_ZERO: 00775 { 00776 for (int j = vfs - 1; j > vfs2; --j) 00777 for (int i = 0; i < w; ++i) 00778 { 00779 TF val = TF(); 00780 for (int k = 0; k < j; ++k) 00781 { 00782 val += sptr[ww[k]] * vFilter[k]; 00783 } 00784 *dptr++ = val; 00785 sptr ++; 00786 } 00787 } 00788 break; 00789 case CONV_BOUNDARY_CLEAN: 00790 { 00791 for (int j = vfs - 1; j > vfs2; --j) 00792 for (int i = 0; i < w; ++i) 00793 { 00794 TF val = TF(); float sum = 0.0; 00795 for (int k = 0; k < j; ++k) 00796 { 00797 val += sptr[ww[k]] * vFilter[k]; 00798 sum += vFilter[k]; 00799 } 00800 *dptr++ = val * sumv / sum; 00801 sptr ++; 00802 } 00803 } 00804 break; 00805 case CONV_BOUNDARY_REPLICATE: 00806 { 00807 for (int j = vfs - 1; j > vfs2; --j) 00808 for (int i = 0; i < w; ++i) 00809 { 00810 TF val = TF(); 00811 for (int k = 0; k < j; ++k) 00812 { 00813 val += sptr[ww[k]] * vFilter[k]; 00814 } 00815 for (int k = j; k < vfs; ++k) 00816 { 00817 val += sptr[ww[j-1]] * vFilter[k]; 00818 } 00819 *dptr++ = val; 00820 sptr ++; 00821 } 00822 } 00823 break; 00824 default: 00825 LFATAL("invalid convolution boundary strategy %d", 00826 (int) boundary); 00827 } 00828 } 00829 delete [] vFilter; 00830 return result; 00831 } 00832 00833 // ###################################################################### 00834 template <class T> 00835 Image<typename promote_trait<T, float>::TP> 00836 sepFilter(const Image<T>& src, const Image<float>& hFilter, 00837 const Image<float>& vFilter, 00838 ConvolutionBoundaryStrategy boundary) 00839 { 00840 ASSERT(hFilter.is1D() || hFilter.getSize() == 0); 00841 ASSERT(vFilter.is1D() || vFilter.getSize() == 0); 00842 return sepFilter(src, hFilter.getArrayPtr(), vFilter.getArrayPtr(), 00843 hFilter.getSize(), vFilter.getSize(), boundary); 00844 } 00845 00846 // ###################################################################### 00847 template <class T> 00848 Image<typename promote_trait<T, float>::TP> 00849 sepFilter(const Image<T>& src, const float* hFilt, const float* vFilt, 00850 const int hfs, const int vfs, 00851 ConvolutionBoundaryStrategy boundary) 00852 { 00853 Image<typename promote_trait<T, float>::TP> result = src; 00854 00855 if (hfs > 0) result = xFilter(result, hFilt, hfs, boundary); 00856 if (vfs > 0) result = yFilter(result, vFilt, vfs, boundary); 00857 00858 return result; 00859 } 00860 00861 // Include the explicit instantiations 00862 #include "inst/Image/Convolutions.I" 00863 00864 // ###################################################################### 00865 /* So things look consistent in everyone's emacs... */ 00866 /* Local Variables: */ 00867 /* mode: c++ */ 00868 /* indent-tabs-mode: nil */ 00869 /* End: */ 00870 00871 #endif // IMAGE_CONVOLUTIONS_C_DEFINED