1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
5
6 #ifndef LIB_JXL_IMAGE_OPS_H_
7 #define LIB_JXL_IMAGE_OPS_H_
8
9 // Operations on images.
10
11 #include <algorithm>
12 #include <array>
13 #include <limits>
14 #include <vector>
15
16 #include "lib/jxl/base/profiler.h"
17 #include "lib/jxl/base/status.h"
18 #include "lib/jxl/image.h"
19
20 namespace jxl {
21
22 template <typename T>
CopyImageTo(const Plane<T> & from,Plane<T> * JXL_RESTRICT to)23 void CopyImageTo(const Plane<T>& from, Plane<T>* JXL_RESTRICT to) {
24 PROFILER_ZONE("CopyImage1");
25 JXL_ASSERT(SameSize(from, *to));
26 if (from.ysize() == 0 || from.xsize() == 0) return;
27 for (size_t y = 0; y < from.ysize(); ++y) {
28 const T* JXL_RESTRICT row_from = from.ConstRow(y);
29 T* JXL_RESTRICT row_to = to->Row(y);
30 memcpy(row_to, row_from, from.xsize() * sizeof(T));
31 }
32 }
33
34 // DEPRECATED - prefer to preallocate result.
35 template <typename T>
CopyImage(const Plane<T> & from)36 Plane<T> CopyImage(const Plane<T>& from) {
37 Plane<T> to(from.xsize(), from.ysize());
38 CopyImageTo(from, &to);
39 return to;
40 }
41
42 // Copies `from:rect_from` to `to:rect_to`.
43 template <typename T>
CopyImageTo(const Rect & rect_from,const Plane<T> & from,const Rect & rect_to,Plane<T> * JXL_RESTRICT to)44 void CopyImageTo(const Rect& rect_from, const Plane<T>& from,
45 const Rect& rect_to, Plane<T>* JXL_RESTRICT to) {
46 PROFILER_ZONE("CopyImageR");
47 JXL_DASSERT(SameSize(rect_from, rect_to));
48 JXL_DASSERT(rect_from.IsInside(from));
49 JXL_DASSERT(rect_to.IsInside(*to));
50 if (rect_from.xsize() == 0) return;
51 for (size_t y = 0; y < rect_from.ysize(); ++y) {
52 const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y);
53 T* JXL_RESTRICT row_to = rect_to.Row(to, y);
54 memcpy(row_to, row_from, rect_from.xsize() * sizeof(T));
55 }
56 }
57
58 // DEPRECATED - Returns a copy of the "image" pixels that lie in "rect".
59 template <typename T>
CopyImage(const Rect & rect,const Plane<T> & image)60 Plane<T> CopyImage(const Rect& rect, const Plane<T>& image) {
61 Plane<T> copy(rect.xsize(), rect.ysize());
62 CopyImageTo(rect, image, ©);
63 return copy;
64 }
65
66 // Copies `from:rect_from` to `to:rect_to`.
67 template <typename T>
CopyImageTo(const Rect & rect_from,const Image3<T> & from,const Rect & rect_to,Image3<T> * JXL_RESTRICT to)68 void CopyImageTo(const Rect& rect_from, const Image3<T>& from,
69 const Rect& rect_to, Image3<T>* JXL_RESTRICT to) {
70 PROFILER_ZONE("CopyImageR");
71 JXL_ASSERT(SameSize(rect_from, rect_to));
72 for (size_t c = 0; c < 3; c++) {
73 CopyImageTo(rect_from, from.Plane(c), rect_to, &to->Plane(c));
74 }
75 }
76
77 template <typename T, typename U>
ConvertPlaneAndClamp(const Rect & rect_from,const Plane<T> & from,const Rect & rect_to,Plane<U> * JXL_RESTRICT to)78 void ConvertPlaneAndClamp(const Rect& rect_from, const Plane<T>& from,
79 const Rect& rect_to, Plane<U>* JXL_RESTRICT to) {
80 PROFILER_ZONE("ConvertPlane");
81 JXL_ASSERT(SameSize(rect_from, rect_to));
82 using M = decltype(T() + U());
83 for (size_t y = 0; y < rect_to.ysize(); ++y) {
84 const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y);
85 U* JXL_RESTRICT row_to = rect_to.Row(to, y);
86 for (size_t x = 0; x < rect_to.xsize(); ++x) {
87 row_to[x] =
88 std::min<M>(std::max<M>(row_from[x], std::numeric_limits<U>::min()),
89 std::numeric_limits<U>::max());
90 }
91 }
92 }
93
94 // Copies `from` to `to`.
95 template <typename T>
CopyImageTo(const T & from,T * JXL_RESTRICT to)96 void CopyImageTo(const T& from, T* JXL_RESTRICT to) {
97 return CopyImageTo(Rect(from), from, Rect(*to), to);
98 }
99
100 // Copies `from:rect_from` to `to`.
101 template <typename T>
CopyImageTo(const Rect & rect_from,const T & from,T * JXL_RESTRICT to)102 void CopyImageTo(const Rect& rect_from, const T& from, T* JXL_RESTRICT to) {
103 return CopyImageTo(rect_from, from, Rect(*to), to);
104 }
105
106 // Copies `from` to `to:rect_to`.
107 template <typename T>
CopyImageTo(const T & from,const Rect & rect_to,T * JXL_RESTRICT to)108 void CopyImageTo(const T& from, const Rect& rect_to, T* JXL_RESTRICT to) {
109 return CopyImageTo(Rect(from), from, rect_to, to);
110 }
111
112 // Copies `from:rect_from` to `to:rect_to`; also copies `padding` pixels of
113 // border around `from:rect_from`, in all directions, whenever they are inside
114 // the first image.
115 template <typename T>
CopyImageToWithPadding(const Rect & from_rect,const T & from,size_t padding,const Rect & to_rect,T * to)116 void CopyImageToWithPadding(const Rect& from_rect, const T& from,
117 size_t padding, const Rect& to_rect, T* to) {
118 size_t xextra0 = std::min(padding, from_rect.x0());
119 size_t xextra1 =
120 std::min(padding, from.xsize() - from_rect.x0() - from_rect.xsize());
121 size_t yextra0 = std::min(padding, from_rect.y0());
122 size_t yextra1 =
123 std::min(padding, from.ysize() - from_rect.y0() - from_rect.ysize());
124 JXL_DASSERT(to_rect.x0() >= xextra0);
125 JXL_DASSERT(to_rect.y0() >= yextra0);
126
127 return CopyImageTo(Rect(from_rect.x0() - xextra0, from_rect.y0() - yextra0,
128 from_rect.xsize() + xextra0 + xextra1,
129 from_rect.ysize() + yextra0 + yextra1),
130 from,
131 Rect(to_rect.x0() - xextra0, to_rect.y0() - yextra0,
132 to_rect.xsize() + xextra0 + xextra1,
133 to_rect.ysize() + yextra0 + yextra1),
134 to);
135 }
136
137 // DEPRECATED - prefer to preallocate result.
138 template <typename T>
CopyImage(const Image3<T> & from)139 Image3<T> CopyImage(const Image3<T>& from) {
140 Image3<T> copy(from.xsize(), from.ysize());
141 CopyImageTo(from, ©);
142 return copy;
143 }
144
145 // DEPRECATED - prefer to preallocate result.
146 template <typename T>
CopyImage(const Rect & rect,const Image3<T> & from)147 Image3<T> CopyImage(const Rect& rect, const Image3<T>& from) {
148 Image3<T> to(rect.xsize(), rect.ysize());
149 CopyImageTo(rect, from.Plane(0), to.Plane(0));
150 CopyImageTo(rect, from.Plane(1), to.Plane(1));
151 CopyImageTo(rect, from.Plane(2), to.Plane(2));
152 return to;
153 }
154
155 // Sets "thickness" pixels on each border to "value". This is faster than
156 // initializing the entire image and overwriting valid/interior pixels.
157 template <typename T>
SetBorder(const size_t thickness,const T value,Image3<T> * image)158 void SetBorder(const size_t thickness, const T value, Image3<T>* image) {
159 const size_t xsize = image->xsize();
160 const size_t ysize = image->ysize();
161 // Top: fill entire row
162 for (size_t c = 0; c < 3; ++c) {
163 for (size_t y = 0; y < std::min(thickness, ysize); ++y) {
164 T* JXL_RESTRICT row = image->PlaneRow(c, y);
165 std::fill(row, row + xsize, value);
166 }
167
168 // Bottom: fill entire row
169 for (size_t y = ysize - thickness; y < ysize; ++y) {
170 T* JXL_RESTRICT row = image->PlaneRow(c, y);
171 std::fill(row, row + xsize, value);
172 }
173
174 // Left/right: fill the 'columns' on either side, but only if the image is
175 // big enough that they don't already belong to the top/bottom rows.
176 if (ysize >= 2 * thickness) {
177 for (size_t y = thickness; y < ysize - thickness; ++y) {
178 T* JXL_RESTRICT row = image->PlaneRow(c, y);
179 std::fill(row, row + thickness, value);
180 std::fill(row + xsize - thickness, row + xsize, value);
181 }
182 }
183 }
184 }
185
186 template <class ImageIn, class ImageOut>
Subtract(const ImageIn & image1,const ImageIn & image2,ImageOut * out)187 void Subtract(const ImageIn& image1, const ImageIn& image2, ImageOut* out) {
188 using T = typename ImageIn::T;
189 const size_t xsize = image1.xsize();
190 const size_t ysize = image1.ysize();
191 JXL_CHECK(xsize == image2.xsize());
192 JXL_CHECK(ysize == image2.ysize());
193
194 for (size_t y = 0; y < ysize; ++y) {
195 const T* const JXL_RESTRICT row1 = image1.Row(y);
196 const T* const JXL_RESTRICT row2 = image2.Row(y);
197 T* const JXL_RESTRICT row_out = out->Row(y);
198 for (size_t x = 0; x < xsize; ++x) {
199 row_out[x] = row1[x] - row2[x];
200 }
201 }
202 }
203
204 // In-place.
205 template <typename Tin, typename Tout>
SubtractFrom(const Plane<Tin> & what,Plane<Tout> * to)206 void SubtractFrom(const Plane<Tin>& what, Plane<Tout>* to) {
207 const size_t xsize = what.xsize();
208 const size_t ysize = what.ysize();
209 for (size_t y = 0; y < ysize; ++y) {
210 const Tin* JXL_RESTRICT row_what = what.ConstRow(y);
211 Tout* JXL_RESTRICT row_to = to->Row(y);
212 for (size_t x = 0; x < xsize; ++x) {
213 row_to[x] -= row_what[x];
214 }
215 }
216 }
217
218 // In-place.
219 template <typename Tin, typename Tout>
AddTo(const Plane<Tin> & what,Plane<Tout> * to)220 void AddTo(const Plane<Tin>& what, Plane<Tout>* to) {
221 const size_t xsize = what.xsize();
222 const size_t ysize = what.ysize();
223 for (size_t y = 0; y < ysize; ++y) {
224 const Tin* JXL_RESTRICT row_what = what.ConstRow(y);
225 Tout* JXL_RESTRICT row_to = to->Row(y);
226 for (size_t x = 0; x < xsize; ++x) {
227 row_to[x] += row_what[x];
228 }
229 }
230 }
231
232 template <typename Tin, typename Tout>
AddTo(Rect rectFrom,const Plane<Tin> & what,Rect rectTo,Plane<Tout> * to)233 void AddTo(Rect rectFrom, const Plane<Tin>& what, Rect rectTo,
234 Plane<Tout>* to) {
235 JXL_ASSERT(SameSize(rectFrom, rectTo));
236 const size_t xsize = rectTo.xsize();
237 const size_t ysize = rectTo.ysize();
238 for (size_t y = 0; y < ysize; ++y) {
239 const Tin* JXL_RESTRICT row_what = rectFrom.ConstRow(what, y);
240 Tout* JXL_RESTRICT row_to = rectTo.Row(to, y);
241 for (size_t x = 0; x < xsize; ++x) {
242 row_to[x] += row_what[x];
243 }
244 }
245 }
246
247 // Returns linear combination of two grayscale images.
248 template <typename T>
LinComb(const T lambda1,const Plane<T> & image1,const T lambda2,const Plane<T> & image2)249 Plane<T> LinComb(const T lambda1, const Plane<T>& image1, const T lambda2,
250 const Plane<T>& image2) {
251 const size_t xsize = image1.xsize();
252 const size_t ysize = image1.ysize();
253 JXL_CHECK(xsize == image2.xsize());
254 JXL_CHECK(ysize == image2.ysize());
255 Plane<T> out(xsize, ysize);
256 for (size_t y = 0; y < ysize; ++y) {
257 const T* const JXL_RESTRICT row1 = image1.Row(y);
258 const T* const JXL_RESTRICT row2 = image2.Row(y);
259 T* const JXL_RESTRICT row_out = out.Row(y);
260 for (size_t x = 0; x < xsize; ++x) {
261 row_out[x] = lambda1 * row1[x] + lambda2 * row2[x];
262 }
263 }
264 return out;
265 }
266
267 // Returns a pixel-by-pixel multiplication of image by lambda.
268 template <typename T>
ScaleImage(const T lambda,const Plane<T> & image)269 Plane<T> ScaleImage(const T lambda, const Plane<T>& image) {
270 Plane<T> out(image.xsize(), image.ysize());
271 for (size_t y = 0; y < image.ysize(); ++y) {
272 const T* const JXL_RESTRICT row = image.Row(y);
273 T* const JXL_RESTRICT row_out = out.Row(y);
274 for (size_t x = 0; x < image.xsize(); ++x) {
275 row_out[x] = lambda * row[x];
276 }
277 }
278 return out;
279 }
280
281 // Multiplies image by lambda in-place
282 template <typename T>
ScaleImage(const T lambda,Plane<T> * image)283 void ScaleImage(const T lambda, Plane<T>* image) {
284 for (size_t y = 0; y < image->ysize(); ++y) {
285 T* const JXL_RESTRICT row = image->Row(y);
286 for (size_t x = 0; x < image->xsize(); ++x) {
287 row[x] = lambda * row[x];
288 }
289 }
290 }
291
292 template <typename T>
Product(const Plane<T> & a,const Plane<T> & b)293 Plane<T> Product(const Plane<T>& a, const Plane<T>& b) {
294 Plane<T> c(a.xsize(), a.ysize());
295 for (size_t y = 0; y < a.ysize(); ++y) {
296 const T* const JXL_RESTRICT row_a = a.Row(y);
297 const T* const JXL_RESTRICT row_b = b.Row(y);
298 T* const JXL_RESTRICT row_c = c.Row(y);
299 for (size_t x = 0; x < a.xsize(); ++x) {
300 row_c[x] = row_a[x] * row_b[x];
301 }
302 }
303 return c;
304 }
305
306 float DotProduct(const ImageF& a, const ImageF& b);
307
308 template <typename T>
FillImage(const T value,Plane<T> * image)309 void FillImage(const T value, Plane<T>* image) {
310 for (size_t y = 0; y < image->ysize(); ++y) {
311 T* const JXL_RESTRICT row = image->Row(y);
312 for (size_t x = 0; x < image->xsize(); ++x) {
313 row[x] = value;
314 }
315 }
316 }
317
318 template <typename T>
ZeroFillImage(Plane<T> * image)319 void ZeroFillImage(Plane<T>* image) {
320 if (image->xsize() == 0) return;
321 for (size_t y = 0; y < image->ysize(); ++y) {
322 T* const JXL_RESTRICT row = image->Row(y);
323 memset(row, 0, image->xsize() * sizeof(T));
324 }
325 }
326
327 // Mirrors out of bounds coordinates and returns valid coordinates unchanged.
328 // We assume the radius (distance outside the image) is small compared to the
329 // image size, otherwise this might not terminate.
330 // The mirror is outside the last column (border pixel is also replicated).
Mirror(int64_t x,const int64_t xsize)331 static inline int64_t Mirror(int64_t x, const int64_t xsize) {
332 JXL_DASSERT(xsize != 0);
333
334 // TODO(janwas): replace with branchless version
335 while (x < 0 || x >= xsize) {
336 if (x < 0) {
337 x = -x - 1;
338 } else {
339 x = 2 * xsize - 1 - x;
340 }
341 }
342 return x;
343 }
344
345 // Wrap modes for ensuring X/Y coordinates are in the valid range [0, size):
346
347 // Mirrors (repeating the edge pixel once). Useful for convolutions.
348 struct WrapMirror {
operatorWrapMirror349 JXL_INLINE int64_t operator()(const int64_t coord, const int64_t size) const {
350 return Mirror(coord, size);
351 }
352 };
353
354 // Returns the same coordinate: required for TFNode with Border(), or useful
355 // when we know "coord" is already valid (e.g. interior of an image).
356 struct WrapUnchanged {
operatorWrapUnchanged357 JXL_INLINE int64_t operator()(const int64_t coord, int64_t /*size*/) const {
358 return coord;
359 }
360 };
361
362 // Similar to Wrap* but for row pointers (reduces Row() multiplications).
363
364 class WrapRowMirror {
365 public:
366 template <class ImageOrView>
WrapRowMirror(const ImageOrView & image,size_t ysize)367 WrapRowMirror(const ImageOrView& image, size_t ysize)
368 : first_row_(image.ConstRow(0)), last_row_(image.ConstRow(ysize - 1)) {}
369
operator()370 const float* operator()(const float* const JXL_RESTRICT row,
371 const int64_t stride) const {
372 if (row < first_row_) {
373 const int64_t num_before = first_row_ - row;
374 // Mirrored; one row before => row 0, two before = row 1, ...
375 return first_row_ + num_before - stride;
376 }
377 if (row > last_row_) {
378 const int64_t num_after = row - last_row_;
379 // Mirrored; one row after => last row, two after = last - 1, ...
380 return last_row_ - num_after + stride;
381 }
382 return row;
383 }
384
385 private:
386 const float* const JXL_RESTRICT first_row_;
387 const float* const JXL_RESTRICT last_row_;
388 };
389
390 struct WrapRowUnchanged {
operatorWrapRowUnchanged391 JXL_INLINE const float* operator()(const float* const JXL_RESTRICT row,
392 int64_t /*stride*/) const {
393 return row;
394 }
395 };
396
397 // Sets "thickness" pixels on each border to "value". This is faster than
398 // initializing the entire image and overwriting valid/interior pixels.
399 template <typename T>
SetBorder(const size_t thickness,const T value,Plane<T> * image)400 void SetBorder(const size_t thickness, const T value, Plane<T>* image) {
401 const size_t xsize = image->xsize();
402 const size_t ysize = image->ysize();
403 // Top: fill entire row
404 for (size_t y = 0; y < std::min(thickness, ysize); ++y) {
405 T* const JXL_RESTRICT row = image->Row(y);
406 std::fill(row, row + xsize, value);
407 }
408
409 // Bottom: fill entire row
410 for (size_t y = ysize - thickness; y < ysize; ++y) {
411 T* const JXL_RESTRICT row = image->Row(y);
412 std::fill(row, row + xsize, value);
413 }
414
415 // Left/right: fill the 'columns' on either side, but only if the image is
416 // big enough that they don't already belong to the top/bottom rows.
417 if (ysize >= 2 * thickness) {
418 for (size_t y = thickness; y < ysize - thickness; ++y) {
419 T* const JXL_RESTRICT row = image->Row(y);
420 std::fill(row, row + thickness, value);
421 std::fill(row + xsize - thickness, row + xsize, value);
422 }
423 }
424 }
425
426 // Computes the minimum and maximum pixel value.
427 template <typename T>
ImageMinMax(const Plane<T> & image,T * const JXL_RESTRICT min,T * const JXL_RESTRICT max)428 void ImageMinMax(const Plane<T>& image, T* const JXL_RESTRICT min,
429 T* const JXL_RESTRICT max) {
430 *min = std::numeric_limits<T>::max();
431 *max = std::numeric_limits<T>::lowest();
432 for (size_t y = 0; y < image.ysize(); ++y) {
433 const T* const JXL_RESTRICT row = image.Row(y);
434 for (size_t x = 0; x < image.xsize(); ++x) {
435 *min = std::min(*min, row[x]);
436 *max = std::max(*max, row[x]);
437 }
438 }
439 }
440
441 // Copies pixels, scaling their value relative to the "from" min/max by
442 // "to_range". Example: U8 [0, 255] := [0.0, 1.0], to_range = 1.0 =>
443 // outputs [0.0, 1.0].
444 template <typename FromType, typename ToType>
ImageConvert(const Plane<FromType> & from,const float to_range,Plane<ToType> * const JXL_RESTRICT to)445 void ImageConvert(const Plane<FromType>& from, const float to_range,
446 Plane<ToType>* const JXL_RESTRICT to) {
447 JXL_ASSERT(SameSize(from, *to));
448 FromType min_from, max_from;
449 ImageMinMax(from, &min_from, &max_from);
450 const float scale = to_range / (max_from - min_from);
451 for (size_t y = 0; y < from.ysize(); ++y) {
452 const FromType* const JXL_RESTRICT row_from = from.Row(y);
453 ToType* const JXL_RESTRICT row_to = to->Row(y);
454 for (size_t x = 0; x < from.xsize(); ++x) {
455 row_to[x] = static_cast<ToType>((row_from[x] - min_from) * scale);
456 }
457 }
458 }
459
460 template <typename From>
ConvertToFloat(const Plane<From> & from)461 Plane<float> ConvertToFloat(const Plane<From>& from) {
462 float factor = 1.0f / std::numeric_limits<From>::max();
463 if (std::is_same<From, double>::value || std::is_same<From, float>::value) {
464 factor = 1.0f;
465 }
466 Plane<float> to(from.xsize(), from.ysize());
467 for (size_t y = 0; y < from.ysize(); ++y) {
468 const From* const JXL_RESTRICT row_from = from.Row(y);
469 float* const JXL_RESTRICT row_to = to.Row(y);
470 for (size_t x = 0; x < from.xsize(); ++x) {
471 row_to[x] = row_from[x] * factor;
472 }
473 }
474 return to;
475 }
476
477 template <typename T>
ImageFromPacked(const std::vector<T> & packed,const size_t xsize,const size_t ysize)478 Plane<T> ImageFromPacked(const std::vector<T>& packed, const size_t xsize,
479 const size_t ysize) {
480 Plane<T> out(xsize, ysize);
481 for (size_t y = 0; y < ysize; ++y) {
482 T* const JXL_RESTRICT row = out.Row(y);
483 const T* const JXL_RESTRICT packed_row = &packed[y * xsize];
484 memcpy(row, packed_row, xsize * sizeof(T));
485 }
486 return out;
487 }
488
489 // Computes independent minimum and maximum values for each plane.
490 template <typename T>
Image3MinMax(const Image3<T> & image,const Rect & rect,std::array<T,3> * out_min,std::array<T,3> * out_max)491 void Image3MinMax(const Image3<T>& image, const Rect& rect,
492 std::array<T, 3>* out_min, std::array<T, 3>* out_max) {
493 for (size_t c = 0; c < 3; ++c) {
494 T min = std::numeric_limits<T>::max();
495 T max = std::numeric_limits<T>::min();
496 for (size_t y = 0; y < rect.ysize(); ++y) {
497 const T* JXL_RESTRICT row = rect.ConstPlaneRow(image, c, y);
498 for (size_t x = 0; x < rect.xsize(); ++x) {
499 min = std::min(min, row[x]);
500 max = std::max(max, row[x]);
501 }
502 }
503 (*out_min)[c] = min;
504 (*out_max)[c] = max;
505 }
506 }
507
508 // Computes independent minimum and maximum values for each plane.
509 template <typename T>
Image3MinMax(const Image3<T> & image,std::array<T,3> * out_min,std::array<T,3> * out_max)510 void Image3MinMax(const Image3<T>& image, std::array<T, 3>* out_min,
511 std::array<T, 3>* out_max) {
512 Image3MinMax(image, Rect(image), out_min, out_max);
513 }
514
515 template <typename T>
Image3Max(const Image3<T> & image,std::array<T,3> * out_max)516 void Image3Max(const Image3<T>& image, std::array<T, 3>* out_max) {
517 for (size_t c = 0; c < 3; ++c) {
518 T max = std::numeric_limits<T>::min();
519 for (size_t y = 0; y < image.ysize(); ++y) {
520 const T* JXL_RESTRICT row = image.ConstPlaneRow(c, y);
521 for (size_t x = 0; x < image.xsize(); ++x) {
522 max = std::max(max, row[x]);
523 }
524 }
525 (*out_max)[c] = max;
526 }
527 }
528
529 // Computes the sum of the pixels in `rect`.
530 template <typename T>
ImageSum(const Plane<T> & image,const Rect & rect)531 T ImageSum(const Plane<T>& image, const Rect& rect) {
532 T result = 0;
533 for (size_t y = 0; y < rect.ysize(); ++y) {
534 const T* JXL_RESTRICT row = rect.ConstRow(image, y);
535 for (size_t x = 0; x < rect.xsize(); ++x) {
536 result += row[x];
537 }
538 }
539 return result;
540 }
541
542 template <typename T>
ImageSum(const Plane<T> & image)543 T ImageSum(const Plane<T>& image) {
544 return ImageSum(image, Rect(image));
545 }
546
547 template <typename T>
Image3Sum(const Image3<T> & image,const Rect & rect)548 std::array<T, 3> Image3Sum(const Image3<T>& image, const Rect& rect) {
549 std::array<T, 3> out_sum = 0;
550 for (size_t c = 0; c < 3; ++c) {
551 (out_sum)[c] = ImageSum(image.Plane(c), rect);
552 }
553 return out_sum;
554 }
555
556 template <typename T>
Image3Sum(const Image3<T> & image)557 std::array<T, 3> Image3Sum(const Image3<T>& image) {
558 return Image3Sum(image, Rect(image));
559 }
560
561 template <typename T>
PackedFromImage(const Plane<T> & image,const Rect & rect)562 std::vector<T> PackedFromImage(const Plane<T>& image, const Rect& rect) {
563 const size_t xsize = rect.xsize();
564 const size_t ysize = rect.ysize();
565 std::vector<T> packed(xsize * ysize);
566 for (size_t y = 0; y < rect.ysize(); ++y) {
567 memcpy(&packed[y * xsize], rect.ConstRow(image, y), xsize * sizeof(T));
568 }
569 return packed;
570 }
571
572 template <typename T>
PackedFromImage(const Plane<T> & image)573 std::vector<T> PackedFromImage(const Plane<T>& image) {
574 return PackedFromImage(image, Rect(image));
575 }
576
577 // Computes the median pixel value.
578 template <typename T>
ImageMedian(const Plane<T> & image,const Rect & rect)579 T ImageMedian(const Plane<T>& image, const Rect& rect) {
580 std::vector<T> pixels = PackedFromImage(image, rect);
581 return Median(&pixels);
582 }
583
584 template <typename T>
ImageMedian(const Plane<T> & image)585 T ImageMedian(const Plane<T>& image) {
586 return ImageMedian(image, Rect(image));
587 }
588
589 template <typename T>
Image3Median(const Image3<T> & image,const Rect & rect)590 std::array<T, 3> Image3Median(const Image3<T>& image, const Rect& rect) {
591 std::array<T, 3> out_median;
592 for (size_t c = 0; c < 3; ++c) {
593 (out_median)[c] = ImageMedian(image.Plane(c), rect);
594 }
595 return out_median;
596 }
597
598 template <typename T>
Image3Median(const Image3<T> & image)599 std::array<T, 3> Image3Median(const Image3<T>& image) {
600 return Image3Median(image, Rect(image));
601 }
602
603 template <typename FromType, typename ToType>
Image3Convert(const Image3<FromType> & from,const float to_range,Image3<ToType> * const JXL_RESTRICT to)604 void Image3Convert(const Image3<FromType>& from, const float to_range,
605 Image3<ToType>* const JXL_RESTRICT to) {
606 JXL_ASSERT(SameSize(from, *to));
607 std::array<FromType, 3> min_from, max_from;
608 Image3MinMax(from, &min_from, &max_from);
609 float scales[3];
610 for (size_t c = 0; c < 3; ++c) {
611 scales[c] = to_range / (max_from[c] - min_from[c]);
612 }
613 float scale = std::min(scales[0], std::min(scales[1], scales[2]));
614 for (size_t c = 0; c < 3; ++c) {
615 for (size_t y = 0; y < from.ysize(); ++y) {
616 const FromType* JXL_RESTRICT row_from = from.ConstPlaneRow(c, y);
617 ToType* JXL_RESTRICT row_to = to->PlaneRow(c, y);
618 for (size_t x = 0; x < from.xsize(); ++x) {
619 const float to = (row_from[x] - min_from[c]) * scale;
620 row_to[x] = static_cast<ToType>(to);
621 }
622 }
623 }
624 }
625
626 template <typename From>
ConvertToFloat(const Image3<From> & from)627 Image3F ConvertToFloat(const Image3<From>& from) {
628 return Image3F(ConvertToFloat(from.Plane(0)), ConvertToFloat(from.Plane(1)),
629 ConvertToFloat(from.Plane(2)));
630 }
631
632 template <typename Tin, typename Tout>
Subtract(const Image3<Tin> & image1,const Image3<Tin> & image2,Image3<Tout> * out)633 void Subtract(const Image3<Tin>& image1, const Image3<Tin>& image2,
634 Image3<Tout>* out) {
635 const size_t xsize = image1.xsize();
636 const size_t ysize = image1.ysize();
637 JXL_CHECK(xsize == image2.xsize());
638 JXL_CHECK(ysize == image2.ysize());
639
640 for (size_t c = 0; c < 3; ++c) {
641 for (size_t y = 0; y < ysize; ++y) {
642 const Tin* const JXL_RESTRICT row1 = image1.ConstPlaneRow(c, y);
643 const Tin* const JXL_RESTRICT row2 = image2.ConstPlaneRow(c, y);
644 Tout* const JXL_RESTRICT row_out = out->PlaneRow(c, y);
645 for (size_t x = 0; x < xsize; ++x) {
646 row_out[x] = row1[x] - row2[x];
647 }
648 }
649 }
650 }
651
652 template <typename Tin, typename Tout>
SubtractFrom(const Image3<Tin> & what,Image3<Tout> * to)653 void SubtractFrom(const Image3<Tin>& what, Image3<Tout>* to) {
654 const size_t xsize = what.xsize();
655 const size_t ysize = what.ysize();
656 for (size_t c = 0; c < 3; ++c) {
657 for (size_t y = 0; y < ysize; ++y) {
658 const Tin* JXL_RESTRICT row_what = what.ConstPlaneRow(c, y);
659 Tout* JXL_RESTRICT row_to = to->PlaneRow(c, y);
660 for (size_t x = 0; x < xsize; ++x) {
661 row_to[x] -= row_what[x];
662 }
663 }
664 }
665 }
666
667 template <typename Tin, typename Tout>
AddTo(const Image3<Tin> & what,Image3<Tout> * to)668 void AddTo(const Image3<Tin>& what, Image3<Tout>* to) {
669 const size_t xsize = what.xsize();
670 const size_t ysize = what.ysize();
671 for (size_t c = 0; c < 3; ++c) {
672 for (size_t y = 0; y < ysize; ++y) {
673 const Tin* JXL_RESTRICT row_what = what.ConstPlaneRow(c, y);
674 Tout* JXL_RESTRICT row_to = to->PlaneRow(c, y);
675 for (size_t x = 0; x < xsize; ++x) {
676 row_to[x] += row_what[x];
677 }
678 }
679 }
680 }
681
682 // Adds `what` of the size of `rect` to `to` in the position of `rect`.
683 template <typename Tin, typename Tout>
AddTo(const Rect & rect,const Image3<Tin> & what,Image3<Tout> * to)684 void AddTo(const Rect& rect, const Image3<Tin>& what, Image3<Tout>* to) {
685 const size_t xsize = what.xsize();
686 const size_t ysize = what.ysize();
687 JXL_ASSERT(xsize == rect.xsize());
688 JXL_ASSERT(ysize == rect.ysize());
689 for (size_t c = 0; c < 3; ++c) {
690 for (size_t y = 0; y < ysize; ++y) {
691 const Tin* JXL_RESTRICT row_what = what.ConstPlaneRow(c, y);
692 Tout* JXL_RESTRICT row_to = rect.PlaneRow(to, c, y);
693 for (size_t x = 0; x < xsize; ++x) {
694 row_to[x] += row_what[x];
695 }
696 }
697 }
698 }
699
700 template <typename T>
ScaleImage(const T lambda,const Image3<T> & image)701 Image3<T> ScaleImage(const T lambda, const Image3<T>& image) {
702 Image3<T> out(image.xsize(), image.ysize());
703 for (size_t c = 0; c < 3; ++c) {
704 for (size_t y = 0; y < image.ysize(); ++y) {
705 const T* JXL_RESTRICT row = image.ConstPlaneRow(c, y);
706 T* JXL_RESTRICT row_out = out.PlaneRow(c, y);
707 for (size_t x = 0; x < image.xsize(); ++x) {
708 row_out[x] = lambda * row[x];
709 }
710 }
711 }
712 return out;
713 }
714
715 // Multiplies image by lambda in-place
716 template <typename T>
ScaleImage(const T lambda,Image3<T> * image)717 void ScaleImage(const T lambda, Image3<T>* image) {
718 for (size_t c = 0; c < 3; ++c) {
719 for (size_t y = 0; y < image->ysize(); ++y) {
720 T* const JXL_RESTRICT row = image->PlaneRow(c, y);
721 for (size_t x = 0; x < image->xsize(); ++x) {
722 row[x] = lambda * row[x];
723 }
724 }
725 }
726 }
727
728 // Initializes all planes to the same "value".
729 template <typename T>
FillImage(const T value,Image3<T> * image)730 void FillImage(const T value, Image3<T>* image) {
731 for (size_t c = 0; c < 3; ++c) {
732 for (size_t y = 0; y < image->ysize(); ++y) {
733 T* JXL_RESTRICT row = image->PlaneRow(c, y);
734 for (size_t x = 0; x < image->xsize(); ++x) {
735 row[x] = value;
736 }
737 }
738 }
739 }
740
741 template <typename T>
FillPlane(const T value,Plane<T> * image)742 void FillPlane(const T value, Plane<T>* image) {
743 for (size_t y = 0; y < image->ysize(); ++y) {
744 T* JXL_RESTRICT row = image->Row(y);
745 for (size_t x = 0; x < image->xsize(); ++x) {
746 row[x] = value;
747 }
748 }
749 }
750
751 template <typename T>
FillImage(const T value,Image3<T> * image,Rect rect)752 void FillImage(const T value, Image3<T>* image, Rect rect) {
753 for (size_t c = 0; c < 3; ++c) {
754 for (size_t y = 0; y < rect.ysize(); ++y) {
755 T* JXL_RESTRICT row = rect.PlaneRow(image, c, y);
756 for (size_t x = 0; x < rect.xsize(); ++x) {
757 row[x] = value;
758 }
759 }
760 }
761 }
762
763 template <typename T>
FillPlane(const T value,Plane<T> * image,Rect rect)764 void FillPlane(const T value, Plane<T>* image, Rect rect) {
765 for (size_t y = 0; y < rect.ysize(); ++y) {
766 T* JXL_RESTRICT row = rect.Row(image, y);
767 for (size_t x = 0; x < rect.xsize(); ++x) {
768 row[x] = value;
769 }
770 }
771 }
772
773 template <typename T>
ZeroFillImage(Image3<T> * image)774 void ZeroFillImage(Image3<T>* image) {
775 for (size_t c = 0; c < 3; ++c) {
776 for (size_t y = 0; y < image->ysize(); ++y) {
777 T* JXL_RESTRICT row = image->PlaneRow(c, y);
778 memset(row, 0, image->xsize() * sizeof(T));
779 }
780 }
781 }
782
783 template <typename T>
ZeroFillPlane(Plane<T> * image,Rect rect)784 void ZeroFillPlane(Plane<T>* image, Rect rect) {
785 for (size_t y = 0; y < rect.ysize(); ++y) {
786 T* JXL_RESTRICT row = rect.Row(image, y);
787 memset(row, 0, rect.xsize() * sizeof(T));
788 }
789 }
790
791 // First, image is padded horizontally, with the rightmost value.
792 // Next, image is padded vertically, by repeating the last line.
793 ImageF PadImage(const ImageF& in, size_t xsize, size_t ysize);
794
795 // Pad an image with xborder columns on each vertical side and yboder rows
796 // above and below, mirroring the image.
797 Image3F PadImageMirror(const Image3F& in, size_t xborder, size_t yborder);
798
799 // First, image is padded horizontally, with the rightmost value.
800 // Next, image is padded vertically, by repeating the last line.
801 // Prefer PadImageToBlockMultipleInPlace if padding to kBlockDim.
802 Image3F PadImageToMultiple(const Image3F& in, size_t N);
803
804 // Same as above, but operates in-place. Assumes that the `in` image was
805 // allocated large enough.
806 void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in);
807
808 // Downsamples an image by a given factor.
809 void DownsampleImage(Image3F* opsin, size_t factor);
810 void DownsampleImage(ImageF* image, size_t factor);
811
812 } // namespace jxl
813
814 #endif // LIB_JXL_IMAGE_OPS_H_
815