1 // Unit tests for DeconvolutionSharpenEffect.
4 #include "gtest/gtest.h"
5 #include "deconvolution_sharpen_effect.h"
7 TEST(DeconvolutionSharpenEffectTest, IdentityTransformDoesNothing) {
10 float data[size * size] = {
16 float out_data[size * size];
18 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
19 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
20 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
21 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
22 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
23 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
24 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
25 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
27 expect_equal(data, out_data, size, size);
30 TEST(DeconvolutionSharpenEffectTest, DeconvolvesCircularBlur) {
33 // Matches exactly a circular blur kernel with radius 2.0.
34 float data[size * size] = {
35 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,
36 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,
37 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,
38 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,
39 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,
40 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,
41 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,
42 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,
43 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,
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.000000, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0,
49 float expected_data[size * size] = {
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,
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,
56 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,
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, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0,
64 float out_data[size * size];
66 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
67 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
68 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
69 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 2.0f));
70 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.0f));
71 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
72 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
73 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
75 // The limits have to be quite lax; deconvolution is not an exact operation.
76 expect_equal(expected_data, out_data, size, size, 0.15f, 0.005f);
79 TEST(DeconvolutionSharpenEffectTest, DeconvolvesGaussianBlur) {
81 const float sigma = 0.5f;
83 float data[size * size], out_data[size * size];
84 float expected_data[] = {
85 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,
86 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,
87 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,
88 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,
89 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,
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, 1.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, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0,
101 for (int y = 0; y < size; ++y) {
102 for (int x = 0; x < size; ++x) {
103 float z = hypot(x - 6, y - 6);
104 data[y * size + x] = exp(-z*z / (2.0 * sigma * sigma)) / (2.0 * M_PI * sigma * sigma);
105 sum += data[y * size + x];
108 for (int y = 0; y < size; ++y) {
109 for (int x = 0; x < size; ++x) {
110 data[y * size + x] /= sum;
114 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
115 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
116 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
117 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
118 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", sigma));
119 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.0001f));
120 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.0f));
121 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
123 // We don't actually need to adjust the limits here; deconvolution of
124 // this kernel is pretty much exact.
125 expect_equal(expected_data, out_data, size, size);
128 TEST(DeconvolutionSharpenEffectTest, NoiseAndCorrelationControlsReduceNoiseBoosting) {
130 const float sigma = 0.5f;
132 float data[size * size], out_data[size * size];
133 float expected_data[size * size] = {
134 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,
135 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,
136 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,
137 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,
138 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,
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, 1.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, 0.000000, 0.000000, 0.000000, 0.0, 0.0, 0.0, 0.0,
151 for (int y = 0; y < size; ++y) {
152 for (int x = 0; x < size; ++x) {
153 float z = hypot(x - 6, y - 6);
154 data[y * size + x] = exp(-z*z / (2.0 * sigma * sigma)) / (2.0 * M_PI * sigma * sigma);
155 sum += data[y * size + x];
158 for (int y = 0; y < size; ++y) {
159 for (int x = 0; x < size; ++x) {
160 data[y * size + x] /= sum;
164 // Corrupt with some uniform noise.
166 for (int i = 0; i < size * size; ++i) {
167 data[i] += 0.1 * ((float)rand() / RAND_MAX - 0.5);
170 EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
171 Effect *deconvolution_effect = tester.get_chain()->add_effect(new DeconvolutionSharpenEffect());
172 ASSERT_TRUE(deconvolution_effect->set_int("matrix_size", 5));
173 ASSERT_TRUE(deconvolution_effect->set_float("circle_radius", 0.0f));
174 ASSERT_TRUE(deconvolution_effect->set_float("gaussian_radius", 0.5f));
175 ASSERT_TRUE(deconvolution_effect->set_float("correlation", 0.5f));
176 ASSERT_TRUE(deconvolution_effect->set_float("noise", 0.1f));
177 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
179 float sumsq_in = 0.0f, sumsq_out = 0.0f;
180 for (int i = 0; i < size * size; ++i) {
181 sumsq_in += data[i] * data[i];
182 sumsq_out += out_data[i] * out_data[i];
185 // The limits have to be quite lax; deconvolution is not an exact operation.
186 // We special-case the center sample since it's the one with the largest error
187 // almost no matter what we do, so we don't want that to be the dominating
188 // factor in the outlier tests.
189 int center = size / 2;
190 EXPECT_GT(out_data[center * size + center], 0.5f);
191 out_data[center * size + center] = 1.0f;
192 expect_equal(expected_data, out_data, size, size, 0.20f, 0.005f);
194 // Check that we didn't boost total energy (which in this case means the noise) more than 10%.
195 EXPECT_LT(sumsq_out, sumsq_in * 1.1f);