1 // Unit tests for DeconvolutionSharpenEffect.
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"
12 TEST(DeconvolutionSharpenEffectTest, IdentityTransformDoesNothing) {
15 float data[size * size] = {
21 float out_data[size * size];
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);
32 expect_equal(data, out_data, size, size);
35 TEST(DeconvolutionSharpenEffectTest, DeconvolvesCircularBlur) {
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,
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,
69 float out_data[size * size];
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);
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);
84 TEST(DeconvolutionSharpenEffectTest, DeconvolvesGaussianBlur) {
86 const float sigma = 0.5f;
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,
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];
113 for (int y = 0; y < size; ++y) {
114 for (int x = 0; x < size; ++x) {
115 data[y * size + x] /= sum;
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);
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);
133 TEST(DeconvolutionSharpenEffectTest, NoiseAndCorrelationControlsReduceNoiseBoosting) {
135 const float sigma = 0.5f;
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,
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];
163 for (int y = 0; y < size; ++y) {
164 for (int x = 0; x < size; ++x) {
165 data[y * size + x] /= sum;
169 // Corrupt with some uniform noise.
171 for (int i = 0; i < size * size; ++i) {
172 data[i] += 0.1 * ((float)rand() / RAND_MAX - 0.5);
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);
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];
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);
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);