1 // Unit tests for DeconvolutionSharpenEffect.
7 #include "deconvolution_sharpen_effect.h"
8 #include "effect_chain.h"
9 #include "gtest/gtest.h"
10 #include "image_format.h"
11 #include "test_util.h"
13 TEST(DeconvolutionSharpenEffectTest, IdentityTransformDoesNothing) {
16 float data[size * size] = {
22 float out_data[size * size];
24 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
25 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
26 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
27 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
28 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
29 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
30 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
31 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
33 expect_equal(data, out_data, size, size);
36 TEST(DeconvolutionSharpenEffectTest, DeconvolvesCircularBlur) {
39 // Matches exactly a circular blur kernel with radius 2.0.
40 float data[size * size] = {
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.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0,
45 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,
46 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,
47 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,
48 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,
49 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,
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 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,
55 float expected_data[size * size] = {
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, 0.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, 1.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 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,
70 float out_data[size * size];
72 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
73 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
74 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
75 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 2.0f));
76 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
77 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
78 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
79 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
81 // The limits have to be quite lax; deconvolution is not an exact operation.
82 expect_equal(expected_data, out_data, size, size, 0.15f, 0.005f);
85 TEST(DeconvolutionSharpenEffectTest, DeconvolvesGaussianBlur) {
87 const float sigma = 0.5f;
89 float data[size * size], out_data[size * size];
90 float expected_data[] = {
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, 0.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, 1.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 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,
107 for (int y = 0; y < size; ++y) {
108 for (int x = 0; x < size; ++x) {
109 float z = hypot(x - 6, y - 6);
110 data[y * size + x] = exp(-z*z / (2.0 * sigma * sigma)) / (2.0 * M_PI * sigma * sigma);
111 sum += data[y * size + x];
114 for (int y = 0; y < size; ++y) {
115 for (int x = 0; x < size; ++x) {
116 data[y * size + x] /= sum;
120 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
121 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
122 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
123 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
124 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", sigma));
125 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
126 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
127 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
129 // We don't actually need to adjust the limits here; deconvolution of
130 // this kernel is pretty much exact.
131 expect_equal(expected_data, out_data, size, size);
134 TEST(DeconvolutionSharpenEffectTest, NoiseAndCorrelationControlsReduceNoiseBoosting) {
136 const float sigma = 0.5f;
138 float data[size * size], out_data[size * size];
139 float expected_data[size * size] = {
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, 0.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, 1.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 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,
157 for (int y = 0; y < size; ++y) {
158 for (int x = 0; x < size; ++x) {
159 float z = hypot(x - 6, y - 6);
160 data[y * size + x] = exp(-z*z / (2.0 * sigma * sigma)) / (2.0 * M_PI * sigma * sigma);
161 sum += data[y * size + x];
164 for (int y = 0; y < size; ++y) {
165 for (int x = 0; x < size; ++x) {
166 data[y * size + x] /= sum;
170 // Corrupt with some uniform noise.
172 for (int i = 0; i < size * size; ++i) {
173 data[i] += 0.1 * ((float)rand() / RAND_MAX - 0.5);
176 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
177 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
178 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
179 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
180 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.5f));
181 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.5f));
182 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.1f));
183 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
185 float sumsq_in = 0.0f, sumsq_out = 0.0f;
186 for (int i = 0; i < size * size; ++i) {
187 sumsq_in += data[i] * data[i];
188 sumsq_out += out_data[i] * out_data[i];
191 // The limits have to be quite lax; deconvolution is not an exact operation.
192 // We special-case the center sample since it's the one with the largest error
193 // almost no matter what we do, so we don't want that to be the dominating
194 // factor in the outlier tests.
195 int center = size / 2;
196 EXPECT_GT(out_data[center * size + center], 0.5f);
197 out_data[center * size + center] = 1.0f;
198 expect_equal(expected_data, out_data, size, size, 0.20f, 0.005f);
200 // Check that we didn't boost total energy (which in this case means the noise) more than 10%.
201 EXPECT_LT(sumsq_out, sumsq_in * 1.1f);
204 TEST(DeconvolutionSharpenEffectTest, CircularDeconvolutionKeepsAlpha) {
205 // Somewhat bigger, to make sure we are much bigger than the matrix size.
208 float data[size * size * 4];
209 float out_data[size * size];
210 float expected_alpha[size * size];
212 // Checkerbox pattern.
213 for (int y = 0; y < size; ++y) {
214 for (int x = 0; x < size; ++x) {
216 data[(y * size + x) * 4 + 0] = c;
217 data[(y * size + x) * 4 + 1] = c;
218 data[(y * size + x) * 4 + 2] = c;
219 data[(y * size + x) * 4 + 3] = 1.0;
220 expected_alpha[y * size + x] = 1.0;
224 EffectChainTester tester(data, size, size, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
225 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
226 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
227 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 2.0f));
228 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
229 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
230 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
231 tester.run(out_data, GL_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
233 expect_equal(expected_alpha, out_data, size, size);