]> git.sesse.net Git - movit/blob - deconvolution_sharpen_effect_test.cpp
Fix an issue with invalid texture number usage in measure_texel_subpixel_precision().
[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 }