core/vil/algo/vil_distance_transform.cxx
Go to the documentation of this file.
00001 #include "vil_distance_transform.h"
00002 //:
00003 // \file
00004 // \brief Compute distance function
00005 // \author Tim Cootes
00006 
00007 #include <vil/vil_fill.h>
00008 #include <vcl_algorithm.h>
00009 #include <vcl_cmath.h>
00010 #include <vcl_cassert.h>
00011 
00012 //: Compute distance function from zeros in original image
00013 //  Image is assumed to be filled with max_dist where there
00014 //  is background, and zero at the places of interest.
00015 //  On exit, the values are the 8-connected distance to the
00016 //  nearest original zero region.
00017 void vil_distance_transform(vil_image_view<float>& image)
00018 {
00019   // Low to high pass
00020   vil_distance_transform_one_way(image);
00021 
00022   // Flip to achieve high to low pass
00023   // Don't use vil_flip* as they assume const images.
00024   unsigned ni = image.ni(), nj = image.nj();
00025   vil_image_view<float> flip_image(image.memory_chunk(),
00026                                    &image(ni-1,nj-1), ni,nj,1,
00027                                    -image.istep(), -image.jstep(),
00028                                    image.nplanes());
00029   vil_distance_transform_one_way(flip_image);
00030 }
00031 
00032 //: Compute directed distance function from zeros in original image
00033 //  Image is assumed to be filled with max_dist where there
00034 //  is background, and zero at the places of interest.
00035 //  On exit, the values are the 8-connected distance to the
00036 //  nearest original zero region above or to the left of current point.
00037 //  One pass of distance transform, going from low to high i,j.
00038 void vil_distance_transform_one_way(vil_image_view<float>& image)
00039 {
00040   assert(image.nplanes()==1);
00041   unsigned ni = image.ni();
00042   unsigned nj = image.nj();
00043   unsigned ni1 = ni-1;
00044   vcl_ptrdiff_t istep = image.istep(),  jstep = image.jstep();
00045   vcl_ptrdiff_t o1 = -istep, o2 = -jstep-istep, o3 = -jstep, o4 = istep-jstep;
00046   float* row0 = image.top_left_ptr();
00047 
00048   const float sqrt2 = vcl_sqrt(2.0f);
00049 
00050   // Process the first row
00051   float* p0 = row0+istep;
00052   for (unsigned i=1;i<ni;++i,p0+=istep)
00053   {
00054       *p0 = vcl_min(p0[-istep]+1.0f,*p0);
00055   }
00056 
00057   row0 += jstep;  // Move to next row
00058 
00059   // Process each subsequent row from low to high values of j
00060   for (unsigned j=1;j<nj;++j,row0+=jstep)
00061   {
00062     // Check first element against first two in previous row
00063     *row0 = vcl_min(row0[o3]+1.0f,*row0);
00064     *row0 = vcl_min(row0[o4]+sqrt2,*row0);
00065 
00066     float* p0 = row0+istep;
00067     for (unsigned i=1;i<ni1;++i,p0+=istep)
00068     {
00069       *p0 = vcl_min(p0[o1]+1.0f ,*p0); // (-1,0)
00070       *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00071       *p0 = vcl_min(p0[o3]+1.0f ,*p0); // (0,-1)
00072       *p0 = vcl_min(p0[o4]+sqrt2,*p0); // (1,-1)
00073     }
00074 
00075     // Check last element in row
00076     *p0 = vcl_min(p0[o1]+1.0f ,*p0); // (-1,0)
00077     *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00078     *p0 = vcl_min(p0[o3]+1.0f ,*p0); // (0,-1)
00079   }
00080 }
00081 
00082 //: Compute distance function from true elements in mask
00083 //  On exit, the values are the 8-connected distance to the
00084 //  nearest original zero region (or max_dist, if that is smaller).
00085 void vil_distance_transform(const vil_image_view<bool>& mask,
00086                             vil_image_view<float>& distance_image,
00087                             float max_dist)
00088 {
00089   distance_image.set_size(mask.ni(),mask.nj());
00090   distance_image.fill(max_dist);
00091   vil_fill_mask(distance_image,mask,0.0f);
00092 
00093   vil_distance_transform(distance_image);
00094 }
00095 
00096 //: Distance function, using neighbours +/-2 in x,y
00097 //  More accurate than vil_distance_function_one_way
00098 void vil_distance_transform_r2_one_way(vil_image_view<float>& image)
00099 {
00100   assert(image.nplanes()==1);
00101   unsigned ni = image.ni();
00102   unsigned nj = image.nj();
00103   unsigned ni2 = ni-2;
00104   vcl_ptrdiff_t istep = image.istep(),  jstep = image.jstep();
00105 
00106   //   Kernel defining points to consider (relative to XX)
00107   //   -- o6 -- o7 --
00108   //   o5 o2 o3 o4 o8
00109   //   -- o1 XX -- --
00110   vcl_ptrdiff_t o1 = -istep, o2 = -jstep-istep;
00111   vcl_ptrdiff_t o3 = -jstep, o4 = istep-jstep;
00112   vcl_ptrdiff_t o5 = -2*istep-jstep;
00113   vcl_ptrdiff_t o6 = -istep-2*jstep;
00114   vcl_ptrdiff_t o7 =  istep-2*jstep;
00115   vcl_ptrdiff_t o8 =  2*istep-jstep;
00116 
00117   float* row0 = image.top_left_ptr();
00118 
00119   const float sqrt2 = vcl_sqrt(2.0f);
00120   const float sqrt5 = vcl_sqrt(5.0f);
00121 
00122   // Process the first row
00123   float* p0 = row0+istep;
00124   for (unsigned i=1;i<ni;++i,p0+=istep)
00125   {
00126       *p0 = vcl_min(p0[-istep]+1.0f,*p0);
00127   }
00128 
00129   row0 += jstep;  // Move to next row
00130 
00131   // ==== Process second row ====
00132   // Check first element against elements in previous row
00133   *row0 = vcl_min(row0[o3]+1.0f,*row0);  // (0,-1)
00134   *row0 = vcl_min(row0[o4]+sqrt5,*row0); // (1,-1)
00135   *row0 = vcl_min(row0[o8]+sqrt5,*row0); // (2,-1)
00136 
00137   p0 = row0+istep;  // Move to element 1
00138   *p0 = vcl_min(p0[o1]+1.0f,*p0); // (-1,0)
00139   *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00140   *p0 = vcl_min(p0[o3]+1.0f,*p0); // (0,-1)
00141   *p0 = vcl_min(p0[o4]+sqrt2,*p0); // (1,-1)
00142   *p0 = vcl_min(p0[o8]+sqrt5,*p0); // (2,-1)
00143 
00144   p0+=istep;  // Move to element 2
00145   for (unsigned i=2;i<ni2;++i,p0+=istep)
00146   {
00147     *p0 = vcl_min(p0[o1]+1.0f,*p0); // (-1,0)
00148     *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00149     *p0 = vcl_min(p0[o3]+1.0f,*p0); // (0,-1)
00150     *p0 = vcl_min(p0[o4]+sqrt2,*p0); // (1,-1)
00151     *p0 = vcl_min(p0[o5]+sqrt5,*p0); // (-2,-1)
00152     *p0 = vcl_min(p0[o8]+sqrt5,*p0); // (2,-1)
00153   }
00154 
00155   // Check element ni-2
00156   *p0 = vcl_min(p0[o1]+1.0f,*p0); // (-1,0)
00157   *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00158   *p0 = vcl_min(p0[o3]+1.0f,*p0); // (0,-1)
00159   *p0 = vcl_min(p0[o4]+sqrt2,*p0); // (1,-1)
00160   *p0 = vcl_min(p0[o5]+sqrt5,*p0); // (-2,-1)
00161 
00162   p0+=istep;  // Move to element ni-1
00163   // Check last element in row
00164   *p0 = vcl_min(p0[o1]+1.0f,*p0); // (-1,0)
00165   *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00166   *p0 = vcl_min(p0[o3]+1.0f,*p0); // (0,-1)
00167   *p0 = vcl_min(p0[o5]+sqrt5,*p0); // (-2,-1)
00168 
00169   row0 += jstep;  // Move to next row (2)
00170 
00171   // Process each subsequent row from low to high values of j
00172   for (unsigned j=2;j<nj;++j,row0+=jstep)
00173   {
00174     // Check first element
00175     *row0 = vcl_min(row0[o3]+1.0f,*row0);  // (0,-1)
00176     *row0 = vcl_min(row0[o4]+sqrt2,*row0); // (1,-1)
00177     *row0 = vcl_min(row0[o7]+sqrt5,*row0); // (1,-2)
00178     *row0 = vcl_min(row0[o8]+sqrt5,*row0); // (2,-1)
00179 
00180     float* p0 = row0+istep;  // Element 1
00181     // Check second element, allowing for boundary conditions
00182     *p0 = vcl_min(p0[o1]+1.0f,*p0); // (-1,0)
00183     *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00184     *p0 = vcl_min(p0[o3]+1.0f,*p0); // (0,-1)
00185     *p0 = vcl_min(p0[o4]+sqrt2,*p0); // (1,-1)
00186     *p0 = vcl_min(p0[o6]+sqrt5,*p0); // (-1,-2)
00187     *p0 = vcl_min(p0[o7]+sqrt5,*p0); // (1,-2)
00188     *p0 = vcl_min(p0[o8]+sqrt5,*p0); // (2,-1)
00189 
00190     p0+=istep;  // Move to next element (2)
00191     for (unsigned i=2;i<ni2;++i,p0+=istep)
00192     {
00193       *p0 = vcl_min(p0[o1]+1.0f,*p0); // (-1,0)
00194       *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00195       *p0 = vcl_min(p0[o3]+1.0f,*p0); // (0,-1)
00196       *p0 = vcl_min(p0[o4]+sqrt2,*p0); // (1,-1)
00197       *p0 = vcl_min(p0[o5]+sqrt5,*p0); // (-2,-1)
00198       *p0 = vcl_min(p0[o6]+sqrt5,*p0); // (-1,-2)
00199       *p0 = vcl_min(p0[o7]+sqrt5,*p0); // (1,-2)
00200       *p0 = vcl_min(p0[o8]+sqrt5,*p0); // (2,-1)
00201     }
00202     // p0 points to element (ni-2,y)
00203 
00204     // Check last but one element in row
00205     *p0 = vcl_min(p0[o1]+1.0f,*p0); // (-1,0)
00206     *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00207     *p0 = vcl_min(p0[o3]+1.0f,*p0); // (0,-1)
00208     *p0 = vcl_min(p0[o4]+sqrt2,*p0); // (1,-1)
00209     *p0 = vcl_min(p0[o5]+sqrt5,*p0); // (-2,-1)
00210     *p0 = vcl_min(p0[o6]+sqrt5,*p0); // (-1,-2)
00211     *p0 = vcl_min(p0[o7]+sqrt5,*p0); // (1,-2)
00212 
00213     p0+=istep; // Move to last element (ni-1,y)
00214     // Process last element in row
00215     *p0 = vcl_min(p0[o1]+1.0f,*p0); // (-1,0)
00216     *p0 = vcl_min(p0[o2]+sqrt2,*p0); // (-1,-1)
00217     *p0 = vcl_min(p0[o3]+1.0f,*p0); // (0,-1)
00218     *p0 = vcl_min(p0[o5]+sqrt5,*p0); // (-2,-1)
00219     *p0 = vcl_min(p0[o6]+sqrt5,*p0); // (-1,-2)
00220   }
00221 }
00222 
00223 //: Compute distance function from zeros in original image
00224 //  Image is assumed to be filled with max_dist where there
00225 //  is background, and zero at the places of interest.
00226 //  On exit, the values are the 24-connected distance to the
00227 //  nearest original zero region. (ie considers neighbours in
00228 //  a +/-2 pixel region around each point).
00229 //  More accurate than vil_distance_transform(image), but
00230 //  approximately twice the processing required.
00231 void vil_distance_transform_r2(vil_image_view<float>& image)
00232 {
00233   // Low to high pass
00234   vil_distance_transform_r2_one_way(image);
00235 
00236   // Flip to achieve high to low pass
00237   // Don't use vil_flip* as they assume const images.
00238   unsigned ni = image.ni(), nj = image.nj();
00239   vil_image_view<float> flip_image(image.memory_chunk(),
00240                                    &image(ni-1,nj-1), ni,nj,1,
00241                                    -image.istep(), -image.jstep(),
00242                                    image.nplanes());
00243   vil_distance_transform_r2_one_way(flip_image);
00244 }
00245