TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
53 / 53
100.0%
Branches:
55 / 57
96.5%
Functions:
4 / 4
100.0%

raster/gaussian_blur.cpp
Line Branch Exec Source
1 #include "terraingen/raster/gaussian_blur.h"
2
3 #include "terraingen/util/checks.h"
4
5 #include <cmath>
6 #include <cstddef>
7 #include <stdexcept>
8 #include <vector>
9
10 namespace terraingen {
11
12 namespace {
13
14 /// Unnormalized Gaussian weights of standard deviation `sigma` cells out to three sigma.
15 24 std::vector<double> GaussianKernel(double sigma) {
16
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 23 times.
24 if (sigma == 0.0) {
17
1/1
✓ Branch 1 taken 1 time.
3 return {1.0};
18 }
19 23 const auto radius = static_cast<std::ptrdiff_t>(std::ceil(3.0 * sigma));
20
1/1
✓ Branch 1 taken 23 times.
23 std::vector<double> kernel(static_cast<std::size_t>(2 * radius + 1));
21
2/2
✓ Branch 0 taken 313 times.
✓ Branch 1 taken 23 times.
336 for (std::ptrdiff_t i = -radius; i <= radius; ++i) {
22 313 const auto x = static_cast<double>(i);
23 313 kernel[static_cast<std::size_t>(i + radius)] = std::exp(-0.5 * x * x / (sigma * sigma));
24 }
25 23 return kernel;
26 23 }
27
28 /// Convolves `values` along one axis of a `width` x `height` grid with `kernel`, truncated at the
29 /// grid's edges.
30 48 std::vector<double> Convolve(const std::vector<double>& values, std::size_t width,
31 std::size_t height, const std::vector<double>& kernel,
32 bool alongRows) {
33 48 const auto radius = static_cast<std::ptrdiff_t>(kernel.size() / 2);
34
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 24 times.
48 const auto length = static_cast<std::ptrdiff_t>(alongRows ? width : height);
35
1/1
✓ Branch 2 taken 48 times.
48 std::vector<double> result(values.size(), 0.0);
36
2/2
✓ Branch 0 taken 960 times.
✓ Branch 1 taken 48 times.
1008 for (std::size_t row = 0; row < height; ++row) {
37
2/2
✓ Branch 0 taken 31292 times.
✓ Branch 1 taken 960 times.
32252 for (std::size_t col = 0; col < width; ++col) {
38
2/2
✓ Branch 0 taken 15646 times.
✓ Branch 1 taken 15646 times.
31292 const auto position = static_cast<std::ptrdiff_t>(alongRows ? col : row);
39 31292 double sum = 0.0;
40
2/2
✓ Branch 0 taken 506892 times.
✓ Branch 1 taken 31292 times.
538184 for (std::ptrdiff_t k = -radius; k <= radius; ++k) {
41 506892 const std::ptrdiff_t at = position + k;
42
4/4
✓ Branch 0 taken 472470 times.
✓ Branch 1 taken 34422 times.
✓ Branch 2 taken 34422 times.
✓ Branch 3 taken 438048 times.
506892 if (at < 0 || at >= length) {
43 68844 continue;
44 }
45
2/2
✓ Branch 0 taken 223962 times.
✓ Branch 1 taken 214086 times.
438048 const std::size_t index = alongRows ? row * width + static_cast<std::size_t>(at)
46 214086 : static_cast<std::size_t>(at) * width + col;
47 438048 sum += kernel[static_cast<std::size_t>(k + radius)] * values[index];
48 }
49 31292 result[row * width + col] = sum;
50 }
51 }
52 48 return result;
53 }
54
55 } // namespace
56
57 10 Raster<float> GaussianBlur(const Raster<float>& raster, double sigmaPixels) {
58 10 return GaussianBlur(raster, sigmaPixels, sigmaPixels);
59 }
60
61 16 Raster<float> GaussianBlur(const Raster<float>& raster, double sigmaColumns, double sigmaRows) {
62
5/6
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 14 times.
16 if (!(IsNonNegative(sigmaColumns) && IsNonNegative(sigmaRows))) {
63
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("Gaussian sigma must be finite and not negative");
64 }
65
1/1
✓ Branch 1 taken 14 times.
14 Raster<float> result = raster;
66
7/8
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 12 times.
14 if ((sigmaColumns == 0.0 && sigmaRows == 0.0) || raster.Empty()) {
67 2 return result;
68 }
69 12 const std::size_t width = raster.Width();
70 12 const std::size_t height = raster.Height();
71
1/1
✓ Branch 2 taken 12 times.
24 std::vector<double> weighted(raster.Size());
72
1/1
✓ Branch 2 taken 12 times.
12 std::vector<double> mask(raster.Size());
73
2/2
✓ Branch 1 taken 7823 times.
✓ Branch 2 taken 12 times.
7835 for (std::size_t i = 0; i < raster.Size(); ++i) {
74 7823 const float value = raster.Values()[i];
75 7823 const bool valid = !raster.IsNoData(value);
76
2/2
✓ Branch 0 taken 7821 times.
✓ Branch 1 taken 2 times.
7823 mask[i] = valid ? 1.0 : 0.0;
77
2/2
✓ Branch 0 taken 7821 times.
✓ Branch 1 taken 2 times.
7823 weighted[i] = valid ? static_cast<double>(value) : 0.0;
78 }
79
1/1
✓ Branch 1 taken 12 times.
12 const std::vector<double> across = GaussianKernel(sigmaColumns);
80
1/1
✓ Branch 1 taken 12 times.
12 const std::vector<double> down = GaussianKernel(sigmaRows);
81 const std::vector<double> numerator =
82
2/2
✓ Branch 1 taken 12 times.
✓ Branch 4 taken 12 times.
12 Convolve(Convolve(weighted, width, height, across, true), width, height, down, false);
83 const std::vector<double> denominator =
84
2/2
✓ Branch 1 taken 12 times.
✓ Branch 4 taken 12 times.
12 Convolve(Convolve(mask, width, height, across, true), width, height, down, false);
85
2/2
✓ Branch 1 taken 7823 times.
✓ Branch 2 taken 12 times.
7835 for (std::size_t i = 0; i < raster.Size(); ++i) {
86
2/2
✓ Branch 1 taken 7821 times.
✓ Branch 2 taken 2 times.
7823 if (mask[i] > 0.0) {
87 7821 result.Values()[i] = static_cast<float>(numerator[i] / denominator[i]);
88 }
89 }
90 12 return result;
91 12 }
92
93 } // namespace terraingen
94