]> git.sesse.net Git - movit/blob - ycbcr_422interleaved_input_test.cpp
Add a unit test for luma interpolation in YCbCr422InterleavedInput.
[movit] / ycbcr_422interleaved_input_test.cpp
1 // Unit tests for YCbCr422InterleavedInput.
2
3 #include <epoxy/gl.h>
4 #include <stddef.h>
5
6 #include "effect_chain.h"
7 #include "gtest/gtest.h"
8 #include "test_util.h"
9 #include "util.h"
10 #include "resize_effect.h"
11 #include "ycbcr_422interleaved_input.h"
12
13 namespace movit {
14
15 // Adapted from the Simple444 test from YCbCrInputTest.
16 TEST(YCbCr422InterleavedInputTest, Simple422) {
17         const int width = 2;
18         const int height = 5;
19
20         // Pure-color test inputs, calculated with the formulas in Rec. 601
21         // section 2.5.4.
22         unsigned char uyvy[width * height * 2] = {
23                 /*U=*/128, /*Y=*/ 16, /*V=*/128, /*Y=*/ 16,
24                 /*U=*/128, /*Y=*/235, /*V=*/128, /*Y=*/235,
25                 /*U=*/ 90, /*Y=*/ 81, /*V=*/240, /*Y=*/ 81,
26                 /*U=*/ 54, /*Y=*/145, /*V=*/ 34, /*Y=*/145,
27                 /*U=*/240, /*Y=*/ 41, /*V=*/110, /*Y=*/ 41,
28         };
29
30         float expected_data[4 * width * height] = {
31                 0.0, 0.0, 0.0, 1.0,   0.0, 0.0, 0.0, 1.0,
32                 1.0, 1.0, 1.0, 1.0,   1.0, 1.0, 1.0, 1.0,
33                 1.0, 0.0, 0.0, 1.0,   1.0, 0.0, 0.0, 1.0,
34                 0.0, 1.0, 0.0, 1.0,   0.0, 1.0, 0.0, 1.0,
35                 0.0, 0.0, 1.0, 1.0,   0.0, 0.0, 1.0, 1.0,
36         };
37         float out_data[4 * width * height];
38
39         EffectChainTester tester(NULL, width, height);
40
41         ImageFormat format;
42         format.color_space = COLORSPACE_sRGB;
43         format.gamma_curve = GAMMA_sRGB;
44
45         YCbCrFormat ycbcr_format;
46         ycbcr_format.luma_coefficients = YCBCR_REC_601;
47         ycbcr_format.full_range = false;
48         ycbcr_format.chroma_subsampling_x = 2;
49         ycbcr_format.chroma_subsampling_y = 1;
50         ycbcr_format.cb_x_position = 0.0f;  // Doesn't really matter here, since Y is constant.
51         ycbcr_format.cb_y_position = 0.5f;
52         ycbcr_format.cr_x_position = 0.0f;
53         ycbcr_format.cr_y_position = 0.5f;
54
55         YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
56         input->set_pixel_data(uyvy);
57         tester.get_chain()->add_input(input);
58
59         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
60
61         // Y'CbCr isn't 100% accurate (the input values are rounded),
62         // so we need some leeway.
63         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
64 }
65
66 TEST(YCbCr422InterleavedInputTest, LumaLinearInterpolation) {
67         const int width = 4;
68         const int height = 1;
69         const int out_width = width * 3;
70
71         // Black, white, black and then gray.
72         unsigned char uyvy[width * height * 2] = {
73                 /*U=*/128, /*Y=*/ 16,
74                 /*V=*/128, /*Y=*/235,
75                 /*U=*/128, /*Y=*/ 16,
76                 /*V=*/128, /*Y=*/128,
77         };
78
79         float expected_data[out_width * height] = {
80                 0.0, /**/0.0, 0.333, 0.667, /**/1.0, 0.667, 0.333, /**/0.0, 0.167, 0.333, /**/0.5, 0.5
81         };
82         float out_data[out_width * height];
83
84         EffectChainTester tester(NULL, out_width, height);
85
86         ImageFormat format;
87         format.color_space = COLORSPACE_sRGB;
88         format.gamma_curve = GAMMA_sRGB;
89
90         YCbCrFormat ycbcr_format;
91         ycbcr_format.luma_coefficients = YCBCR_REC_601;
92         ycbcr_format.full_range = false;
93         ycbcr_format.chroma_subsampling_x = 2;
94         ycbcr_format.chroma_subsampling_y = 1;
95         ycbcr_format.cb_x_position = 0.0f;  // Doesn't really matter here, since U/V are constant.
96         ycbcr_format.cb_y_position = 0.5f;
97         ycbcr_format.cr_x_position = 0.0f;
98         ycbcr_format.cr_y_position = 0.5f;
99
100         YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
101         input->set_pixel_data(uyvy);
102         tester.get_chain()->add_input(input);
103
104         ResizeEffect *upscale = new ResizeEffect();
105         ASSERT_TRUE(upscale->set_int("width", out_width));
106         ASSERT_TRUE(upscale->set_int("height", height));
107         tester.get_chain()->add_effect(upscale);
108
109         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
110
111         // Y'CbCr isn't 100% accurate (the input values are rounded),
112         // so we need some leeway.
113         expect_equal(expected_data, out_data, out_width, height, 0.025, 0.002);
114 }
115
116 // Adapted from the YCbCrInput test of the same name.
117 TEST(YCbCr422InterleavedInputTest, DifferentCbAndCrPositioning) {
118         const int width = 4;
119         const int height = 4;
120
121         unsigned char uyvy[width * height * 2] = {
122                 /*U=*/ 64, /*Y=*/126, /*V=*/ 48, /*Y=*/126,  /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126,
123                 /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126,  /*U=*/192, /*Y=*/126, /*V=*/208, /*Y=*/126,
124                 /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126,  /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126,
125                 /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126,  /*U=*/128, /*Y=*/126, /*V=*/128, /*Y=*/126,
126         };
127
128         // Chroma samples in this case are always co-sited with a luma sample;
129         // their associated color values and position are marked off in comments.
130         float expected_data_blue[width * height] = {
131                    0.000 /* 0.0 */, 0.250,           0.500 /* 0.5 */, 0.500, 
132                    0.500 /* 0.5 */, 0.750,           1.000 /* 1.0 */, 1.000, 
133                    0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 0.500, 
134                    0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 0.500, 
135         };
136         float expected_data_red[width * height] = {
137                    0.000,           0.000 /* 0.0 */, 0.250,           0.500 /* 0.5 */, 
138                    0.500,           0.500 /* 0.5 */, 0.750,           1.000 /* 1.0 */, 
139                    0.500,           0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 
140                    0.500,           0.500 /* 0.5 */, 0.500,           0.500 /* 0.5 */, 
141         };
142         float out_data[width * height];
143
144         EffectChainTester tester(NULL, width, height);
145
146         ImageFormat format;
147         format.color_space = COLORSPACE_sRGB;
148         format.gamma_curve = GAMMA_sRGB;
149
150         YCbCrFormat ycbcr_format;
151         ycbcr_format.luma_coefficients = YCBCR_REC_601;
152         ycbcr_format.full_range = false;
153         ycbcr_format.chroma_subsampling_x = 2;
154         ycbcr_format.chroma_subsampling_y = 1;
155         ycbcr_format.cb_x_position = 0.0f;
156         ycbcr_format.cb_y_position = 0.5f;
157         ycbcr_format.cr_x_position = 1.0f;
158         ycbcr_format.cr_y_position = 0.5f;
159
160         YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
161         input->set_pixel_data(uyvy);
162         tester.get_chain()->add_input(input);
163
164         // Y'CbCr isn't 100% accurate (the input values are rounded),
165         // so we need some leeway.
166         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
167         expect_equal(expected_data_red, out_data, width, height, 0.02, 0.002);
168
169         tester.run(out_data, GL_BLUE, COLORSPACE_sRGB, GAMMA_sRGB);
170         expect_equal(expected_data_blue, out_data, width, height, 0.01, 0.001);
171 }
172
173 TEST(YCbCr422InterleavedInputTest, PBO) {
174         const int width = 2;
175         const int height = 5;
176
177         // Pure-color test inputs, calculated with the formulas in Rec. 601
178         // section 2.5.4.
179         unsigned char uyvy[width * height * 2] = {
180                 /*U=*/128, /*Y=*/ 16, /*V=*/128, /*Y=*/ 16,
181                 /*U=*/128, /*Y=*/235, /*V=*/128, /*Y=*/235,
182                 /*U=*/ 90, /*Y=*/ 81, /*V=*/240, /*Y=*/ 81,
183                 /*U=*/ 54, /*Y=*/145, /*V=*/ 34, /*Y=*/145,
184                 /*U=*/240, /*Y=*/ 41, /*V=*/110, /*Y=*/ 41,
185         };
186
187         float expected_data[4 * width * height] = {
188                 0.0, 0.0, 0.0, 1.0,   0.0, 0.0, 0.0, 1.0,
189                 1.0, 1.0, 1.0, 1.0,   1.0, 1.0, 1.0, 1.0,
190                 1.0, 0.0, 0.0, 1.0,   1.0, 0.0, 0.0, 1.0,
191                 0.0, 1.0, 0.0, 1.0,   0.0, 1.0, 0.0, 1.0,
192                 0.0, 0.0, 1.0, 1.0,   0.0, 0.0, 1.0, 1.0,
193         };
194         float out_data[4 * width * height];
195
196         GLuint pbo;
197         glGenBuffers(1, &pbo);
198         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
199         glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, width * height * 2, uyvy, GL_STREAM_DRAW);
200         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
201
202         EffectChainTester tester(NULL, width, height);
203
204         ImageFormat format;
205         format.color_space = COLORSPACE_sRGB;
206         format.gamma_curve = GAMMA_sRGB;
207
208         YCbCrFormat ycbcr_format;
209         ycbcr_format.luma_coefficients = YCBCR_REC_601;
210         ycbcr_format.full_range = false;
211         ycbcr_format.chroma_subsampling_x = 2;
212         ycbcr_format.chroma_subsampling_y = 1;
213         ycbcr_format.cb_x_position = 0.0f;  // Doesn't really matter here, since Y is constant.
214         ycbcr_format.cb_y_position = 0.5f;
215         ycbcr_format.cr_x_position = 0.0f;
216         ycbcr_format.cr_y_position = 0.5f;
217
218         YCbCr422InterleavedInput *input = new YCbCr422InterleavedInput(format, ycbcr_format, width, height);
219         input->set_pixel_data((unsigned char *)BUFFER_OFFSET(0), pbo);
220         tester.get_chain()->add_input(input);
221
222         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_sRGB);
223
224         // Y'CbCr isn't 100% accurate (the input values are rounded),
225         // so we need some leeway.
226         expect_equal(expected_data, out_data, 4 * width, height, 0.025, 0.002);
227
228         glDeleteBuffers(1, &pbo);
229 }
230
231 }  // namespace movit