vil_convolve_1d.h
Go to the documentation of this file.
1 // This is core/vil/algo/vil_convolve_1d.h
2 #ifndef vil_convolve_1d_h_
3 #define vil_convolve_1d_h_
4 //:
5 // \file
6 // \brief 1D Convolution with cunning boundary options
7 // \author Tim Cootes, Ian Scott (based on work by fsm)
8 //
9 // \note The convolution operation is defined by
10 // $(f*g)(x) = \int f(x-y) g(y) dy$
11 // i.e. the kernel g is reflected before the integration is performed.
12 // If you don't want this to happen, the behaviour you want is not
13 // called "convolution". So don't break the convolution routines in
14 // that particular way.
15 
16 #include <algorithm>
17 #include <cstdlib>
18 #include <cstring>
19 #include <iostream>
20 #ifdef _MSC_VER
21 # include <vcl_msvc_warnings.h>
22 #endif
23 #include <cassert>
24 #include <vil/vil_image_view.h>
25 #include <vil/vil_image_resource.h>
26 #include <vil/vil_property.h>
27 
28 
29 //: Available options for boundary behavior
30 // When convolving a finite signal the boundaries may be
31 // treated in various ways which can often be expressed in terms
32 // of ways to extend the signal outside its original range.
34 {
35  //: Do not fill destination edges at all.
36  // i.e. leave them unchanged.
38 
39  //: Do not to extend the signal, but pad with zeros.
40  // \verbatim
41  // | |
42  // K ----*-------
43  // in ... ---------------------------
44  // out ... --------------------0000000
45  // \endverbatim
47 
48  //: Zero-extend the input signal beyond the boundary.
49  // \verbatim
50  // | |
51  // K ----*--------
52  // in ... ---------------------------000000000000...
53  // out ... ---------------------------
54  // \endverbatim
56 
57  //: Extend the signal to be constant beyond the boundary.
58  // \verbatim
59  // | |
60  // K ----*--------
61  // in ... --------------------------aaaaaaaaaaaaa...
62  // out ... ---------------------------
63  // \endverbatim
65 
66  //: Extend the signal periodically beyond the boundary.
67  // \verbatim
68  // | |
69  // K ----*--------
70  // in abc...-------------------------abc...------..
71  // out ... ---------------------------
72  // \endverbatim
74 
75  //: Extend the signal by reflection about the boundary.
76  // \verbatim
77  // | |
78  // K ----*--------
79  // in ... -------------------...edcbabcde...
80  // out ... ---------------------------
81  // \endverbatim
83 
84  //: Kernel is trimmed and reweighed, to allow convolution up to boundary.
85  // This one is slightly different. The input signal is not
86  // extended in any way, but the kernel is trimmed to allow
87  // convolution to proceed up to the boundary and reweighed
88  // to keep the total area the same.
89  // \note may not work with kernels which take negative values.
91 };
92 
93 //: Convolve edge with kernel[x*kstep] x in [k_lo,k_hi] (k_hi>=0)
94 // Fills only edge: dest[i], i=0..(k_hi-1)
95 template <class srcT, class destT, class kernelT, class accumT>
96 inline void vil_convolve_edge_1d(const srcT* src, unsigned n, std::ptrdiff_t s_step,
97  destT* dest, std::ptrdiff_t d_step,
98  const kernelT* kernel,
99  std::ptrdiff_t k_lo, std::ptrdiff_t k_hi,
100  std::ptrdiff_t kstep, accumT,
102 {
103  switch (option)
104  {
106  return;
108  // Initialise first elements of row to zero
109  for (std::ptrdiff_t i=-k_hi;i<0;++i,dest+=d_step)
110  *dest = 0;
111  return;
113  // Assume src[i]==0 for i<0
114 // for (std::ptrdiff_t i=-k_hi+1;i<=0;++i,dest+=d_step,src+=s_step)
115  for (std::ptrdiff_t i=0;i<k_hi;++i,dest+=d_step)
116  {
117  accumT sum = 0;
118  const srcT* s = src;
119  const kernelT* k = kernel+i*kstep;
120  for (std::ptrdiff_t j=i;j>=k_lo;--j,s+=s_step,k-=kstep)
121  sum+= (accumT)((*s)*(*k));
122  *dest=(destT)sum;
123  }
124  return;
126  {
127  // Assume src[i]=src[0] for i<0
128  std::ptrdiff_t i_max = k_hi-1;
129  for (std::ptrdiff_t i=0;i<=i_max;++i)
130  {
131  accumT sum=0;
132  for (std::ptrdiff_t j=-k_hi;j<=-k_lo;++j)
133  {
134  if ((i+j)<0) sum+=(accumT)(src[0]*kernel[j*(-kstep)]);
135  else sum+=(accumT)(src[(i+j)*s_step]*kernel[j*(-kstep)]);
136  }
137  dest[i*d_step]=(destT)sum;
138  }
139  return;
140  }
142  {
143  // Assume src[i]=src[0] for i<0
144  std::ptrdiff_t i_max = k_hi-1;
145  for (std::ptrdiff_t i=0;i<=i_max;++i)
146  {
147  accumT sum=0;
148  for (std::ptrdiff_t j=-k_hi;j<=-k_lo;++j)
149  {
150  if ((i+j)<0) sum+=(accumT)(src[-(i+j)*s_step]*kernel[j*(-kstep)]);
151  else sum+=(accumT)(src[(i+j)*s_step]*kernel[j*(-kstep)]);
152  }
153  dest[i*d_step]=(destT)sum;
154  }
155  return;
156  }
158  {
159  // Assume src[i]=src[n+i] for i<0
160  std::ptrdiff_t i_max = k_hi-1;
161  for (int i=0;i<=i_max;++i)
162  {
163  accumT sum=0;
164  for (std::ptrdiff_t j=k_hi;j>=k_lo;--j)
165  sum+=(accumT)(src[((i-j+n)%n)*s_step]*kernel[j*kstep]);
166  dest[i*d_step]=(destT)sum;
167  }
168  return;
169  }
170  case vil_convolve_trim:
171  {
172  // Truncate and reweight kernel
173  accumT k_sum_all=0;
174  for (std::ptrdiff_t j=-k_hi;j<=-k_lo;++j) k_sum_all+=(accumT)(kernel[j*(-kstep)]);
175 
176  std::ptrdiff_t i_max = k_hi-1;
177  for (std::ptrdiff_t i=0;i<=i_max;++i)
178  {
179  accumT sum=0;
180  accumT k_sum=0;
181  // Sum elements which overlap src
182  // ie i+j>=0 (so j starts at -i)
183  for (std::ptrdiff_t j=-i;j<=-k_lo;++j)
184  {
185  sum+=(accumT)(src[(i+j)*s_step]*kernel[j*(-kstep)]);
186  k_sum += (accumT)(kernel[j*(-kstep)]);
187  }
188  dest[i*d_step]=(destT)(sum*k_sum_all/k_sum);
189  }
190  return;
191  }
192  default:
193  std::cout<<"ERROR: vil_convolve_edge_1d: "
194  <<"Sorry, can't deal with supplied edge option.\n";
195  std::abort();
196  }
197 }
198 
199 //: Convolve kernel[x] (x in [k_lo,k_hi]) with srcT
200 // Assumes dest and src same size (nx)
201 // Kernel must not be larger than nx;
202 template <class srcT, class destT, class kernelT, class accumT>
203 inline void vil_convolve_1d(const srcT* src0, unsigned nx, std::ptrdiff_t s_step,
204  destT* dest0, std::ptrdiff_t d_step,
205  const kernelT* kernel,
206  std::ptrdiff_t k_lo, std::ptrdiff_t k_hi,
207  accumT ac,
208  vil_convolve_boundary_option start_option,
209  vil_convolve_boundary_option end_option)
210 {
211  assert(k_hi - k_lo < int(nx));
212 
213  // Deal with start (fill elements 0..1+k_hi of dest)
214  vil_convolve_edge_1d(src0,nx,s_step,dest0,d_step,kernel,k_lo,k_hi,1,ac,start_option);
215 
216  const kernelT* k_rbegin = kernel+k_hi;
217  const kernelT* k_rend = kernel+k_lo-1;
218  assert(k_rbegin >= k_rend);
219  const srcT* src = src0;
220 
221  for (destT * dest = dest0 + d_step*k_hi,
222  * const end_dest = dest0 + d_step*(int(nx)+k_lo);
223  dest!=end_dest;
224  dest+=d_step,src+=s_step)
225  {
226  accumT sum = 0;
227  const srcT* s= src;
228  for (const kernelT *k = k_rbegin;k!=k_rend;--k,s+=s_step)
229  sum+= (accumT)((*k)*(*s));
230  *dest = destT(sum);
231  }
232 
233  // Deal with end (reflect data and kernel!)
234  vil_convolve_edge_1d(src0+(nx-1)*s_step,nx,-s_step,
235  dest0+(nx-1)*d_step,-d_step,
236  kernel,-k_hi,-k_lo,-1,ac,end_option);
237 }
238 
239 //: Convolve kernel[i] (i in [k_lo,k_hi]) with srcT in i-direction
240 // On exit dest_im(i,j) = sum src(i-x,j)*kernel(x) (x=k_lo..k_hi)
241 // \note This function reverses the kernel. If you don't want the
242 // kernel reversed, use vil_correlate_1d instead. The kernel must
243 // not be larger than src_im.ni()
244 // \param kernel should point to tap 0.
245 // \param dest_im will be resized to size of src_im.
246 // \relatesalso vil_image_view
247 template <class srcT, class destT, class kernelT, class accumT>
248 inline void vil_convolve_1d(const vil_image_view<srcT>& src_im,
249  vil_image_view<destT>& dest_im,
250  const kernelT* kernel,
251  std::ptrdiff_t k_lo, std::ptrdiff_t k_hi,
252  accumT ac,
253  vil_convolve_boundary_option start_option,
254  vil_convolve_boundary_option end_option)
255 {
256  unsigned n_i = src_im.ni();
257  unsigned n_j = src_im.nj();
258  assert(k_hi - k_lo +1 <= (int) n_i);
259  std::ptrdiff_t s_istep = src_im.istep(), s_jstep = src_im.jstep();
260 
261  dest_im.set_size(n_i,n_j,src_im.nplanes());
262  std::ptrdiff_t d_istep = dest_im.istep(),d_jstep = dest_im.jstep();
263 
264  for (unsigned int p=0;p<src_im.nplanes();++p)
265  {
266  // Select first row of p-th plane
267  const srcT* src_row = src_im.top_left_ptr()+p*src_im.planestep();
268  destT* dest_row = dest_im.top_left_ptr()+p*dest_im.planestep();
269 
270  // Apply convolution to each row in turn
271  // First check if either istep is 1 for speed optimisation.
272 
273  if (s_istep == 1)
274  {
275  if (d_istep == 1)
276  for (unsigned int j=0;j<n_j;++j,src_row+=s_jstep,dest_row+=d_jstep)
277  vil_convolve_1d(src_row,n_i,1, dest_row,1,
278  kernel,k_lo,k_hi,ac,start_option,end_option);
279  else
280  for (unsigned int j=0;j<n_j;++j,src_row+=s_jstep,dest_row+=d_jstep)
281  vil_convolve_1d(src_row,n_i,1, dest_row,d_istep,
282  kernel,k_lo,k_hi,ac,start_option,end_option);
283  }
284  else
285  {
286  if (d_istep == 1)
287  for (unsigned int j=0;j<n_j;++j,src_row+=s_jstep,dest_row+=d_jstep)
288  vil_convolve_1d(src_row,n_i,s_istep, dest_row,1,
289  kernel,k_lo,k_hi,ac,start_option,end_option);
290  else
291  for (unsigned int j=0;j<n_j;++j,src_row+=s_jstep,dest_row+=d_jstep)
292  vil_convolve_1d(src_row,n_i,s_istep, dest_row,d_istep,
293  kernel,k_lo,k_hi,ac,start_option,end_option);
294  }
295  }
296 }
297 
298 template <class destT, class kernelT, class accumT>
300  const vil_image_resource_sptr& src_im,
301  const destT dt,
302  const kernelT* kernel, int k_lo, int k_hi,
303  const accumT ac,
304  vil_convolve_boundary_option start_option,
305  vil_convolve_boundary_option end_option);
306 
307 //: A resource adaptor that behaves like a convolved version of its input
308 template <class kernelT, class accumT, class destT>
310 {
311  public:
312  //: Construct a convolve filter.
313  // You can't create one of these directly, use vil_convolve_1d instead
315  const kernelT* kernel, int k_lo, int k_hi,
316  vil_convolve_boundary_option start_option,
317  vil_convolve_boundary_option end_option) :
318  src_(src), kernel_(kernel), klo_(k_lo), khi_(k_hi),
319  start_option_(start_option), end_option_(end_option)
320  {
321  // Can't do period extension yet.
322  assert (start_option != vil_convolve_periodic_extend ||
323  end_option != vil_convolve_periodic_extend);
324  }
325 
326  friend vil_image_resource_sptr vil_convolve_1d <> (
327  const vil_image_resource_sptr& src_im, const destT dt, const kernelT* kernel,
328  int k_lo, int k_hi, const accumT ac,
329  vil_convolve_boundary_option start_option,
330  vil_convolve_boundary_option end_option);
331 
332  public:
333  vil_image_view_base_sptr get_copy_view(unsigned i0, unsigned n_i,
334  unsigned j0, unsigned n_j) const override
335  {
336  if (i0 + n_i > src_->ni() || j0 + n_j > src_->nj()) return nullptr;
337  const unsigned lsrc = (unsigned) std::max(0,(int)i0 + klo_); // lhs of input window
338  const unsigned hsrc = std::min(src_->ni(),i0 + n_i - klo_ + khi_); // 1+rhs of input window.
339  const unsigned lboundary = std::min((unsigned) -klo_, i0); // width of lhs boundary area.
340  assert (hsrc > lsrc);
341  vil_image_view_base_sptr vs = src_->get_view(lsrc, hsrc-lsrc, j0, n_j);
342  vil_image_view<destT> dest(vs->ni(), vs->nj(), vs->nplanes());
343  switch (vs->pixel_format())
344  {
345 #define macro( F , T ) \
346  case F : \
347  vil_convolve_1d(static_cast<vil_image_view<T >&>(*vs),dest, \
348  kernel_, klo_, khi_, accumT(), start_option_, end_option_); \
349  return new vil_image_view<destT>(vil_crop(dest, lboundary, n_i, 0, n_j));
350 
351  macro(VIL_PIXEL_FORMAT_BYTE , vxl_byte )
352  macro(VIL_PIXEL_FORMAT_SBYTE , vxl_sbyte )
353  macro(VIL_PIXEL_FORMAT_UINT_32 , vxl_uint_32 )
354  macro(VIL_PIXEL_FORMAT_UINT_16 , vxl_uint_16 )
355  macro(VIL_PIXEL_FORMAT_INT_32 , vxl_int_32 )
356  macro(VIL_PIXEL_FORMAT_INT_16 , vxl_int_16 )
358  macro(VIL_PIXEL_FORMAT_FLOAT , float )
359  macro(VIL_PIXEL_FORMAT_DOUBLE , double )
360 // complex<float> should work - but causes all manner of compiler template errors.
361 // maybe need a better compiler, maybe there is a code fix - IMS
362 #undef macro
363  default:
364  return nullptr;
365  }
366  }
367 
368  unsigned nplanes() const override { return src_->nplanes(); }
369  unsigned ni() const override { return src_->ni(); }
370  unsigned nj() const override { return src_->nj(); }
371 
372  enum vil_pixel_format pixel_format() const override
373  { return vil_pixel_format_of(accumT()); }
374 
375 
376  //: Put the data in this view back into the image source.
377  bool put_view(const vil_image_view_base& /*im*/, unsigned /*i0*/, unsigned /*j0*/) override
378  {
379  std::cerr << "WARNING: vil_convolve_1d_resource::put_back\n"
380  << "\tYou can't push data back into a convolve filter.\n";
381  return false;
382  }
383 
384  //: Extra property information
385  bool get_property(char const* tag, void* property_value = nullptr) const override
386  {
387  if (0==std::strcmp(tag, vil_property_read_only))
388  return property_value ? (*static_cast<bool*>(property_value)) = true : true;
389 
390  return src_->get_property(tag, property_value);
391  }
392 
393  protected:
395  const kernelT* kernel_;
396  int klo_, khi_;
398 };
399 
400 //: Create an image_resource object which convolve kernel[x] x in [k_lo,k_hi] with srcT
401 // \note This function reverses the kernel. If you don't want the
402 // kernel reversed, use vil_correlate_1d instead.
403 // \param kernel should point to tap 0.
404 // \relatesalso vil_image_resource
405 template <class destT, class kernelT, class accumT>
407  const vil_image_resource_sptr& src_im,
408  const destT /*dt*/,
409  const kernelT* kernel, int k_lo, int k_hi,
410  const accumT,
411  vil_convolve_boundary_option start_option,
412  vil_convolve_boundary_option end_option)
413 {
414  return new vil_convolve_1d_resource<kernelT, accumT, destT>(src_im, kernel, k_lo, k_hi, start_option, end_option);
415 }
416 
417 #endif // vil_convolve_1d_h_
An abstract base class of smart pointers to actual image data in memory.
vil_pixel_format
Describes the type of the concrete data.
vil_convolve_boundary_option end_option_
Extend the signal to be constant beyond the boundary.
Extend the signal periodically beyond the boundary.
Concrete view of image data of type T held in memory.
Definition: vil_fwd.h:13
enum vil_pixel_format pixel_format() const override
Pixel Format.
unsigned ni() const override
Dimensions: Planes x ni x nj.
void set_size(unsigned ni, unsigned nj) override
resize current planes to ni x nj.
void vil_convolve_edge_1d(const srcT *src, unsigned n, std::ptrdiff_t s_step, destT *dest, std::ptrdiff_t d_step, const kernelT *kernel, std::ptrdiff_t k_lo, std::ptrdiff_t k_hi, std::ptrdiff_t kstep, accumT, vil_convolve_boundary_option option)
Convolve edge with kernel[x*kstep] x in [k_lo,k_hi] (k_hi>=0).
unsigned nj() const override
Dimensions: Planes x ni x nj.
std::ptrdiff_t jstep() const
Add this to your pixel pointer to get next j pixel.
vil_convolve_boundary_option
Available options for boundary behavior.
unsigned ni() const
Width.
A resource adaptor that behaves like a convolved version of its input.
unsigned nj() const
Height.
vil_convolve_1d_resource(const vil_image_resource_sptr &src, const kernelT *kernel, int k_lo, int k_hi, vil_convolve_boundary_option start_option, vil_convolve_boundary_option end_option)
Construct a convolve filter.
vil_convolve_boundary_option start_option_
std::ptrdiff_t planestep() const
Add this to your pixel pointer to get pixel on next plane.
vil_image_view_base_sptr get_copy_view(unsigned i0, unsigned n_i, unsigned j0, unsigned n_j) const override
Create a read/write view of a copy of this data.
Do not fill destination edges at all.
Kernel is trimmed and reweighed, to allow convolution up to boundary.
Abstract representation of an image source or image destination.
A base class reference-counting view of some image data.
Do not to extend the signal, but pad with zeros.
There is no class or function called vil_property.
T * top_left_ptr()
Pointer to the first (top left in plane 0) pixel.
void vil_convolve_1d(const vil_image_view< srcT > &src_im, vil_image_view< destT > &dest_im, const kernelT *kernel, std::ptrdiff_t k_lo, std::ptrdiff_t k_hi, accumT ac, vil_convolve_boundary_option start_option, vil_convolve_boundary_option end_option)
Convolve kernel[i] (i in [k_lo,k_hi]) with srcT in i-direction.
Zero-extend the input signal beyond the boundary.
unsigned nplanes() const
Number of planes.
bool get_property(char const *tag, void *property_value=nullptr) const override
Extra property information.
bool put_view(const vil_image_view_base &, unsigned, unsigned) override
Put the data in this view back into the image source.
Representation of a generic image source or destination.
#define macro(F, T)
Extend the signal by reflection about the boundary.
unsigned nplanes() const override
Dimensions: Planes x ni x nj.
std::ptrdiff_t istep() const
Add this to your pixel pointer to get next i pixel.
vil_pixel_format vil_pixel_format_of(T)
The pixel format enumeration corresponding to the C++ type.
vil_image_resource_sptr src_
#define vil_property_read_only
Indicate that you can't call put_view on this image.
Definition: vil_property.h:47