vspline 1.1.0
Generic C++11 Code for Uniform B-Splines
Public Types | Public Member Functions | List of all members
vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type > Struct Template Reference

struct separable_filter is the central object used for 'wielding' filters. The filters themselves are defined as 1D operations, which is sufficient for a separable filter: the 1D operation is applied to each axis in turn. If the data themselves are 1D, this is inefficient if the run of data is very long: we'd end up with a single thread processing the data without vectorization. So for this special case, we use a bit of trickery: long runs of 1D data are folded up, processed as 2D (with multithreading and vectorization) and the result of this operation, which isn't correct everywhere, is 'mended' where it is wrong. If the data are nD, we process them by buffering chunks collinear to the processing axis and applying the 1D filter to these chunks. 'Chunks' isn't quite the right word to use here - what we're buffering are 'bundles' of 1D subarrays, where a bundle holds as many 1D subarrays as a SIMD vector is wide. this makes it possible to process the buffered data with vectorized code. While most of the time the buffering will simply copy data into and out of the buffer, we use a distinct data type for the buffer which makes sure that arithmetic can be performed in floating point and with sufficient precision to do the data justice. With this provision we can safely process arrays of integral type. Such data are 'promoted' to this type when they are buffered and converted to the result type afterwards. Of course there will be quantization errors if the data are converted to an integral result type; it's best to use a real result type. The type for arithmetic operations inside the filter is fixed via stripe_handler_type, which takes a template argument '_math_ele_type'. This way, the arithmetic type is distributed consistently. Also note that an integral target type will receive the data via a simple type conversion and not with saturation arithmetics. If this is an issue, filter to a real-typed target and process separately. A good way of using integral data is to have integral input and real-typed output. Promoting the integral data to a real type preserves them precisely, and the 'exact' result is then stored in floating point. With such a scheme, raw data (like image data, which are often 8 or 16 bit integers) can be 'sucked in' without need for previous conversion, producing filtered data in, say, float for further processing. More...

#include <filter.h>

Public Types

enum  { dimension = input_array_type::actual_dimension }
 
enum  { channels = vigra::ExpandElementResult < in_value_type > :: size }
 
typedef input_array_type::value_type in_value_type
 
typedef output_array_type::value_type out_value_type
 
typedef vigra::ExpandElementResult< in_value_type >::type in_ele_type
 
typedef vigra::ExpandElementResult< out_value_type >::type out_ele_type
 
typedef std::integral_constant< bool, dimension==1 > is_1d_type
 
typedef std::integral_constant< bool, channels==1 > is_1_channel_type
 

Public Member Functions

template<class filter_args >
void operator() (const input_array_type &input, output_array_type &output, const filter_args &handler_args, int njobs=vspline::default_njobs) const
 this is the standard entry point to the separable filter code for processing all axes of an array. first we use a dispatch to separate processing of 1D data from processing of nD data. More...
 
template<typename ... types>
void _on_dimension (std::true_type, types ... args) const
 
template<typename ... types>
void _on_dimension (std::false_type, types ... args) const
 
template<typename in_vt , typename out_vt >
void _process_1d (const in_vt &input, out_vt &output, const std::vector< typename stripe_handler_type::arg_type > &handler_args, int njobs) const
 specialized processing of 1D input/output. We have established that the data are 1D. we have received a std::vector of handler arguments. It has to contain precisely one element which we unpack and use to call the overload below. More...
 
template<typename in_vt , typename out_vt >
void _process_1d (const in_vt &input, out_vt &output, int axis, const typename stripe_handler_type::arg_type &handler_args, int njobs) const
 specialized processing of 1D input/output. We have established that the data are 1D and we have a single handler argument. This routine may come as a surprise and it's quite long and complex. The strategy is this: More...
 
template<typename in_vt , typename out_vt >
void _process_1d (const in_vt &input, out_vt &output, const typename stripe_handler_type::arg_type &handler_args, int njobs) const
 
void _process_nd (const input_array_type &input, output_array_type &output, const std::vector< typename stripe_handler_type::arg_type > &handler_args, int njobs) const
 specialized processing of nD input/output. We have established that the data are nD. Now we process the axes in turn, passing the per-axis handler args. More...
 
