vspline 1.1.0
Generic C++11 Code for Uniform B-Splines
n_shift.cc
Go to the documentation of this file.
1/************************************************************************/
2/* */
3/* vspline - a set of generic tools for creation and evaluation */
4/* of uniform b-splines */
5/* */
6/* Copyright 2017 - 2023 by Kay F. Jahnke */
7/* */
8/* Permission is hereby granted, free of charge, to any person */
9/* obtaining a copy of this software and associated documentation */
10/* files (the "Software"), to deal in the Software without */
11/* restriction, including without limitation the rights to use, */
12/* copy, modify, merge, publish, distribute, sublicense, and/or */
13/* sell copies of the Software, and to permit persons to whom the */
14/* Software is furnished to do so, subject to the following */
15/* conditions: */
16/* */
17/* The above copyright notice and this permission notice shall be */
18/* included in all copies or substantial portions of the */
19/* Software. */
20/* */
21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
23/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
24/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
25/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
26/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
27/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
28/* OTHER DEALINGS IN THE SOFTWARE. */
29/* */
30/************************************************************************/
31
32/// \file n_shift.cc
33///
34/// \brief fidelity test
35///
36/// This is a test to see how much a signal degrades when it is submitted
37/// to a sequence of operations:
38///
39/// - create a b-spline over the signal
40/// - evaluate the spline at unit-spaced locations with an arbitrary offset
41/// - yielding a shifted signal, for which the process is repeated
42///
43/// Finally, a last shift is performed which samples the penultimate version
44/// of the signal at points coinciding with coordinates 0...N-1 of the
45/// original signal. This last iteration should ideally recreate the
46/// original sequence.
47///
48/// The test is done with a periodic signal to avoid margin effects.
49/// The initial sequence is created by evaluating a periodic high-degree
50/// b-spline of half the size at steps n/2. This way we start out
51/// with a signal with low high-frequency content - a signal which can
52/// be approximated well with a b-spline. Optionally, the supersampling
53/// factor can be passed on the command line to experiment with different
54/// values than the default of 2. Supersampling factors should be whole
55/// numbers (halves also work, but less precise) - so that the knot
56/// points of the original signal coincide with knot points of the
57/// supersampled signal. This way, every interval contains a partial
58/// polynomial with no discontinuities, and the spline can faithfully
59/// represent the signal.
60/// Both this program and bls.cpp show that there is a significant
61/// threshold involved: if the initial signal contains *no frequencies
62/// above half the Nyquist frequency*, a b-spline of sufficiently
63/// large degree becomes *immune to resampling*. See also bls.cpp,
64/// which produces test signals using IFFT, which is a clearer way
65/// of excluding part of the frequency spectrum, because the signal
66/// generated by IFT can - by definition - only contain frequencies
67/// which have non-zero corresponding coefficients in the FT.
68///
69/// compile with: clang++ -pthread -O3 -std=c++11 n_shift.cc -o n_shift
70///
71/// invoke like: n_shift 17 500
72
73#include <iostream>
74#include <random>
75#include <vigra/accumulator.hxx>
76#include <vigra/multi_math.hxx>
77#include <vspline/vspline.h>
78
79int main ( int argc , char * argv[] )
80{
81 if ( argc < 3 )
82 {
83 std::cerr << "pass the spline's degree, the number of iterations"
84 << std::endl
85 << "and optionally the supersampling factor"
86 << std::endl ;
87 exit ( -1 ) ;
88 }
89
90 int degree = std::atoi ( argv[1] ) ;
91
92 assert ( degree >= 0 && degree <= vspline_constants::max_degree ) ;
93
94 int iterations = 1 + std::atoi ( argv[2] ) ;
95
96 const int sz = 1024 ;
97
98 long double widen = 2.0 ;
99
100 if ( argc > 3 )
101 widen = atof ( argv[3] ) ;
102
103 assert ( widen >= 1.0 ) ;
104
105 int wsz = sz * widen ;
106
107 vigra::MultiArray < 1 , long double > original ( wsz ) ;
108 vigra::MultiArray < 1 , long double > target ( wsz ) ;
109
110 // we start out by filling the first bit of 'original' with random data
111 // between -1 and 1
112
113 std::random_device rd ;
114 std::mt19937 gen ( rd() ) ;
115// gen.seed ( 1 ) ; // if desired, level playing field
116 std::uniform_real_distribution<> dis ( -1 , 1 ) ;
117 for ( int x = 0 ; x < sz ; x++ )
118 original [ x ] = dis ( gen ) ;
119
120 // create the bspline object to produce the data we'll work with
121
122 vspline::bspline < long double , // the spline's data type
123 1 > // one dimension only
124 bsp ( sz , // sz values
125 20 , // high degree for smoothness
126 vspline::PERIODIC , // periodic boundary conditions
127 0.0 ) ; // no tolerance
128
129 vigra::MultiArrayView < 1 , long double > initial
130 ( vigra::Shape1(sz) , original.data() ) ;
131
132 // pull in the data while prefiltering
133
134 bsp.prefilter ( initial ) ;
135
136 // create an evaluator to obtain interpolated values
137
139
140 ev_type ev ( bsp ) ; // from the bspline object we just made
141
142 // now we evaluate at 1/widen steps, into target
143
144 for ( int x = 0 ; x < wsz ; x++ )
145 target [ x ] = ev ( (long double) ( x ) / (long double) ( widen ) ) ;
146
147 // we take this as our original signal. Since this is a sampling
148 // of a periodic signal (the spline in bsp) representing a full
149 // period, we assume that a b-spline over this signal will, within
150 // the spline's capacity, approximate the signal in 'original'.
151 // what we want to see is how sampling at offsetted positions
152 // and recreating a spline over the offsetted signal will degrade
153 // the signal with different-degree b-splines and different numbers
154 // of iterations.
155
156 original = target ;
157
158 // now we set up the working spline
159
160 vspline::bspline < long double , // spline's data type
161 1 > // one dimension
162 bspw ( wsz , // wsz values
163 degree , // degree as per command line
164 vspline::PERIODIC , // periodic boundary conditions
165 0.0 ) ; // no tolerance
166
167 // we pull in the working data we've just generated
168
169 bspw.prefilter ( original ) ;
170
171 // and set up the evaluator for the test
172
173 ev_type evw ( bspw ) ;
174
175 // we want to map the incoming coordinates into the defined range.
176 // Since we're using a periodic spline, the range is from 0...N,
177 // rather than 0...N-1 for non-periodic splines
178
179 auto gate = vspline::periodic ( 0.0L , (long double)(wsz) ) ;
180
181 // now we do a bit of functional programming.
182 // we chain gate and evaluator:
183
184 auto periodic_ev = gate + evw ;
185
186 // we cumulate the offsets so we can 'undo' the cumulated offset
187 // in the last iteration
188
189 long double cumulated_offset = 0.0 ;
190
191 for ( int n = 0 ; n < iterations ; n++ )
192 {
193 using namespace vigra::multi_math ;
194 using namespace vigra::acc;
195
196 // use a random, largish offset (+/- 1000). any offset
197 // will do, since we have a periodic gate, mapping the
198 // coordinates for evaluation into the spline's range
199
200 long double offset = 1000.0 * dis ( gen ) ;
201
202 // with the last iteration, we shift back to the original
203 // 0-based locations. This last shift should recreate the
204 // original signal as best as a spline of this degree can
205 // do after so many iterations.
206
207 if ( n == iterations - 1 )
208 offset = - cumulated_offset ;
209
210 cumulated_offset += offset ;
211
212 if ( n > ( iterations - 10 ) )
213 std::cout << "iteration " << n << " offset " << offset
214 << " cumulated offset " << cumulated_offset << std::endl ;
215
216 // we evaluate the spline at unit-stepped offsetted locations,
217 // so, 0 + offset , 1 + offset ...
218 // in the last iteration, this should ideally reproduce the original
219 // signal.
220
221 for ( int x = 0 ; x < wsz ; x++ )
222 {
223 auto arg = x + offset ;
224 target [ x ] = periodic_ev ( arg ) ;
225 }
226
227 // now we create a new spline over target, reusing bspw
228 // note how this merely changes the coefficients of the spline,
229 // the container for the coefficients is reused, and therefore
230 // the evaluator (evw) will look at the new set of coefficients.
231 // So we don't need to create a new evaluator.
232
233 bspw.prefilter ( target ) ;
234
235 // to convince ourselves that we really are working on a different
236 // sampling of the signal signal - and to see how close we get to the
237 // original signal after n iterations, when we use an offset to get
238 // the sampling locations back to 0, 1, ...
239
240 vigra::MultiArray < 1 , long double > error_array
241 ( vigra::multi_math::squaredNorm ( target - original ) ) ;
242
243 AccumulatorChain < long double , Select < Mean, Maximum > > ac ;
244 extractFeatures ( error_array.begin() , error_array.end() , ac ) ;
245
246 if ( n > ( iterations - 10 ) )
247 {
248 if ( n == iterations - 1 )
249 std::cout << "final result, evaluating at original unit steps"
250 << std::endl ;
251 std::cout << "signal difference Mean: "
252 << sqrt(get<Mean>(ac)) << std::endl;
253 std::cout << "signal difference Maximum: "
254 << sqrt(get<Maximum>(ac)) << std::endl;
255 }
256 }
257}
vspline::evaluator< coordinate_type, float > ev_type
Definition: ca_correct.cc:121
class evaluator encodes evaluation of a spline-like object. This is a generalization of b-spline eval...
Definition: eval.h:1718
int main(int argc, char *argv[])
Definition: n_shift.cc:79
vspline::periodic_gate< rc_type, _vsize > periodic(rc_type lower, rc_type upper)
factory function to create a periodic_gate type functor given a lower and upper limit for the allowed...
Definition: map.h:449
@ PERIODIC
Definition: common.h:73
class bspline now builds on class bspline_base, adding coefficient storage, while bspline_base provid...
Definition: bspline.h:499
includes all headers from vspline (most of them indirectly)