]> git.sesse.net Git - movit/blob - deconvolution_sharpen_effect_test.cpp
Fix YCbCrInput after the ResourcePool texture changes.
[movit] / deconvolution_sharpen_effect_test.cpp
1 // Unit tests for DeconvolutionSharpenEffect.
2
3 #include <math.h>
4 #include <stdlib.h>
5
6 #include "deconvolution_sharpen_effect.h"
7 #include "effect_chain.h"
8 #include "gtest/gtest.h"
9 #include "image_format.h"
10 #include "test_util.h"
11
12 TEST(DeconvolutionSharpenEffectTest, IdentityTransformDoesNothing) {
13         const int size = 4;
14
15         float data[size * size] = {
16                 0.0, 1.0, 0.0, 1.0,
17                 0.0, 1.0, 1.0, 0.0,
18                 0.0, 0.5, 1.0, 0.5,
19                 0.0, 0.0, 0.0, 0.0,
20         };
21         float out_data[size * size];
22
23         EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
24         Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
25         ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
26         ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
27         ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
28         ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
29         ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
30         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
31
32         expect_equal(data, out_data, size, size);
33 }
34
35 TEST(DeconvolutionSharpenEffectTest, DeconvolvesCircularBlur) {
36         const int size = 13;
37
38         // Matches exactly a circular blur kernel with radius 2.0.
39         float data[size * size] = {
40                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
41                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
42                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
43                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
44                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.017016, 0.038115, 0.017016, 0.000000, 0.0, 0.0, 0.0, 0.0, 
45                 0.0, 0.0, 0.0, 0.0, 0.017016, 0.078381, 0.079577, 0.078381, 0.017016, 0.0, 0.0, 0.0, 0.0, 
46                 0.0, 0.0, 0.0, 0.0, 0.038115, 0.079577, 0.079577, 0.079577, 0.038115, 0.0, 0.0, 0.0, 0.0, 
47                 0.0, 0.0, 0.0, 0.0, 0.017016, 0.078381, 0.079577, 0.078381, 0.017016, 0.0, 0.0, 0.0, 0.0, 
48                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.017016, 0.038115, 0.017016, 0.000000, 0.0, 0.0, 0.0, 0.0, 
49                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
50                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
51                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
52                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
53         };
54         float expected_data[size * size] = {
55                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
56                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
57                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
58                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
59                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
60                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
61                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
62                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
63                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
64                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
65                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
66                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
67                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
68         };
69         float out_data[size * size];
70
71         EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
72         Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
73         ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
74         ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 2.0f));
75         ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
76         ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
77         ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
78         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
79
80         // The limits have to be quite lax; deconvolution is not an exact operation.
81         expect_equal(expected_data, out_data, size, size, 0.15f, 0.005f);
82 }
83
84 TEST(DeconvolutionSharpenEffectTest, DeconvolvesGaussianBlur) {
85         const int size = 13;
86         const float sigma = 0.5f;
87
88         float data[size * size], out_data[size * size];
89         float expected_data[] = {
90                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
91                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
92                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
93                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
94                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
95                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
96                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
97                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
98                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
99                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
100                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
101                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
102                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
103         };
104
105         float sum = 0.0f;       
106         for (int y = 0; y < size; ++y) {
107                 for (int x = 0; x < size; ++x) {
108                         float z = hypot(x - 6, y - 6);
109                         data[y * size + x] = exp(-z*z / (2.0 * sigma * sigma)) / (2.0 * M_PI * sigma * sigma);
110                         sum += data[y * size + x];
111                 }
112         }
113         for (int y = 0; y < size; ++y) {
114                 for (int x = 0; x < size; ++x) {
115                         data[y * size + x] /= sum;
116                 }
117         }
118
119         EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
120         Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
121         ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
122         ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
123         ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", sigma));
124         ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
125         ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
126         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
127
128         // We don't actually need to adjust the limits here; deconvolution of
129         // this kernel is pretty much exact.
130         expect_equal(expected_data, out_data, size, size);
131 }
132
133 TEST(DeconvolutionSharpenEffectTest, NoiseAndCorrelationControlsReduceNoiseBoosting) {
134         const int size = 13;
135         const float sigma = 0.5f;
136
137         float data[size * size], out_data[size * size];
138         float expected_data[size * size] = {
139                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
140                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
141                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
142                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
143                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
144                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
145                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
146                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
147                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
148                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
149                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
150                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
151                 0.0, 0.0, 0.0, 0.0, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 
152         };
153
154         // Gaussian kernel.
155         float sum = 0.0f;       
156         for (int y = 0; y < size; ++y) {
157                 for (int x = 0; x < size; ++x) {
158                         float z = hypot(x - 6, y - 6);
159                         data[y * size + x] = exp(-z*z / (2.0 * sigma * sigma)) / (2.0 * M_PI * sigma * sigma);
160                         sum += data[y * size + x];
161                 }
162         }
163         for (int y = 0; y < size; ++y) {
164                 for (int x = 0; x < size; ++x) {
165                         data[y * size + x] /= sum;
166                 }
167         }
168
169         // Corrupt with some uniform noise.
170         srand(1234);
171         for (int i = 0; i < size * size; ++i) {
172                 data[i] += 0.1 * ((float)rand() / RAND_MAX - 0.5);
173         }
174
175         EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
176         Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
177         ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
178         ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
179         ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.5f));
180         ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.5f));
181         ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.1f));
182         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
183
184         float sumsq_in = 0.0f, sumsq_out = 0.0f;
185         for (int i = 0; i < size * size; ++i) {
186                 sumsq_in += data[i] * data[i];
187                 sumsq_out += out_data[i] * out_data[i];
188         }
189
190         // The limits have to be quite lax; deconvolution is not an exact operation.
191         // We special-case the center sample since it's the one with the largest error
192         // almost no matter what we do, so we don't want that to be the dominating
193         // factor in the outlier tests.
194         int center = size / 2;
195         EXPECT_GT(out_data[center * size + center], 0.5f);
196         out_data[center * size + center] = 1.0f;
197         expect_equal(expected_data, out_data, size, size, 0.20f, 0.005f);
198
199         // Check that we didn't boost total energy (which in this case means the noise) more than 10%.
200         EXPECT_LT(sumsq_out, sumsq_in * 1.1f);
201 }
202
203 TEST(DeconvolutionSharpenEffectTest, CircularDeconvolutionKeepsAlpha) {
204         // Somewhat bigger, to make sure we are much bigger than the matrix size.
205         const int size = 32;
206
207         float data[size * size * 4];
208         float out_data[size * size];
209         float expected_alpha[size * size];
210
211         // Checkerbox pattern.
212         for (int y = 0; y < size; ++y) {
213                 for (int x = 0; x < size; ++x) {
214                         int c = (y ^ x) & 1;
215                         data[(y * size + x) * 4 + 0] = c;
216                         data[(y * size + x) * 4 + 1] = c;
217                         data[(y * size + x) * 4 + 2] = c;
218                         data[(y * size + x) * 4 + 3] = 1.0;
219                         expected_alpha[y * size + x] = 1.0;
220                 }
221         }
222
223         EffectChainTester tester(data, size, size, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
224         Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
225         ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
226         ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 2.0f));
227         ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
228         ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
229         ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
230         tester.run(out_data, GL_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
231
232         expect_equal(expected_alpha, out_data, size, size);
233 }