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
201 void BM_DeinterlaceEffect(benchmark::State &state, size_t bytes_per_pixel, MovitPixelFormat input_format, GLenum output_format)
203 unsigned width = state.range(0), height = state.range(1);
204 unsigned field_height = height / 2;
206 unique_ptr<float[]> field1(new float[width * field_height * bytes_per_pixel]);
207 unique_ptr<float[]> field2(new float[width * field_height * bytes_per_pixel]);
208 unique_ptr<float[]> field3(new float[width * field_height * bytes_per_pixel]);
209 unique_ptr<float[]> field4(new float[width * field_height * bytes_per_pixel]);
210 unique_ptr<float[]> field5(new float[width * field_height * bytes_per_pixel]);
211 unique_ptr<float[]> out_data(new float[width * height * bytes_per_pixel]);
213 for (unsigned i = 0; i < width * field_height * bytes_per_pixel; ++i) {
221 EffectChainTester tester(nullptr, width, height);
222 Effect *input1 = tester.add_input(field1.get(), input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
223 Effect *input2 = tester.add_input(field2.get(), input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
224 Effect *input3 = tester.add_input(field3.get(), input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
225 Effect *input4 = tester.add_input(field4.get(), input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
226 Effect *input5 = tester.add_input(field5.get(), input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
227 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
229 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
231 tester.benchmark(state, out_data.get(), output_format, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
233 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, Gray, 1, FORMAT_GRAYSCALE, GL_RED)->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
234 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, BGRA, 4, FORMAT_BGRA_POSTMULTIPLIED_ALPHA, GL_BGRA)->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);