Lagrange
Loading...
Searching...
No Matches
RawInputImage.impl.h
1/*
2 * Copyright 2020 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <lagrange/image/RawInputImage.h>
15#include <lagrange/utils/warning.h>
16
17namespace lagrange {
18namespace image {
19
20
22template <typename Archive>
23inline void RawInputImage::serialize_impl(Archive& ar)
24{
26
27 // basic parameters
28 ar.object([&](auto& ar) {
29 ar("width") & m_width;
30 ar("height") & m_height;
31 ar("row_byte_stride") & m_row_byte_stride;
32 ar("pixel_precision") & m_pixel_precision;
33 ar("color_space") & m_color_space;
34 ar("tex_format") & m_tex_format;
35 ar("wrap_u") & m_wrap_u;
36 ar("wrap_v") & m_wrap_v;
37 ar("storage_format") & m_storage_format;
38
39 // aligned with sizeof(SerializationStorageOfRawInputImage::Scalar)
40 // because lerida does not support array of byte
41 // using ArrayScalar = SerializationStorageOfRawInputImage::Scalar;
42 using ArrayScalar = int32_t;
43 const bool padding_required = (0 != ((get_row_stride() * m_height) % sizeof(ArrayScalar)));
44 const size_t aligned_image_size =
45 get_row_stride() * m_height / sizeof(ArrayScalar) + (padding_required ? 1 : 0);
46
47 // create a buffer pixel storage
48 std::vector<unsigned char> buf(aligned_image_size * sizeof(ArrayScalar));
49 ArrayScalar* data = reinterpret_cast<ArrayScalar*>(buf.data());
50 if (ar.is_input()) {
51 set_pixel_data_buffer(std::move(buf));
52 } else {
53 std::copy_n(
54 static_cast<const unsigned char*>(get_pixel_data()) - get_pixel_data_offset(),
55 get_row_stride() * m_height,
56 buf.data());
57 }
58
59 // serialize pixels
60 ar("pixels").template array_of<ArrayScalar>(
61 aligned_image_size, // <-- only used during serialization
62 [&](size_t /*size*/) { // <-- only used during deserialization
63 // memory is already allocated above
64 },
65 [&](size_t i, auto& ar) { ar& data[i]; });
66 });
67
68 LA_IGNORE_SHADOW_WARNING_END
69}
70
71template <
72 typename TexcoordScalar,
73 typename Scalar,
74 uint32_t NumChannels,
75 typename InternalScalar,
76 uint32_t InternalNumChannels>
77typename RawInputImage::PixelTraits<Scalar, NumChannels>::Pixel RawInputImage::sample(
78 const TexcoordScalar u,
79 const TexcoordScalar v,
80 const texture_filtering filtering) const
81{
82 using ReturnTraits = PixelTraits<Scalar, NumChannels>;
83 using ReturnPixel = typename ReturnTraits::Pixel;
84 using InternalTraits = PixelTraits<InternalScalar, InternalNumChannels>;
85 using InternalPixel = typename InternalTraits::Pixel;
86
87 ReturnPixel rtn = ReturnTraits::zero();
88
89 // const for TexcoordScalar
90 const TexcoordScalar _0 = static_cast<TexcoordScalar>(0);
91 const TexcoordScalar _1 = static_cast<TexcoordScalar>(1);
92 const TexcoordScalar _05 = static_cast<TexcoordScalar>(0.5);
93
94 // static sanity check
95 static_assert(
96 std::is_same<TexcoordScalar, float>::value || std::is_same<TexcoordScalar, double>::value,
97 "RawInputImage::sample, texcoord must be float or double");
98 static_assert(
99 std::is_same<Scalar, unsigned char>::value || std::is_same<Scalar, Eigen::half>::value ||
100 std::is_same<Scalar, float>::value || std::is_same<Scalar, double>::value,
101 "RawInputImage::sample, unsupported Scalar");
102 static_assert(
103 0u < NumChannels && NumChannels <= (static_cast<uint32_t>(texture_format::max) >> 8u),
104 "RawInputImage::sample, unsupported NumChannels");
105 static_assert(
106 std::is_same<InternalScalar, unsigned char>::value ||
107 std::is_same<InternalScalar, Eigen::half>::value ||
108 std::is_same<InternalScalar, float>::value ||
109 std::is_same<InternalScalar, double>::value,
110 "RawInputImage::sample, unsupported InternalScalar");
111 static_assert(
112 0u < InternalNumChannels &&
113 InternalNumChannels <= (static_cast<uint32_t>(texture_format::max) >> 8u),
114 "RawInputImage::sample, unsupported InternalNumChannels");
115
116 // runtime sanity check
117 if (sizeof(InternalScalar) != get_size_precision()) {
118 throw std::runtime_error("RawInputImage::sample, INTERNAL_PRECISION is incorrect!");
119 }
120 if (InternalNumChannels != get_num_channels()) {
121 throw std::runtime_error("RawInputImage::sample, InternalNumChannels is incorrect!");
122 }
123
124 // something have not been implemented yet
125 if (std::is_same<Scalar, Eigen::half>::value ||
126 std::is_same<InternalScalar, Eigen::half>::value) {
127 throw std::runtime_error("RawInputImage::sample, half is not implemented yet!");
128 }
129 if (color_space::sRGB == m_color_space) {
130 throw std::runtime_error("RawInputImage::sample, sRGB is not implemented yet!");
131 }
132
133 // wrap coord
134 auto wrap = [_0, _1](const TexcoordScalar c, const wrap_mode m) -> TexcoordScalar {
135 if (_0 <= c && c <= _1) {
136 return c;
137 } else if (wrap_mode::repeat == m) {
138 return c - std::floor(c);
139 } else if (wrap_mode::clamp == m) {
140 return std::clamp(c, _0, _1);
141 } else if (wrap_mode::mirror == m) {
142 const auto f = std::floor(c);
143 if (static_cast<int>(f) & 1) {
144 return _1 - (c - f);
145 } else {
146 return c - f;
147 }
148 } else {
149 throw std::runtime_error("unknown wrap_mode!");
150 return c;
151 }
152 };
153 auto _u = wrap(u, m_wrap_u);
154 auto _v = wrap(v, m_wrap_v);
155 assert(_0 <= _u && _u <= _1);
156 assert(_0 <= _v && _v <= _1);
157
158 // v is bottom up in image space, convert it to memory space if storage is topdown
159 if (image_storage_format::first_pixel_row_at_top == m_storage_format) {
160 _v = _1 - _v;
161 }
162
163 // memory
164 const auto size_pixel = get_size_pixel();
165 const auto row_stride = get_row_stride();
166 const auto ptr = static_cast<const unsigned char*>(get_pixel_data()) - get_pixel_data_offset();
167 auto get_pixel = [&](size_t x, size_t y) -> InternalPixel {
168 assert(x < m_width && y < m_height);
169 return *reinterpret_cast<const InternalPixel*>(ptr + x * size_pixel + y * row_stride);
170 };
171
172 // sampling
173 const auto x_coord = _u * static_cast<TexcoordScalar>(m_width);
174 const auto y_coord = _v * static_cast<TexcoordScalar>(m_height);
175 const auto min_num_channels = std::min(NumChannels, InternalNumChannels);
176 if (filtering == texture_filtering::nearest) {
177 const auto x = std::clamp(
178 static_cast<size_t>(x_coord),
179 static_cast<size_t>(0),
180 static_cast<size_t>(m_width - 1));
181 const auto y = std::clamp(
182 static_cast<size_t>(y_coord),
183 static_cast<size_t>(0),
184 static_cast<size_t>(m_height - 1));
185 const auto pix = get_pixel(x, y);
186 for (uint32_t i = 0; i < min_num_channels; ++i) {
187 ReturnTraits::coeff(rtn, i) = static_cast<Scalar>(InternalTraits::coeff(pix, i));
188 }
189 } else if (filtering == texture_filtering::bilinear) {
190 auto sample_coord =
191 [=](const TexcoordScalar coord,
192 const size_t size,
193 const wrap_mode wrap_) -> std::tuple<size_t, size_t, TexcoordScalar> {
194 assert(_0 <= coord && coord <= static_cast<TexcoordScalar>(size));
195 size_t coord0, coord1;
196 TexcoordScalar t;
197 if (coord <= _05) {
198 coord0 = wrap_mode::repeat == wrap_ ? size - 1 : 0;
199 coord1 = 0;
200 t = _05 + coord;
201 } else if (coord + _05 >= static_cast<TexcoordScalar>(size)) {
202 coord0 = size - 1;
203 coord1 = wrap_mode::repeat == wrap_ ? 0 : size - 1;
204 t = coord - (static_cast<TexcoordScalar>(coord0) + _05);
205 } else {
206 assert(1 < size);
207 coord0 = std::min(size - 2, static_cast<size_t>(coord - _05));
208 coord1 = coord0 + 1;
209 t = coord - (static_cast<TexcoordScalar>(coord0) + _05);
210 }
211 return std::make_tuple(coord0, coord1, t);
212 };
213 const auto sample_x = sample_coord(x_coord, m_width, m_wrap_u);
214 const auto sample_y = sample_coord(y_coord, m_height, m_wrap_v);
215 InternalPixel pix[4] = {
216 get_pixel(std::get<0>(sample_x), std::get<0>(sample_y)),
217 get_pixel(std::get<1>(sample_x), std::get<0>(sample_y)),
218 get_pixel(std::get<0>(sample_x), std::get<1>(sample_y)),
219 get_pixel(std::get<1>(sample_x), std::get<1>(sample_y))};
220 TexcoordScalar weight[4] = {
221 (_1 - std::get<2>(sample_x)) * (_1 - std::get<2>(sample_y)),
222 std::get<2>(sample_x) * (_1 - std::get<2>(sample_y)),
223 (_1 - std::get<2>(sample_x)) * std::get<2>(sample_y),
224 std::get<2>(sample_x) * std::get<2>(sample_y)};
225 for (uint32_t i = 0; i < min_num_channels; ++i) {
226 TexcoordScalar sum = _0;
227 for (int j = 0; j < 4; ++j) {
228 sum += static_cast<TexcoordScalar>(InternalTraits::coeff(pix[j], i)) * weight[j];
229 }
230 ReturnTraits::coeff(rtn, i) = static_cast<Scalar>(sum);
231 }
232 } else {
233 throw std::runtime_error("RawInputImage::sample, unknown filtering type!");
234 }
235
236 return rtn;
237}
238
239template <typename TexcoordScalar, typename Scalar, uint32_t NumChannels>
240typename RawInputImage::PixelTraits<Scalar, NumChannels>::Pixel RawInputImage::sample(
241 const TexcoordScalar u,
242 const TexcoordScalar v,
243 const texture_filtering filtering) const
244{
245 using ReturnTraits = PixelTraits<Scalar, NumChannels>;
246 using ReturnPixel = typename ReturnTraits::Pixel;
247
248 ReturnPixel rtn = ReturnTraits::zero();
249
250 // the function is implemented assuming the max channels is 4
251 static_assert(
252 4u == (static_cast<uint32_t>(texture_format::max) >> 8u),
253 "the max channels are not 4 any more, need to update the following code");
254
255 const auto channels = get_num_channels();
256
257#define LA_RAWINPUTIMAGE_SAMPLE_IMPL(P, C, V) \
258 if (P == m_pixel_precision && C == channels) { \
259 return sample<TexcoordScalar, Scalar, NumChannels, V, C>(u, v, filtering); \
260 }
261
262 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::byte_p, 1u, unsigned char)
263 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::byte_p, 2u, unsigned char)
264 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::byte_p, 3u, unsigned char)
265 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::byte_p, 4u, unsigned char)
266
267 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::half_p, 1u, Eigen::half)
268 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::half_p, 2u, Eigen::half)
269 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::half_p, 3u, Eigen::half)
270 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::half_p, 4u, Eigen::half)
271
272 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::single_p, 1u, float)
273 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::single_p, 2u, float)
274 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::single_p, 3u, float)
275 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::single_p, 4u, float)
276
277 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::double_p, 1u, double)
278 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::double_p, 2u, double)
279 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::double_p, 3u, double)
280 LA_RAWINPUTIMAGE_SAMPLE_IMPL(precision_semantic::double_p, 4u, double)
281
282#undef LA_RAWINPUTIMAGE_SAMPLE_IMPL
283 throw std::runtime_error(
284 "RawInputImage::sample, cannot deduce InternalScalar or InternalNumChannels!");
285 return rtn;
286}
287
288template <typename TexcoordScalar, typename Scalar, uint32_t NumChannels>
289typename RawInputImage::PixelTraits<Scalar, NumChannels>::Pixel RawInputImage::sample_float(
290 const TexcoordScalar u,
291 const TexcoordScalar v,
292 const texture_filtering filtering) const
293{
294 static_assert(std::is_floating_point_v<Scalar>, "Pixel scalar type must be floating point");
295
296 auto pixel = sample<TexcoordScalar, Scalar, NumChannels>(u, v, filtering);
297 return (get_pixel_precision() == precision_semantic::byte_p ? pixel / Scalar(255) : pixel);
298}
299
300} // namespace image
301} // namespace lagrange
@ first_pixel_row_at_top
+y down
Definition RawInputImage.h:67
@ sRGB
pixel format is in sRGB space
Definition RawInputImage.h:48
wrap_mode
Definition RawInputImage.h:70
@ repeat
tiles the texture
Definition RawInputImage.h:71
@ clamp
clamp to the last pixel on the edge
Definition RawInputImage.h:72
@ mirror
tiles the texture, mirrored when the integer coord is odd
Definition RawInputImage.h:73
PixelTraits< Scalar, NumChannels >::Pixel sample(const TexcoordScalar u, const TexcoordScalar v, const texture_filtering filtering=texture_filtering::bilinear) const
Sample an image at a given location.
Definition RawInputImage.impl.h:240
PixelTraits< Scalar, NumChannels >::Pixel sample_float(const TexcoordScalar u, const TexcoordScalar v, const texture_filtering filtering=texture_filtering::bilinear) const
Similar to sample, but remaps the output values from [0, 255] to [0, 1] if the pixel data is stored a...
Definition RawInputImage.impl.h:289
texture_filtering
Definition RawInputImage.h:76
@ bilinear
bilinear interpolation
Definition RawInputImage.h:78
@ nearest
nearest neighbor interpolation
Definition RawInputImage.h:77
@ half_p
16-bit floating point
Definition RawInputImage.h:41
@ byte_p
8-bit uint8_t
Definition RawInputImage.h:40
@ single_p
32-bit floating point
Definition RawInputImage.h:42
@ double_p
64-bit floating point
Definition RawInputImage.h:43
void serialize_impl(Archive &ar)
Serialization.
Definition RawInputImage.impl.h:23
void set_pixel_data_buffer(std::vector< unsigned char > pixel_data_buffer)
m_local_pixel_data takes over pixel_data_buffer m_pixel_data is set to nullptr
Definition RawInputImage.cpp:35
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
#define LA_IGNORE_SHADOW_WARNING_BEGIN
Ignore shadow warnings.
Definition warning.h:68
Basic image data structure.
Main namespace for Lagrange.
Definition RawInputImage.h:222