[ VIGRA Homepage | Class Index | Function Index | File Index | Main Page ]

details vigra/stdconvolution.hxx VIGRA

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)
Cognitive Systems Group, University of Hamburg, Germany

html generated using doxygen and Python
VIGRA 1.2.0 (7 Aug 2003)