void operator() (const input_array_type &input, output_array_type &output, int axis, const typename stripe_handler_type::arg_type &handler_args, int njobs=vspline::default_njobs) const
 this operator() overload for single-axis processing takes plain arrays for input and output, they may be either 1D or nD. again we use _on_dimension, now with a different argument signature (we have 'axis' now). As _on_dimension is a variadic template, we 'reemerge' in the right place. More...
 
template<typename ... types>
void _process_nd (types ... args) const
 processing of nD input/output. we have established that the data are nD. now we look at the data type. If the data are multi-channel, they need to be element-expanded for further processing, which is done by '_on_expand'. Note that there are two variants of _on_expand, one for plain arrays and one for stacks of arrays, which are coded after the operator() overload taking stacks of arrays further down. More...
 
template<typename in_t , typename out_t >
void _on_expand (std::false_type, const in_t &input, out_t &output, int axis, const typename stripe_handler_type::arg_type &handler_args, int njobs) const
 variant of _on_expand for single arrays. this overload is called if the data are multi-channel. we element-expand the arrays, then call the single-channel overload below More...
 
template<typename in_t , typename out_t >
void _on_expand (std::true_type, const in_t &input, out_t &output, int axis, const typename stripe_handler_type::arg_type &handler_args, int njobs) const
 Variant of _on_expand for single arrays. This is the single-channel overload. The arrays now hold fundamentals, either because that was their original data type or because they have been element-expanded. Now we finally get to do the filtering. Note how we introduce input and output as templates, since we can't be sure of their type: they may or may not have been element-expanded. More...
 
void operator() (const std::vector< input_array_type > &input, std::vector< output_array_type > &output, int axis, const typename stripe_handler_type::arg_type &handler_args, int njobs=vspline::default_njobs) const
 this operator() overload for single-axis processing takes std::vectors ('stacks') of arrays. this is only supported for nD data. This is a rarely-used variant; throughout vspline there isn't currently any place where this routine is called, but it's needed for some special applications. If you are studying the code, you may safely disregard the remainder of the code in this class definition; the two _on_expand variants below are also for the special case of 'stacks' of arrays. More...
 
template<typename in_vt , typename out_vt >
void _on_expand (std::false_type, const std::vector< in_vt > &input, std::vector< out_vt > &output, int axis, const typename stripe_handler_type::arg_type &handler_args, int njobs) const
 variant of _on_expand for stacks of arrays. this overload is called if the data are multi-channel. we element-expand the arrays, then call the single-channel overload below More...
 
template<typename in_vt , typename out_vt >
void _on_expand (std::true_type, const std::vector< in_vt > &input, std::vector< out_vt > &output, int axis, const typename stripe_handler_type::arg_type &handler_args, int njobs) const
 variant of _on_expand for stacks of arrays this is the single-channel overload. The arrays now hold fundamentals, either because that was their original data type or because they have been element-expanded. We end up in this routine for all processing of stacks and finally get to do the filtering. More...
 

Detailed Description

template<typename input_array_type, typename output_array_type, typename stripe_handler_type>
struct vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >

struct separable_filter is the central object used for 'wielding' filters. The filters themselves are defined as 1D operations, which is sufficient for a separable filter: the 1D operation is applied to each axis in turn. If the data themselves are 1D, this is inefficient if the run of data is very long: we'd end up with a single thread processing the data without vectorization. So for this special case, we use a bit of trickery: long runs of 1D data are folded up, processed as 2D (with multithreading and vectorization) and the result of this operation, which isn't correct everywhere, is 'mended' where it is wrong. If the data are nD, we process them by buffering chunks collinear to the processing axis and applying the 1D filter to these chunks. 'Chunks' isn't quite the right word to use here - what we're buffering are 'bundles' of 1D subarrays, where a bundle holds as many 1D subarrays as a SIMD vector is wide. this makes it possible to process the buffered data with vectorized code. While most of the time the buffering will simply copy data into and out of the buffer, we use a distinct data type for the buffer which makes sure that arithmetic can be performed in floating point and with sufficient precision to do the data justice. With this provision we can safely process arrays of integral type. Such data are 'promoted' to this type when they are buffered and converted to the result type afterwards. Of course there will be quantization errors if the data are converted to an integral result type; it's best to use a real result type. The type for arithmetic operations inside the filter is fixed via stripe_handler_type, which takes a template argument '_math_ele_type'. This way, the arithmetic type is distributed consistently. Also note that an integral target type will receive the data via a simple type conversion and not with saturation arithmetics. If this is an issue, filter to a real-typed target and process separately. A good way of using integral data is to have integral input and real-typed output. Promoting the integral data to a real type preserves them precisely, and the 'exact' result is then stored in floating point. With such a scheme, raw data (like image data, which are often 8 or 16 bit integers) can be 'sucked in' without need for previous conversion, producing filtered data in, say, float for further processing.

