1 // Unit tests for DeinterlaceEffect.
4 #include <benchmark/benchmark.h>
11 #include "effect_chain.h"
12 #include "gtest/gtest.h"
13 #include "image_format.h"
15 #include "deinterlace_effect.h"
16 #include "test_util.h"
22 TEST(DeinterlaceTest, ConstantColor) {
28 float expected_data[] = {
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);
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);
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);
54 // Also tests that top/bottom change works like expected.
55 TEST(DeinterlaceTest, VerticalInterpolation) {
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.
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.
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.
74 float neg_blowout_data[width * height];
75 float pos_blowout_data[width * height];
76 float out_data[width * height * 2];
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);
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);
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);
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);
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.
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.
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,
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.
126 float neg_blowout_data[width * height];
127 float pos_blowout_data[width * height];
128 float out_data[width * height * 2];
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);
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);
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);
148 TEST(DeinterlaceTest, FlickerBox) {
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,
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,
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,
173 float out_data[width * height * 2];
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);
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);
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);
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);
200 #ifdef HAVE_BENCHMARK
204 MovitPixelFormat input_format;
205 GLenum output_format;
206 size_t bytes_per_pixel;
208 TestFormat gray_format = { FORMAT_GRAYSCALE, GL_RED, 1 };
209 TestFormat bgra_format = { FORMAT_BGRA_PREMULTIPLIED_ALPHA, GL_BGRA, 4 };
213 void BM_DeinterlaceEffect(benchmark::State &state, TestFormat format, bool spatial_interlacing_check)
215 unsigned width = state.range(0), height = state.range(1);
216 unsigned field_height = height / 2;
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]);
225 for (unsigned i = 0; i < width * field_height * format.bytes_per_pixel; ++i) {
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);
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));
244 tester.benchmark(state, out_data.get(), format.output_format, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
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);