Wildmeshing Toolkit
Loading...
Searching...
No Matches
Image.cpp
Go to the documentation of this file.
1#include "Image.hpp"
2
3#include <stb_image.h>
4#include <stb_image_write.h>
6#include "load_image_exr.hpp"
7#include "save_image_exr.hpp"
8
10namespace {
11float modulo(double x, double n)
12{
13 float y = fmod(x, n);
14 if (y < 0) {
15 y += n;
16 }
17 return y;
18}
19
20unsigned char double_to_unsignedchar(const double d)
21{
22 return round(std::max(std::min(1., d), 0.) * 255);
23}
24} // namespace
25
26int Image::get_coordinate(const int x, const WrappingMode mode) const
27{
28 auto size = std::max(width(), height());
29 assert(-size < x && x < 2 * size);
30 switch (mode) {
31 case WrappingMode::REPEAT: return (x + size) % size;
32
34 if (x < 0)
35 return -(x % size);
36 else if (x < size)
37 return x;
38 else
39 return size - 1 - (x - size) % size;
40
41 case WrappingMode::CLAMP_TO_EDGE: return std::clamp(x, 0, size - 1);
42 default: return (x + size) % size;
43 }
44}
45
46std::pair<int, int> Image::get_pixel_index(const double& u, const double& v) const
47{
48 int w = width();
49 int h = height();
50 auto size = std::max(w, h);
51 // x, y are between 0 and 1
52 auto x = u * size;
53 auto y = v * size;
54 const auto sx = static_cast<int>(std::floor(x - 0.5));
55 const auto sy = static_cast<int>(std::floor(y - 0.5));
56
57 return {sx, sy};
58}
59
60// set an image to have same value as the analytical function and save it to the file given
62 const std::function<float(const double&, const double&)>& f,
63 WrappingMode mode_x,
64 WrappingMode mode_y)
65{
66 int h = height();
67 int w = width();
68
69 m_image.resize(h, w);
70
71 for (int i = 0; i < h; i++) {
72 for (int j = 0; j < w; j++) {
73 double u, v;
74 u = (static_cast<double>(j) + 0.5) / static_cast<double>(w);
75 v = (static_cast<double>(i) + 0.5) / static_cast<double>(h);
76 m_image(i, j) = f(u, v);
77 }
78 }
79 set_wrapping_mode(mode_x, mode_y);
80 return true;
81}
82
83// save to hdr or exr
84bool Image::save(const std::filesystem::path& path) const
85{
86 wmtk::logger().trace("[save_image_hdr] start \"{}\"", path.string());
87 int w = width();
88 int h = height();
89 std::vector<float> buffer;
90 buffer.resize(w * h);
91
92 for (auto i = 0; i < h; i++) {
93 for (auto j = 0; j < w; j++) {
94 buffer[i * w + j] = m_image(i, j);
95 }
96 }
97 if (path.extension() == ".hdr") {
98 auto res = stbi_write_hdr(path.string().c_str(), w, h, 1, buffer.data());
99 assert(res);
100 } else if (path.extension() == ".exr") {
101 auto res = save_image_exr_red_channel(w, h, buffer, path);
102 } else {
103 wmtk::logger().trace("[save_image_hdr] format doesn't support \"{}\"", path.string());
104 return false;
105 }
106
107 wmtk::logger().trace("[save_image] done \"{}\"", path.string());
108
109 return true;
110}
111
112// load from hdr or exr
114 const std::filesystem::path& path,
115 const WrappingMode mode_x,
116 const WrappingMode mode_y)
117{
118 int w, h, channels;
119 channels = 1;
120 std::vector<float> buffer;
121 if (path.extension() == ".exr") {
122 std::tie(w, h, buffer) = load_image_exr_red_channel(path);
123 assert(!buffer.empty());
124 } else if (path.extension() == ".hdr") {
125 auto res = stbi_loadf(path.string().c_str(), &w, &h, &channels, 1);
126 buffer.assign(res, res + w * h);
127 } else {
128 wmtk::logger().trace("[load_image] format doesn't support \"{}\"", path.string());
129 return;
130 }
131
132 m_image.resize(w, h);
133
134 for (int i = 0, k = 0; i < h; i++) {
135 for (int j = 0; j < w; j++) {
136 m_image(i, j) = buffer[k++];
137 }
138 }
139 m_image.colwise().reverseInPlace();
140 set_wrapping_mode(mode_x, mode_y);
141}
142
143// down sample a image to size/2 by size/2
144// used for mipmap construction
146{
147 auto h = height();
148 auto w = width();
149 Image low_res_image(h / 2, w / 2);
150 for (int r = 0; r < h / 2; r++) {
151 for (int c = 0; c < w / 2; c++) {
152 low_res_image.set(
153 r,
154 c,
155 (m_image(r * 2, c * 2) + m_image(r * 2, c * 2 + 1) + m_image(r * 2 + 1, c * 2) +
156 m_image(r * 2 + 1, c * 2 + 1)) /
157 4.);
158 }
159 }
160 return low_res_image;
161}
162
164 double normalization_scale,
165 const Eigen::Matrix<double, 1, 3>& offset,
166 const std::filesystem::path& position_path,
167 const std::filesystem::path& normal_path,
168 const std::filesystem::path& height_path,
169 float min_height,
170 float max_height)
171{
172 assert(std::filesystem::exists(position_path));
173 auto [w_p, h_p, index_red_p, index_green_p, index_blue_p, buffer_r_p, buffer_g_p, buffer_b_p] =
174 load_image_exr_split_3channels(position_path);
175
176 auto buffer_size = buffer_r_p.size();
177 std::vector<float> buffer_r_d(buffer_size);
178 std::vector<float> buffer_g_d(buffer_size);
179 std::vector<float> buffer_b_d(buffer_size);
180
181 if (std::filesystem::exists(normal_path) && std::filesystem::exists(height_path)) {
182 // Load normal + heightmap and compute displaced positions.
183 auto
184 [w_n,
185 h_n,
186 index_red_n,
187 index_green_n,
188 index_blue_n,
189 buffer_r_n,
190 buffer_g_n,
191 buffer_b_n] = load_image_exr_split_3channels(normal_path);
192 auto
193 [w_h,
194 h_h,
195 index_red_h,
196 index_green_h,
197 index_blue_h,
198 buffer_r_h,
199 buffer_g_h,
200 buffer_b_h] = load_image_exr_split_3channels(height_path);
201 assert(buffer_r_p.size() == buffer_r_n.size());
202 assert(buffer_r_p.size() == buffer_r_h.size());
203 assert(buffer_r_p.size() == buffer_g_p.size());
204 assert(buffer_r_p.size() == buffer_b_p.size());
205 auto scale = [&](float h) { return min_height * (1.f - h) + max_height * h; };
206 // displaced = positions * normalization_scale + heights * (2.0 * normals - 1.0) - offset
207 for (int i = 0; i < buffer_size; i++) {
208 buffer_r_d[i] = buffer_r_p[i] * normalization_scale +
209 scale(buffer_r_h[i]) * (2.0 * buffer_r_n[i] - 1.0) - offset[0];
210 buffer_g_d[i] = buffer_g_p[i] * normalization_scale +
211 scale(buffer_g_h[i]) * (2.0 * buffer_g_n[i] - 1.0) - offset[1];
212 buffer_b_d[i] = buffer_b_p[i] * normalization_scale +
213 scale(buffer_b_h[i]) * (2.0 * buffer_b_n[i] - 1.0) - offset[2];
214 }
215 } else {
216 // Missing heightmap info: we use the position map as our displaced coordinates.
217 wmtk::logger().info("No heightmap provided: using positions as displaced coordinates.");
218 // displaced = positions * normalization_scale - offset
219 for (int i = 0; i < buffer_size; i++) {
220 buffer_r_d[i] = buffer_r_p[i] * normalization_scale - offset[0];
221 buffer_g_d[i] = buffer_g_p[i] * normalization_scale - offset[1];
222 buffer_b_d[i] = buffer_b_p[i] * normalization_scale - offset[2];
223 }
224 }
225
226 return {{
227 buffer_to_image(buffer_r_d, w_p, h_p),
228 buffer_to_image(buffer_g_d, w_p, h_p),
229 buffer_to_image(buffer_b_d, w_p, h_p),
230 }};
231}
232
233void split_and_save_3channels(const std::filesystem::path& path)
234{
235 int w, h, channels, index_red, index_blue, index_green;
236 channels = 1;
237 std::vector<float> buffer_r, buffer_g, buffer_b;
238 if (path.extension() == ".exr") {
239 std::tie(w, h, index_red, index_green, index_blue, buffer_r, buffer_g, buffer_b) =
241 assert(!buffer_r.empty());
242 assert(!buffer_g.empty());
243 assert(!buffer_b.empty());
244 } else {
245 logger().error("[split_image] format doesn't support \"{}\"", path.string());
246 return;
247 }
248 const std::filesystem::path directory = path.parent_path();
249 const std::string file = path.stem().string();
250 const std::filesystem::path path_r = directory / (file + "_r.exr");
251 const std::filesystem::path path_g = directory / (file + "_g.exr");
252 const std::filesystem::path path_b = directory / (file + "_b.exr");
253 // just saves single channel data to red channel
254 auto res = save_image_exr_red_channel(w, h, buffer_r, path_r);
255 assert(res);
256 res = save_image_exr_red_channel(w, h, buffer_g, path_g);
257 assert(res);
258 res = save_image_exr_red_channel(w, h, buffer_b, path_b);
259 assert(res);
260}
261
262Image buffer_to_image(const std::vector<float>& buffer, int w, int h)
263{
264 Image image(w, h);
265 for (int i = 0, k = 0; i < h; i++) {
266 for (int j = 0; j < w; j++) {
267 image.set(h - i - 1, j, buffer[k++]);
268 }
269 }
270 return image;
271}
272
273std::array<Image, 3> load_rgb_image(const std::filesystem::path& path)
274{
275 int w, h, channels, index_red, index_blue, index_green;
276 channels = 1;
277 std::vector<float> buffer_r, buffer_g, buffer_b;
278 if (path.extension() == ".exr") {
279 std::tie(w, h, index_red, index_green, index_blue, buffer_r, buffer_g, buffer_b) =
281 assert(!buffer_r.empty());
282 assert(!buffer_g.empty());
283 assert(!buffer_b.empty());
284 } else {
285 wmtk::logger().error("[load_rgb_image] format doesn't support \"{}\"", path.string());
286 exit(-1);
287 }
288 return {{
289 buffer_to_image(buffer_r, w, h),
290 buffer_to_image(buffer_g, w, h),
291 buffer_to_image(buffer_b, w, h),
292 }};
293}
294} // namespace wmtk::components::adaptive_tessellation::image
int get_coordinate(const int x, const WrappingMode mode) const
Definition Image.cpp:26
void load(const std::filesystem::path &path, WrappingMode mode_x, WrappingMode mode_y)
Definition Image.cpp:113
void set_wrapping_mode(WrappingMode mode_x, WrappingMode mode_y)
Definition Image.hpp:60
std::pair< int, int > get_pixel_index(const double &u, const double &v) const
Definition Image.cpp:46
bool save(const std::filesystem::path &path) const
Definition Image.cpp:84
bool set(const std::function< float(const double &, const double &)> &f, const WrappingMode mode_x=WrappingMode::CLAMP_TO_EDGE, const WrappingMode mode_y=WrappingMode::CLAMP_TO_EDGE)
Definition Image.cpp:61
std::array< Image, 3 > load_rgb_image(const std::filesystem::path &path)
Definition Image.cpp:273
std::array< Image, 3 > combine_position_normal_texture(double normalization_scale, const Eigen::Matrix< double, 1, 3 > &offset, const std::filesystem::path &position_path, const std::filesystem::path &normal_path, const std::filesystem::path &height_path, float min_height, float max_height)
Definition Image.cpp:163
auto load_image_exr_red_channel(const std::filesystem::path &path) -> std::tuple< size_t, size_t, std::vector< float > >
Image buffer_to_image(const std::vector< float > &buffer, int w, int h)
Definition Image.cpp:262
auto load_image_exr_split_3channels(const std::filesystem::path &path) -> std::tuple< size_t, size_t, int, int, int, std::vector< float >, std::vector< float >, std::vector< float > >
bool save_image_exr_red_channel(size_t width, size_t height, const std::vector< float > &data, const std::filesystem::path &path)
void split_and_save_3channels(const std::filesystem::path &path)
Definition Image.cpp:233
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:58