Definition at line 729 of file filter.h.

Member Typedef Documentation

◆ in_ele_type

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
typedef vigra::ExpandElementResult<in_value_type>::type vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::in_ele_type

Definition at line 744 of file filter.h.

◆ in_value_type

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
typedef input_array_type::value_type vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::in_value_type

Definition at line 735 of file filter.h.

◆ is_1_channel_type

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
typedef std::integral_constant< bool , channels == 1 > vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::is_1_channel_type

Definition at line 750 of file filter.h.

◆ is_1d_type

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
typedef std::integral_constant< bool , dimension == 1 > vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::is_1d_type

Definition at line 749 of file filter.h.

◆ out_ele_type

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
typedef vigra::ExpandElementResult<out_value_type>::type vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::out_ele_type

Definition at line 747 of file filter.h.

◆ out_value_type

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
typedef output_array_type::value_type vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::out_value_type

Definition at line 736 of file filter.h.

Member Enumeration Documentation

◆ anonymous enum

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
anonymous enum
Enumerator
dimension 

Definition at line 731 of file filter.h.

◆ anonymous enum

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
anonymous enum
Enumerator
channels 

Definition at line 738 of file filter.h.

Member Function Documentation

◆ _on_dimension() [1/2]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename ... types>
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_on_dimension ( std::false_type  ,
types ...  args 
) const
inline

Definition at line 783 of file filter.h.

◆ _on_dimension() [2/2]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename ... types>
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_on_dimension ( std::true_type  ,
types ...  args 
) const
inline

Definition at line 773 of file filter.h.

◆ _on_expand() [1/4]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename in_t , typename out_t >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_on_expand ( std::false_type  ,
const in_t &  input,
out_t &  output,
int  axis,
const typename stripe_handler_type::arg_type &  handler_args,
int  njobs 
) const
inline

variant of _on_expand for single arrays. this overload is called if the data are multi-channel. we element-expand the arrays, then call the single-channel overload below

Definition at line 1271 of file filter.h.

◆ _on_expand() [2/4]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename in_vt , typename out_vt >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_on_expand ( std::false_type  ,
const std::vector< in_vt > &  input,
std::vector< out_vt > &  output,
int  axis,
const typename stripe_handler_type::arg_type &  handler_args,
int  njobs 
) const
inline

variant of _on_expand for stacks of arrays. this overload is called if the data are multi-channel. we element-expand the arrays, then call the single-channel overload below

Definition at line 1384 of file filter.h.

◆ _on_expand() [3/4]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename in_t , typename out_t >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_on_expand ( std::true_type  ,
const in_t &  input,
out_t &  output,
int  axis,
const typename stripe_handler_type::arg_type &  handler_args,
int  njobs 
) const
inline

Variant of _on_expand for single arrays. This is the single-channel overload. The arrays now hold fundamentals, either because that was their original data type or because they have been element-expanded. Now we finally get to do the filtering. Note how we introduce input and output as templates, since we can't be sure of their type: they may or may not have been element-expanded.

Definition at line 1308 of file filter.h.

◆ _on_expand() [4/4]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename in_vt , typename out_vt >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_on_expand ( std::true_type  ,
const std::vector< in_vt > &  input,
std::vector< out_vt > &  output,
int  axis,
const typename stripe_handler_type::arg_type &  handler_args,
int  njobs 
) const
inline

variant of _on_expand for stacks of arrays this is the single-channel overload. The arrays now hold fundamentals, either because that was their original data type or because they have been element-expanded. We end up in this routine for all processing of stacks and finally get to do the filtering.

Definition at line 1434 of file filter.h.

◆ _process_1d() [1/3]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename in_vt , typename out_vt >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_process_1d ( const in_vt &  input,
out_vt &  output,
const std::vector< typename stripe_handler_type::arg_type > &  handler_args,
int  njobs 
) const
inline

