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