1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
11 #include "base/basictypes.h"
12 #include "base/logging.h"
13 #include "base/time/time.h"
14 #include "skia/ext/convolver.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "third_party/skia/include/core/SkBitmap.h"
17 #include "third_party/skia/include/core/SkColorPriv.h"
18 #include "third_party/skia/include/core/SkRect.h"
19 #include "third_party/skia/include/core/SkTypes.h"
25 // Fills the given filter with impulse functions for the range 0->num_entries.
26 void FillImpulseFilter(int num_entries
, ConvolutionFilter1D
* filter
) {
28 for (int i
= 0; i
< num_entries
; i
++)
29 filter
->AddFilter(i
, &one
, 1);
32 // Filters the given input with the impulse function, and verifies that it
34 void TestImpulseConvolution(const unsigned char* data
, int width
, int height
) {
35 int byte_count
= width
* height
* 4;
37 ConvolutionFilter1D filter_x
;
38 FillImpulseFilter(width
, &filter_x
);
40 ConvolutionFilter1D filter_y
;
41 FillImpulseFilter(height
, &filter_y
);
43 std::vector
<unsigned char> output
;
44 output
.resize(byte_count
);
45 BGRAConvolve2D(data
, width
* 4, true, filter_x
, filter_y
,
46 filter_x
.num_values() * 4, &output
[0], false);
48 // Output should exactly match input.
49 EXPECT_EQ(0, memcmp(data
, &output
[0], byte_count
));
52 // Fills the destination filter with a box filter averaging every two pixels
53 // to produce the output.
54 void FillBoxFilter(int size
, ConvolutionFilter1D
* filter
) {
55 const float box
[2] = { 0.5, 0.5 };
56 for (int i
= 0; i
< size
; i
++)
57 filter
->AddFilter(i
* 2, box
, 2);
62 // Tests that each pixel, when set and run through the impulse filter, does
64 TEST(Convolver
, Impulse
) {
65 // We pick an "odd" size that is not likely to fit on any boundaries so that
66 // we can see if all the widths and paddings are handled properly.
69 int byte_count
= width
* height
* 4;
70 std::vector
<unsigned char> input
;
71 input
.resize(byte_count
);
73 unsigned char* input_ptr
= &input
[0];
74 for (int y
= 0; y
< height
; y
++) {
75 for (int x
= 0; x
< width
; x
++) {
76 for (int channel
= 0; channel
< 3; channel
++) {
77 memset(input_ptr
, 0, byte_count
);
78 input_ptr
[(y
* width
+ x
) * 4 + channel
] = 0xff;
79 // Always set the alpha channel or it will attempt to "fix" it for us.
80 input_ptr
[(y
* width
+ x
) * 4 + 3] = 0xff;
81 TestImpulseConvolution(input_ptr
, width
, height
);
87 // Tests that using a box filter to halve an image results in every square of 4
88 // pixels in the original get averaged to a pixel in the output.
89 TEST(Convolver
, Halve
) {
90 static const int kSize
= 16;
92 int src_width
= kSize
;
93 int src_height
= kSize
;
94 int src_row_stride
= src_width
* 4;
95 int src_byte_count
= src_row_stride
* src_height
;
96 std::vector
<unsigned char> input
;
97 input
.resize(src_byte_count
);
99 int dest_width
= src_width
/ 2;
100 int dest_height
= src_height
/ 2;
101 int dest_byte_count
= dest_width
* dest_height
* 4;
102 std::vector
<unsigned char> output
;
103 output
.resize(dest_byte_count
);
105 // First fill the array with a bunch of random data.
106 srand(static_cast<unsigned>(time(NULL
)));
107 for (int i
= 0; i
< src_byte_count
; i
++)
108 input
[i
] = rand() * 255 / RAND_MAX
;
110 // Compute the filters.
111 ConvolutionFilter1D filter_x
, filter_y
;
112 FillBoxFilter(dest_width
, &filter_x
);
113 FillBoxFilter(dest_height
, &filter_y
);
115 // Do the convolution.
116 BGRAConvolve2D(&input
[0], src_width
, true, filter_x
, filter_y
,
117 filter_x
.num_values() * 4, &output
[0], false);
119 // Compute the expected results and check, allowing for a small difference
120 // to account for rounding errors.
121 for (int y
= 0; y
< dest_height
; y
++) {
122 for (int x
= 0; x
< dest_width
; x
++) {
123 for (int channel
= 0; channel
< 4; channel
++) {
124 int src_offset
= (y
* 2 * src_row_stride
+ x
* 2 * 4) + channel
;
125 int value
= input
[src_offset
] + // Top left source pixel.
126 input
[src_offset
+ 4] + // Top right source pixel.
127 input
[src_offset
+ src_row_stride
] + // Lower left.
128 input
[src_offset
+ src_row_stride
+ 4]; // Lower right.
129 value
/= 4; // Average.
130 int difference
= value
- output
[(y
* dest_width
+ x
) * 4 + channel
];
131 EXPECT_TRUE(difference
>= -1 || difference
<= 1);
137 // Tests the optimization in Convolver1D::AddFilter that avoids storing
138 // leading/trailing zeroes.
139 TEST(Convolver
, AddFilter
) {
140 skia::ConvolutionFilter1D filter
;
142 const skia::ConvolutionFilter1D::Fixed
* values
= NULL
;
143 int filter_offset
= 0;
144 int filter_length
= 0;
146 // An all-zero filter is handled correctly, all factors ignored
147 static const float factors1
[] = { 0.0f
, 0.0f
, 0.0f
};
148 filter
.AddFilter(11, factors1
, arraysize(factors1
));
149 ASSERT_EQ(0, filter
.max_filter());
150 ASSERT_EQ(1, filter
.num_values());
152 values
= filter
.FilterForValue(0, &filter_offset
, &filter_length
);
153 ASSERT_TRUE(values
== NULL
); // No values => NULL.
154 ASSERT_EQ(11, filter_offset
); // Same as input offset.
155 ASSERT_EQ(0, filter_length
); // But no factors since all are zeroes.
157 // Zeroes on the left are ignored
158 static const float factors2
[] = { 0.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
};
159 filter
.AddFilter(22, factors2
, arraysize(factors2
));
160 ASSERT_EQ(4, filter
.max_filter());
161 ASSERT_EQ(2, filter
.num_values());
163 values
= filter
.FilterForValue(1, &filter_offset
, &filter_length
);
164 ASSERT_TRUE(values
!= NULL
);
165 ASSERT_EQ(23, filter_offset
); // 22 plus 1 leading zero
166 ASSERT_EQ(4, filter_length
); // 5 - 1 leading zero
168 // Zeroes on the right are ignored
169 static const float factors3
[] = { 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 0.0f
, 0.0f
};
170 filter
.AddFilter(33, factors3
, arraysize(factors3
));
171 ASSERT_EQ(5, filter
.max_filter());
172 ASSERT_EQ(3, filter
.num_values());
174 values
= filter
.FilterForValue(2, &filter_offset
, &filter_length
);
175 ASSERT_TRUE(values
!= NULL
);
176 ASSERT_EQ(33, filter_offset
); // 33, same as input due to no leading zero
177 ASSERT_EQ(5, filter_length
); // 7 - 2 trailing zeroes
179 // Zeroes in leading & trailing positions
180 static const float factors4
[] = { 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
, 0.0f
, 0.0f
};
181 filter
.AddFilter(44, factors4
, arraysize(factors4
));
182 ASSERT_EQ(5, filter
.max_filter()); // No change from existing value.
183 ASSERT_EQ(4, filter
.num_values());
185 values
= filter
.FilterForValue(3, &filter_offset
, &filter_length
);
186 ASSERT_TRUE(values
!= NULL
);
187 ASSERT_EQ(46, filter_offset
); // 44 plus 2 leading zeroes
188 ASSERT_EQ(3, filter_length
); // 7 - (2 leading + 2 trailing) zeroes
190 // Zeroes surrounded by non-zero values are ignored
191 static const float factors5
[] = { 0.0f
, 0.0f
,
192 1.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 1.0f
,
194 filter
.AddFilter(55, factors5
, arraysize(factors5
));
195 ASSERT_EQ(6, filter
.max_filter());
196 ASSERT_EQ(5, filter
.num_values());
198 values
= filter
.FilterForValue(4, &filter_offset
, &filter_length
);
199 ASSERT_TRUE(values
!= NULL
);
200 ASSERT_EQ(57, filter_offset
); // 55 plus 2 leading zeroes
201 ASSERT_EQ(6, filter_length
); // 9 - (2 leading + 1 trailing) zeroes
203 // All-zero filters after the first one also work
204 static const float factors6
[] = { 0.0f
};
205 filter
.AddFilter(66, factors6
, arraysize(factors6
));
206 ASSERT_EQ(6, filter
.max_filter());
207 ASSERT_EQ(6, filter
.num_values());
209 values
= filter
.FilterForValue(5, &filter_offset
, &filter_length
);
210 ASSERT_TRUE(values
== NULL
); // filter_length == 0 => values is NULL
211 ASSERT_EQ(66, filter_offset
); // value passed in
212 ASSERT_EQ(0, filter_length
);
215 #if defined(THREAD_SANITIZER)
216 // Times out under ThreadSanitizer, http://crbug.com/134400.
217 #define MAYBE_SIMDVerification DISABLED_SIMDVerification
219 #define MAYBE_SIMDVerification SIMDVerification
221 TEST(Convolver
, MAYBE_SIMDVerification
) {
222 int source_sizes
[][2] = {
223 {1,1}, {1,2}, {1,3}, {1,4}, {1,5},
224 {2,1}, {2,2}, {2,3}, {2,4}, {2,5},
225 {3,1}, {3,2}, {3,3}, {3,4}, {3,5},
226 {4,1}, {4,2}, {4,3}, {4,4}, {4,5},
235 int dest_sizes
[][2] = { {1280, 1024}, {480, 270}, {177, 123} };
237 int dest_sizes
[][2] = { {128, 102}, {48, 27}, {17, 13} };
239 float filter
[] = { 0.05f
, -0.15f
, 0.6f
, 0.6f
, -0.15f
, 0.05f
};
241 srand(static_cast<unsigned int>(time(0)));
243 // Loop over some specific source and destination dimensions.
244 for (unsigned int i
= 0; i
< arraysize(source_sizes
); ++i
) {
245 unsigned int source_width
= source_sizes
[i
][0];
246 unsigned int source_height
= source_sizes
[i
][1];
247 for (unsigned int j
= 0; j
< arraysize(dest_sizes
); ++j
) {
248 unsigned int dest_width
= dest_sizes
[j
][0];
249 unsigned int dest_height
= dest_sizes
[j
][1];
251 // Preparing convolve coefficients.
252 ConvolutionFilter1D x_filter
, y_filter
;
253 for (unsigned int p
= 0; p
< dest_width
; ++p
) {
254 unsigned int offset
= source_width
* p
/ dest_width
;
255 EXPECT_LT(offset
, source_width
);
256 x_filter
.AddFilter(offset
, filter
,
257 std::min
<int>(arraysize(filter
),
258 source_width
- offset
));
260 x_filter
.PaddingForSIMD();
261 for (unsigned int p
= 0; p
< dest_height
; ++p
) {
262 unsigned int offset
= source_height
* p
/ dest_height
;
263 y_filter
.AddFilter(offset
, filter
,
264 std::min
<int>(arraysize(filter
),
265 source_height
- offset
));
267 y_filter
.PaddingForSIMD();
269 // Allocate input and output skia bitmap.
270 SkBitmap source
, result_c
, result_sse
;
271 source
.setConfig(SkBitmap::kARGB_8888_Config
,
272 source_width
, source_height
);
273 source
.allocPixels();
274 result_c
.setConfig(SkBitmap::kARGB_8888_Config
,
275 dest_width
, dest_height
);
276 result_c
.allocPixels();
277 result_sse
.setConfig(SkBitmap::kARGB_8888_Config
,
278 dest_width
, dest_height
);
279 result_sse
.allocPixels();
281 // Randomize source bitmap for testing.
282 unsigned char* src_ptr
= static_cast<unsigned char*>(source
.getPixels());
283 for (int y
= 0; y
< source
.height(); y
++) {
284 for (unsigned int x
= 0; x
< source
.rowBytes(); x
++)
285 src_ptr
[x
] = rand() % 255;
286 src_ptr
+= source
.rowBytes();
289 // Test both cases with different has_alpha.
290 for (int alpha
= 0; alpha
< 2; alpha
++) {
291 // Convolve using C code.
292 base::TimeTicks resize_start
;
293 base::TimeDelta delta_c
, delta_sse
;
294 unsigned char* r1
= static_cast<unsigned char*>(result_c
.getPixels());
295 unsigned char* r2
= static_cast<unsigned char*>(result_sse
.getPixels());
297 resize_start
= base::TimeTicks::Now();
298 BGRAConvolve2D(static_cast<const uint8
*>(source
.getPixels()),
299 static_cast<int>(source
.rowBytes()),
300 (alpha
!= 0), x_filter
, y_filter
,
301 static_cast<int>(result_c
.rowBytes()), r1
, false);
302 delta_c
= base::TimeTicks::Now() - resize_start
;
304 resize_start
= base::TimeTicks::Now();
305 // Convolve using SSE2 code
306 BGRAConvolve2D(static_cast<const uint8
*>(source
.getPixels()),
307 static_cast<int>(source
.rowBytes()),
308 (alpha
!= 0), x_filter
, y_filter
,
309 static_cast<int>(result_sse
.rowBytes()), r2
, true);
310 delta_sse
= base::TimeTicks::Now() - resize_start
;
312 // Unfortunately I could not enable the performance check now.
313 // Most bots use debug version, and there are great difference between
314 // the code generation for intrinsic, etc. In release version speed
315 // difference was 150%-200% depend on alpha channel presence;
316 // while in debug version speed difference was 96%-120%.
317 // TODO(jiesun): optimize further until we could enable this for
318 // debug version too.
319 // EXPECT_LE(delta_sse, delta_c);
321 int64 c_us
= delta_c
.InMicroseconds();
322 int64 sse_us
= delta_sse
.InMicroseconds();
323 VLOG(1) << "from:" << source_width
<< "x" << source_height
324 << " to:" << dest_width
<< "x" << dest_height
325 << (alpha
? " with alpha" : " w/o alpha");
326 VLOG(1) << "c:" << c_us
<< " sse:" << sse_us
;
327 VLOG(1) << "ratio:" << static_cast<float>(c_us
) / sse_us
;
330 for (unsigned int i
= 0; i
< dest_height
; i
++) {
331 for (unsigned int x
= 0; x
< dest_width
* 4; x
++) { // RGBA always.
332 EXPECT_EQ(r1
[x
], r2
[x
]);
334 r1
+= result_c
.rowBytes();
335 r2
+= result_sse
.rowBytes();
342 TEST(Convolver
, SeparableSingleConvolution
) {
343 static const int kImgWidth
= 1024;
344 static const int kImgHeight
= 1024;
345 static const int kChannelCount
= 3;
346 static const int kStrideSlack
= 22;
347 ConvolutionFilter1D filter
;
348 const float box
[5] = { 0.2f
, 0.2f
, 0.2f
, 0.2f
, 0.2f
};
349 filter
.AddFilter(0, box
, 5);
351 // Allocate a source image and set to 0.
352 const int src_row_stride
= kImgWidth
* kChannelCount
+ kStrideSlack
;
353 int src_byte_count
= src_row_stride
* kImgHeight
;
354 std::vector
<unsigned char> input
;
355 const int signal_x
= kImgWidth
/ 2;
356 const int signal_y
= kImgHeight
/ 2;
357 input
.resize(src_byte_count
, 0);
358 // The image has a single impulse pixel in channel 1, smack in the middle.
359 const int non_zero_pixel_index
=
360 signal_y
* src_row_stride
+ signal_x
* kChannelCount
+ 1;
361 input
[non_zero_pixel_index
] = 255;
363 // Destination will be a single channel image with stide matching width.
364 const int dest_row_stride
= kImgWidth
;
365 const int dest_byte_count
= dest_row_stride
* kImgHeight
;
366 std::vector
<unsigned char> output
;
367 output
.resize(dest_byte_count
);
369 // Apply convolution in X.
370 SingleChannelConvolveX1D(&input
[0], src_row_stride
, 1, kChannelCount
,
371 filter
, SkISize::Make(kImgWidth
, kImgHeight
),
372 &output
[0], dest_row_stride
, 0, 1, false);
373 for (int x
= signal_x
- 2; x
<= signal_x
+ 2; ++x
)
374 EXPECT_GT(output
[signal_y
* dest_row_stride
+ x
], 0);
376 EXPECT_EQ(output
[signal_y
* dest_row_stride
+ signal_x
- 3], 0);
377 EXPECT_EQ(output
[signal_y
* dest_row_stride
+ signal_x
+ 3], 0);
379 // Apply convolution in Y.
380 SingleChannelConvolveY1D(&input
[0], src_row_stride
, 1, kChannelCount
,
381 filter
, SkISize::Make(kImgWidth
, kImgHeight
),
382 &output
[0], dest_row_stride
, 0, 1, false);
383 for (int y
= signal_y
- 2; y
<= signal_y
+ 2; ++y
)
384 EXPECT_GT(output
[y
* dest_row_stride
+ signal_x
], 0);
386 EXPECT_EQ(output
[(signal_y
- 3) * dest_row_stride
+ signal_x
], 0);
387 EXPECT_EQ(output
[(signal_y
+ 3) * dest_row_stride
+ signal_x
], 0);
389 EXPECT_EQ(output
[signal_y
* dest_row_stride
+ signal_x
- 1], 0);
390 EXPECT_EQ(output
[signal_y
* dest_row_stride
+ signal_x
+ 1], 0);
392 // The main point of calling this is to invoke the routine on input without
394 std::vector
<unsigned char> output2
;
395 output2
.resize(dest_byte_count
);
396 SingleChannelConvolveX1D(&output
[0], dest_row_stride
, 0, 1,
397 filter
, SkISize::Make(kImgWidth
, kImgHeight
),
398 &output2
[0], dest_row_stride
, 0, 1, false);
399 // This should be a result of 2D convolution.
400 for (int x
= signal_x
- 2; x
<= signal_x
+ 2; ++x
) {
401 for (int y
= signal_y
- 2; y
<= signal_y
+ 2; ++y
)
402 EXPECT_GT(output2
[y
* dest_row_stride
+ x
], 0);
404 EXPECT_EQ(output2
[0], 0);
405 EXPECT_EQ(output2
[dest_row_stride
- 1], 0);
406 EXPECT_EQ(output2
[dest_byte_count
- 1], 0);
409 TEST(Convolver
, SeparableSingleConvolutionEdges
) {
410 // The purpose of this test is to check if the implementation treats correctly
411 // edges of the image.
412 static const int kImgWidth
= 600;
413 static const int kImgHeight
= 800;
414 static const int kChannelCount
= 3;
415 static const int kStrideSlack
= 22;
416 static const int kChannel
= 1;
417 ConvolutionFilter1D filter
;
418 const float box
[5] = { 0.2f
, 0.2f
, 0.2f
, 0.2f
, 0.2f
};
419 filter
.AddFilter(0, box
, 5);
421 // Allocate a source image and set to 0.
422 int src_row_stride
= kImgWidth
* kChannelCount
+ kStrideSlack
;
423 int src_byte_count
= src_row_stride
* kImgHeight
;
424 std::vector
<unsigned char> input(src_byte_count
);
426 // Draw a frame around the image.
427 for (int i
= 0; i
< src_byte_count
; ++i
) {
428 int row
= i
/ src_row_stride
;
429 int col
= i
% src_row_stride
/ kChannelCount
;
430 int channel
= i
% src_row_stride
% kChannelCount
;
431 if (channel
!= kChannel
|| col
> kImgWidth
) {
433 } else if (row
== 0 || col
== 0 ||
434 col
== kImgWidth
- 1 || row
== kImgHeight
- 1) {
436 } else if (row
== 1 || col
== 1 ||
437 col
== kImgWidth
- 2 || row
== kImgHeight
- 2) {
444 // Destination will be a single channel image with stide matching width.
445 int dest_row_stride
= kImgWidth
;
446 int dest_byte_count
= dest_row_stride
* kImgHeight
;
447 std::vector
<unsigned char> output
;
448 output
.resize(dest_byte_count
);
450 // Apply convolution in X.
451 SingleChannelConvolveX1D(&input
[0], src_row_stride
, 1, kChannelCount
,
452 filter
, SkISize::Make(kImgWidth
, kImgHeight
),
453 &output
[0], dest_row_stride
, 0, 1, false);
455 // Sadly, comparison is not as simple as retaining all values.
456 int invalid_values
= 0;
457 const unsigned char first_value
= output
[0];
458 EXPECT_NEAR(first_value
, 100, 1);
459 for (int i
= 0; i
< dest_row_stride
; ++i
) {
460 if (output
[i
] != first_value
)
463 EXPECT_EQ(0, invalid_values
);
466 EXPECT_NEAR(output
[test_row
* dest_row_stride
], 100, 1);
467 EXPECT_NEAR(output
[test_row
* dest_row_stride
+ 1], 80, 1);
468 EXPECT_NEAR(output
[test_row
* dest_row_stride
+ 2], 60, 1);
469 EXPECT_NEAR(output
[test_row
* dest_row_stride
+ 3], 40, 1);
470 EXPECT_NEAR(output
[(test_row
+ 1) * dest_row_stride
- 1], 100, 1);
471 EXPECT_NEAR(output
[(test_row
+ 1) * dest_row_stride
- 2], 80, 1);
472 EXPECT_NEAR(output
[(test_row
+ 1) * dest_row_stride
- 3], 60, 1);
473 EXPECT_NEAR(output
[(test_row
+ 1) * dest_row_stride
- 4], 40, 1);
475 SingleChannelConvolveY1D(&input
[0], src_row_stride
, 1, kChannelCount
,
476 filter
, SkISize::Make(kImgWidth
, kImgHeight
),
477 &output
[0], dest_row_stride
, 0, 1, false);
479 int test_column
= 42;
480 EXPECT_NEAR(output
[test_column
], 100, 1);
481 EXPECT_NEAR(output
[test_column
+ dest_row_stride
], 80, 1);
482 EXPECT_NEAR(output
[test_column
+ dest_row_stride
* 2], 60, 1);
483 EXPECT_NEAR(output
[test_column
+ dest_row_stride
* 3], 40, 1);
485 EXPECT_NEAR(output
[test_column
+ dest_row_stride
* (kImgHeight
- 1)], 100, 1);
486 EXPECT_NEAR(output
[test_column
+ dest_row_stride
* (kImgHeight
- 2)], 80, 1);
487 EXPECT_NEAR(output
[test_column
+ dest_row_stride
* (kImgHeight
- 3)], 60, 1);
488 EXPECT_NEAR(output
[test_column
+ dest_row_stride
* (kImgHeight
- 4)], 40, 1);
491 TEST(Convolver
, SetUpGaussianConvolutionFilter
) {
492 ConvolutionFilter1D smoothing_filter
;
493 ConvolutionFilter1D gradient_filter
;
494 SetUpGaussianConvolutionKernel(&smoothing_filter
, 4.5f
, false);
495 SetUpGaussianConvolutionKernel(&gradient_filter
, 3.0f
, true);
497 int specified_filter_length
;
501 const ConvolutionFilter1D::Fixed
* smoothing_kernel
=
502 smoothing_filter
.GetSingleFilter(
503 &specified_filter_length
, &filter_offset
, &filter_length
);
504 EXPECT_TRUE(smoothing_kernel
);
505 std::vector
<float> fp_smoothing_kernel(filter_length
);
506 std::transform(smoothing_kernel
,
507 smoothing_kernel
+ filter_length
,
508 fp_smoothing_kernel
.begin(),
509 ConvolutionFilter1D::FixedToFloat
);
510 // Should sum-up to 1 (nearly), and all values whould be in ]0, 1[.
511 EXPECT_NEAR(std::accumulate(
512 fp_smoothing_kernel
.begin(), fp_smoothing_kernel
.end(), 0.0f
),
514 EXPECT_GT(*std::min_element(fp_smoothing_kernel
.begin(),
515 fp_smoothing_kernel
.end()), 0.0f
);
516 EXPECT_LT(*std::max_element(fp_smoothing_kernel
.begin(),
517 fp_smoothing_kernel
.end()), 1.0f
);
519 const ConvolutionFilter1D::Fixed
* gradient_kernel
=
520 gradient_filter
.GetSingleFilter(
521 &specified_filter_length
, &filter_offset
, &filter_length
);
522 EXPECT_TRUE(gradient_kernel
);
523 std::vector
<float> fp_gradient_kernel(filter_length
);
524 std::transform(gradient_kernel
,
525 gradient_kernel
+ filter_length
,
526 fp_gradient_kernel
.begin(),
527 ConvolutionFilter1D::FixedToFloat
);
528 // Should sum-up to 0, and all values whould be in ]-1.5, 1.5[.
529 EXPECT_NEAR(std::accumulate(
530 fp_gradient_kernel
.begin(), fp_gradient_kernel
.end(), 0.0f
),
532 EXPECT_GT(*std::min_element(fp_gradient_kernel
.begin(),
533 fp_gradient_kernel
.end()), -1.5f
);
534 EXPECT_LT(*std::min_element(fp_gradient_kernel
.begin(),
535 fp_gradient_kernel
.end()), 0.0f
);
536 EXPECT_LT(*std::max_element(fp_gradient_kernel
.begin(),
537 fp_gradient_kernel
.end()), 1.5f
);
538 EXPECT_GT(*std::max_element(fp_gradient_kernel
.begin(),
539 fp_gradient_kernel
.end()), 0.0f
);