specialized processing of 1D input/output. We have established that the data are 1D. we have received a std::vector of handler arguments. It has to contain precisely one element which we unpack and use to call the overload below.

Definition at line 799 of file filter.h.

◆ _process_1d() [2/3]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename in_vt , typename out_vt >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_process_1d ( const in_vt &  input,
out_vt &  output,
const typename stripe_handler_type::arg_type &  handler_args,
int  njobs 
) const
inline

Definition at line 859 of file filter.h.

◆ _process_1d() [3/3]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename in_vt , typename out_vt >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_process_1d ( const in_vt &  input,
out_vt &  output,
int  axis,
const typename stripe_handler_type::arg_type &  handler_args,
int  njobs 
) const
inline

specialized processing of 1D input/output. We have established that the data are 1D and we have a single handler argument. This routine may come as a surprise and it's quite long and complex. The strategy is this:

  • if the data are 'quite short', simply run a 1D filter directly on the data, without any multithreading or vectorization. If the user has specified 'zero tolerance', do the same.
  • otherwise employ 'fake 2D processing': pretend the data are 2D, filter them with 2D code (which offers multithreading and vectorization) and then 'mend' the result, which is wrong in parts due to the inappropriate processing. expect 'fake 2D processing' to kick in for buffer sizes somewhere in the low thousands, to give you a rough idea. All data paths in this routine make sure that the maths are done in math_type, there won't be storing of intermediate values to a lesser type. If the user has specified 'zero tolerance' and the output type is not the same as math_type, we have a worst-case scenario where the entire length of data is buffered in math_type and the operation is single-threaded and unvectorized, but this should rarely happen and requires the user to explicitly override the defaults. If the data are too short for fake 2D processing, the operation will also fail to multithread or vectorize.

Definition at line 848 of file filter.h.

◆ _process_nd() [1/2]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_process_nd ( const input_array_type &  input,
output_array_type &  output,
const std::vector< typename stripe_handler_type::arg_type > &  handler_args,
int  njobs 
) const
inline

specialized processing of nD input/output. We have established that the data are nD. Now we process the axes in turn, passing the per-axis handler args.

Definition at line 1196 of file filter.h.

◆ _process_nd() [2/2]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<typename ... types>
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::_process_nd ( types ...  args) const
inline

processing of nD input/output. we have established that the data are nD. now we look at the data type. If the data are multi-channel, they need to be element-expanded for further processing, which is done by '_on_expand'. Note that there are two variants of _on_expand, one for plain arrays and one for stacks of arrays, which are coded after the operator() overload taking stacks of arrays further down.

Definition at line 1251 of file filter.h.

◆ operator()() [1/3]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
template<class filter_args >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::operator() ( const input_array_type &  input,
output_array_type &  output,
const filter_args &  handler_args,
int  njobs = vspline::default_njobs 
) const
inline

this is the standard entry point to the separable filter code for processing all axes of an array. first we use a dispatch to separate processing of 1D data from processing of nD data.

Definition at line 757 of file filter.h.

◆ operator()() [2/3]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::operator() ( const input_array_type &  input,
output_array_type &  output,
int  axis,
const typename stripe_handler_type::arg_type &  handler_args,
int  njobs = vspline::default_njobs 
) const
inline

this operator() overload for single-axis processing takes plain arrays for input and output, they may be either 1D or nD. again we use _on_dimension, now with a different argument signature (we have 'axis' now). As _on_dimension is a variadic template, we 'reemerge' in the right place.

Definition at line 1230 of file filter.h.

◆ operator()() [3/3]

template<typename input_array_type , typename output_array_type , typename stripe_handler_type >
void vspline::detail::separable_filter< input_array_type, output_array_type, stripe_handler_type >::operator() ( const std::vector< input_array_type > &  input,
std::vector< output_array_type > &  output,
int  axis,
const typename stripe_handler_type::arg_type &  handler_args,
int  njobs = vspline::default_njobs 
) const
inline

this operator() overload for single-axis processing takes std::vectors ('stacks') of arrays. this is only supported for nD data. This is a rarely-used variant; throughout vspline there isn't currently any place where this routine is called, but it's needed for some special applications. If you are studying the code, you may safely disregard the remainder of the code in this class definition; the two _on_expand variants below are also for the special case of 'stacks' of arrays.

Definition at line 1364 of file filter.h.


The documentation for this struct was generated from the following file: