Add a unit test for the colorspace conversions.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 14 Oct 2012 10:29:55 +0000 (12:29 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 14 Oct 2012 10:29:55 +0000 (12:29 +0200)
.gitignore
Makefile
colorspace_conversion_effect_test.cpp [new file with mode: 0644]

index afc81dc..e119ad8 100644 (file)
@@ -11,6 +11,7 @@ demo
 effect_chain_test
 gamma_compression_effect_test
 gamma_expansion_effect_test
+colorspace_conversion_effect_test
 mix_effect_test
 saturation_effect_test
 deconvolution_sharpen_effect_test
index 42d8cc6..12bd209 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@ TESTS=effect_chain_test
 TESTS += mix_effect_test
 TESTS += gamma_expansion_effect_test
 TESTS += gamma_compression_effect_test
+TESTS += colorspace_conversion_effect_test
 TESTS += saturation_effect_test
 TESTS += deconvolution_sharpen_effect_test
 TESTS += blur_effect_test
@@ -68,6 +69,8 @@ gamma_expansion_effect_test: gamma_expansion_effect_test.o $(TEST_OBJS) libmovit
        $(CXX) -o $@ $^ $(LDFLAGS)
 gamma_compression_effect_test: gamma_compression_effect_test.o $(TEST_OBJS) libmovit.a
        $(CXX) -o $@ $^ $(LDFLAGS)
+colorspace_conversion_effect_test: colorspace_conversion_effect_test.o $(TEST_OBJS) libmovit.a
+       $(CXX) -o $@ $^ $(LDFLAGS)
 saturation_effect_test: saturation_effect_test.o $(TEST_OBJS) libmovit.a
        $(CXX) -o $@ $^ $(LDFLAGS)
 deconvolution_sharpen_effect_test: deconvolution_sharpen_effect_test.o $(TEST_OBJS) libmovit.a
diff --git a/colorspace_conversion_effect_test.cpp b/colorspace_conversion_effect_test.cpp
new file mode 100644 (file)
index 0000000..34793ab
--- /dev/null
@@ -0,0 +1,228 @@
+// Unit tests for ColorSpaceConversionEffect.
+
+#include "test_util.h"
+#include "gtest/gtest.h"
+#include "colorspace_conversion_effect.h"
+
+TEST(ColorSpaceConversionEffectTest, Reversible) {
+       float data[] = {
+               0.0f, 0.0f, 0.0f, 1.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               1.0f, 0.0f, 0.0f, 1.0f,
+               0.0f, 1.0f, 0.0f, 1.0f,
+               0.0f, 0.0f, 1.0f, 1.0f,
+               0.0f, 1.0f, 1.0f, 0.5f,
+       };
+       float temp_data[4 * 6], out_data[4 * 6];
+
+       {
+               EffectChainTester tester(data, 1, 6, FORMAT_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
+               tester.run(temp_data, GL_RGBA, COLORSPACE_REC_601_525, GAMMA_LINEAR);
+       }
+       {
+               EffectChainTester tester(temp_data, 1, 6, FORMAT_RGBA, COLORSPACE_REC_601_525, GAMMA_LINEAR);
+               tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
+       }
+
+       expect_equal(data, out_data, 4, 6);
+}
+
+TEST(ColorSpaceConversionEffectTest, sRGB_Primaries) {
+       float data[] = {
+               0.0f, 0.0f, 0.0f, 1.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               1.0f, 0.0f, 0.0f, 1.0f,
+               0.0f, 1.0f, 0.0f, 1.0f,
+               0.0f, 0.0f, 1.0f, 1.0f,
+       };
+       float out_data[4 * 5];
+
+       EffectChainTester tester(data, 1, 5, FORMAT_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.run(out_data, GL_RGBA, COLORSPACE_XYZ, GAMMA_LINEAR);
+
+       // Black should stay black.
+       EXPECT_FLOAT_EQ(0.0f, out_data[0 * 4 + 0]);
+       EXPECT_FLOAT_EQ(0.0f, out_data[0 * 4 + 1]);
+       EXPECT_FLOAT_EQ(0.0f, out_data[0 * 4 + 2]);
+       EXPECT_FLOAT_EQ(1.0f, out_data[0 * 4 + 3]);
+
+       // White point should be D65.
+       // XYZ values from http://en.wikipedia.org/wiki/CIE_Standard_Illuminant_D65.
+       EXPECT_NEAR(0.9505, out_data[1 * 4 + 0], 1e-3);
+       EXPECT_NEAR(1.0000, out_data[1 * 4 + 1], 1e-3);
+       EXPECT_NEAR(1.0889, out_data[1 * 4 + 2], 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[1 * 4 + 3]);
+
+       float white_xyz_sum = out_data[1 * 4 + 0] + out_data[1 * 4 + 1] + out_data[1 * 4 + 2];
+       float white_x = out_data[1 * 4 + 0] / white_xyz_sum;
+       float white_y = out_data[1 * 4 + 1] / white_xyz_sum;
+       EXPECT_NEAR(0.3127, white_x, 1e-3);
+       EXPECT_NEAR(0.3290, white_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[1 * 4 + 3]);
+
+       // Convert the primaries from XYZ to xyz, and compare to the references
+       // given by Rec. 709 (which are shared with sRGB).
+
+       float red_xyz_sum = out_data[2 * 4 + 0] + out_data[2 * 4 + 1] + out_data[2 * 4 + 2];
+       float red_x = out_data[2 * 4 + 0] / red_xyz_sum;
+       float red_y = out_data[2 * 4 + 1] / red_xyz_sum;
+       EXPECT_NEAR(0.640, red_x, 1e-3);
+       EXPECT_NEAR(0.330, red_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[2 * 4 + 3]);
+
+       float green_xyz_sum = out_data[3 * 4 + 0] + out_data[3 * 4 + 1] + out_data[3 * 4 + 2];
+       float green_x = out_data[3 * 4 + 0] / green_xyz_sum;
+       float green_y = out_data[3 * 4 + 1] / green_xyz_sum;
+       EXPECT_NEAR(0.300, green_x, 1e-3);
+       EXPECT_NEAR(0.600, green_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[3 * 4 + 3]);
+
+       float blue_xyz_sum = out_data[4 * 4 + 0] + out_data[4 * 4 + 1] + out_data[4 * 4 + 2];
+       float blue_x = out_data[4 * 4 + 0] / blue_xyz_sum;
+       float blue_y = out_data[4 * 4 + 1] / blue_xyz_sum;
+       EXPECT_NEAR(0.150, blue_x, 1e-3);
+       EXPECT_NEAR(0.060, blue_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[4 * 4 + 3]);
+}
+
+TEST(ColorSpaceConversionEffectTest, Rec601_525_Primaries) {
+       float data[] = {
+               0.0f, 0.0f, 0.0f, 1.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               1.0f, 0.0f, 0.0f, 1.0f,
+               0.0f, 1.0f, 0.0f, 1.0f,
+               0.0f, 0.0f, 1.0f, 1.0f,
+       };
+       float out_data[4 * 5];
+
+       EffectChainTester tester(data, 1, 5, FORMAT_RGBA, COLORSPACE_REC_601_525, GAMMA_LINEAR);
+       tester.run(out_data, GL_RGBA, COLORSPACE_XYZ, GAMMA_LINEAR);
+
+       // Black should stay black.
+       EXPECT_FLOAT_EQ(0.0f, out_data[0 * 4 + 0]);
+       EXPECT_FLOAT_EQ(0.0f, out_data[0 * 4 + 1]);
+       EXPECT_FLOAT_EQ(0.0f, out_data[0 * 4 + 2]);
+       EXPECT_FLOAT_EQ(1.0f, out_data[0 * 4 + 3]);
+
+       // Convert the primaries from XYZ to xyz, and compare to the references
+       // given by Rec. 601.
+       float white_xyz_sum = out_data[1 * 4 + 0] + out_data[1 * 4 + 1] + out_data[1 * 4 + 2];
+       float white_x = out_data[1 * 4 + 0] / white_xyz_sum;
+       float white_y = out_data[1 * 4 + 1] / white_xyz_sum;
+       EXPECT_NEAR(0.3127, white_x, 1e-3);
+       EXPECT_NEAR(0.3290, white_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[1 * 4 + 3]);
+
+       float red_xyz_sum = out_data[2 * 4 + 0] + out_data[2 * 4 + 1] + out_data[2 * 4 + 2];
+       float red_x = out_data[2 * 4 + 0] / red_xyz_sum;
+       float red_y = out_data[2 * 4 + 1] / red_xyz_sum;
+       EXPECT_NEAR(0.630, red_x, 1e-3);
+       EXPECT_NEAR(0.340, red_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[2 * 4 + 3]);
+
+       float green_xyz_sum = out_data[3 * 4 + 0] + out_data[3 * 4 + 1] + out_data[3 * 4 + 2];
+       float green_x = out_data[3 * 4 + 0] / green_xyz_sum;
+       float green_y = out_data[3 * 4 + 1] / green_xyz_sum;
+       EXPECT_NEAR(0.310, green_x, 1e-3);
+       EXPECT_NEAR(0.595, green_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[3 * 4 + 3]);
+
+       float blue_xyz_sum = out_data[4 * 4 + 0] + out_data[4 * 4 + 1] + out_data[4 * 4 + 2];
+       float blue_x = out_data[4 * 4 + 0] / blue_xyz_sum;
+       float blue_y = out_data[4 * 4 + 1] / blue_xyz_sum;
+       EXPECT_NEAR(0.155, blue_x, 1e-3);
+       EXPECT_NEAR(0.070, blue_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[4 * 4 + 3]);
+}
+
+TEST(ColorSpaceConversionEffectTest, Rec601_625_Primaries) {
+       float data[] = {
+               0.0f, 0.0f, 0.0f, 1.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               1.0f, 0.0f, 0.0f, 1.0f,
+               0.0f, 1.0f, 0.0f, 1.0f,
+               0.0f, 0.0f, 1.0f, 1.0f,
+       };
+       float out_data[4 * 5];
+
+       EffectChainTester tester(data, 1, 5, FORMAT_RGBA, COLORSPACE_REC_601_625, GAMMA_LINEAR);
+       tester.run(out_data, GL_RGBA, COLORSPACE_XYZ, GAMMA_LINEAR);
+
+       // Black should stay black.
+       EXPECT_FLOAT_EQ(0.0f, out_data[0 * 4 + 0]);
+       EXPECT_FLOAT_EQ(0.0f, out_data[0 * 4 + 1]);
+       EXPECT_FLOAT_EQ(0.0f, out_data[0 * 4 + 2]);
+       EXPECT_FLOAT_EQ(1.0f, out_data[0 * 4 + 3]);
+
+       // Convert the primaries from XYZ to xyz, and compare to the references
+       // given by Rec. 601.
+       float white_xyz_sum = out_data[1 * 4 + 0] + out_data[1 * 4 + 1] + out_data[1 * 4 + 2];
+       float white_x = out_data[1 * 4 + 0] / white_xyz_sum;
+       float white_y = out_data[1 * 4 + 1] / white_xyz_sum;
+       EXPECT_NEAR(0.3127, white_x, 1e-3);
+       EXPECT_NEAR(0.3290, white_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[1 * 4 + 3]);
+
+       float red_xyz_sum = out_data[2 * 4 + 0] + out_data[2 * 4 + 1] + out_data[2 * 4 + 2];
+       float red_x = out_data[2 * 4 + 0] / red_xyz_sum;
+       float red_y = out_data[2 * 4 + 1] / red_xyz_sum;
+       EXPECT_NEAR(0.640, red_x, 1e-3);
+       EXPECT_NEAR(0.330, red_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[2 * 4 + 3]);
+
+       float green_xyz_sum = out_data[3 * 4 + 0] + out_data[3 * 4 + 1] + out_data[3 * 4 + 2];
+       float green_x = out_data[3 * 4 + 0] / green_xyz_sum;
+       float green_y = out_data[3 * 4 + 1] / green_xyz_sum;
+       EXPECT_NEAR(0.290, green_x, 1e-3);
+       EXPECT_NEAR(0.600, green_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[3 * 4 + 3]);
+
+       float blue_xyz_sum = out_data[4 * 4 + 0] + out_data[4 * 4 + 1] + out_data[4 * 4 + 2];
+       float blue_x = out_data[4 * 4 + 0] / blue_xyz_sum;
+       float blue_y = out_data[4 * 4 + 1] / blue_xyz_sum;
+       EXPECT_NEAR(0.150, blue_x, 1e-3);
+       EXPECT_NEAR(0.060, blue_y, 1e-3);
+       EXPECT_FLOAT_EQ(1.0f, out_data[4 * 4 + 3]);
+}
+
+TEST(ColorSpaceConversionEffectTest, sRGBToRec601_525) {
+       float data[] = {
+               0.0f, 0.0f, 0.0f, 1.0f,
+               1.0f, 1.0f, 1.0f, 1.0f,
+               1.0f, 0.0f, 0.0f, 1.0f,
+               0.0f, 1.0f, 0.0f, 1.0f,
+               0.0f, 0.0f, 1.0f, 1.0f,
+               0.0f, 1.0f, 1.0f, 0.5f,
+       };
+
+       // I have to admit that most of these come from the code itself;
+       // however, they do make sense if you look at the two gamuts
+       // in xy space.
+       float expected_data[] = {
+               // Black should stay black.
+               0.0f, 0.0f, 0.0f, 1.0f,
+
+               // White should stay white (both use the D65 white point).
+               1.0f, 1.0f, 1.0f, 1.0f,
+
+               // sRGB red is slightly out-of-gamut for Rec. 601/525.
+               1.064f, -0.020f, 0.0f, 1.0f,
+
+               // Green too.
+               -0.055f, 1.036f, 0.004f, 1.0f,
+
+               // The blues are much closer; it _is_ still out-of-gamut,
+               // but not actually more saturated (farther from the
+               // white point).
+               -0.010f, -0.017f, 0.994f, 1.0f,
+
+               // Cyan is a mix of green and blue. Note: The alpha is kept.
+               -0.065f, 1.0195f, 0.998f, 0.5f,
+       };
+       float out_data[4 * 6];
+
+       EffectChainTester tester(data, 1, 6, FORMAT_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.run(out_data, GL_RGBA, COLORSPACE_REC_601_525, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, 4, 6);
+}