Hard-assert on something that has bitten me too many times now.
[movit] / deinterlace_effect_test.cpp
1 // Unit tests for DeinterlaceEffect.
2
3 #include <epoxy/gl.h>
4
5 #include <algorithm>
6
7 #include "effect_chain.h"
8 #include "gtest/gtest.h"
9 #include "image_format.h"
10 #include "input.h"
11 #include "deinterlace_effect.h"
12 #include "test_util.h"
13
14 using namespace std;
15
16 namespace movit {
17
18 TEST(DeinterlaceTest, ConstantColor) {
19         float data[] = {
20                 0.3f, 0.3f,
21                 0.3f, 0.3f,
22                 0.3f, 0.3f,
23         };
24         float expected_data[] = {
25                 0.3f, 0.3f,
26                 0.3f, 0.3f,
27                 0.3f, 0.3f,
28                 0.3f, 0.3f,
29                 0.3f, 0.3f,
30                 0.3f, 0.3f,
31         };
32         float out_data[12];
33         EffectChainTester tester(NULL, 2, 6);
34         Effect *input1 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
35         Effect *input2 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
36         Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
37         Effect *input4 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
38         Effect *input5 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
39         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
40
41         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
42         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
43         expect_equal(expected_data, out_data, 2, 6);
44
45         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
46         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
47         expect_equal(expected_data, out_data, 2, 6);
48 }
49
50 // Also tests that top/bottom change works like expected.
51 TEST(DeinterlaceTest, VerticalInterpolation) {
52         const int width = 11;
53         const int height = 2;
54         float data[width * height] = {
55                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 
56                 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.
57         };
58         float expected_data_top[width * height * 2] = {
59                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
60                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
61                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
62                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Repeated.
63         };
64         float expected_data_bottom[width * height * 2] = {
65                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Repeated
66                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
67                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
68                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
69         };
70         float neg_blowout_data[width * height];
71         float pos_blowout_data[width * height];
72         float out_data[width * height * 2];
73
74         // Set previous and next fields to something so big that all the temporal checks
75         // are effectively turned off.
76         fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
77         fill(neg_blowout_data, pos_blowout_data + width * height,  100.0f);
78
79         EffectChainTester tester(NULL, width, height * 2);
80         Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
81         Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
82         Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
83         Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
84         Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
85         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
86
87         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
88         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
89         expect_equal(expected_data_top, out_data, width, height * 2);
90
91         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
92         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
93         expect_equal(expected_data_bottom, out_data, width, height * 2);
94 }
95
96 TEST(DeinterlaceTest, DiagonalInterpolation) {
97         const int width = 11;
98         const int height = 3;
99         float data[width * height] = {
100                 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f,
101                 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.
102                 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.
103         };
104
105         // Expected degrees are marked in comments. Mostly we want +45 for the second line
106         // and -63 for the fourth, but due to the score being over three neighboring pixels,
107         // sometimes it doesn't work ideally like that.
108         float expected_data_top[width * height * 2] = {
109                 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f,   // Unchanged.
110                 // |    /     /     /     /     /     /     /     /     /    |
111                 // 0  +45   +45   +45   +45   +45   +45   +45   +45   +45    0
112                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
113                 // | /     /     /     /     /     /     /     /     /       |  
114                 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 0.0f,   // Unchanged.
115
116                 // 0  -45   -63   -63   -63   -63   -63   -63   +63!  +63!  +63!
117                 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.2f, 0.3f, 0.2f,
118
119                 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f,   // Unchanged.
120                 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f,   // Repeated.
121         };
122         float neg_blowout_data[width * height];
123         float pos_blowout_data[width * height];
124         float out_data[width * height * 2];
125
126         // Set previous and next fields to something so big that all the temporal checks
127         // are effectively turned off.
128         fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
129         fill(pos_blowout_data, pos_blowout_data + width * height,  100.0f);
130
131         EffectChainTester tester(NULL, width, height * 2);
132         Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
133         Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
134         Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
135         Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
136         Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
137         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
138
139         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
140         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
141         expect_equal(expected_data_top, out_data, width, height * 2);
142 }
143
144 TEST(DeinterlaceTest, FlickerBox) {
145         const int width = 4;
146         const int height = 4;
147         float white_data[width * height] = {
148                 1.0f, 1.0f, 1.0f, 1.0f,
149                 1.0f, 1.0f, 1.0f, 1.0f,
150                 1.0f, 1.0f, 1.0f, 1.0f,
151                 1.0f, 1.0f, 1.0f, 1.0f,
152         };
153         float black_data[width * height] = {
154                 0.0f, 0.0f, 0.0f, 0.0f,
155                 0.0f, 0.0f, 0.0f, 0.0f,
156                 0.0f, 0.0f, 0.0f, 0.0f,
157                 0.0f, 0.0f, 0.0f, 0.0f,
158         };
159         float striped_data[width * height * 2] = {
160                 1.0f, 1.0f, 1.0f, 1.0f,
161                 0.0f, 0.0f, 0.0f, 0.0f,
162                 1.0f, 1.0f, 1.0f, 1.0f,
163                 0.0f, 0.0f, 0.0f, 0.0f,
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         };
169         float out_data[width * height * 2];
170
171         {
172                 EffectChainTester tester(NULL, width, height * 2);
173                 Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
174                 Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
175                 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
176
177                 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
178                 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
179                 expect_equal(white_data, out_data, width, height);
180                 expect_equal(white_data, out_data + width * height, width, height);
181         }
182
183         {
184                 EffectChainTester tester(NULL, width, height * 2);
185                 Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
186                 Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
187                 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
188
189                 ASSERT_TRUE(deinterlace_effect->set_int("enable_spatial_interlacing_check", 0));
190                 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
191                 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
192                 expect_equal(striped_data, out_data, width, height * 2);
193         }
194 }
195
196 }  // namespace movit