[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