]> git.sesse.net Git - movit/blob - deinterlace_effect_test.cpp
0c625c519fcd656758831b0228830e78dac40e32
[movit] / deinterlace_effect_test.cpp
1 // Unit tests for DeinterlaceEffect.
2
3 #ifdef HAVE_BENCHMARK
4 #include <benchmark/benchmark.h>
5 #endif
6 #include <epoxy/gl.h>
7
8 #include <algorithm>
9 #include <memory>
10
11 #include "effect_chain.h"
12 #include "gtest/gtest.h"
13 #include "image_format.h"
14 #include "input.h"
15 #include "deinterlace_effect.h"
16 #include "test_util.h"
17
18 using namespace std;
19
20 namespace movit {
21
22 TEST(DeinterlaceTest, ConstantColor) {
23         float data[] = {
24                 0.3f, 0.3f,
25                 0.3f, 0.3f,
26                 0.3f, 0.3f,
27         };
28         float expected_data[] = {
29                 0.3f, 0.3f,
30                 0.3f, 0.3f,
31                 0.3f, 0.3f,
32                 0.3f, 0.3f,
33                 0.3f, 0.3f,
34                 0.3f, 0.3f,
35         };
36         float out_data[12];
37         EffectChainTester tester(nullptr, 2, 6);
38         Effect *input1 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
39         Effect *input2 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
40         Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
41         Effect *input4 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
42         Effect *input5 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
43         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
44
45         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
46         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
47         expect_equal(expected_data, out_data, 2, 6);
48
49         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
50         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
51         expect_equal(expected_data, out_data, 2, 6);
52 }
53
54 // Also tests that top/bottom change works like expected.
55 TEST(DeinterlaceTest, VerticalInterpolation) {
56         const int width = 11;
57         const int height = 2;
58         float data[width * height] = {
59                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 
60                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Differs from previous.
61         };
62         float expected_data_top[width * height * 2] = {
63                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
64                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
65                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
66                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Repeated.
67         };
68         float expected_data_bottom[width * height * 2] = {
69                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Repeated
70                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
71                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
72                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
73         };
74         float neg_blowout_data[width * height];
75         float pos_blowout_data[width * height];
76         float out_data[width * height * 2];
77
78         // Set previous and next fields to something so big that all the temporal checks
79         // are effectively turned off.
80         fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
81         fill(pos_blowout_data, pos_blowout_data + width * height,  100.0f);
82
83         EffectChainTester tester(nullptr, width, height * 2);
84         Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
85         Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
86         Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
87         Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
88         Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
89         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
90
91         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
92         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
93         expect_equal(expected_data_top, out_data, width, height * 2);
94
95         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
96         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
97         expect_equal(expected_data_bottom, out_data, width, height * 2);
98 }
99
100 TEST(DeinterlaceTest, DiagonalInterpolation) {
101         const int width = 11;
102         const int height = 3;
103         float data[width * height] = {
104                 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f,
105                 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 0.0f,   // Offset two pixels, one value modified.
106                 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f,   // Offset four the other way.
107         };
108
109         // Expected degrees are marked in comments. Mostly we want +45 for the second line
110         // and -63 for the fourth, but due to the score being over three neighboring pixels,
111         // sometimes it doesn't work ideally like that.
112         float expected_data_top[width * height * 2] = {
113                 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f,   // Unchanged.
114                 // |    /     /     /     /     /     /     /     /     /    |
115                 // 0  +45   +45   +45   +45   +45   +45   +45   +45   +45    0
116                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
117                 // | /     /     /     /     /     /     /     /     /       |  
118                 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 0.0f,   // Unchanged.
119
120                 // 0  -45   -63   -63   -63   -63   -63   -63   +63!  +63!  +63!
121                 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.2f, 0.3f, 0.2f,
122
123                 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f,   // Unchanged.
124                 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f,   // Repeated.
125         };
126         float neg_blowout_data[width * height];
127         float pos_blowout_data[width * height];
128         float out_data[width * height * 2];
129
130         // Set previous and next fields to something so big that all the temporal checks
131         // are effectively turned off.
132         fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
133         fill(pos_blowout_data, pos_blowout_data + width * height,  100.0f);
134
135         EffectChainTester tester(nullptr, width, height * 2);
136         Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
137         Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
138         Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
139         Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
140         Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
141         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
142
143         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
144         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
145         expect_equal(expected_data_top, out_data, width, height * 2);
146 }
147
148 TEST(DeinterlaceTest, FlickerBox) {
149         const int width = 4;
150         const int height = 4;
151         float white_data[width * height] = {
152                 1.0f, 1.0f, 1.0f, 1.0f,
153                 1.0f, 1.0f, 1.0f, 1.0f,
154                 1.0f, 1.0f, 1.0f, 1.0f,
155                 1.0f, 1.0f, 1.0f, 1.0f,
156         };
157         float black_data[width * height] = {
158                 0.0f, 0.0f, 0.0f, 0.0f,
159                 0.0f, 0.0f, 0.0f, 0.0f,
160                 0.0f, 0.0f, 0.0f, 0.0f,
161                 0.0f, 0.0f, 0.0f, 0.0f,
162         };
163         float striped_data[width * height * 2] = {
164                 1.0f, 1.0f, 1.0f, 1.0f,
165                 0.0f, 0.0f, 0.0f, 0.0f,
166                 1.0f, 1.0f, 1.0f, 1.0f,
167                 0.0f, 0.0f, 0.0f, 0.0f,
168                 1.0f, 1.0f, 1.0f, 1.0f,
169                 0.0f, 0.0f, 0.0f, 0.0f,
170                 1.0f, 1.0f, 1.0f, 1.0f,
171                 0.0f, 0.0f, 0.0f, 0.0f,
172         };
173         float out_data[width * height * 2];
174
175         {
176                 EffectChainTester tester(nullptr, width, height * 2);
177                 Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
178                 Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
179                 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
180
181                 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
182                 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
183                 expect_equal(white_data, out_data, width, height);
184                 expect_equal(white_data, out_data + width * height, width, height);
185         }
186
187         {
188                 EffectChainTester tester(nullptr, width, height * 2);
189                 Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
190                 Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
191                 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
192
193                 ASSERT_TRUE(deinterlace_effect->set_int("enable_spatial_interlacing_check", 0));
194                 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
195                 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
196                 expect_equal(striped_data, out_data, width, height * 2);
197         }
198 }
199
200 #ifdef HAVE_BENCHMARK
201 namespace {
202
203 struct TestFormat {
204         MovitPixelFormat input_format;
205         GLenum output_format;
206         size_t bytes_per_pixel;
207 };
208 TestFormat gray_format = { FORMAT_GRAYSCALE, GL_RED, 1 };
209 TestFormat bgra_format = { FORMAT_BGRA_PREMULTIPLIED_ALPHA, GL_BGRA, 4 };
210
211 }  // namespace
212
213 void BM_DeinterlaceEffect(benchmark::State &state, TestFormat format, bool spatial_interlacing_check)
214 {
215         unsigned width = state.range(0), height = state.range(1);
216         unsigned field_height = height / 2;
217
218         unique_ptr<float[]> field1(new float[width * field_height * format.bytes_per_pixel]);
219         unique_ptr<float[]> field2(new float[width * field_height * format.bytes_per_pixel]);
220         unique_ptr<float[]> field3(new float[width * field_height * format.bytes_per_pixel]);
221         unique_ptr<float[]> field4(new float[width * field_height * format.bytes_per_pixel]);
222         unique_ptr<float[]> field5(new float[width * field_height * format.bytes_per_pixel]);
223         unique_ptr<float[]> out_data(new float[width * height * format.bytes_per_pixel]);
224
225         for (unsigned i = 0; i < width * field_height * format.bytes_per_pixel; ++i) {
226                 field1[i] = rand();
227                 field2[i] = rand();
228                 field3[i] = rand();
229                 field4[i] = rand();
230                 field5[i] = rand();
231         }
232
233         EffectChainTester tester(nullptr, width, height);
234         Effect *input1 = tester.add_input(field1.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
235         Effect *input2 = tester.add_input(field2.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
236         Effect *input3 = tester.add_input(field3.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
237         Effect *input4 = tester.add_input(field4.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
238         Effect *input5 = tester.add_input(field5.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
239         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
240
241         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
242         ASSERT_TRUE(deinterlace_effect->set_int("enable_spatial_interlacing_check", spatial_interlacing_check));
243
244         tester.benchmark(state, out_data.get(), format.output_format, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
245 }
246 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, Gray, gray_format, true)->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
247 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, BGRA, bgra_format, true)->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
248 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, BGRANoSpatialCheck, bgra_format, false)->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
249
250 #endif
251
252 }  // namespace movit