image_ops.h (10162B)
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 <jxl/memory_manager.h> 12 13 #include <algorithm> 14 #include <cstddef> 15 #include <cstdint> 16 #include <limits> 17 18 #include "lib/jxl/base/compiler_specific.h" 19 #include "lib/jxl/base/rect.h" 20 #include "lib/jxl/base/status.h" 21 #include "lib/jxl/frame_dimensions.h" 22 #include "lib/jxl/image.h" 23 24 namespace jxl { 25 26 // Works for mixed image-like argument types. 27 template <class Image1, class Image2> 28 bool SameSize(const Image1& image1, const Image2& image2) { 29 return image1.xsize() == image2.xsize() && image1.ysize() == image2.ysize(); 30 } 31 32 template <typename T> 33 Status CopyImageTo(const Plane<T>& from, Plane<T>* JXL_RESTRICT to) { 34 JXL_ENSURE(SameSize(from, *to)); 35 if (from.ysize() == 0 || from.xsize() == 0) return true; 36 for (size_t y = 0; y < from.ysize(); ++y) { 37 const T* JXL_RESTRICT row_from = from.ConstRow(y); 38 T* JXL_RESTRICT row_to = to->Row(y); 39 memcpy(row_to, row_from, from.xsize() * sizeof(T)); 40 } 41 return true; 42 } 43 44 // Copies `from:rect_from` to `to:rect_to`. 45 template <typename T> 46 Status CopyImageTo(const Rect& rect_from, const Plane<T>& from, 47 const Rect& rect_to, Plane<T>* JXL_RESTRICT to) { 48 JXL_ENSURE(SameSize(rect_from, rect_to)); 49 JXL_ENSURE(rect_from.IsInside(from)); 50 JXL_ENSURE(rect_to.IsInside(*to)); 51 if (rect_from.xsize() == 0) return true; 52 for (size_t y = 0; y < rect_from.ysize(); ++y) { 53 const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y); 54 T* JXL_RESTRICT row_to = rect_to.Row(to, y); 55 memcpy(row_to, row_from, rect_from.xsize() * sizeof(T)); 56 } 57 return true; 58 } 59 60 // Copies `from:rect_from` to `to:rect_to`. 61 template <typename T> 62 Status CopyImageTo(const Rect& rect_from, const Image3<T>& from, 63 const Rect& rect_to, Image3<T>* JXL_RESTRICT to) { 64 JXL_ENSURE(SameSize(rect_from, rect_to)); 65 for (size_t c = 0; c < 3; c++) { 66 JXL_RETURN_IF_ERROR( 67 CopyImageTo(rect_from, from.Plane(c), rect_to, &to->Plane(c))); 68 } 69 return true; 70 } 71 72 template <typename T, typename U> 73 Status ConvertPlaneAndClamp(const Rect& rect_from, const Plane<T>& from, 74 const Rect& rect_to, Plane<U>* JXL_RESTRICT to) { 75 JXL_ENSURE(SameSize(rect_from, rect_to)); 76 using M = decltype(T() + U()); 77 for (size_t y = 0; y < rect_to.ysize(); ++y) { 78 const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y); 79 U* JXL_RESTRICT row_to = rect_to.Row(to, y); 80 for (size_t x = 0; x < rect_to.xsize(); ++x) { 81 row_to[x] = 82 std::min<M>(std::max<M>(row_from[x], std::numeric_limits<U>::min()), 83 std::numeric_limits<U>::max()); 84 } 85 } 86 return true; 87 } 88 89 // Copies `from` to `to`. 90 template <typename T> 91 Status CopyImageTo(const T& from, T* JXL_RESTRICT to) { 92 return CopyImageTo(Rect(from), from, Rect(*to), to); 93 } 94 95 // Copies `from:rect_from` to `to:rect_to`; also copies `padding` pixels of 96 // border around `from:rect_from`, in all directions, whenever they are inside 97 // the first image. 98 template <typename T> 99 Status CopyImageToWithPadding(const Rect& from_rect, const T& from, 100 size_t padding, const Rect& to_rect, T* to) { 101 size_t xextra0 = std::min(padding, from_rect.x0()); 102 size_t xextra1 = 103 std::min(padding, from.xsize() - from_rect.x0() - from_rect.xsize()); 104 size_t yextra0 = std::min(padding, from_rect.y0()); 105 size_t yextra1 = 106 std::min(padding, from.ysize() - from_rect.y0() - from_rect.ysize()); 107 JXL_ENSURE(to_rect.x0() >= xextra0); 108 JXL_ENSURE(to_rect.y0() >= yextra0); 109 110 return CopyImageTo(Rect(from_rect.x0() - xextra0, from_rect.y0() - yextra0, 111 from_rect.xsize() + xextra0 + xextra1, 112 from_rect.ysize() + yextra0 + yextra1), 113 from, 114 Rect(to_rect.x0() - xextra0, to_rect.y0() - yextra0, 115 to_rect.xsize() + xextra0 + xextra1, 116 to_rect.ysize() + yextra0 + yextra1), 117 to); 118 } 119 120 // Returns linear combination of two grayscale images. 121 template <typename T> 122 StatusOr<Plane<T>> LinComb(const T lambda1, const Plane<T>& image1, 123 const T lambda2, const Plane<T>& image2) { 124 const size_t xsize = image1.xsize(); 125 const size_t ysize = image1.ysize(); 126 JXL_ENSURE(xsize == image2.xsize()); 127 JXL_ENSURE(ysize == image2.ysize()); 128 JxlMemoryManager* memory_manager = image1.memory_manager(); 129 JXL_ASSIGN_OR_RETURN(Plane<T> out, 130 Plane<T>::Create(memory_manager, xsize, ysize)); 131 for (size_t y = 0; y < ysize; ++y) { 132 const T* const JXL_RESTRICT row1 = image1.Row(y); 133 const T* const JXL_RESTRICT row2 = image2.Row(y); 134 T* const JXL_RESTRICT row_out = out.Row(y); 135 for (size_t x = 0; x < xsize; ++x) { 136 row_out[x] = lambda1 * row1[x] + lambda2 * row2[x]; 137 } 138 } 139 return out; 140 } 141 142 // Multiplies image by lambda in-place 143 template <typename T> 144 void ScaleImage(const T lambda, Plane<T>* image) { 145 for (size_t y = 0; y < image->ysize(); ++y) { 146 T* const JXL_RESTRICT row = image->Row(y); 147 for (size_t x = 0; x < image->xsize(); ++x) { 148 row[x] = lambda * row[x]; 149 } 150 } 151 } 152 153 // Multiplies image by lambda in-place 154 template <typename T> 155 void ScaleImage(const T lambda, Image3<T>* image) { 156 for (size_t c = 0; c < 3; ++c) { 157 ScaleImage(lambda, &image->Plane(c)); 158 } 159 } 160 161 template <typename T> 162 void FillImage(const T value, Plane<T>* image) { 163 for (size_t y = 0; y < image->ysize(); ++y) { 164 T* const JXL_RESTRICT row = image->Row(y); 165 for (size_t x = 0; x < image->xsize(); ++x) { 166 row[x] = value; 167 } 168 } 169 } 170 171 template <typename T> 172 void ZeroFillImage(Plane<T>* image) { 173 if (image->xsize() == 0) return; 174 for (size_t y = 0; y < image->ysize(); ++y) { 175 T* const JXL_RESTRICT row = image->Row(y); 176 memset(row, 0, image->xsize() * sizeof(T)); 177 } 178 } 179 180 // Mirrors out of bounds coordinates and returns valid coordinates unchanged. 181 // We assume the radius (distance outside the image) is small compared to the 182 // image size, otherwise this might not terminate. 183 // The mirror is outside the last column (border pixel is also replicated). 184 static inline int64_t Mirror(int64_t x, const int64_t xsize) { 185 JXL_DASSERT(xsize != 0); 186 187 // TODO(janwas): replace with branchless version 188 while (x < 0 || x >= xsize) { 189 if (x < 0) { 190 x = -x - 1; 191 } else { 192 x = 2 * xsize - 1 - x; 193 } 194 } 195 return x; 196 } 197 198 // Wrap modes for ensuring X/Y coordinates are in the valid range [0, size): 199 200 // Mirrors (repeating the edge pixel once). Useful for convolutions. 201 struct WrapMirror { 202 JXL_INLINE int64_t operator()(const int64_t coord, const int64_t size) const { 203 return Mirror(coord, size); 204 } 205 }; 206 207 // Returns the same coordinate: required for TFNode with Border(), or useful 208 // when we know "coord" is already valid (e.g. interior of an image). 209 struct WrapUnchanged { 210 JXL_INLINE int64_t operator()(const int64_t coord, int64_t /*size*/) const { 211 return coord; 212 } 213 }; 214 215 // Similar to Wrap* but for row pointers (reduces Row() multiplications). 216 217 class WrapRowMirror { 218 public: 219 template <class ImageOrView> 220 WrapRowMirror(const ImageOrView& image, size_t ysize) 221 : first_row_(image.ConstRow(0)), last_row_(image.ConstRow(ysize - 1)) {} 222 223 const float* operator()(const float* const JXL_RESTRICT row, 224 const int64_t stride) const { 225 if (row < first_row_) { 226 const int64_t num_before = first_row_ - row; 227 // Mirrored; one row before => row 0, two before = row 1, ... 228 return first_row_ + num_before - stride; 229 } 230 if (row > last_row_) { 231 const int64_t num_after = row - last_row_; 232 // Mirrored; one row after => last row, two after = last - 1, ... 233 return last_row_ - num_after + stride; 234 } 235 return row; 236 } 237 238 private: 239 const float* const JXL_RESTRICT first_row_; 240 const float* const JXL_RESTRICT last_row_; 241 }; 242 243 struct WrapRowUnchanged { 244 JXL_INLINE const float* operator()(const float* const JXL_RESTRICT row, 245 int64_t /*stride*/) const { 246 return row; 247 } 248 }; 249 250 // Computes the minimum and maximum pixel value. 251 template <typename T> 252 void ImageMinMax(const Plane<T>& image, T* const JXL_RESTRICT min, 253 T* const JXL_RESTRICT max) { 254 *min = std::numeric_limits<T>::max(); 255 *max = std::numeric_limits<T>::lowest(); 256 for (size_t y = 0; y < image.ysize(); ++y) { 257 const T* const JXL_RESTRICT row = image.Row(y); 258 for (size_t x = 0; x < image.xsize(); ++x) { 259 *min = std::min(*min, row[x]); 260 *max = std::max(*max, row[x]); 261 } 262 } 263 } 264 265 // Initializes all planes to the same "value". 266 template <typename T> 267 void FillImage(const T value, Image3<T>* image) { 268 for (size_t c = 0; c < 3; ++c) { 269 for (size_t y = 0; y < image->ysize(); ++y) { 270 T* JXL_RESTRICT row = image->PlaneRow(c, y); 271 for (size_t x = 0; x < image->xsize(); ++x) { 272 row[x] = value; 273 } 274 } 275 } 276 } 277 278 template <typename T> 279 void FillPlane(const T value, Plane<T>* image, Rect rect) { 280 for (size_t y = 0; y < rect.ysize(); ++y) { 281 T* JXL_RESTRICT row = rect.Row(image, y); 282 for (size_t x = 0; x < rect.xsize(); ++x) { 283 row[x] = value; 284 } 285 } 286 } 287 288 template <typename T> 289 void ZeroFillImage(Image3<T>* image) { 290 for (size_t c = 0; c < 3; ++c) { 291 for (size_t y = 0; y < image->ysize(); ++y) { 292 T* JXL_RESTRICT row = image->PlaneRow(c, y); 293 if (image->xsize() != 0) memset(row, 0, image->xsize() * sizeof(T)); 294 } 295 } 296 } 297 298 // Same as above, but operates in-place. Assumes that the `in` image was 299 // allocated large enough. 300 Status PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in, 301 size_t block_dim = kBlockDim); 302 303 // Downsamples an image by a given factor. 304 StatusOr<Image3F> DownsampleImage(const Image3F& opsin, size_t factor); 305 StatusOr<ImageF> DownsampleImage(const ImageF& image, size_t factor); 306 307 } // namespace jxl 308 309 #endif // LIB_JXL_IMAGE_OPS_H_