[vspline] 65/72: change to vspline::bspline's parameter signature, polishing I moved the parameter _space to the end of bspline's constructor's argument list, since it should rarely be used. The other changes were to the comments, and also a change to class bspline's method container_size() which had a bug with SPHERICAL/REFLECT BCs. This caused problems with pano_extract in the examples, when the source image had an alpha channel, so pano_extract was changed as well.
Kay F. Jahnke
kfj-guest at moszumanska.debian.org
Sun Jul 2 09:02:43 UTC 2017
This is an automated email from the git hooks/post-receive script.
kfj-guest pushed a commit to branch master
in repository vspline.
commit 392ee6b54a3dbfbbe037d26000e811b8b1ae0036
Author: Kay F. Jahnke <kfjahnke at gmail.com>
Date: Sun May 21 11:55:15 2017 +0200
change to vspline::bspline's parameter signature, polishing
I moved the parameter _space to the end of bspline's constructor's
argument list, since it should rarely be used. The other changes were
to the comments, and also a change to class bspline's method
container_size() which had a bug with SPHERICAL/REFLECT BCs. This
caused problems with pano_extract in the examples, when the source
image had an alpha channel, so pano_extract was changed as well.
---
bspline.h | 68 +++++++++++++++++----
common.h | 21 ++++---
eval.h | 157 ++++++++++++++++++++++++++++--------------------
example/gradient.cc | 1 -
example/pano_extract.cc | 7 ++-
filter.h | 12 +++-
6 files changed, 172 insertions(+), 94 deletions(-)
diff --git a/bspline.h b/bspline.h
index 4a6bd5b..12ab2c7 100644
--- a/bspline.h
+++ b/bspline.h
@@ -310,27 +310,57 @@ public:
switch ( strategy )
{
case UNBRACED:
+ {
return core_shape ;
break ;
+ }
case BRACED:
- return bracer<view_type>::target_shape ( core_shape , bcv , spline_degree ) ;
+ {
+ auto ts = bracer<view_type>::target_shape
+ ( core_shape , bcv , spline_degree ) ;
+
+ if ( spline_degree & 1 )
+ {
+ for ( int d = 0 ; d < dimension ; d++ )
+ {
+ if ( bcv[d] == REFLECT || bcv[d] == SPHERICAL )
+ ts[d]++ ;
+ }
+ }
+ return ts ;
break ;
+ }
case EXPLICIT:
- shape_type braced_shape = bracer<view_type>::target_shape ( core_shape , bcv , spline_degree ) ;
- braced_shape += 2 * horizon ;
- return braced_shape ;
+ {
+ auto ts = bracer<view_type>::target_shape
+ ( core_shape , bcv , spline_degree ) ;
+
+ if ( spline_degree & 1 )
+ {
+ for ( int d = 0 ; d < dimension ; d++ )
+ {
+ if ( bcv[d] == REFLECT || bcv[d] == SPHERICAL )
+ ts[d]++ ;
+ }
+ }
+ ts += 2 * horizon ;
+ return ts ;
break ;
+ }
}
}
/// construct a bspline object with appropriate storage space to contain and process an array
/// of knot point data with shape core_shape. Depending on the strategy chosen and the other
/// parameters passed, more space than core_shape may be allocated. Once the bspline object
- /// is ready, it has to be filled with the knot point data and then the prefiltering needs
+ /// is ready, usually it is filled with the knot point data and then the prefiltering needs
/// to be done. This sequence assures that the knot point data are present in memory only once,
/// the prefiltering is done in-place. So the user can create the bspline, fill in data (like,
/// from a file), prefilter, and then evaluate.
///
+ /// alternatively, if the knot point data are already manifest elsewhere, they can be passed
+ /// to prefilter(). With this mode of operation, they are 'pulled in' during prefiltering.
+ ///
/// It's possible to pass in a view to an array providing space for the coefficients,
/// or even the coefficients themselves. This is done via the parameter _space. This has
/// to be an array of the same or larger shape than the container array would end up having
@@ -346,6 +376,16 @@ public:
/// The additional parameter 'headroom' is used to make the 'frame' even wider. This is
/// needed if the spline is to be 'shifted' up (evaluated as if it had been prefiltered
/// with a higher-degree prefilter) - see shift().
+ ///
+ /// While bspline objects allow very specific parametrization, most use cases won't use
+ /// parameters beyond the first few. The only mandatory parameter is, obviously, the
+ /// shape of the knot point data, the original data which the spline is built over.
+ /// This shape 'returns' as the bspline object's 'core' shape. If this is the only
+ /// parameter passed to the constructor, the resulting bspline object will be a
+ /// cubic b-spline with mirror boundary conditions, generated with an implicit
+ /// extrapolation scheme to a 'good' quality, no smoothing, and allocating it's own
+ /// storage for the coefficients, and the resuling bspline object will be suitable for
+ /// use with vspline's evaluation code.
// TODO: when bracing/framing is applied, we might widen the array size to a
// multiple of the Vc:Vector's Size for the given data type to have better-aligned
@@ -362,10 +402,10 @@ public:
bcv_type _bcv = bcv_type ( MIRROR ) , ///< boundary conditions and common default
prefilter_strategy _strategy = BRACED , ///< default strategy is the 'implicit' scheme
int _horizon = -1 , ///< width of frame for explicit scheme
- view_type _space = view_type() , ///< coefficient storage to 'adopt'
double _tolerance = -1.0 , ///< acceptable error (relative to unit pulse)
double _smoothing = 0.0 , ///< apply smoothing to data before prefiltering
- int headroom = 0 ///< additional headroom, for 'shifting'
+ int headroom = 0 , ///< additional headroom, for 'shifting'
+ view_type _space = view_type() ///< coefficient storage to 'adopt'
)
: core_shape ( _core_shape ) ,
spline_degree ( _spline_degree ) ,
@@ -400,6 +440,7 @@ public:
// first, calculate all the various shapes and sizes used internally
setup_metrics ( headroom ) ;
+ std::cout << "container shape: " << container_shape << std::endl ;
// now either adopt external memory or allocate memory for the coefficients
if ( _space.hasData() )
@@ -484,10 +525,11 @@ public:
bcv ,
strategy ,
horizon ,
- channel_container , // coefficient storage to 'adopt'
tolerance ,
smoothing ,
- 0 ) ;
+ 0 ,
+ channel_container // coefficient storage to 'adopt'
+ ) ;
} ;
/// prefilter converts the knot point data in the 'core' area into b-spline
@@ -665,20 +707,20 @@ public:
bc_code _bc = MIRROR , ///< boundary conditions and common default
prefilter_strategy _strategy = BRACED , ///< default strategy is the 'implicit' scheme
int _horizon = -1 , ///< width of frame for explicit scheme
- view_type _space = view_type() , ///< coefficient storage to 'adopt'
double _tolerance = -1.0 , ///< acceptable error (relative to unit pulse)
double _smoothing = 0.0 , ///< apply smoothing to data before prefiltering
- int headroom = 0 ///< additional headroom, for 'shifting'
+ int headroom = 0 , ///< additional headroom, for 'shifting'
+ view_type _space = view_type() ///< coefficient storage to 'adopt'
)
:bspline ( TinyVector < long , 1 > ( _core_shape ) ,
_spline_degree ,
bcv_type ( _bc ) ,
_strategy ,
_horizon ,
- _space ,
_tolerance ,
_smoothing ,
- headroom
+ headroom ,
+ _space
)
{
static_assert ( _dimension == 1 , "bspline: 1D constructor only usable for 1D splines" ) ;
diff --git a/common.h b/common.h
index 6d65172..76d98ea 100644
--- a/common.h
+++ b/common.h
@@ -73,7 +73,7 @@ template < class T >
using ET = typename vigra::ExpandElementResult < T > :: type ;
/// struct vector_traits is used throughout vspline to determine simdized
-/// data types. while inside the vspline code base, vsize, the number of
+/// data types. While inside the vspline code base, vsize, the number of
/// elementary type members in a simdized type, is kept variable in form of
/// the template argument _vsize, in code using vspline you'd expect to find
/// a program-wide definition deriving the number of elements from the
@@ -91,7 +91,7 @@ using ET = typename vigra::ExpandElementResult < T > :: type ;
/// typedef vspline::vector_traits<triplet_type,VSIZE>::type triplet_v_type ;
///
/// using a default _vsize of twice the elementary type's Vc::Vector's size
-/// works best on my system, but may not be optimal everywhere.
+/// works best on my system, but may not be optimal elsewhere.
template < class T ,
int _vsize = 2 * Vc::Vector < ET<T> > :: Size >
@@ -131,8 +131,8 @@ struct dimension_mismatch
: std::invalid_argument ( msg ) { } ;
} ;
-/// shape mismatch is the exception which is thrown if the shapes of an input array and
-/// an output array do not match.
+/// shape mismatch is the exception which is thrown if the shapes of
+/// an input array and an output array do not match.
struct shape_mismatch
: std::invalid_argument
@@ -141,7 +141,8 @@ struct shape_mismatch
: std::invalid_argument ( msg ) { } ;
} ;
-/// exception which is thrown if an opertion is requested which vspline does not support
+/// exception which is thrown if an opertion is requested which vspline
+/// does not support
struct not_supported
: std::invalid_argument
@@ -177,16 +178,16 @@ struct numeric_overflow
/// the core array.
typedef enum {
- MIRROR , ///< mirror on the bounds
+ MIRROR , ///< mirror on the bounds, so that f(-x) == f(x)
PERIODIC, ///< periodic boundary conditions
- REFLECT , ///< reflect, so that f(-1) == f(0)
- NATURAL, ///< natural boundary conditions
- CONSTANT , ///< used for framing, with explicit prefilter scheme
+ REFLECT , ///< reflect, so that f(-1) == f(0) (mirror between bounds)
+ NATURAL, ///< natural boundary conditions, f(-x) + f(x) == 2 * f(0)
+ CONSTANT , ///< clamp. used for framing, with explicit prefilter scheme
ZEROPAD , ///< used for boundary condition, bracing
IGNORE , ///< used for boundary condition, bracing
IDENTITY , ///< used as solver argument, mostly internal use
GUESS , ///< used with EXPLICIT scheme to keep margin errors low
- SPHERICAL , ///< use for spherical panoramas y axis
+ SPHERICAL , ///< use for spherical panoramas, y axis
} bc_code;
/// 'mapping' in vspline means the application of a functor to real coordinates,
diff --git a/eval.h b/eval.h
index a5e4a1e..2175b6e 100644
--- a/eval.h
+++ b/eval.h
@@ -359,8 +359,30 @@ struct average_weight_functor
*/
-/// class evaluator encodes evaluation of a B-spline.
-/// I have already hinted at the process used, but here it is again in a nutshell:
+/// class evaluator encodes evaluation of a B-spline. The evaluation relies on 'braced'
+/// coefficients, as they are normally provided by a vspline::bspline object (the exception
+/// being bspline objects created with UNBRACED or MANUAL strategy). While the most
+/// general constructor will accept a MultiArrayView to coefficients (including the necessary
+/// 'brace'), this will rarely be used, and an evaluator will be constructed from a bspline
+/// object. In the most trivial case there are only two thing which need to be done:
+///
+/// The specific type of evaluator has to be established by providing the relevant template
+/// arguments. Here, we need two types: the 'coordinate type' and the 'value type'.
+///
+/// - The coordinate type is encoded as a vigra::TinyVector of some real data type - if you're
+/// doing image processing, the typical type would be a vigra::TinyVector < float , 2 >.
+///
+/// - The value type has to be either an elementary real data type such as 'float' or 'double',
+/// or a vigra::TinyVector of such an elementary type. Other data types which can be handled
+/// by vigra's ExpandElementResult mechanism should also work. When processing colour images,
+/// your value type would typically be a vigra::TinyVector < float , 3 >.
+///
+/// With the evaluator's type established, an evaluator of this type can be constructed by
+/// passing a vspline::bspline object to the constructor. Naturally, the bspline object has
+/// to contain data of the same value type, and the spline has to have the same number of
+/// dimensions as the coordinate type.
+///
+/// I have already hinted at the evaluation process used, but here it is again in a nutshell:
/// The coordinate at which the spline is to be evaluated is split into it's integral part
/// and a remaining fraction. The integral part defines the location where a window from the
/// coefficient array is taken, and the fractional part defines the weights to use in calculating
@@ -371,28 +393,32 @@ struct average_weight_functor
/// to bring all the components together, which happens in class evaluator. The workhorse
/// code in the subclasses _eval and _v_eval takes care of performing the necessary operations
/// recursively over the dimensions of the spline.
-/// As ever, my code is bottom-up, avoiding forward declarations.
+///
/// The code in class evaluator begins with a sizeable constructor which sets up as much
/// as possible to support the evaluation code to do it's job as fast as possible. Next follows
/// the unvectorized code, finally the vectorized code.
-/// There is a great variety of overloads of class evaluator's eval() method available, because
+///
+/// There is a variety of overloads of class evaluator's eval() method available, because
/// class evaluator inherits from vspline::unary_functor.
+///
/// The evaluation strategy is to have all dependencies of the evaluation except for the actual
/// coordinates taken care of by the constructor - and immutable for the evaluator's lifetime.
/// The resulting object has no state which is modified after construction, making it thread-safe.
/// It also constitutes a 'pure' function in a functional-programming sense, because it has
/// no mutable state and no side-effects, as can be seen by the fact that the eval methods
/// are all marked const.
-/// The eval() overloads also
-/// form a hierarchy, as evaluation progresses from accepting unsplit real coordinates to
-/// split coordinates and finally offsets and weights. This allows calling code to handle
-/// parts of the delegation hierarchy itself, only using class evaluator at a specific level.
+///
+/// The eval() overloads form a hierarchy, as evaluation progresses from accepting unsplit real
+/// coordinates to split coordinates and finally offsets and weights. This allows calling code to
+/// handle parts of the delegation hierarchy itself, only using class evaluator at a specific level.
+///
/// By providing the evaluation in this way, it becomes easy for calling code to integrate
/// the evaluation into more complex functors. Consider, for example, code
/// which generates coordinates with a functor, then evaluates a b-spline at these coordinates,
/// and finally subjects the resultant values to some postprocessing. All these processing
/// steps can be bound into a single functor, and the calling code can be reduced to polling
/// this functor until it has obtained the desired number of output values.
+///
/// While the 'unspecialized' evaluator will try and do 'the right thing' by using general
/// purpose code fit for all eventualities, for time-critical operation there are two
/// specializations which can be used to make the code faster:
@@ -429,11 +455,11 @@ template < typename _coordinate_type , // nD real coordinate
int specialize = -1 , // specialize for degree 0 or 1
int evenness = -1 , // specialize for raw mapping, even or odd spline
#ifdef USE_VC
- int _vsize = simdized_ele_type < _value_type > :: Size
+ int _vsize = simdized_ele_type < _value_type > :: Size // nr. of vector elements
#else
int _vsize = 1
#endif
- > // nr. of vector elements
+ >
class evaluator
: public unary_functor < _coordinate_type , _value_type , _vsize >
{
@@ -554,8 +580,13 @@ public:
}
/// this constructor is the most flexible variant and will ultimately be called by all other
- /// constructor overloads. class evaluator is coded to be (thread)safe as a functor: all state
- /// (in terms of member data) is fixed at construction time and remains constant afterwards.
+ /// constructor overloads. This constructor will not usually be called directly - rather
+ /// use the overloads taking vspline::bspline objects. The constructor takes four arguments:
+ /// - a vigra::MultiArrayView to (braced) coefficients
+ /// - a functor used to treat incoming coordinates so that they are either in range or rejected
+ /// - the degree of the spline (3 == cubic spline)
+ /// - specification of the desired derivative of the spline, defaults to 0 (plain evaluation).
+
// TODO: It would be desirable to make this explicit for all member data, but some of them need
// more involved initialization than the one-liners after the colon... so I'm a bit sloppy here.
// in the calling code, it's fine to use a const evaluator, that sorts it as well.
@@ -635,10 +666,14 @@ public:
/// simplified constructors from a bspline object
/// when using the higher-level interface to vspline's facilities - via class bspline -
- /// class bspline objects already have a fair amount of metadata which are perfectly
- /// sufficient to create a suitable evaluator object. If evaluation of the spline's value
- /// (no derivative spec) is wanted, all this constructor takes is a const reference
- /// to a bspline object, which is about as simple as it can get:
+ /// the bspline object provides the coefficients. The derivative specification is passed
+ /// in just as for the general constructor. In this constructor overload, we don't
+ /// require a functor for coordinate treatment, but instead accept a vigra::TinyVector
+ /// of 'mapping codes' - the functor is created according to this specification. This is
+ /// less flexible than passing the functor itself, but sufficient in most cases. Per
+ /// default, mapping mode MAP_REJECT will be used: if out-of-bounds coordinates come in,
+ /// an exception is thrown. Note how both the derivative specification and the mapping
+ /// code can be individually chosen for each axis.
typedef vigra::TinyVector < vspline::map_code , dimension > mcv_type ;
@@ -656,7 +691,10 @@ public:
} ;
/// constructor variant accepting a common derivative specification and mapping code
- /// for all axes of the spline
+ /// for all axes of the spline. This constructor has default argments for both the
+ /// derivative specification and the mapping code, so if an evaluator is constructed
+ /// in the simplest way possible by passing nothing but a vspline::bspline object,
+ /// this is the constructor which will be called.
evaluator ( const bspline < value_type , dimension > & bspl ,
int _derivative = 0 ,
@@ -672,28 +710,15 @@ public:
vspline::map_code mc )
: evaluator ( bspl , derivative_spec_type ( 0 ) , mcv_type ( mc ) )
{ } ;
-
+
+ /// get_mapping returns the functor used to pre-treat incoming coordinates.
+ /// This is mainly useful for testing
+
const nd_mapping_type& get_mapping() const
{
return mmap ;
}
-#ifdef USE_VC
-
- // provide interleave indices for nD and multichannel types
-
- static ic_v nd_interleave ( int d )
- {
- return ic_v::IndexesFromZero() * ic_type ( dimension ) + ic_type ( d ) ;
- }
-
- static ic_v mc_interleave ( int ch )
- {
- return ic_v::IndexesFromZero() * ic_type ( channels ) + ic_type ( ch ) ;
- }
-
-#endif
-
/// obtain_weights calculates the weights to be applied to a section of the coefficients from
/// the fractional parts of the split coordinates. What is calculated here is the evaluation
/// of the spline's basis function at dx, dx+1 , dx+2..., but doing it naively is computationally
@@ -1521,37 +1546,39 @@ public:
eval ( select , tune , result ) ;
}
- /// special handling of 1D splines
-
- void eval ( const rc_type * const pmc , // pointer to vsize 1D coordinates
- value_type * result ) const // pointer to vsize result values
- {
- static_assert ( dimension == 1 ,
- "this eval variant is intended for 1D splines only" ) ;
-
- in_v input ;
- out_v v_result ;
-
- // gather the incoming (interleaved) coordinates
-
- input[0].load ( (const rc_type* const)pmc ) ;
-
- // call eval() for vectorized data
-
- eval ( input , v_result ) ;
-
- // and deposit it in the memory the caller provides
-
- if ( channels == 1 )
- {
- v_result[0].store ( (ele_type*)result ) ;
- }
- else
- {
- for ( int ch = 0 ; ch < channels ; ch++ )
- v_result[ch].scatter ( (ele_type*)result , mc_interleave(ch) ) ;
- }
- }
+// /// special handling of 1D splines
+//
+// void eval ( const rc_type * const pmc , // pointer to vsize 1D coordinates
+// value_type * result ) const // pointer to vsize result values
+// {
+// static_assert ( dimension == 1 ,
+// "this eval variant is intended for 1D splines only" ) ;
+//
+// in_v input ;
+// out_v v_result ;
+//
+// // gather the incoming (interleaved) coordinates
+//
+// input[0].load ( (const rc_type* const)pmc ) ;
+//
+// // call eval() for vectorized data
+//
+// eval ( input , v_result ) ;
+//
+// // and deposit it in the memory the caller provides
+//
+// if ( channels == 1 )
+// {
+// v_result[0].store ( (ele_type*)result ) ;
+// }
+// else
+// {
+// for ( int ch = 0 ; ch < channels ; ch++ )
+// v_result[ch].scatter ( (ele_type*)result ,
+// ic_v::IndexesFromZero() * ic_type ( channels )
+// + ic_type ( ch ) ) ;
+// }
+// }
#endif // USE_VC
diff --git a/example/gradient.cc b/example/gradient.cc
index e059cf7..3df63c7 100644
--- a/example/gradient.cc
+++ b/example/gradient.cc
@@ -67,7 +67,6 @@ int main ( int argc , char * argv[] )
bcv_type ( vspline::NATURAL ) , // natural boundary conditions
vspline::BRACED , // implicit scheme, bracing coeffs
-1 , // default, not using EXPLICIT
- view_type() , // empty view
0.0 ) ; // tolerance 0.0 for this test!
// get a view to the bspline's core, to fill it with data
diff --git a/example/pano_extract.cc b/example/pano_extract.cc
index 860ac52..b423a2d 100644
--- a/example/pano_extract.cc
+++ b/example/pano_extract.cc
@@ -799,7 +799,7 @@ void process_image ( const char * name ,
#endif
typedef evaluator < coordinate_type , pixel_type > eval_type ;
- eval_type ev ( bspl ) ;
+ eval_type ev ( bspl , { 0 , 0 } , { MAP_PERIODIC , MAP_REFLECT } ) ;
typedef projector < eval_type ,
tf_spherical_rectilinear<float> ,
@@ -819,7 +819,8 @@ void process_image ( const char * name ,
// bracing the data, since we're only using a degree 1 spline (linear interpolation)
// on the alpha channel.
- bspline < float , 2 > bspl_alpha ( core_shape , 1 , bcv , BRACED , -1 , alpha_channel ) ;
+ bspline < float , 2 > bspl_alpha ( core_shape , 1 , bcv , BRACED , -1 ,
+ -1.0 , 0.0 , 0 , alpha_channel ) ;
bspl_alpha.prefilter() ;
// swap data into alpha_result (fill it with life, it was created empty)
@@ -831,7 +832,7 @@ void process_image ( const char * name ,
// create an evaluator for bspl_alpha
typedef evaluator < coordinate_type , float > alpha_ev_type ;
- alpha_ev_type ev_alpha ( bspl_alpha ) ;
+ alpha_ev_type ev_alpha ( bspl_alpha , { 0 , 0 } , { MAP_PERIODIC , MAP_REFLECT } ) ;
typedef projector < alpha_ev_type ,
tf_spherical_rectilinear<float> ,
diff --git a/filter.h b/filter.h
index 6bbf0ad..59bbd86 100644
--- a/filter.h
+++ b/filter.h
@@ -1004,6 +1004,11 @@ gather ( const source_type * source ,
{
while ( count-- )
{
+// while Vc hasn't yet implemented gathering using intrinsices (from AVX2)
+// I played with using tem directly to see if I could get better performance.
+// So far it looks like as if the prefiltering code doesn't benefit.
+// __m256i ix = _mm256_loadu_si256 ( (const __m256i *)&(indexes) ) ;
+// __m256 fv = _mm256_i32gather_ps (source, ix, 4) ;
simdized_source_type x ( source , indexes ) ;
* target = target_type ( x ) ;
source += stride ;
@@ -1064,8 +1069,11 @@ scatter ( const source_type * source ,
}
}
-/// aggregating_filter keeps a buffer of vector-aligned memory, which it fills with
+/// aggregating_filter keeps a buffer of vector-aligned memory, which it fills from
/// vsize 1D subarrays of the source array which are collinear to the processing axis.
+/// Note that the vectorization, or aggregation axis is *orthogonal* to the processing
+/// axis, since the adjacency of neighbours along the processing axis needs to be
+/// preserved for filtering.
/// The buffer is then submitted to vectorized forward-backward recursive filtering
/// and finally stored back to the corresponding memory area in target, which may
/// be the same as source, in which case the operation is seemingly performed
@@ -1460,7 +1468,7 @@ template < typename input_array_type , ///< type of array with knot point data
class filter_1d
{
public:
- void operator() ( input_array_type &input , ///< source data. the routine can also operate in-place
+ void operator() ( input_array_type &input , ///< source data. can also operate in-place,
output_array_type &output , ///< where input == output.
int axis ,
double gain ,
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/vspline.git
More information about the debian-science-commits
mailing list