]> git.sesse.net Git - movit/blobdiff - deinterlace_effect_test.cpp
Add a deinterlacer based on YADIF.
[movit] / deinterlace_effect_test.cpp
diff --git a/deinterlace_effect_test.cpp b/deinterlace_effect_test.cpp
new file mode 100644 (file)
index 0000000..31bd363
--- /dev/null
@@ -0,0 +1,196 @@
+// Unit tests for DeinterlaceEffect.
+
+#include <epoxy/gl.h>
+
+#include <algorithm>
+
+#include "effect_chain.h"
+#include "gtest/gtest.h"
+#include "image_format.h"
+#include "input.h"
+#include "deinterlace_effect.h"
+#include "test_util.h"
+
+using namespace std;
+
+namespace movit {
+
+TEST(DeinterlaceTest, ConstantColor) {
+       float data[] = {
+               0.3f, 0.3f,
+               0.3f, 0.3f,
+               0.3f, 0.3f,
+       };
+       float expected_data[] = {
+               0.3f, 0.3f,
+               0.3f, 0.3f,
+               0.3f, 0.3f,
+               0.3f, 0.3f,
+               0.3f, 0.3f,
+               0.3f, 0.3f,
+       };
+       float out_data[12];
+       EffectChainTester tester(NULL, 2, 6);
+       Effect *input1 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
+       Effect *input2 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
+       Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
+       Effect *input4 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
+       Effect *input5 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
+       Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
+
+       ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+       expect_equal(expected_data, out_data, 2, 6);
+
+       ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+       expect_equal(expected_data, out_data, 2, 6);
+}
+
+// Also tests that top/bottom change works like expected.
+TEST(DeinterlaceTest, VerticalInterpolation) {
+       const int width = 11;
+       const int height = 2;
+       float data[width * height] = {
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 
+               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.
+       };
+       float expected_data_top[width * height * 2] = {
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Repeated.
+       };
+       float expected_data_bottom[width * height * 2] = {
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Repeated
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
+       };
+       float neg_blowout_data[width * height];
+       float pos_blowout_data[width * height];
+       float out_data[width * height * 2];
+
+       // Set previous and next fields to something so big that all the temporal checks
+       // are effectively turned off.
+       fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
+       fill(neg_blowout_data, pos_blowout_data + width * height,  100.0f);
+
+       EffectChainTester tester(NULL, width, height * 2);
+       Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
+
+       ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+       expect_equal(expected_data_top, out_data, width, height * 2);
+
+       ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+       expect_equal(expected_data_bottom, out_data, width, height * 2);
+}
+
+TEST(DeinterlaceTest, DiagonalInterpolation) {
+       const int width = 11;
+       const int height = 3;
+       float data[width * height] = {
+               0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f,
+               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.
+               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.
+       };
+
+       // Expected degrees are marked in comments. Mostly we want +45 for the second line
+       // and -63 for the fourth, but due to the score being over three neighboring pixels,
+       // sometimes it doesn't work ideally like that.
+       float expected_data_top[width * height * 2] = {
+               0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f,   // Unchanged.
+               // |    /     /     /     /     /     /     /     /     /    |
+               // 0  +45   +45   +45   +45   +45   +45   +45   +45   +45    0
+               0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
+               // | /     /     /     /     /     /     /     /     /       |  
+               0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 0.0f,   // Unchanged.
+
+               // 0  -45   -63   -63   -63   -63   -63   -63   +63!  +63!  +63!
+               0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.2f, 0.3f, 0.2f,
+
+               0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f,   // Unchanged.
+               0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f,   // Repeated.
+       };
+       float neg_blowout_data[width * height];
+       float pos_blowout_data[width * height];
+       float out_data[width * height * 2];
+
+       // Set previous and next fields to something so big that all the temporal checks
+       // are effectively turned off.
+       fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
+       fill(pos_blowout_data, pos_blowout_data + width * height,  100.0f);
+
+       EffectChainTester tester(NULL, width, height * 2);
+       Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+       Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
+
+       ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
+       expect_equal(expected_data_top, out_data, width, height * 2);
+}
+
+TEST(DeinterlaceTest, FlickerBox) {
+       const int width = 4;
+       const int height = 4;
+       float white_data[width * height] = {
+               1.0f, 1.0f, 1.0f, 1.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+       };
+       float black_data[width * height] = {
+               0.0f, 0.0f, 0.0f, 0.0f,
+               0.0f, 0.0f, 0.0f, 0.0f,
+               0.0f, 0.0f, 0.0f, 0.0f,
+               0.0f, 0.0f, 0.0f, 0.0f,
+       };
+       float striped_data[width * height * 2] = {
+               1.0f, 1.0f, 1.0f, 1.0f,
+               0.0f, 0.0f, 0.0f, 0.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               0.0f, 0.0f, 0.0f, 0.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               0.0f, 0.0f, 0.0f, 0.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               0.0f, 0.0f, 0.0f, 0.0f,
+       };
+       float out_data[width * height * 2];
+
+       {
+               EffectChainTester tester(NULL, width, height * 2);
+               Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+               Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+               Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
+
+               ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
+               tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
+               expect_equal(white_data, out_data, width, height);
+               expect_equal(white_data, out_data + width * height, width, height);
+       }
+
+       {
+               EffectChainTester tester(NULL, width, height * 2);
+               Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+               Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
+               Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
+
+               ASSERT_TRUE(deinterlace_effect->set_int("enable_spatial_interlacing_check", 0));
+               ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
+               tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
+               expect_equal(striped_data, out_data, width, height * 2);
+       }
+}
+
+}  // namespace movit