1515 */
1616public class FastGaussCalculator
1717{
18- private final Parameters fc ;
19- // we will utilize ring buffers whose current positions will be driven
20- // by the currently processed index inside the input array -- we will use
21- // bit masking to achieve fast the effect of the operation modulo, furthermore
22- // it does not suffer from sign effect: -5 % 8 = -5 in Java; I need it be +3
23- // to make it work nicely for the ring buffer (which cannot have negative indices)
24- //
25- // remember current plus last two results for every yk-term (there's M of them),
26- // so we need either 3*3 or 3*4=12 values => 4 bits = capacity for 16 values,
27- //
28- // actually, we will use 4 ring sub-buffers (each of length 4, 2 bits) for the
29- // individual yks...: y1, y3, y5, y7 (the order in which they are touched during
30- // the computation of the filter)
31- //
32- private final double [] yk0 = new double [ 4 ];
33-
34- private final double [] yk1 = new double [ 4 ];
35-
36- private final double [] yk2 = new double [ 4 ];
37-
38- private final double [] yk3 = new double [ 4 ];
39-
40- private int x ;
18+ private double [] y_n = new double [ 4 ]; // stores y values for k=1,3,5,7 of the current step
19+
20+ private double [] y_n_minus_1 = new double [ 4 ]; // stores the y_n value of the previous step
21+
22+ private double [] y_n_minus_2 = new double [ 4 ]; // stores the y_n value of the step before the previous step
23+
24+ private final double [] nk_2 ;
25+
26+ private final double [] dk_1 ;
27+
28+ private final int M ;
4129
4230 public FastGaussCalculator ( final Parameters fc )
4331 {
44- this .fc = fc ;
32+ nk_2 = fc .nk_2 ;
33+ dk_1 = fc .dk_1 ;
34+ M = fc .M ;
4535 }
4636
4737 public void initialize ( final double boundaryValue )
4838 {
4939 //calculate yk that one would get on constant signal of 1.0
50- //(Vlado's invention by solving eq. (35) assuming yk_n = yk_n-1 = yk_n-2)
51- final double y1_mN = 2.0 * fc .nk_2 [ 0 ] / ( fc .dk_1 [ 0 ] + 2.0 );
52- final double y3_mN = 2.0 * fc .nk_2 [ 1 ] / ( fc .dk_1 [ 1 ] + 2.0 );
53- final double y5_mN = 2.0 * fc .nk_2 [ 2 ] / ( fc .dk_1 [ 2 ] + 2.0 );
54- final double y7_mN = fc .M == 4 ? 2.0 * fc .nk_2 [ 3 ] / ( fc .dk_1 [ 3 ] + 2.0 ) : 0.0 ;
55-
56- //calculate yk that one would get on constant signal of array[0],
57- //and use this value for yk[x-2] and yk[x-1] when x=-Nm1
58- x = -1 ;
59- yk0 [ x & 3 ] = yk0 [ ( x - 1 ) & 3 ] = boundaryValue * y1_mN ;
60- yk1 [ x & 3 ] = yk1 [ ( x - 1 ) & 3 ] = boundaryValue * y3_mN ;
61- yk2 [ x & 3 ] = yk2 [ ( x - 1 ) & 3 ] = boundaryValue * y5_mN ;
62- yk3 [ x & 3 ] = yk3 [ ( x - 1 ) & 3 ] = boundaryValue * y7_mN ;
40+ //(Vlado's invention by solving eq. (35) assuming y_n = y_n_minus_1 = y_n_minus_2)
41+
42+ for ( int i = 0 ; i < M ; i ++ )
43+ y_n [ i ] = y_n_minus_1 [ i ] = boundaryValue * 2.0 * nk_2 [ i ] / ( dk_1 [ i ] + 2.0 );
6344 }
6445
6546 public void update ( final double tmp )
6647 {
67- ++x ;
68- yk0 [ x & 3 ] = fc .nk_2 [ 0 ] * tmp - fc .dk_1 [ 0 ] * yk0 [ ( x - 1 ) & 3 ] - yk0 [ ( x - 2 ) & 3 ];
69- yk1 [ x & 3 ] = fc .nk_2 [ 1 ] * tmp - fc .dk_1 [ 1 ] * yk1 [ ( x - 1 ) & 3 ] - yk1 [ ( x - 2 ) & 3 ];
70- yk2 [ x & 3 ] = fc .nk_2 [ 2 ] * tmp - fc .dk_1 [ 2 ] * yk2 [ ( x - 1 ) & 3 ] - yk2 [ ( x - 2 ) & 3 ];
71- yk3 [ x & 3 ] = fc .M == 4 ? fc .nk_2 [ 3 ] * tmp - fc .dk_1 [ 3 ] * yk3 [ ( x - 1 ) & 3 ] - yk3 [ ( x - 2 ) & 3 ] : 0 ;
48+ double [] t = y_n_minus_2 ;
49+ y_n_minus_2 = y_n_minus_1 ;
50+ y_n_minus_1 = y_n ;
51+ y_n = t ;
52+ y_n [ 0 ] = nk_2 [ 0 ] * tmp - dk_1 [ 0 ] * y_n_minus_1 [ 0 ] - y_n_minus_2 [ 0 ];
53+ y_n [ 1 ] = nk_2 [ 1 ] * tmp - dk_1 [ 1 ] * y_n_minus_1 [ 1 ] - y_n_minus_2 [ 1 ];
54+ y_n [ 2 ] = nk_2 [ 2 ] * tmp - dk_1 [ 2 ] * y_n_minus_1 [ 2 ] - y_n_minus_2 [ 2 ];
55+ y_n [ 3 ] = nk_2 [ 3 ] * tmp - dk_1 [ 3 ] * y_n_minus_1 [ 3 ] - y_n_minus_2 [ 3 ];
7256 }
7357
7458 public double getValue ()
7559 {
76- return yk0 [ x & 3 ] + yk1 [ x & 3 ] + yk2 [ x & 3 ] + yk3 [ x & 3 ];
60+ return y_n [ 0 ] + y_n [ 1 ] + y_n [ 2 ] + y_n [ 3 ];
7761 }
7862
7963 /**
@@ -100,7 +84,7 @@ public static class Parameters
10084 /**
10185 * the width of the filter
10286 */
103- public int N = 0 ;
87+ public final int N ;
10488
10589 /**
10690 * the filtering coefficients
@@ -116,36 +100,31 @@ public static class Parameters
116100
117101 public static Parameters fast ( final double sigma )
118102 {
119- return new Parameters ( 3 , sigma );
103+ return new Parameters ( 3 , sigma , round ( 3.2795 * sigma + 0.25460 ) );
120104 }
121105
122106 public static Parameters exact ( final double sigma )
123107 {
124- return new Parameters ( 4 , sigma );
108+ return new Parameters ( 4 , sigma , round ( 3.7210 * sigma + 0.20157 ) );
125109 }
126110
127111 /**
128112 * Construct with the same accuracy as the Gauss1D for which you want to
129113 * use it.
130114 */
131- private Parameters ( final int _M , final double sigma )
115+ private Parameters ( final int _M , final double sigma , final int N )
132116 {
133117 if ( sigma <= 0 )
134118 throw new IllegalArgumentException ( "Sigma must be positive." );
135119 M = ( _M == 3 || _M == 4 ) ? _M : 3 ;
136120 nk_2 = new double [ 4 ];
137121 dk_1 = new double [ 4 ];
138122
139- // eq. (57), the filter width
140- final double N1 = ( int ) ( ( M == 3 ? 3.2795 * sigma + 0.25460 // M==3
141- : 3.7210 * sigma + 0.20157 // M==4
142- ) + 0.5 );
143-
144123 // Table I, 1st/top objective
145- final double omega_1 = 1.0 * Math .PI / ( 2.0 * N1 );
146- final double omega_3 = 3.0 * Math .PI / ( 2.0 * N1 );
147- final double omega_5 = 5.0 * Math .PI / ( 2.0 * N1 );
148- final double omega_7 = 7.0 * Math .PI / ( 2.0 * N1 );
124+ final double omega_1 = 1.0 * Math .PI / ( 2.0 * N );
125+ final double omega_3 = 3.0 * Math .PI / ( 2.0 * N );
126+ final double omega_5 = 5.0 * Math .PI / ( 2.0 * N );
127+ final double omega_7 = 7.0 * Math .PI / ( 2.0 * N );
149128
150129 // eq. (37) i=1,3,5,7
151130 final double p_1 = 1.0 / Math .tan ( 0.5 * omega_1 );
@@ -161,10 +140,10 @@ private Parameters( final int _M, final double sigma )
161140
162141 // approximate rho_i:
163142 // eq. (50) i=1,3,5,7
164- final double rho_1 = Math .exp ( -0.5 * sigma * sigma * omega_1 * omega_1 ) / N1 ;
165- final double rho_3 = Math .exp ( -0.5 * sigma * sigma * omega_3 * omega_3 ) / N1 ;
166- final double rho_5 = Math .exp ( -0.5 * sigma * sigma * omega_5 * omega_5 ) / N1 ;
167- final double rho_7 = Math .exp ( -0.5 * sigma * sigma * omega_7 * omega_7 ) / N1 ;
143+ final double rho_1 = Math .exp ( -0.5 * sigma * sigma * omega_1 * omega_1 ) / N ;
144+ final double rho_3 = Math .exp ( -0.5 * sigma * sigma * omega_3 * omega_3 ) / N ;
145+ final double rho_5 = Math .exp ( -0.5 * sigma * sigma * omega_5 * omega_5 ) / N ;
146+ final double rho_7 = Math .exp ( -0.5 * sigma * sigma * omega_7 * omega_7 ) / N ;
168147 /*
169148 //accurate rho_i:
170149 // eq. (50) i=1,3,5,7
@@ -202,6 +181,8 @@ private Parameters( final int _M, final double sigma )
202181
203182 // get beta_k
204183 double beta_1 , beta_3 ;
184+ double gamma_2 = N * N - sigma * sigma ;
185+ double gamma_3 = C_15 * rho_1 + C_35 * rho_3 + rho_5 ;
205186 if ( M == 3 )
206187 {
207188 //build 6 minor 2x2 matrices to invA by excluding i-th row and j-th column from invA,
@@ -222,10 +203,8 @@ private Parameters( final int _M, final double sigma )
222203 final double det_invA = p_1 * D11 - r_1 * D21 + C_15 * D31 ;
223204
224205 // eq. (53), only first two rows of matrix A_N
225- beta_1 = ( +D11 - ( N1 * N1 - sigma * sigma ) * D21
226- + ( C_15 * rho_1 + C_35 * rho_3 + rho_5 ) * D31 ) / det_invA ;
227- beta_3 = ( -D12 + ( N1 * N1 - sigma * sigma ) * D22
228- - ( C_15 * rho_1 + C_35 * rho_3 + rho_5 ) * D32 ) / det_invA ;
206+ beta_1 = ( +D11 - gamma_2 * D21 + gamma_3 * D31 ) / det_invA ;
207+ beta_3 = ( -D12 + gamma_2 * D22 - gamma_3 * D32 ) / det_invA ;
229208 }
230209 else
231210 { // M == 4
@@ -239,7 +218,7 @@ private Parameters( final int _M, final double sigma )
239218
240219 final double D11 = r_3 - r_5 * C_35 - r_7 * C_37 ;
241220 final double D21 = p_3 - p_5 * C_35 - p_7 * C_37 ;
242- final double D31 = p_3 * r_5 + p_5 * r_7 + C_37 - p_5 * r_3 - p_7 * r_5 * C_37 ;
221+ final double D31 = p_3 * r_5 + p_5 * r_7 * C_37 - p_5 * r_3 - p_7 * r_5 * C_37 ;
243222 final double D41 = p_5 * r_7 * C_35 + p_7 * r_3 - p_3 * r_7 - p_7 * r_5 * C_35 ;
244223
245224 final double D12 = r_1 - r_5 * C_15 - r_7 * C_17 ;
@@ -250,10 +229,9 @@ private Parameters( final int _M, final double sigma )
250229 final double det_invA = p_1 * D11 - r_1 * D21 + C_15 * D31 - C_17 * D41 ;
251230
252231 // eq. (53), only first two rows of matrix A_N
253- beta_1 = ( +D11 - ( N1 * N1 - sigma * sigma ) * D21 + ( C_15 * rho_1 + C_35 * rho_3 + rho_5 ) * D31
254- - ( C_17 * rho_1 + C_37 * rho_3 + rho_7 ) * D41 ) / det_invA ;
255- beta_3 = ( -D12 + ( N1 * N1 - sigma * sigma ) * D22 - ( C_15 * rho_1 + C_35 * rho_3 + rho_5 ) * D32
256- + ( C_17 * rho_1 + C_37 * rho_3 + rho_7 ) * D42 ) / det_invA ;
232+ double gamma4 = C_17 * rho_1 + C_37 * rho_3 + rho_7 ;
233+ beta_1 = ( +D11 - gamma_2 * D21 + gamma_3 * D31 - gamma4 * D41 ) / det_invA ;
234+ beta_3 = ( -D12 + gamma_2 * D22 - gamma_3 * D32 + gamma4 * D42 ) / det_invA ;
257235 }
258236
259237 // eq. (49), since I didn't want to continue building A_N to be used in eq. (53),
@@ -263,25 +241,31 @@ private Parameters( final int _M, final double sigma )
263241 final double beta_7 = rho_7 + C_17 * ( rho_1 - beta_1 ) + C_37 * ( rho_3 - beta_3 );
264242
265243 // fill the output container FilteringCoeffs
266- N = ( int ) N1 ;
244+ this . N = N ;
267245
268- nk_2 [ 0 ] = -beta_1 * Math .cos ( omega_1 * ( N1 + 1.0 ) );
269- nk_2 [ 1 ] = -beta_3 * Math .cos ( omega_3 * ( N1 + 1.0 ) );
270- nk_2 [ 2 ] = -beta_5 * Math .cos ( omega_5 * ( N1 + 1.0 ) );
246+ nk_2 [ 0 ] = -beta_1 * Math .cos ( omega_1 * ( N + 1 ) );
247+ nk_2 [ 1 ] = -beta_3 * Math .cos ( omega_3 * ( N + 1 ) );
248+ nk_2 [ 2 ] = -beta_5 * Math .cos ( omega_5 * ( N + 1 ) );
271249
272250 dk_1 [ 0 ] = -2.0 * Math .cos ( omega_1 );
273251 dk_1 [ 1 ] = -2.0 * Math .cos ( omega_3 );
274252 dk_1 [ 2 ] = -2.0 * Math .cos ( omega_5 );
275253
276254 if ( M == 4 )
277255 {
278- nk_2 [ 3 ] = -beta_7 * Math .cos ( omega_7 * ( N1 + 1.0 ) );
256+ nk_2 [ 3 ] = -beta_7 * Math .cos ( omega_7 * ( N + 1 ) );
279257 dk_1 [ 3 ] = -2.0 * Math .cos ( omega_7 );
280258 // NB: array length was guarded at the beginning of this function
281259 }
282260
283261 // declare to what sigma the coefficients belong to
284262 Sigma = sigma ;
285263 }
264+
265+ }
266+
267+ private static int round ( double value )
268+ {
269+ return ( int ) ( value + 0.5 );
286270 }
287271}
0 commit comments