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"
15 TEST(DeconvolutionSharpenEffectTest, IdentityTransformDoesNothing) {
18 float data[size * size] = {
24 float out_data[size * size];
26 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
27 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
28 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
29 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
30 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
31 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
32 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
33 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
35 expect_equal(data, out_data, size, size);
38 TEST(DeconvolutionSharpenEffectTest, DeconvolvesCircularBlur) {
41 // Matches exactly a circular blur kernel with radius 2.0.
42 float data[size * size] = {
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.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0,
46 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,
47 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,
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.038115, 0.079577, 0.079577, 0.079577, 0.038115, 0.0, 0.0, 0.0, 0.0,
50 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,
51 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,
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,
54 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 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 float expected_data[size * size] = {
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, 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, 1.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,
69 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 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,
72 float out_data[size * size];
74 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
75 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
76 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
77 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 2.0f));
78 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
79 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
80 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
81 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
83 // The limits have to be quite lax; deconvolution is not an exact operation.
84 expect_equal(expected_data, out_data, size, size, 0.15f, 0.005f);
87 TEST(DeconvolutionSharpenEffectTest, DeconvolvesGaussianBlur) {
89 const float sigma = 0.5f;
91 float data[size * size], out_data[size * size];
92 float expected_data[] = {
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, 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, 1.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,
104 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,
105 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,
109 for (int y = 0; y < size; ++y) {
110 for (int x = 0; x < size; ++x) {
111 float z = hypot(x - 6, y - 6);
112 data[y * size + x] = exp(-z*z / (2.0 * sigma * sigma)) / (2.0 * M_PI * sigma * sigma);
113 sum += data[y * size + x];
116 for (int y = 0; y < size; ++y) {
117 for (int x = 0; x < size; ++x) {
118 data[y * size + x] /= sum;
122 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
123 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
124 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
125 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
126 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", sigma));
127 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
128 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
129 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
131 // We don't actually need to adjust the limits here; deconvolution of
132 // this kernel is pretty much exact.
133 expect_equal(expected_data, out_data, size, size);
136 TEST(DeconvolutionSharpenEffectTest, NoiseAndCorrelationControlsReduceNoiseBoosting) {
138 const float sigma = 0.5f;
140 float data[size * size], out_data[size * size];
141 float expected_data[size * size] = {
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, 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, 1.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,
153 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,
154 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,
159 for (int y = 0; y < size; ++y) {
160 for (int x = 0; x < size; ++x) {
161 float z = hypot(x - 6, y - 6);
162 data[y * size + x] = exp(-z*z / (2.0 * sigma * sigma)) / (2.0 * M_PI * sigma * sigma);
163 sum += data[y * size + x];
166 for (int y = 0; y < size; ++y) {
167 for (int x = 0; x < size; ++x) {
168 data[y * size + x] /= sum;
172 // Corrupt with some uniform noise.
174 for (int i = 0; i < size * size; ++i) {
175 data[i] += 0.1 * ((float)rand() / RAND_MAX - 0.5);
178 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
179 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
180 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
181 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
182 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.5f));
183 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.5f));
184 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.1f));
185 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
187 float sumsq_in = 0.0f, sumsq_out = 0.0f;
188 for (int i = 0; i < size * size; ++i) {
189 sumsq_in += data[i] * data[i];
190 sumsq_out += out_data[i] * out_data[i];
193 // The limits have to be quite lax; deconvolution is not an exact operation.
194 // We special-case the center sample since it's the one with the largest error
195 // almost no matter what we do, so we don't want that to be the dominating
196 // factor in the outlier tests.
197 int center = size / 2;
198 EXPECT_GT(out_data[center * size + center], 0.5f);
199 out_data[center * size + center] = 1.0f;
200 expect_equal(expected_data, out_data, size, size, 0.20f, 0.005f);
202 // Check that we didn't boost total energy (which in this case means the noise) more than 10%.
203 EXPECT_LT(sumsq_out, sumsq_in * 1.1f);
206 TEST(DeconvolutionSharpenEffectTest, CircularDeconvolutionKeepsAlpha) {
207 // Somewhat bigger, to make sure we are much bigger than the matrix size.
210 float data[size * size * 4];
211 float out_data[size * size];
212 float expected_alpha[size * size];
214 // Checkerbox pattern.
215 for (int y = 0; y < size; ++y) {
216 for (int x = 0; x < size; ++x) {
218 data[(y * size + x) * 4 + 0] = c;
219 data[(y * size + x) * 4 + 1] = c;
220 data[(y * size + x) * 4 + 2] = c;
221 data[(y * size + x) * 4 + 3] = 1.0;
222 expected_alpha[y * size + x] = 1.0;
226 EffectChainTester tester(data, size, size, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
227 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
228 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
229 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 2.0f));
230 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
231 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
232 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
233 tester.run(out_data, GL_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
235 expect_equal(expected_alpha, out_data, size, size);