vil_distance_transform.cxx
Go to the documentation of this file.
1 #include <algorithm>
2 #include <cmath>
4 //:
5 // \file
6 // \brief Compute distance function
7 // \author Tim Cootes
8 
9 #include <vil/vil_fill.h>
10 #ifdef _MSC_VER
11 # include <vcl_msvc_warnings.h>
12 #endif
13 #include <cassert>
14 
15 //: Compute distance function from zeros in original image
16 // Image is assumed to be filled with max_dist where there
17 // is background, and zero at the places of interest.
18 // On exit, the values are the 8-connected distance to the
19 // nearest original zero region.
21 {
22  // Low to high pass
24 
25  // Flip to achieve high to low pass
26  // Don't use vil_flip* as they assume const images.
27  unsigned ni = image.ni(), nj = image.nj();
28  vil_image_view<float> flip_image(image.memory_chunk(),
29  &image(ni-1,nj-1), ni,nj,1,
30  -image.istep(), -image.jstep(),
31  image.nplanes());
33 }
34 
35 //: Compute directed distance function from zeros in original image
36 // Image is assumed to be filled with max_dist where there
37 // is background, and zero at the places of interest.
38 // On exit, the values are the 8-connected distance to the
39 // nearest original zero region above or to the left of current point.
40 // One pass of distance transform, going from low to high i,j.
42 {
43  assert(image.nplanes()==1);
44  unsigned ni = image.ni();
45  unsigned nj = image.nj();
46  unsigned ni1 = ni-1;
47  std::ptrdiff_t istep = image.istep(), jstep = image.jstep();
48  std::ptrdiff_t o1 = -istep, o2 = -jstep-istep, o3 = -jstep, o4 = istep-jstep;
49  float* row0 = image.top_left_ptr();
50 
51  const float sqrt2 = std::sqrt(2.0f);
52 
53  // Process the first row
54  float* p0 = row0+istep;
55  for (unsigned i=1;i<ni;++i,p0+=istep)
56  {
57  *p0 = std::min(p0[-istep]+1.0f,*p0);
58  }
59 
60  row0 += jstep; // Move to next row
61 
62  // Process each subsequent row from low to high values of j
63  for (unsigned j=1;j<nj;++j,row0+=jstep)
64  {
65  // Check first element against first two in previous row
66  *row0 = std::min(row0[o3]+1.0f,*row0);
67  *row0 = std::min(row0[o4]+sqrt2,*row0);
68 
69  float* p0 = row0+istep;
70  for (unsigned i=1;i<ni1;++i,p0+=istep)
71  {
72  *p0 = std::min(p0[o1]+1.0f ,*p0); // (-1,0)
73  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
74  *p0 = std::min(p0[o3]+1.0f ,*p0); // (0,-1)
75  *p0 = std::min(p0[o4]+sqrt2,*p0); // (1,-1)
76  }
77 
78  // Check last element in row
79  *p0 = std::min(p0[o1]+1.0f ,*p0); // (-1,0)
80  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
81  *p0 = std::min(p0[o3]+1.0f ,*p0); // (0,-1)
82  }
83 }
84 
85 //: Compute distance function from true elements in mask
86 // On exit, the values are the 8-connected distance to the
87 // nearest original zero region (or max_dist, if that is smaller).
89  vil_image_view<float>& distance_image,
90  float max_dist)
91 {
92  distance_image.set_size(mask.ni(),mask.nj());
93  distance_image.fill(max_dist);
94  vil_fill_mask(distance_image,mask,0.0f);
95 
96  vil_distance_transform(distance_image);
97 }
98 
99 //: Distance function, using neighbours +/-2 in x,y
100 // More accurate than vil_distance_function_one_way
102 {
103  assert(image.nplanes()==1);
104  unsigned ni = image.ni();
105  unsigned nj = image.nj();
106  unsigned ni2 = ni-2;
107  std::ptrdiff_t istep = image.istep(), jstep = image.jstep();
108 
109  // Kernel defining points to consider (relative to XX)
110  // -- o6 -- o7 --
111  // o5 o2 o3 o4 o8
112  // -- o1 XX -- --
113  std::ptrdiff_t o1 = -istep, o2 = -jstep-istep;
114  std::ptrdiff_t o3 = -jstep, o4 = istep-jstep;
115  std::ptrdiff_t o5 = -2*istep-jstep;
116  std::ptrdiff_t o6 = -istep-2*jstep;
117  std::ptrdiff_t o7 = istep-2*jstep;
118  std::ptrdiff_t o8 = 2*istep-jstep;
119 
120  float* row0 = image.top_left_ptr();
121 
122  const float sqrt2 = std::sqrt(2.0f);
123  const float sqrt5 = std::sqrt(5.0f);
124 
125  // Process the first row
126  float* p0 = row0+istep;
127  for (unsigned i=1;i<ni;++i,p0+=istep)
128  {
129  *p0 = std::min(p0[-istep]+1.0f,*p0);
130  }
131 
132  row0 += jstep; // Move to next row
133 
134  // ==== Process second row ====
135  // Check first element against elements in previous row
136  *row0 = std::min(row0[o3]+1.0f,*row0); // (0,-1)
137  *row0 = std::min(row0[o4]+sqrt5,*row0); // (1,-1)
138  *row0 = std::min(row0[o8]+sqrt5,*row0); // (2,-1)
139 
140  p0 = row0+istep; // Move to element 1
141  *p0 = std::min(p0[o1]+1.0f,*p0); // (-1,0)
142  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
143  *p0 = std::min(p0[o3]+1.0f,*p0); // (0,-1)
144  *p0 = std::min(p0[o4]+sqrt2,*p0); // (1,-1)
145  *p0 = std::min(p0[o8]+sqrt5,*p0); // (2,-1)
146 
147  p0+=istep; // Move to element 2
148  for (unsigned i=2;i<ni2;++i,p0+=istep)
149  {
150  *p0 = std::min(p0[o1]+1.0f,*p0); // (-1,0)
151  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
152  *p0 = std::min(p0[o3]+1.0f,*p0); // (0,-1)
153  *p0 = std::min(p0[o4]+sqrt2,*p0); // (1,-1)
154  *p0 = std::min(p0[o5]+sqrt5,*p0); // (-2,-1)
155  *p0 = std::min(p0[o8]+sqrt5,*p0); // (2,-1)
156  }
157 
158  // Check element ni-2
159  *p0 = std::min(p0[o1]+1.0f,*p0); // (-1,0)
160  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
161  *p0 = std::min(p0[o3]+1.0f,*p0); // (0,-1)
162  *p0 = std::min(p0[o4]+sqrt2,*p0); // (1,-1)
163  *p0 = std::min(p0[o5]+sqrt5,*p0); // (-2,-1)
164 
165  p0+=istep; // Move to element ni-1
166  // Check last element in row
167  *p0 = std::min(p0[o1]+1.0f,*p0); // (-1,0)
168  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
169  *p0 = std::min(p0[o3]+1.0f,*p0); // (0,-1)
170  *p0 = std::min(p0[o5]+sqrt5,*p0); // (-2,-1)
171 
172  row0 += jstep; // Move to next row (2)
173 
174  // Process each subsequent row from low to high values of j
175  for (unsigned j=2;j<nj;++j,row0+=jstep)
176  {
177  // Check first element
178  *row0 = std::min(row0[o3]+1.0f,*row0); // (0,-1)
179  *row0 = std::min(row0[o4]+sqrt2,*row0); // (1,-1)
180  *row0 = std::min(row0[o7]+sqrt5,*row0); // (1,-2)
181  *row0 = std::min(row0[o8]+sqrt5,*row0); // (2,-1)
182 
183  float* p0 = row0+istep; // Element 1
184  // Check second element, allowing for boundary conditions
185  *p0 = std::min(p0[o1]+1.0f,*p0); // (-1,0)
186  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
187  *p0 = std::min(p0[o3]+1.0f,*p0); // (0,-1)
188  *p0 = std::min(p0[o4]+sqrt2,*p0); // (1,-1)
189  *p0 = std::min(p0[o6]+sqrt5,*p0); // (-1,-2)
190  *p0 = std::min(p0[o7]+sqrt5,*p0); // (1,-2)
191  *p0 = std::min(p0[o8]+sqrt5,*p0); // (2,-1)
192 
193  p0+=istep; // Move to next element (2)
194  for (unsigned i=2;i<ni2;++i,p0+=istep)
195  {
196  *p0 = std::min(p0[o1]+1.0f,*p0); // (-1,0)
197  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
198  *p0 = std::min(p0[o3]+1.0f,*p0); // (0,-1)
199  *p0 = std::min(p0[o4]+sqrt2,*p0); // (1,-1)
200  *p0 = std::min(p0[o5]+sqrt5,*p0); // (-2,-1)
201  *p0 = std::min(p0[o6]+sqrt5,*p0); // (-1,-2)
202  *p0 = std::min(p0[o7]+sqrt5,*p0); // (1,-2)
203  *p0 = std::min(p0[o8]+sqrt5,*p0); // (2,-1)
204  }
205  // p0 points to element (ni-2,y)
206 
207  // Check last but one element in row
208  *p0 = std::min(p0[o1]+1.0f,*p0); // (-1,0)
209  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
210  *p0 = std::min(p0[o3]+1.0f,*p0); // (0,-1)
211  *p0 = std::min(p0[o4]+sqrt2,*p0); // (1,-1)
212  *p0 = std::min(p0[o5]+sqrt5,*p0); // (-2,-1)
213  *p0 = std::min(p0[o6]+sqrt5,*p0); // (-1,-2)
214  *p0 = std::min(p0[o7]+sqrt5,*p0); // (1,-2)
215 
216  p0+=istep; // Move to last element (ni-1,y)
217  // Process last element in row
218  *p0 = std::min(p0[o1]+1.0f,*p0); // (-1,0)
219  *p0 = std::min(p0[o2]+sqrt2,*p0); // (-1,-1)
220  *p0 = std::min(p0[o3]+1.0f,*p0); // (0,-1)
221  *p0 = std::min(p0[o5]+sqrt5,*p0); // (-2,-1)
222  *p0 = std::min(p0[o6]+sqrt5,*p0); // (-1,-2)
223  }
224 }
225 
226 //: Compute distance function from zeros in original image
227 // Image is assumed to be filled with max_dist where there
228 // is background, and zero at the places of interest.
229 // On exit, the values are the 24-connected distance to the
230 // nearest original zero region. (ie considers neighbours in
231 // a +/-2 pixel region around each point).
232 // More accurate than vil_distance_transform(image), but
233 // approximately twice the processing required.
235 {
236  // Low to high pass
238 
239  // Flip to achieve high to low pass
240  // Don't use vil_flip* as they assume const images.
241  unsigned ni = image.ni(), nj = image.nj();
242  vil_image_view<float> flip_image(image.memory_chunk(),
243  &image(ni-1,nj-1), ni,nj,1,
244  -image.istep(), -image.jstep(),
245  image.nplanes());
247 }
Various functions for manipulating image views.
Concrete view of image data of type T held in memory.
Definition: vil_fwd.h:13
void fill(T value)
Fill view with given value.
void set_size(unsigned ni, unsigned nj) override
resize current planes to ni x nj.
void vil_distance_transform_one_way(vil_image_view< float > &image)
Compute directed distance function from zeros in original image.
std::ptrdiff_t jstep() const
Add this to your pixel pointer to get next j pixel.
unsigned ni() const
Width.
unsigned nj() const
Height.
void vil_distance_transform_r2_one_way(vil_image_view< float > &image)
Distance function, using neighbours +/-2 in x,y.
void vil_distance_transform_r2(vil_image_view< float > &image)
Compute distance function from zeros in original image.
T * top_left_ptr()
Pointer to the first (top left in plane 0) pixel.
unsigned nplanes() const
Number of planes.
const vil_memory_chunk_sptr & memory_chunk() const
Smart pointer to the object holding the data for this view.
void vil_distance_transform(vil_image_view< float > &image)
Compute distance function from zeros in original image.
void vil_fill_mask(vil_image_view< srcT > &image, const vil_image_view< bool > &mask, srcT value, bool b=true)
Writes given value into each pixel of image under the elements of the mask set to b.
Definition: vil_fill.h:165
std::ptrdiff_t istep() const
Add this to your pixel pointer to get next i pixel.
Compute distance function.