[ VIGRA Homepage | Class Index | Function Index | File Index | Main Page ]
![]() |
vigra/stdconvolution.hxx | ![]() |
---|
00001 /************************************************************************/ 00002 /* */ 00003 /* Copyright 1998-2002 by Ullrich Koethe */ 00004 /* Cognitive Systems Group, University of Hamburg, Germany */ 00005 /* */ 00006 /* This file is part of the VIGRA computer vision library. */ 00007 /* ( Version 1.2.0, Aug 07 2003 ) */ 00008 /* You may use, modify, and distribute this software according */ 00009 /* to the terms stated in the LICENSE file included in */ 00010 /* the VIGRA distribution. */ 00011 /* */ 00012 /* The VIGRA Website is */ 00013 /* http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/ */ 00014 /* Please direct questions, bug reports, and contributions to */ 00015 /* koethe@informatik.uni-hamburg.de */ 00016 /* */ 00017 /* THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR */ 00018 /* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ 00019 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ 00020 /* */ 00021 /************************************************************************/ 00022 00023 00024 #ifndef VIGRA_STDCONVOLUTION_HXX 00025 #define VIGRA_STDCONVOLUTION_HXX 00026 00027 #include <cmath> 00028 #include "vigra/stdimage.hxx" 00029 #include "vigra/bordertreatment.hxx" 00030 #include "vigra/separableconvolution.hxx" 00031 #include "vigra/utilities.hxx" 00032 00033 namespace vigra { 00034 00035 template <class SrcIterator, class SrcAccessor, 00036 class DestIterator, class DestAccessor, 00037 class KernelIterator, class KernelAccessor> 00038 void internalPixelEvaluationByClip(int x, int y, int w, int h, SrcIterator xs, 00039 SrcAccessor src_acc, DestIterator xd, DestAccessor dest_acc, 00040 KernelIterator ki, Diff2D kul, Diff2D klr, KernelAccessor ak) 00041 { 00042 typedef typename 00043 NumericTraits<typename SrcAccessor::value_type>::RealPromote SumType; 00044 typedef typename 00045 NumericTraits<typename KernelAccessor::value_type>::RealPromote KSumType; 00046 typedef 00047 NumericTraits<typename DestAccessor::value_type> DestTraits; 00048 00049 // calculate width and height of the kernel 00050 int kernel_width = klr.x - kul.x + 1; 00051 int kernel_height = klr.y - kul.y + 1; 00052 00053 KSumType norm = NumericTraits<KSumType>::zero(); 00054 SumType sum = NumericTraits<SumType>::zero(); 00055 int xx, yy; 00056 00057 //klr (kernel_lowerright) ist Diff2D !!! 00058 KernelIterator yk = ki + klr; 00059 00060 //Die Summe der Punkte im Kernel wird ermittelt (= norm) 00061 for(yy=0; yy<kernel_height; ++yy, --yk.y) 00062 { 00063 KernelIterator xk = yk; 00064 for(xx=0; xx<kernel_width; ++xx, --xk.x) 00065 { 00066 norm += ak(xk); 00067 } 00068 } 00069 00070 int x0, y0, x1, y1; 00071 00072 y0 = (y<klr.y) ? -y : -klr.y; 00073 y1 = (h-y-1<-kul.y) ? h-y-1 : -kul.y; 00074 00075 x0 = (x<klr.x) ? -x : -klr.x; 00076 x1 = (w-x-1<-kul.x) ? w-x-1 : -kul.x; 00077 00078 SrcIterator yys = xs + Diff2D(x0, y0); 00079 yk = ki - Diff2D(x0, y0); 00080 00081 KSumType ksum = NumericTraits<KSumType>::zero(); 00082 kernel_width = x1 - x0 + 1; 00083 kernel_height = y1 - y0 + 1; 00084 00085 //es wird zuerst abgeschnitten und dann gespigelt! 00086 00087 for(yy=0; yy<kernel_height; ++yy, ++yys.y, --yk.y) 00088 { 00089 SrcIterator xxs = yys; 00090 KernelIterator xk = yk; 00091 00092 for(xx=0; xx<kernel_width; ++xx, ++xxs.x, --xk.x) 00093 { 00094 sum += ak(xk) * src_acc(xxs); 00095 ksum += ak(xk); 00096 } 00097 } 00098 00099 // store average in destination pixel 00100 dest_acc.set(DestTraits::fromRealPromote((norm / ksum) * sum), xd); 00101 00102 } 00103 00104 00105 template <class SrcIterator, class SrcAccessor, 00106 class DestIterator, class DestAccessor, 00107 class KernelIterator, class KernelAccessor> 00108 void internalPixelEvaluationByWrapReflectRepeat(int x, int y, int src_width, int src_height, SrcIterator xs, 00109 SrcAccessor src_acc, DestIterator xd, DestAccessor dest_acc, 00110 KernelIterator ki, Diff2D kul, Diff2D klr, KernelAccessor ak, 00111 BorderTreatmentMode border) 00112 { 00113 00114 typedef typename 00115 NumericTraits<typename SrcAccessor::value_type>::RealPromote SumType; 00116 typedef 00117 NumericTraits<typename DestAccessor::value_type> DestTraits; 00118 00119 SumType sum = NumericTraits<SumType>::zero(); 00120 00121 SrcIterator src_ul = xs - Diff2D(x, y); 00122 SrcIterator src_lr = src_ul + Diff2D(src_width, src_height); 00123 00124 SrcIterator yys = xs; 00125 KernelIterator yk = ki; 00126 00127 // calculate width and height of the kernel 00128 int kernel_width = klr.x - kul.x + 1; 00129 int kernel_height = klr.y - kul.y + 1; 00130 00131 //Zeigt an wo der Kernel ’ber die Grenzen hinausgeht 00132 bool top_to_much = (y<klr.y) ? true : false; 00133 bool down_to_much = (src_height-y-1<-kul.y)? true : false; 00134 bool left_to_much = (x<klr.x)? true : false; 00135 bool right_to_much = (src_width-x-1<-kul.x)? true : false; 00136 00137 //Die Richtung x und y !!! 00138 //in der bei der Iteration ’ber das aktuelle Bereich im Bild 00139 //iteriert wird. Also wenn von ur->ll dann (-1, +1) und wenn lr->ul 00140 //dann (-1, -1). 00141 Diff2D way_increment; 00142 00143 /* iteriert wird immer aus dem g’ltigen in den ung’ltigen 00144 Bereich! dieser Tupel setzt sich wie folgt zusammen: 00145 1. Wird bei der Iteration in X-Richtung ung’ltiger Bereich 00146 erreicht so wird mit border_increment.first gesprungen und 00147 mit border_increment.third weiter iteriert. 00148 2. Wird bei der Iteration in Y-Richtung ung’ltiger Bereich 00149 erreicht so wird mit border_increment.second gesprungen und 00150 mit border_increment.fourth weiter iteriert. 00151 */ 00152 tuple4<int, int, int, int> border_increment; 00153 if (border == BORDER_TREATMENT_REPEAT){ 00154 border_increment = tuple4<int, int, int, int>(1, 1, 0, 0); 00155 }else if (border == BORDER_TREATMENT_REFLECT){ 00156 border_increment = tuple4<int, int, int, int>(2, 2, -1, -1); 00157 }else{ // BORDER_TREATMENT_WRAP 00158 border_increment = tuple4<int, int, int, int>(src_width, src_height, 1, 1); 00159 } 00160 00161 pair<int, int> valid_step_count; 00162 00163 if(left_to_much && !top_to_much && !down_to_much) 00164 { 00165 yys += klr; 00166 yk += kul; 00167 way_increment = Diff2D(-1, -1); 00168 border_increment.third = -border_increment.third; 00169 border_increment.fourth = -border_increment.fourth; 00170 valid_step_count = std::make_pair((yys - src_ul).x + 1, kernel_height); 00171 } 00172 else if(top_to_much && !left_to_much && !right_to_much) 00173 { 00174 yys += klr; 00175 yk += kul; 00176 way_increment = Diff2D(-1, -1); 00177 border_increment.third = -border_increment.third; 00178 border_increment.fourth = -border_increment.fourth; 00179 valid_step_count = std::make_pair(kernel_width, (yys - src_ul).y + 1); 00180 } 00181 else if(right_to_much && !top_to_much && !down_to_much) 00182 { 00183 yys += kul; 00184 yk += klr; 00185 way_increment = Diff2D(1, 1); 00186 border_increment.first = -border_increment.first; 00187 border_increment.second = -border_increment.second; 00188 valid_step_count = std::make_pair((src_lr - yys).x, kernel_height); 00189 } 00190 else if(down_to_much && !left_to_much && !right_to_much) 00191 { 00192 yys += kul; 00193 yk += klr; 00194 way_increment = Diff2D(1, 1); 00195 border_increment.first = -border_increment.first; 00196 border_increment.second = -border_increment.second; 00197 valid_step_count = std::make_pair(kernel_width, (src_lr - yys).y); 00198 } 00199 else if(down_to_much && left_to_much) 00200 { 00201 yys += kul + Diff2D(kernel_width - 1, 0); 00202 yk += kul + Diff2D(0, kernel_height - 1); 00203 way_increment = Diff2D(-1, 1); 00204 border_increment.second = -border_increment.second; 00205 border_increment.third = -border_increment.third; 00206 valid_step_count = std::make_pair((yys - src_ul).x + 1, (src_lr - yys).y); 00207 } 00208 else if(down_to_much && right_to_much) 00209 { 00210 yys += kul; 00211 yk += klr; 00212 way_increment = Diff2D(1, 1); 00213 border_increment.first = -border_increment.first; 00214 border_increment.second = -border_increment.second; 00215 valid_step_count = std::make_pair((src_lr - yys).x, (src_lr - yys).y); 00216 } 00217 else if(top_to_much && left_to_much) 00218 { 00219 yys += klr; 00220 yk += kul; 00221 way_increment = Diff2D(-1, -1); 00222 border_increment.third = -border_increment.third; 00223 border_increment.fourth = -border_increment.fourth; 00224 valid_step_count = std::make_pair((yys - src_ul).x + 1, (yys - src_ul).y + 1); 00225 } 00226 else 00227 { //top_to_much && right_to_much 00228 yys += kul + Diff2D(0, kernel_height - 1); 00229 yk += kul + Diff2D(kernel_width - 1, 0); 00230 way_increment = Diff2D(1, -1); 00231 border_increment.first = -border_increment.first; 00232 border_increment.fourth = -border_increment.fourth; 00233 valid_step_count = std::make_pair((src_lr - yys).x, (yys - src_ul).y + 1); 00234 } 00235 00236 int yy = 0, xx; 00237 00238 //laeuft den zul„ssigen Bereich in y-Richtung durch 00239 for(; yy < valid_step_count.second; ++yy, yys.y += way_increment.y, yk.y -= way_increment.y ) 00240 { 00241 SrcIterator xxs = yys; 00242 KernelIterator xk = yk; 00243 00244 //laeuft den zul„ssigen Bereich in x-Richtung durch 00245 for(xx = 0; xx < valid_step_count.first; ++xx, xxs.x += way_increment.x, xk.x -= way_increment.x) 00246 { 00247 sum += ak(xk) * src_acc(xxs); 00248 } 00249 00250 //N„chstes ++xxs.x wuerde in unzul„ssigen Bereich 00251 //bringen => Sprung in zulaessigen Bereich 00252 xxs.x += border_increment.first; 00253 00254 for( ; xx < kernel_width; ++xx, xxs.x += border_increment.third, xk.x -= way_increment.x ) 00255 { 00256 sum += ak(xk) * src_acc(xxs); 00257 } 00258 } 00259 00260 //N„chstes ++yys.y wuerde in unzul„ssigen Bereich 00261 //bringen => Sprung in zulaessigen Bereich 00262 yys.y += border_increment.second; 00263 00264 for( ; yy < kernel_height; ++yy, yys.y += border_increment.third, yk.y -= way_increment.y) 00265 { 00266 SrcIterator xxs = yys; 00267 KernelIterator xk = yk; 00268 00269 for(xx=0; xx < valid_step_count.first; ++xx, xxs.x += way_increment.x, xk.x -= way_increment.x) 00270 { 00271 sum += ak(xk) * src_acc(xxs); 00272 } 00273 00274 //Sprung in den zulaessigen Bereich 00275 xxs.x += border_increment.first; 00276 00277 for( ; xx < kernel_width; ++xx, xxs.x += border_increment.third, xk.x -= way_increment.x ) 00278 { 00279 sum += ak(xk) * src_acc(xxs); 00280 } 00281 } 00282 00283 // store average in destination pixel 00284 dest_acc.set(DestTraits::fromRealPromote(sum), xd); 00285 00286 }// end of internalPixelEvaluationByWrapReflectRepeat 00287 00288 00289 /** \addtogroup StandardConvolution Two-dimensional convolution functions 00290 00291 Perform 2D non-separable convolution, with and without ROI mask. 00292 00293 These generic convolution functions implement 00294 the standard 2D convolution operation for images that fit 00295 into the required interface. Arbitrary ROI's are supported 00296 by the mask version of the algorithm. 00297 The functions need a suitable 2D kernel to operate. 00298 */ 00299 //@{ 00300 00301 /** \brief Performs a 2 dimensional convolution of the source image using the given 00302 kernel. 00303 00304 The KernelIterator must point to the center of the kernel, and 00305 the kernel's size is given by its upper left (x and y of distance <= 0) and 00306 lower right (distance >= 0) corners. The image must always be larger than the 00307 kernel. At those positions where the kernel does not completely fit 00308 into the image, the specified \ref BorderTreatmentMode is 00309 applied. You can choice between following BorderTreatmentModes: 00310 <ul> 00311 <li>BORDER_TREATMENT_CLIP</li> 00312 <li>BORDER_TREATMENT_AVOID</li> 00313 <li>BORDER_TREATMENT_WRAP</li> 00314 <li>BORDER_TREATMENT_REFLECT</li> 00315 <li>BORDER_TREATMENT_REPEAT</li> 00316 </ul><br> 00317 The images's pixel type (SrcAccessor::value_type) must be a 00318 linear space over the kernel's value_type (KernelAccessor::value_type), 00319 i.e. addition of source values, multiplication with kernel values, 00320 and NumericTraits must be defined. 00321 The kernel's value_type must be an algebraic field, 00322 i.e. the arithmetic operations (+, -, *, /) and NumericTraits must 00323 be defined. 00324 00325 <b> Declarations:</b> 00326 00327 pass arguments explicitly: 00328 \code 00329 namespace vigra { 00330 template <class SrcIterator, class SrcAccessor, 00331 class DestIterator, class DestAccessor, 00332 class KernelIterator, class KernelAccessor> 00333 void convolveImage(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc, 00334 DestIterator dest_ul, DestAccessor dest_acc, 00335 KernelIterator ki, KernelAccessor ak, 00336 Diff2D kul, Diff2D klr, BorderTreatmentMode border); 00337 } 00338 \endcode 00339 00340 00341 use argument objects in conjuction with \ref ArgumentObjectFactories: 00342 \code 00343 namespace vigra { 00344 template <class SrcIterator, class SrcAccessor, 00345 class DestIterator, class DestAccessor, 00346 class KernelIterator, class KernelAccessor> 00347 void convolveImage(triple<SrcIterator, SrcIterator, SrcAccessor> src, 00348 pair<DestIterator, DestAccessor> dest, 00349 tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D, 00350 BorderTreatmentMode> kernel); 00351 } 00352 \endcode 00353 00354 <b> Usage:</b> 00355 00356 <b>\#include</b> "<a href="stdconvolution_8hxx-source.html">vigra/stdconvolution.hxx</a>"<br> 00357 Namespace: vigra 00358 00359 00360 \code 00361 vigra::FImage src(w,h), dest(w,h); 00362 ... 00363 00364 // define horizontal Sobel filter 00365 vigra::Kernel2D<float> sobel; 00366 00367 sobel.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = // upper left and lower right 00368 0.125, 0.0, -0.125, 00369 0.25, 0.0, -0.25, 00370 0.125, 0.0, -0.125; 00371 00372 vigra::convolveImage(srcImageRange(src), destImage(dest), kernel2d(sobel)); 00373 \endcode 00374 00375 <b> Required Interface:</b> 00376 00377 \code 00378 ImageIterator src_ul, src_lr; 00379 ImageIterator dest_ul; 00380 ImageIterator ik; 00381 00382 SrcAccessor src_accessor; 00383 DestAccessor dest_accessor; 00384 KernelAccessor kernel_accessor; 00385 00386 NumericTraits<SrcAccessor::value_type>::RealPromote s = src_accessor(src_ul); 00387 00388 s = s + s; 00389 s = kernel_accessor(ik) * s; 00390 s -= s; 00391 00392 dest_accessor.set( 00393 NumericTraits<DestAccessor::value_type>::fromRealPromote(s), dest_ul); 00394 00395 NumericTraits<KernelAccessor::value_type>::RealPromote k = kernel_accessor(ik); 00396 00397 k += k; 00398 k -= k; 00399 k = k / k; 00400 00401 \endcode 00402 00403 <b> Preconditions:</b> 00404 00405 \code 00406 kul.x <= 0 00407 kul.y <= 0 00408 klr.x >= 0 00409 klr.y >= 0 00410 src_lr.x - src_ul.x >= klr.x + kul.x + 1 00411 src_lr.y - src_ul.y >= klr.y + kul.y + 1 00412 border == BORDER_TREATMENT_CLIP || border == BORDER_TREATMENT_AVOID 00413 \endcode 00414 00415 If border == BORDER_TREATMENT_CLIP: Sum of kernel elements must be 00416 != 0. 00417 00418 */ 00419 template <class SrcIterator, class SrcAccessor, 00420 class DestIterator, class DestAccessor, 00421 class KernelIterator, class KernelAccessor> 00422 void convolveImage(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc, 00423 DestIterator dest_ul, DestAccessor dest_acc, 00424 KernelIterator ki, KernelAccessor ak, 00425 Diff2D kul, Diff2D klr, BorderTreatmentMode border) 00426 { 00427 vigra_precondition((border == BORDER_TREATMENT_CLIP || 00428 border == BORDER_TREATMENT_AVOID || 00429 border == BORDER_TREATMENT_REFLECT || 00430 border == BORDER_TREATMENT_REPEAT || 00431 border == BORDER_TREATMENT_WRAP), 00432 "convolveImage():\n" 00433 " Border treatment must be one of follow treatments:\n" 00434 " - BORDER_TREATMENT_CLIP\n" 00435 " - BORDER_TREATMENT_AVOID\n" 00436 " - BORDER_TREATMENT_REFLECT\n" 00437 " - BORDER_TREATMENT_REPEAT\n" 00438 " - BORDER_TREATMENT_WRAP\n"); 00439 00440 vigra_precondition(kul.x <= 0 && kul.y <= 0, 00441 "convolveImage(): coordinates of " 00442 "kernel's upper left must be <= 0."); 00443 vigra_precondition(klr.x >= 0 && klr.y >= 0, 00444 "convolveImage(): coordinates of " 00445 "kernel's lower right must be >= 0."); 00446 00447 // use traits to determine SumType as to prevent possible overflow 00448 typedef typename 00449 NumericTraits<typename SrcAccessor::value_type>::RealPromote SumType; 00450 typedef 00451 NumericTraits<typename DestAccessor::value_type> DestTraits; 00452 00453 // calculate width and height of the image 00454 int w = src_lr.x - src_ul.x; 00455 int h = src_lr.y - src_ul.y; 00456 00457 // calculate width and height of the kernel 00458 int kernel_width = klr.x - kul.x + 1; 00459 int kernel_height = klr.y - kul.y + 1; 00460 00461 vigra_precondition(w >= kernel_width && h >= kernel_height, 00462 "convolveImage(): kernel larger than image."); 00463 00464 int x,y; 00465 00466 // The start and endpoints of the image, that will be modified. 00467 // It's ever (0,0) and (w, h) 00468 // except by AVOID treatment mode. 00469 int ystart = (border == BORDER_TREATMENT_AVOID) ? klr.y : 0; 00470 int yend = (border == BORDER_TREATMENT_AVOID) ? h+kul.y : h; 00471 int xstart = (border == BORDER_TREATMENT_AVOID) ? klr.x : 0; 00472 int xend = (border == BORDER_TREATMENT_AVOID) ? w+kul.x : w; 00473 00474 // create y iterators (Ausser AVOID bleibt alles bei *_ul) 00475 DestIterator yd = dest_ul + Diff2D(xstart, ystart); 00476 SrcIterator ys = src_ul + Diff2D(xstart, ystart); 00477 00478 // Durchlauf ’ber das ganze Bild (bei AVOID nur die "Mitte") 00479 for(y=ystart; y < yend; ++y, ++ys.y, ++yd.y) 00480 { 00481 // create x iterators 00482 DestIterator xd(yd); 00483 SrcIterator xs(ys); 00484 00485 for(x=xstart; x < xend; ++x, ++xs.x, ++xd.x) 00486 { 00487 // init the sum 00488 SumType sum = NumericTraits<SumType>::zero(); 00489 00490 // how much of the kernel fits into the image ? 00491 bool nearBorder = false; 00492 00493 nearBorder = (y<klr.y) || (h-y-1<-kul.y) || (x<klr.x) || (w-x-1<-kul.x); 00494 00495 if(!nearBorder) 00496 { 00497 SrcIterator yys = xs - klr; 00498 KernelIterator yk = ki + klr; 00499 00500 int xx, yy; 00501 for(yy=0; yy<kernel_height; ++yy, ++yys.y, --yk.y) 00502 { 00503 SrcIterator xxs = yys; 00504 KernelIterator xk = yk; 00505 00506 for(xx=0; xx<kernel_width; ++xx, ++xxs.x, --xk.x) 00507 { 00508 sum += ak(xk) * src_acc(xxs); 00509 } 00510 } 00511 00512 // store average in destination pixel 00513 dest_acc.set(DestTraits::fromRealPromote(sum), xd); 00514 } 00515 else 00516 { 00517 if (border == BORDER_TREATMENT_CLIP) 00518 { 00519 00520 internalPixelEvaluationByClip(x, y, w, h, xs, src_acc, xd, dest_acc, ki, kul, klr, ak); 00521 00522 } 00523 else 00524 { 00525 00526 internalPixelEvaluationByWrapReflectRepeat(x, y, w, h, xs, src_acc, xd, dest_acc, ki, kul, klr, ak, border); 00527 00528 } 00529 } 00530 } 00531 } 00532 } 00533 00534 00535 template <class SrcIterator, class SrcAccessor, 00536 class DestIterator, class DestAccessor, 00537 class KernelIterator, class KernelAccessor> 00538 inline 00539 void convolveImage( 00540 triple<SrcIterator, SrcIterator, SrcAccessor> src, 00541 pair<DestIterator, DestAccessor> dest, 00542 tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D, 00543 BorderTreatmentMode> kernel) 00544 { 00545 convolveImage(src.first, src.second, src.third, 00546 dest.first, dest.second, 00547 kernel.first, kernel.second, kernel.third, 00548 kernel.fourth, kernel.fifth); 00549 } 00550 00551 00552 /** \brief Performs a 2 dimensional convolution of the source image within the 00553 given ROI mask using the given kernel. 00554 00555 The ROI is applied as follows: 00556 Only pixel under the ROI are used in the calculations. Whenever a part of the 00557 kernel lies outside the ROI, the kernel is renormalized to its original 00558 norm (analogous to the CLIP \ref BorderTreatmentMode). An convolution result is 00559 calculated whenever at the current kernel position <i>at least one pixel of the 00560 kernel is within the ROI</i>. I.e., pixels not under the ROI may nevertheless 00561 be assigned a value if they are <i>near</i> the ROI. Thus, this algorithm is also 00562 useful as an interpolator. To get rid of the results outside the ROI mask, a 00563 subsequent \ref copyImageIf() must be performed. 00564 00565 The KernelIterator must point to the center of the kernel, and 00566 the kernel's size is given by its upper left (x and y of distance <= 0) and 00567 lower right (distance >= 0) corners. The image must always be larger than the 00568 kernel. At those positions where the kernel does not completely fit 00569 into the image, the specified \ref BorderTreatmentMode is 00570 applied. Only BORDER_TREATMENT_CLIP and BORDER_TREATMENT_AVOID are currently 00571 supported. 00572 00573 The images's pixel type (SrcAccessor::value_type) must be a 00574 linear space over the kernel's value_type (KernelAccessor::value_type), 00575 i.e. addition of source values, multiplication with kernel values, 00576 and NumericTraits must be defined. 00577 The kernel's value_type must be an algebraic field, 00578 i.e. the arithmetic operations (+, -, *, /) and NumericTraits must 00579 be defined. 00580 00581 <b> Declarations:</b> 00582 00583 pass arguments explicitly: 00584 \code 00585 namespace vigra { 00586 template <class SrcIterator, class SrcAccessor, 00587 class MaskIterator, class MaskAccessor, 00588 class DestIterator, class DestAccessor, 00589 class KernelIterator, class KernelAccessor> 00590 void 00591 convolveImageWithMask(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc, 00592 MaskIterator mul, MaskAccessor am, 00593 DestIterator dest_ul, DestAccessor dest_acc, 00594 KernelIterator ki, KernelAccessor ak, 00595 Diff2D kul, Diff2D klr, BorderTreatmentMode border); 00596 } 00597 \endcode 00598 00599 00600 use argument objects in conjuction with \ref ArgumentObjectFactories: 00601 \code 00602 namespace vigra { 00603 template <class SrcIterator, class SrcAccessor, 00604 class MaskIterator, class MaskAccessor, 00605 class DestIterator, class DestAccessor, 00606 class KernelIterator, class KernelAccessor> 00607 inline 00608 void convolveImageWithMask(triple<SrcIterator, SrcIterator, SrcAccessor> src, 00609 pair<MaskIterator, MaskAccessor> mask, 00610 pair<DestIterator, DestAccessor> dest, 00611 tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D, 00612 BorderTreatmentMode> kernel); 00613 } 00614 \endcode 00615 00616 <b> Usage:</b> 00617 00618 <b>\#include</b> "<a href="stdconvolution_8hxx-source.html">vigra/stdconvolution.hxx</a>"<br> 00619 Namespace: vigra 00620 00621 00622 \code 00623 vigra::FImage src(w,h), dest(w,h); 00624 vigra::CImage mask(w,h); 00625 ... 00626 00627 // define 3x3 binomial filter 00628 vigra::Kernel2D<float> binom; 00629 00630 binom.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = // upper left and lower right 00631 0.0625, 0.125, 0.0625, 00632 0.125, 0.25, 0.125, 00633 0.0625, 0.125, 0.0625; 00634 00635 vigra::convolveImage(srcImageRange(src), maskImage(mask), destImage(dest), kernel2d(binom)); 00636 \endcode 00637 00638 <b> Required Interface:</b> 00639 00640 \code 00641 ImageIterator src_ul, src_lr; 00642 ImageIterator mul; 00643 ImageIterator dest_ul; 00644 ImageIterator ik; 00645 00646 SrcAccessor src_accessor; 00647 MaskAccessor mask_accessor; 00648 DestAccessor dest_accessor; 00649 KernelAccessor kernel_accessor; 00650 00651 NumericTraits<SrcAccessor::value_type>::RealPromote s = src_accessor(src_ul); 00652 00653 s = s + s; 00654 s = kernel_accessor(ik) * s; 00655 s -= s; 00656 00657 if(mask_accessor(mul)) ...; 00658 00659 dest_accessor.set( 00660 NumericTraits<DestAccessor::value_type>::fromRealPromote(s), dest_ul); 00661 00662 NumericTraits<KernelAccessor::value_type>::RealPromote k = kernel_accessor(ik); 00663 00664 k += k; 00665 k -= k; 00666 k = k / k; 00667 00668 \endcode 00669 00670 <b> Preconditions:</b> 00671 00672 \code 00673 kul.x <= 0 00674 kul.y <= 0 00675 klr.x >= 0 00676 klr.y >= 0 00677 src_lr.x - src_ul.x >= klr.x + kul.x + 1 00678 src_lr.y - src_ul.y >= klr.y + kul.y + 1 00679 border == BORDER_TREATMENT_CLIP || border == BORDER_TREATMENT_AVOID 00680 \endcode 00681 00682 Sum of kernel elements must be != 0. 00683 00684 */ 00685 template <class SrcIterator, class SrcAccessor, 00686 class DestIterator, class DestAccessor, 00687 class MaskIterator, class MaskAccessor, 00688 class KernelIterator, class KernelAccessor> 00689 void 00690 convolveImageWithMask(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc, 00691 MaskIterator mul, MaskAccessor am, 00692 DestIterator dest_ul, DestAccessor dest_acc, 00693 KernelIterator ki, KernelAccessor ak, 00694 Diff2D kul, Diff2D klr, BorderTreatmentMode border) 00695 { 00696 vigra_precondition((border == BORDER_TREATMENT_CLIP || 00697 border == BORDER_TREATMENT_AVOID), 00698 "convolveImageWithMask(): " 00699 "Border treatment must be BORDER_TREATMENT_CLIP or BORDER_TREATMENT_AVOID."); 00700 00701 vigra_precondition(kul.x <= 0 && kul.y <= 0, 00702 "convolveImageWithMask(): left borders must be <= 0."); 00703 vigra_precondition(klr.x >= 0 && klr.y >= 0, 00704 "convolveImageWithMask(): right borders must be >= 0."); 00705 00706 // use traits to determine SumType as to prevent possible overflow 00707 typedef typename 00708 NumericTraits<typename SrcAccessor::value_type>::RealPromote SumType; 00709 typedef typename 00710 NumericTraits<typename KernelAccessor::value_type>::RealPromote KSumType; 00711 typedef 00712 NumericTraits<typename DestAccessor::value_type> DestTraits; 00713 00714 // calculate width and height of the image 00715 int w = src_lr.x - src_ul.x; 00716 int h = src_lr.y - src_ul.y; 00717 int kernel_width = klr.x - kul.x + 1; 00718 int kernel_height = klr.y - kul.y + 1; 00719 00720 int x,y; 00721 int ystart = (border == BORDER_TREATMENT_AVOID) ? klr.y : 0; 00722 int yend = (border == BORDER_TREATMENT_AVOID) ? h+kul.y : h; 00723 int xstart = (border == BORDER_TREATMENT_AVOID) ? klr.x : 0; 00724 int xend = (border == BORDER_TREATMENT_AVOID) ? w+kul.x : w; 00725 00726 // create y iterators 00727 DestIterator yd = dest_ul + Diff2D(xstart, ystart); 00728 SrcIterator ys = src_ul + Diff2D(xstart, ystart); 00729 MaskIterator ym = mul + Diff2D(xstart, ystart); 00730 00731 KSumType norm = ak(ki); 00732 int xx, yy; 00733 KernelIterator yk = ki + klr; 00734 for(yy=0; yy<hk; ++yy, --yk.y) 00735 { 00736 KernelIterator xk = yk; 00737 00738 for(xx=0; xx<wk; ++xx, --xk.x) 00739 { 00740 norm += ak(xk); 00741 } 00742 } 00743 norm -= ak(ki); 00744 00745 00746 for(y=ystart; y < yend; ++y, ++ys.y, ++yd.y, ++ym.y) 00747 { 00748 // create x iterators 00749 DestIterator xd(yd); 00750 SrcIterator xs(ys); 00751 MaskIterator xm(ym); 00752 00753 for(x=xstart; x < xend; ++x, ++xs.x, ++xd.x, ++xm.x) 00754 { 00755 // how much of the kernel fits into the image ? 00756 int x0, y0, x1, y1; 00757 00758 y0 = (y<klr.y) ? -y : -klr.y; 00759 y1 = (h-y-1<-kul.y) ? h-y-1 : -kul.y; 00760 x0 = (x<klr.x) ? -x : -klr.x; 00761 x1 = (w-x-1<-kul.x) ? w-x-1 : -kul.x; 00762 00763 bool first = true; 00764 // init the sum 00765 SumType sum; 00766 KSumType ksum; 00767 00768 SrcIterator yys = xs + Diff2D(x0, y0); 00769 MaskIterator yym = xm + Diff2D(x0, y0); 00770 KernelIterator yk = ki - Diff2D(x0, y0); 00771 00772 int xx, yy, kernel_width, kernel_height; 00773 kernel_width = x1 - x0 + 1; 00774 kernel_height = y1 - y0 + 1; 00775 for(yy=0; yy<kernel_height; ++yy, ++yys.y, --yk.y, ++yym.y) 00776 { 00777 SrcIterator xxs = yys; 00778 MaskIterator xxm = yym; 00779 KernelIterator xk = yk; 00780 00781 for(xx=0; xx<kernel_width; ++xx, ++xxs.x, --xk.x, ++xxm.x) 00782 { 00783 if(!am(xxm)) continue; 00784 00785 if(first) 00786 { 00787 sum = ak(xk) * src_acc(xxs); 00788 ksum = ak(xk); 00789 first = false; 00790 } 00791 else 00792 { 00793 sum += ak(xk) * src_acc(xxs); 00794 ksum += ak(xk); 00795 } 00796 } 00797 } 00798 // store average in destination pixel 00799 if(!first && 00800 ksum != NumericTraits<KSumType>::zero()) 00801 { 00802 dest_acc.set(DestTraits::fromRealPromote((norm / ksum) * sum), xd); 00803 } 00804 } 00805 } 00806 } 00807 00808 00809 template <class SrcIterator, class SrcAccessor, 00810 class DestIterator, class DestAccessor, 00811 class MaskIterator, class MaskAccessor, 00812 class KernelIterator, class KernelAccessor> 00813 inline 00814 void convolveImageWithMask( 00815 triple<SrcIterator, SrcIterator, SrcAccessor> src, 00816 pair<MaskIterator, MaskAccessor> mask, 00817 pair<DestIterator, DestAccessor> dest, 00818 tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D, 00819 BorderTreatmentMode> kernel) 00820 { 00821 convolveImageWithMask(src.first, src.second, src.third, 00822 mask.first, mask.second, 00823 dest.first, dest.second, 00824 kernel.first, kernel.second, kernel.third, 00825 kernel.fourth, kernel.fifth); 00826 } 00827 00828 //@} 00829 00830 /********************************************************/ 00831 /* */ 00832 /* Kernel2D */ 00833 /* */ 00834 /********************************************************/ 00835 00836 /** \brief Generic 2 dimensional convolution kernel. 00837 00838 This kernel may be used for convolution of 2 dimensional signals. 00839 00840 Convolution functions access the kernel via an ImageIterator 00841 which they get by calling \ref center(). This iterator 00842 points to the center of the kernel. The kernel's size is given by its upperLeft() 00843 (upperLeft().x <= 0, upperLeft().y <= 0) 00844 and lowerRight() (lowerRight().x >= 0, lowerRight().y >= 0) methods. 00845 The desired border treatment mode is returned by borderTreatment(). 00846 (Note that the \ref StandardConvolution "2D convolution functions" don't currently 00847 support all modes.) 00848 00849 The different init functions create a kernel with the specified 00850 properties. The requirements for the kernel's value_type depend 00851 on the init function used. At least NumericTraits must be defined. 00852 00853 The kernel defines a factory function kernel2d() to create an argument object 00854 (see \ref KernelArgumentObjectFactories). 00855 00856 <b> Usage:</b> 00857 00858 <b>\#include</b> "<a href="stdconvolution_8hxx-source.html">vigra/stdconvolution.hxx</a>"<br> 00859 Namespace: vigra 00860 00861 \code 00862 vigra::FImage src(w,h), dest(w,h); 00863 ... 00864 00865 // define horizontal Sobel filter 00866 vigra::Kernel2D<float> sobel; 00867 00868 sobel.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = // upper left and lower right 00869 0.125, 0.0, -0.125, 00870 0.25, 0.0, -0.25, 00871 0.125, 0.0, -0.125; 00872 00873 vigra::convolveImage(srcImageRange(src), destImage(dest), kernel2d(sobel)); 00874 \endcode 00875 00876 <b> Required Interface:</b> 00877 00878 \code 00879 value_type v = NumericTraits<value_type>::one(); 00880 \endcode 00881 00882 See also the init functions. 00883 */ 00884 template <class ARITHTYPE> 00885 class Kernel2D 00886 { 00887 public: 00888 /** the kernel's value type 00889 */ 00890 typedef ARITHTYPE value_type; 00891 00892 /** 2D random access iterator over the kernel's values 00893 */ 00894 typedef typename BasicImage<value_type>::traverser Iterator; 00895 00896 /** const 2D random access iterator over the kernel's values 00897 */ 00898 typedef typename BasicImage<value_type>::const_traverser ConstIterator; 00899 00900 /** the kernel's accessor 00901 */ 00902 typedef typename BasicImage<value_type>::Accessor Accessor; 00903 00904 /** the kernel's const accessor 00905 */ 00906 typedef typename BasicImage<value_type>::ConstAccessor ConstAccessor; 00907 00908 struct InitProxy 00909 { 00910 typedef typename 00911 BasicImage<value_type>::ScanOrderIterator Iterator; 00912 00913 InitProxy(Iterator i, int count, value_type & norm) 00914 : iter_(i), base_(i), 00915 count_(count), sum_(count), 00916 norm_(norm) 00917 {} 00918 00919 ~InitProxy() 00920 { 00921 vigra_precondition(count_ == 1 || count_ == sum_, 00922 "Kernel2D::initExplicitly(): " 00923 "Too few init values."); 00924 } 00925 00926 InitProxy & operator,(value_type const & v) 00927 { 00928 if(count_ == sum_) norm_ = *iter_; 00929 00930 --count_; 00931 vigra_precondition(count_ > 0, 00932 "Kernel2D::initExplicitly(): " 00933 "Too many init values."); 00934 00935 norm_ += v; 00936 00937 ++iter_; 00938 *iter_ = v; 00939 00940 return *this; 00941 } 00942 00943 Iterator iter_, base_; 00944 int count_, sum_; 00945 value_type & norm_; 00946 }; 00947 00948 static value_type one() { return NumericTraits<value_type>::one(); } 00949 00950 /** Default constructor. 00951 Creates a kernel of size 1x1 which would copy the signal 00952 unchanged. 00953 */ 00954 Kernel2D() 00955 : kernel_(1, 1, Kernel2D<ARITHTYPE>::one()), 00956 left_(0, 0), 00957 right_(0, 0), 00958 border_treatment_(BORDER_TREATMENT_CLIP) 00959 {} 00960 00961 /** Copy constructor. 00962 */ 00963 Kernel2D(Kernel2D const & k) 00964 : left_(k.left_), 00965 right_(k.right_), 00966 norm_(k.norm_), 00967 kernel_(k.kernel_), 00968 border_treatment_(k.border_treatment_) 00969 {} 00970 00971 /** Copy assignment. 00972 */ 00973 Kernel2D & operator=(Kernel2D const & k) 00974 { 00975 if(this != &k) 00976 { 00977 left_ = k.left_; 00978 right_ = k.right_; 00979 norm_ = k.norm_; 00980 kernel_ = k.kernel_; 00981 } 00982 return *this; 00983 } 00984 00985 /** Initialisation. 00986 This initializes the kernel with the given constant. The norm becomes 00987 v*width()*height(). 00988 00989 Instead of a single value an initializer list of length width()*height() 00990 can be used like this: 00991 00992 \code 00993 vigra::Kernel2D<float> binom; 00994 00995 binom.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = 00996 0.0625, 0.125, 0.0625, 00997 0.125, 0.25, 0.125, 00998 0.0625, 0.125, 0.0625; 00999 \endcode 01000 01001 In this case, the norm will be set to the sum of the init values. 01002 An initializer list of wrong length will result in a run-time error. 01003 */ 01004 InitProxy operator=(value_type const & v) 01005 { 01006 int size = (right_.x - left_.x + 1) * 01007 (right_.y - left_.y + 1); 01008 kernel_ = v; 01009 norm_ = (double)size*v; 01010 01011 return InitProxy(kernel_.begin(), size, norm_); 01012 } 01013 01014 /** Destructor. 01015 */ 01016 ~Kernel2D() 01017 {} 01018 01019 /** Init the 2D kernel as the cartesian product of two 1D kernels 01020 of type \ref Kernel1D. The norm becomes the product of the two original 01021 norms. 01022 01023 <b> Required Interface:</b> 01024 01025 The kernel's value_type must be a linear algebra. 01026 01027 \code 01028 vigra::Kernel2D<...>::value_type v; 01029 v = v * v; 01030 \endcode 01031 */ 01032 void initSeparable(Kernel1D<value_type> & kx, 01033 Kernel1D<value_type> & ky) 01034 { 01035 left_ = Diff2D(kx.left(), ky.left()); 01036 right_ = Diff2D(kx.right(), ky.right()); 01037 int w = right_.x - left_.x + 1; 01038 int h = right_.y - left_.y + 1; 01039 kernel_.resize(w, h); 01040 01041 norm_ = kx.norm() * ky.norm(); 01042 01043 typedef typename Kernel1D<value_type>::Iterator KIter; 01044 typename Kernel1D<value_type>::Accessor ka; 01045 01046 KIter kiy = ky.center() + left_.y; 01047 Iterator iy = center() + left_; 01048 01049 for(int y=left_.y; y<=right_.y; ++y, ++kiy, ++iy.y) 01050 { 01051 KIter kix = kx.center() + left_.x; 01052 Iterator ix = iy; 01053 for(int x=left_.x; x<=right_.x; ++x, ++kix, ++ix.x) 01054 { 01055 *ix = ka(kix) * ka(kiy); 01056 } 01057 } 01058 } 01059 01060 /** Init the 2D kernel as the cartesian product of two 1D kernels 01061 given explicitly by iterators and sizes. The norm becomes the 01062 sum of the resulting kernel values. 01063 01064 <b> Required Interface:</b> 01065 01066 The kernel's value_type must be a linear algebra. 01067 01068 \code 01069 vigra::Kernel2D<...>::value_type v; 01070 v = v * v; 01071 v += v; 01072 \endcode 01073 01074 <b> Preconditions:</b> 01075 01076 \code 01077 xleft <= 0; 01078 xright >= 0; 01079 yleft <= 0; 01080 yright >= 0; 01081 \endcode 01082 */ 01083 template <class KernelIterator> 01084 void initSeparable(KernelIterator kxcenter, int xleft, int xright, 01085 KernelIterator kycenter, int yleft, int yright) 01086 { 01087 vigra_precondition(xleft <= 0 && yleft <= 0, 01088 "Kernel2D::initSeparable(): left borders must be <= 0."); 01089 vigra_precondition(xright >= 0 && yright >= 0, 01090 "Kernel2D::initSeparable(): right borders must be >= 0."); 01091 01092 left_ = Point2D(xleft, yleft); 01093 right_ = Point2D(xright, yright); 01094 01095 int w = right_.x - left_.x + 1; 01096 int h = right_.y - left_.y + 1; 01097 kernel_.resize(w, h); 01098 01099 KernelIterator kiy = kycenter + left_.y; 01100 Iterator iy = center() + left_; 01101 01102 for(int y=left_.y; y<=right_.y; ++y, ++kiy, ++iy.y) 01103 { 01104 KernelIterator kix = kxcenter + left_.x; 01105 Iterator ix = iy; 01106 for(int x=left_.x; x<=right_.x; ++x, ++kix, ++ix.x) 01107 { 01108 *ix = *kix * *kiy; 01109 } 01110 } 01111 01112 typename BasicImage<value_type>::iterator i = kernel_.begin(); 01113 typename BasicImage<value_type>::iterator iend = kernel_.end(); 01114 norm_ = *i; 01115 ++i; 01116 01117 for(; i!= iend; ++i) 01118 { 01119 norm_ += *i; 01120 } 01121 } 01122 01123 /** Init the 2D kernel as a circular averaging filter. The norm will be 01124 calculated as 01125 <TT>NumericTraits<value_type>::one() / (number of non-zero kernel values)</TT>. 01126 The kernel's value_type must be a linear space. 01127 01128 <b> Required Interface:</b> 01129 01130 \code 01131 value_type v = vigra::NumericTraits<value_type>::one(); 01132 01133 double d; 01134 v = d * v; 01135 \endcode 01136 01137 <b> Precondition:</b> 01138 01139 \code 01140 radius > 0; 01141 \endcode 01142 */ 01143 void initDisk(int radius) 01144 { 01145 vigra_precondition(radius > 0, 01146 "Kernel2D::initDisk(): radius must be > 0."); 01147 01148 left_ = Point2D(-radius, -radius); 01149 right_ = Point2D(radius, radius); 01150 int w = right_.x - left_.x + 1; 01151 int h = right_.y - left_.y + 1; 01152 kernel_.resize(w, h); 01153 norm_ = NumericTraits<value_type>::one(); 01154 01155 kernel_ = NumericTraits<value_type>::zero(); 01156 double count = 0.0; 01157 01158 Iterator k = center(); 01159 double r2 = (double)radius*radius; 01160 01161 int i; 01162 for(i=0; i<= radius; ++i) 01163 { 01164 double r = (double) i - 0.5; 01165 int w = (int)(VIGRA_CSTD::sqrt(r2 - r*r) + 0.5); 01166 for(int j=-w; j<=w; ++j) 01167 { 01168 k(j, i) = NumericTraits<value_type>::one(); 01169 k(j, -i) = NumericTraits<value_type>::one(); 01170 count += (i != 0) ? 2.0 : 1.0; 01171 } 01172 } 01173 01174 count = 1.0 / count; 01175 01176 for(int y=-radius; y<=radius; ++y) 01177 { 01178 for(int x=-radius; x<=radius; ++x) 01179 { 01180 k(x,y) = count * k(x,y); 01181 } 01182 } 01183 } 01184 01185 /** Init the kernel by an explicit initializer list. 01186 The upper left and lower right corners of the kernel must be passed. 01187 A comma-separated initializer list is given after the assignment operator. 01188 This function is used like this: 01189 01190 \code 01191 // define horizontal Sobel filter 01192 vigra::Kernel2D<float> sobel; 01193 01194 sobel.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = 01195 0.125, 0.0, -0.125, 01196 0.25, 0.0, -0.25, 01197 0.125, 0.0, -0.125; 01198 \endcode 01199 01200 The norm is set to the sum of the initialzer values. If the wrong number of 01201 values is given, a run-time error results. It is, however, possible to give 01202 just one initializer. This creates an averaging filter with the given constant: 01203 01204 \code 01205 vigra::Kernel2D<float> average3x3; 01206 01207 average3x3.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = 1.0/9.0; 01208 \endcode 01209 01210 Here, the norm is set to value*width()*height(). 01211 01212 <b> Preconditions:</b> 01213 01214 \code 01215 1. upperleft.x <= 0; 01216 2. upperleft.y <= 0; 01217 3. lowerright.x >= 0; 01218 4. lowerright.y >= 0; 01219 5. the number of values in the initializer list 01220 is 1 or equals the size of the kernel. 01221 \endcode 01222 */ 01223 Kernel2D & initExplicitly(Diff2D upperleft, Diff2D lowerright) 01224 { 01225 vigra_precondition(upperleft.x <= 0 && upperleft.y <= 0, 01226 "Kernel2D::initExplicitly(): left borders must be <= 0."); 01227 vigra_precondition(lowerright.x >= 0 && lowerright.y >= 0, 01228 "Kernel2D::initExplicitly(): right borders must be >= 0."); 01229 01230 left_ = Point2D(upperleft); 01231 right_ = Point2D(lowerright); 01232 01233 int w = right_.x - left_.x + 1; 01234 int h = right_.y - left_.y + 1; 01235 kernel_.resize(w, h); 01236 01237 return *this; 01238 } 01239 01240 /** Coordinates of the upper left corner of the kernel. 01241 */ 01242 Point2D upperLeft() const { return left_; } 01243 01244 /** Coordinates of the lower right corner of the kernel. 01245 */ 01246 Point2D lowerRight() const { return right_; } 01247 01248 /** Width of the kernel. 01249 */ 01250 int width() const { return right_.x - left_.x + 1; } 01251 01252 /** Height of the kernel. 01253 */ 01254 int height() const { return right_.y - left_.y + 1; } 01255 01256 /** ImageIterator that points to the center of the kernel (coordinate (0,0)). 01257 */ 01258 Iterator center() { return kernel_.upperLeft() - left_; } 01259 01260 /** ImageIterator that points to the center of the kernel (coordinate (0,0)). 01261 */ 01262 ConstIterator center() const { return kernel_.upperLeft() - left_; } 01263 01264 /** Access kernel entry at given position. 01265 */ 01266 value_type & operator()(int x, int y) 01267 { return kernel_[Diff2D(x,y) - left_]; } 01268 01269 /** Read kernel entry at given position. 01270 */ 01271 value_type operator()(int x, int y) const 01272 { return kernel_[Diff2D(x,y) - left_]; } 01273 01274 /** Norm of the kernel (i.e. sum of its elements). 01275 */ 01276 value_type norm() const { return norm_; } 01277 01278 /** The kernels default accessor. 01279 */ 01280 Accessor accessor() { return Accessor(); } 01281 01282 /** The kernels default const accessor. 01283 */ 01284 ConstAccessor accessor() const { return ConstAccessor(); } 01285 01286 /** Normalize the kernel to the given value. (The norm is the sum of all kernel 01287 elements.) The kernel's value_type must be a division algebra or 01288 algebraic field. 01289 01290 <b> Required Interface:</b> 01291 01292 \code 01293 value_type v = vigra::NumericTraits<value_type>::one(); // if norm is not 01294 // given explicitly 01295 01296 v += v; 01297 v = v * v; 01298 v = v / v; 01299 \endcode 01300 */ 01301 void normalize(value_type norm) 01302 { 01303 typename BasicImage<value_type>::iterator i = kernel_.begin(); 01304 typename BasicImage<value_type>::iterator iend = kernel_.end(); 01305 typename NumericTraits<value_type>::RealPromote sum = *i; 01306 ++i; 01307 01308 for(; i!= iend; ++i) 01309 { 01310 sum += *i; 01311 } 01312 01313 sum = norm / sum; 01314 i = kernel_.begin(); 01315 for(; i != iend; ++i) 01316 { 01317 *i = *i * sum; 01318 } 01319 01320 norm_ = norm; 01321 } 01322 01323 /** Normalize the kernel to norm 1. 01324 */ 01325 void normalize() 01326 { 01327 normalize(one()); 01328 } 01329 01330 /** current border treatment mode 01331 */ 01332 BorderTreatmentMode borderTreatment() const 01333 { return border_treatment_; } 01334 01335 /** Set border treatment mode. 01336 Only <TT>BORDER_TREATMENT_CLIP</TT> and <TT>BORDER_TREATMENT_AVOID</TT> are currently 01337 allowed. 01338 */ 01339 void setBorderTreatment( BorderTreatmentMode new_mode) 01340 { 01341 vigra_precondition((new_mode == BORDER_TREATMENT_CLIP || 01342 new_mode == BORDER_TREATMENT_AVOID || 01343 new_mode == BORDER_TREATMENT_REFLECT || 01344 new_mode == BORDER_TREATMENT_REPEAT || 01345 new_mode == BORDER_TREATMENT_WRAP), 01346 "convolveImage():\n" 01347 " Border treatment must be one of follow treatments:\n" 01348 " - BORDER_TREATMENT_CLIP\n" 01349 " - BORDER_TREATMENT_AVOID\n" 01350 " - BORDER_TREATMENT_REFLECT\n" 01351 " - BORDER_TREATMENT_REPEAT\n" 01352 " - BORDER_TREATMENT_WRAP\n"); 01353 01354 border_treatment_ = new_mode; 01355 } 01356 01357 01358 private: 01359 BasicImage<value_type> kernel_; 01360 Point2D left_, right_; 01361 value_type norm_; 01362 BorderTreatmentMode border_treatment_; 01363 }; 01364 01365 /**************************************************************/ 01366 /* */ 01367 /* Argument object factories for Kernel2D */ 01368 /* */ 01369 /* (documentation: see vigra/convolution.hxx) */ 01370 /* */ 01371 /**************************************************************/ 01372 01373 template <class KernelIterator, class KernelAccessor> 01374 inline 01375 tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D, BorderTreatmentMode> 01376 kernel2d(KernelIterator ik, KernelAccessor ak, Diff2D kul, Diff2D klr, 01377 BorderTreatmentMode border) 01378 01379 { 01380 return 01381 tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D, BorderTreatmentMode> ( 01382 ik, ak, kul, klr, border); 01383 } 01384 01385 template <class T> 01386 inline 01387 tuple5<typename Kernel2D<T>::ConstIterator, 01388 typename Kernel2D<T>::ConstAccessor, 01389 Diff2D, Diff2D, BorderTreatmentMode> 01390 kernel2d(Kernel2D<T> const & k) 01391 01392 { 01393 return 01394 tuple5<typename Kernel2D<T>::ConstIterator, 01395 typename Kernel2D<T>::ConstAccessor, 01396 Diff2D, Diff2D, BorderTreatmentMode>( 01397 k.center(), 01398 k.accessor(), 01399 k.upperLeft(), k.lowerRight(), 01400 k.borderTreatment()); 01401 } 01402 01403 template <class T> 01404 inline 01405 tuple5<typename Kernel2D<T>::ConstIterator, 01406 typename Kernel2D<T>::ConstAccessor, 01407 Diff2D, Diff2D, BorderTreatmentMode> 01408 kernel2d(Kernel2D<T> const & k, BorderTreatmentMode border) 01409 01410 { 01411 return 01412 tuple5<typename Kernel2D<T>::ConstIterator, 01413 typename Kernel2D<T>::ConstAccessor, 01414 Diff2D, Diff2D, BorderTreatmentMode>( 01415 k.center(), 01416 k.accessor(), 01417 k.upperLeft(), k.lowerRight(), 01418 border); 01419 } 01420 01421 01422 } // namespace vigra 01423 01424 #endif // VIGRA_STDCONVOLUTION_HXX
© Ullrich Köthe (koethe@informatik.uni-hamburg.de) |
html generated using doxygen and Python
|