RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
Spline.h
Go to the documentation of this file.
1/*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2017 Robert Bieber
5 Copyright (C) 2018 Roman Lebedev
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20*/
21
22#pragma once
23
24#include "adt/Casts.h"
25#include "adt/Point.h"
26#include <algorithm>
27#include <cassert>
28#include <cstdint>
29#include <limits>
30#include <type_traits>
31#include <vector>
32
33#ifndef NDEBUG
34#include <functional>
35#endif
36
37namespace rawspeed {
38class iPoint2D;
39
40// This is a Natural Cubic Spline. The second derivative at curve ends are zero.
41// See https://en.wikipedia.org/wiki/Spline_(mathematics)
42// section "Algorithm for computing natural cubic splines"
43
44template <typename T = uint16_t>
45 requires std::is_arithmetic_v<T>
46class Spline final {
47public:
48 using value_type = T;
49
50 // These are the constant factors for each segment of the curve.
51 // Each segment i will have the formula:
52 // f(x) = a[i] + b[i]*(x - x[i]) + c[i]*(x - x[i])^2 + d[i]*(x - x[i])^3
53 struct Segment final {
54 double a;
55 double b;
56 double c;
57 double d;
58 };
59
60private:
63
64 std::vector<int> xCp;
65 std::vector<Segment> segments;
66
67 void prepare() {
68 // Extra values used during computation
69 std::vector<double> h(num_segments);
70 std::vector<double> alpha(num_segments);
71 std::vector<double> mu(num_coords);
72 std::vector<double> z(num_coords);
73
74 for (int i = 0; i < num_segments; i++)
75 h[i] = xCp[i + 1] - xCp[i];
76
77 for (int i = 1; i < num_segments; i++) {
78 Segment& sp = segments[i - 1];
79 Segment& s = segments[i];
80 Segment& sn = segments[i + 1];
81
82 alpha[i] = (3. / h[i]) * (sn.a - s.a) - (3. / h[i - 1]) * (s.a - sp.a);
83 }
84
85 mu[0] = z[0] = 0;
86
87 for (int i = 1; i < num_segments; i++) {
88 const double l = (2 * (xCp[i + 1] - xCp[i - 1])) - (h[i - 1] * mu[i - 1]);
89 mu[i] = h[i] / l;
90 z[i] = (alpha[i] - h[i - 1] * z[i - 1]) / l;
91 }
92
93 z.back() = segments.back().c = 0;
94
95 for (int i = num_segments - 1; i >= 0; i--) {
96 Segment& s = segments[i];
97 Segment& sn = segments[i + 1];
98
99 s.c = z[i] - mu[i] * sn.c;
100 s.b = (sn.a - s.a) / h[i] - h[i] * (sn.c + 2 * s.c) / 3.;
101 s.d = (sn.c - s.c) / (3. * h[i]);
102 }
103
104 // The last segment is nonsensical, and was only used to temporarily store
105 // the a and c to simplify calculations, so drop that 'segment' now
106 segments.pop_back();
107
108 assert(static_cast<typename decltype(segments)::size_type>(num_segments) ==
109 segments.size());
110 }
111
112public:
113 explicit Spline(const std::vector<iPoint2D>& control_points) {
114 assert(control_points.size() >= 2 &&
115 "Need at least two points to interpolate between");
116
117 // Expect the X coords of the curve to start/end at the extreme values
118 assert(control_points.front().x == 0);
119 assert(control_points.back().x == 65535);
120
121 assert(std::adjacent_find(
122 control_points.cbegin(), control_points.cend(),
123 [](const iPoint2D& lhs, const iPoint2D& rhs) -> bool {
124 return std::greater_equal<>()(lhs.x, rhs.x);
125 }) == control_points.cend() &&
126 "The X coordinates must all be strictly increasing");
127
128#ifndef NDEBUG
129 if constexpr (!std::is_floating_point_v<value_type>) {
130 // The Y coords must be limited to the range of value_type
131 std::for_each(control_points.cbegin(), control_points.cend(),
132 [](const iPoint2D& p) {
133 assert(p.y >= std::numeric_limits<value_type>::min());
134 assert(p.y <= std::numeric_limits<value_type>::max());
135 });
136 }
137#endif
138
139 num_coords = implicit_cast<int>(control_points.size());
141
142 xCp.resize(num_coords);
143 segments.resize(num_coords);
144 for (int i = 0; i < num_coords; i++) {
145 xCp[i] = control_points[i].x;
146 segments[i].a = control_points[i].y;
147 }
148
149 prepare();
150 }
151
152 [[nodiscard]] std::vector<Segment> getSegments() const { return segments; }
153
154 [[nodiscard]] std::vector<value_type> calculateCurve() const {
155 std::vector<value_type> curve(65536);
156
157 for (int i = 0; i < num_segments; i++) {
158 const Segment& s = segments[i];
159
160 for (int x = xCp[i]; x <= xCp[i + 1]; x++) {
161 double diff = x - xCp[i];
162 double diff_2 = diff * diff;
163 double diff_3 = diff * diff * diff;
164
165 double interpolated =
166 s.a + (s.b * diff) + (s.c * diff_2) + (s.d * diff_3);
167
168 if constexpr (!std::is_floating_point_v<value_type>) {
169 interpolated = std::max(
170 interpolated, double(std::numeric_limits<value_type>::min()));
171 interpolated = std::min(
172 interpolated, double(std::numeric_limits<value_type>::max()));
173 }
174
175 curve[x] = implicit_cast<T>(interpolated);
176 }
177 }
178
179 return curve;
180 }
181};
182
183} // namespace rawspeed
#define s
assert(dim.area() >=area)
dim x
Definition Common.cpp:50
std::vector< int > xCp
Definition Spline.h:64
std::vector< Segment > segments
Definition Spline.h:65
std::vector< Segment > getSegments() const
Definition Spline.h:152
Spline(const std::vector< iPoint2D > &control_points)
Definition Spline.h:113
void prepare()
Definition Spline.h:67
std::vector< value_type > calculateCurve() const
Definition Spline.h:154
constexpr RAWSPEED_READNONE Ttgt implicit_cast(Tsrc value)
Definition Casts.h:32
throw T(buf.data())