ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
IPPImage.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include <unordered_map>
11 
12 #if IPP_VERSION_INT < \
13  20211000 // macOS IPP v2021.9.11 uses old directory layout
14 #include <ippi.h>
15 
16 #include <iw++/iw_image_color.hpp>
17 #include <iw++/iw_image_filter.hpp>
18 #include <iw++/iw_image_op.hpp>
19 #include <iw++/iw_image_transform.hpp>
20 #else // Linux and Windows IPP >=v2021.10 uses new directory layout
21 #include <ipp/ippi.h>
22 
23 #include <ipp/iw++/iw_image_color.hpp>
24 #include <ipp/iw++/iw_image_filter.hpp>
25 #include <ipp/iw++/iw_image_op.hpp>
26 #include <ipp/iw++/iw_image_transform.hpp>
27 #endif
28 
29 #include <Logging.h>
30 
32 #include "cloudViewer/core/Dtype.h"
36 
37 namespace cloudViewer {
38 namespace t {
39 namespace geometry {
40 namespace ipp {
41 
42 void To(const core::Tensor &src_im,
43  core::Tensor &dst_im,
44  double scale,
45  double offset) {
46  // Supported device and datatype checking happens in calling code and will
47  // result in an exception if there are errors.
48 
49  auto src_dtype = src_im.GetDtype();
50  auto dst_dtype = dst_im.GetDtype();
51  // Create IPP wrappers for all CloudViewer tensors
52  const ::ipp::IwiImage ipp_src_im(
53  ::ipp::IwiSize(src_im.GetShape(1), src_im.GetShape(0)),
54  ToIppDataType(src_dtype), src_im.GetShape(2) /* channels */,
55  0 /* border buffer size */, const_cast<void *>(src_im.GetDataPtr()),
56  src_im.GetStride(0) * src_dtype.ByteSize());
57  ::ipp::IwiImage ipp_dst_im(
58  ::ipp::IwiSize(dst_im.GetShape(1), dst_im.GetShape(0)),
59  ToIppDataType(dst_dtype), dst_im.GetShape(2) /* channels */,
60  0 /* border buffer size */, dst_im.GetDataPtr(),
61  dst_im.GetStride(0) * dst_dtype.ByteSize());
62 
63  try {
64  ::ipp::iwiScale(ipp_src_im, ipp_dst_im, scale, offset);
65  } catch (const ::ipp::IwException &e) {
66  // See comments in ipp/ipptypes.h for m_status meaning
67  utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string);
68  }
69 }
70 
71 void RGBToGray(const core::Tensor &src_im, core::Tensor &dst_im) {
72  auto dtype = src_im.GetDtype();
73  // Create IPP wrappers for all CloudViewer tensors
74  const ::ipp::IwiImage ipp_src_im(
75  ::ipp::IwiSize(src_im.GetShape(1), src_im.GetShape(0)),
76  ToIppDataType(dtype), src_im.GetShape(2) /* channels */,
77  0 /* border buffer size */, const_cast<void *>(src_im.GetDataPtr()),
78  src_im.GetStride(0) * dtype.ByteSize());
79  ::ipp::IwiImage ipp_dst_im(
80  ::ipp::IwiSize(dst_im.GetShape(1), dst_im.GetShape(0)),
81  ToIppDataType(dtype), dst_im.GetShape(2) /* channels */,
82  0 /* border buffer size */, dst_im.GetDataPtr(),
83  dst_im.GetStride(0) * dtype.ByteSize());
84 
85  try {
86  ::ipp::iwiColorConvert(ipp_src_im, ::ipp::iwiColorRGB, ipp_dst_im,
87  ::ipp::iwiColorGray);
88  } catch (const ::ipp::IwException &e) {
89  // See comments in ipp/ipptypes.h for m_status meaning
90  utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string);
91  }
92 }
93 
94 void Resize(const core::Tensor &src_im,
95  core::Tensor &dst_im,
96  Image::InterpType interp_type) {
97  auto dtype = src_im.GetDtype();
98  // Create IPP wrappers for all CloudViewer tensors
99  const ::ipp::IwiImage ipp_src_im(
100  ::ipp::IwiSize(src_im.GetShape(1), src_im.GetShape(0)),
101  ToIppDataType(dtype), src_im.GetShape(2) /* channels */,
102  0 /* border buffer size */, const_cast<void *>(src_im.GetDataPtr()),
103  src_im.GetStride(0) * dtype.ByteSize());
104  ::ipp::IwiImage ipp_dst_im(
105  ::ipp::IwiSize(dst_im.GetShape(1), dst_im.GetShape(0)),
106  ToIppDataType(dtype), dst_im.GetShape(2) /* channels */,
107  0 /* border buffer size */, dst_im.GetDataPtr(),
108  dst_im.GetStride(0) * dtype.ByteSize());
109 
110  static const std::unordered_map<Image::InterpType, IppiInterpolationType>
111  type_dict = {
112  {Image::InterpType::Nearest, ippNearest},
113  {Image::InterpType::Linear, ippLinear},
114  {Image::InterpType::Cubic, ippCubic},
115  {Image::InterpType::Lanczos, ippLanczos},
116  {Image::InterpType::Super, ippSuper},
117  };
118 
119  auto it = type_dict.find(interp_type);
120  if (it == type_dict.end()) {
121  utility::LogError("Unsupported interp type {}",
122  static_cast<int>(interp_type));
123  }
124 
125  try {
126  ::ipp::iwiResize(ipp_src_im, ipp_dst_im, it->second);
127  } catch (const ::ipp::IwException &e) {
128  // See comments in ipp/ipptypes.h for m_status meaning
129  utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string);
130  }
131 }
132 
133 void Dilate(const core::Tensor &src_im, core::Tensor &dst_im, int kernel_size) {
134  // Supported device and datatype checking happens in calling code and will
135  // result in an exception if there are errors.
136 
137  // Create mask.
138  core::Tensor mask =
139  core::Tensor::Ones(core::SizeVector{kernel_size, kernel_size, 1},
140  core::UInt8, src_im.GetDevice());
141 
142  auto dtype = src_im.GetDtype();
143  // Create IPP wrappers for all CloudViewer tensors
144  const ::ipp::IwiImage ipp_src_im(
145  ::ipp::IwiSize(src_im.GetShape(1), src_im.GetShape(0)),
146  ToIppDataType(dtype), src_im.GetShape(2) /* channels */,
147  0 /* border buffer size */, const_cast<void *>(src_im.GetDataPtr()),
148  src_im.GetStride(0) * dtype.ByteSize());
149  ::ipp::IwiImage ipp_dst_im(
150  ::ipp::IwiSize(dst_im.GetShape(1), dst_im.GetShape(0)),
151  ToIppDataType(dtype), dst_im.GetShape(2) /* channels */,
152  0 /* border buffer size */, dst_im.GetDataPtr(),
153  dst_im.GetStride(0) * dtype.ByteSize());
154  ::ipp::IwiImage ipp_mask_im(
155  ::ipp::IwiSize(mask.GetShape(1), mask.GetShape(0)),
156  ToIppDataType(mask.GetDtype()), mask.GetShape(2) /* channels */,
157  0 /* border buffer size */, mask.GetDataPtr(),
158  mask.GetStride(0) * mask.GetDtype().ByteSize());
159  try {
160  ::ipp::iwiFilterMorphology(
161  ipp_src_im, ipp_dst_im, ::ipp::iwiMorphDilate, ipp_mask_im,
162  ::ipp::IwDefault(), /* Do not use IwiFilterMorphologyParams() */
163  ippBorderRepl);
164  } catch (const ::ipp::IwException &e) {
165  // See comments in ipp/ipptypes.h for m_status meaning
166  utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string);
167  }
168 }
169 
170 void Filter(const core::Tensor &src_im,
171  core::Tensor &dst_im,
172  const core::Tensor &kernel) {
173  // Supported device and datatype checking happens in calling code and will
174  // result in an exception if there are errors.
175  auto dtype = src_im.GetDtype();
176 
177  // Create IPP wrappers for all CloudViewer tensors
178  const ::ipp::IwiImage ipp_src_im(
179  ::ipp::IwiSize(src_im.GetShape(1), src_im.GetShape(0)),
180  ToIppDataType(dtype), src_im.GetShape(2) /* channels */,
181  0 /* border buffer size */, const_cast<void *>(src_im.GetDataPtr()),
182  src_im.GetStride(0) * dtype.ByteSize());
183  ::ipp::IwiImage ipp_dst_im(
184  ::ipp::IwiSize(dst_im.GetShape(1), dst_im.GetShape(0)),
185  ToIppDataType(dtype), dst_im.GetShape(2) /* channels */,
186  0 /* border buffer size */, dst_im.GetDataPtr(),
187  dst_im.GetStride(0) * dtype.ByteSize());
188  ::ipp::IwiImage ipp_kernel(
189  ::ipp::IwiSize(kernel.GetShape(1), kernel.GetShape(0)),
190  ToIppDataType(core::Float32), 1 /* channels */,
191  0 /* border buffer size */, const_cast<void *>(kernel.GetDataPtr()),
192  kernel.GetStride(0) * core::Float32.ByteSize());
193 
194  try {
195  ::ipp::iwiFilter(ipp_src_im, ipp_dst_im, ipp_kernel);
196  } catch (const ::ipp::IwException &e) {
197  // See comments in ipp/ipptypes.h for m_status meaning
198  utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string);
199  }
200 };
201 
202 void FilterBilateral(const core::Tensor &src_im,
203  core::Tensor &dst_im,
204  int kernel_size,
205  float value_sigma,
206  float distance_sigma) {
207  // Supported device and datatype checking happens in calling code and will
208  // result in an exception if there are errors.
209  auto dtype = src_im.GetDtype();
210 
211  // Create IPP wrappers for all CloudViewer tensors
212  const ::ipp::IwiImage ipp_src_im(
213  ::ipp::IwiSize(src_im.GetShape(1), src_im.GetShape(0)),
214  ToIppDataType(dtype), src_im.GetShape(2) /* channels */,
215  0 /* border buffer size */, const_cast<void *>(src_im.GetDataPtr()),
216  src_im.GetStride(0) * dtype.ByteSize());
217  ::ipp::IwiImage ipp_dst_im(
218  ::ipp::IwiSize(dst_im.GetShape(1), dst_im.GetShape(0)),
219  ToIppDataType(dtype), dst_im.GetShape(2) /* channels */,
220  0 /* border buffer size */, dst_im.GetDataPtr(),
221  dst_im.GetStride(0) * dtype.ByteSize());
222 
223  try {
224  ::ipp::iwiFilterBilateral(ipp_src_im, ipp_dst_im, kernel_size / 2,
225  value_sigma * value_sigma,
226  distance_sigma * distance_sigma);
227  } catch (const ::ipp::IwException &e) {
228  // See comments in ipp/ipptypes.h for m_status meaning
229  utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string);
230  }
231 }
232 
233 void FilterGaussian(const core::Tensor &src_im,
234  core::Tensor &dst_im,
235  int kernel_size,
236  float sigma) {
237  // Supported device and datatype checking happens in calling code and will
238  // result in an exception if there are errors.
239  auto dtype = src_im.GetDtype();
240 
241  // Create IPP wrappers for all CloudViewer tensors
242  const ::ipp::IwiImage ipp_src_im(
243  ::ipp::IwiSize(src_im.GetShape(1), src_im.GetShape(0)),
244  ToIppDataType(dtype), src_im.GetShape(2) /* channels */,
245  0 /* border buffer size */, const_cast<void *>(src_im.GetDataPtr()),
246  src_im.GetStride(0) * dtype.ByteSize());
247  ::ipp::IwiImage ipp_dst_im(
248  ::ipp::IwiSize(dst_im.GetShape(1), dst_im.GetShape(0)),
249  ToIppDataType(dtype), dst_im.GetShape(2) /* channels */,
250  0 /* border buffer size */, dst_im.GetDataPtr(),
251  dst_im.GetStride(0) * dtype.ByteSize());
252 
253  try {
254  ::ipp::iwiFilterGaussian(ipp_src_im, ipp_dst_im, kernel_size, sigma);
255  } catch (const ::ipp::IwException &e) {
256  // See comments in ipp/ipptypes.h for m_status meaning
257  utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string);
258  }
259 }
260 
261 void FilterSobel(const core::Tensor &src_im,
262  core::Tensor &dst_im_dx,
263  core::Tensor &dst_im_dy,
264  int kernel_size) {
265  const static std::unordered_map<int, IppiMaskSize> kKernelSizeMap = {
266  {3, ::ipp::ippMskSize3x3},
267  {5, ::ipp::ippMskSize5x5},
268  };
269  auto it = kKernelSizeMap.find(kernel_size);
270  if (it == kKernelSizeMap.end()) {
271  utility::LogError("Unsupported kernel size {} for IPP FilterSobel",
272  kernel_size);
273  }
274 
275  // Create IPP wrappers for all CloudViewer tensors
276  const ::ipp::IwiImage ipp_src_im(
277  ::ipp::IwiSize(src_im.GetShape(1), src_im.GetShape(0)),
278  ToIppDataType(src_im.GetDtype()), src_im.GetShape(2) /* channels */,
279  0 /* border buffer size */, const_cast<void *>(src_im.GetDataPtr()),
280  src_im.GetStride(0) * src_im.GetDtype().ByteSize());
281  ::ipp::IwiImage ipp_dst_im_dx(
282  ::ipp::IwiSize(dst_im_dx.GetShape(1), dst_im_dx.GetShape(0)),
283  ToIppDataType(dst_im_dx.GetDtype()),
284  dst_im_dx.GetShape(2) /* channels */, 0 /* border buffer size */,
285  dst_im_dx.GetDataPtr(),
286  dst_im_dx.GetStride(0) * dst_im_dx.GetDtype().ByteSize());
287  ::ipp::IwiImage ipp_dst_im_dy(
288  ::ipp::IwiSize(dst_im_dy.GetShape(1), dst_im_dy.GetShape(0)),
289  ToIppDataType(dst_im_dy.GetDtype()),
290  dst_im_dy.GetShape(2) /* channels */, 0 /* border buffer size */,
291  dst_im_dy.GetDataPtr(),
292  dst_im_dy.GetStride(0) * dst_im_dy.GetDtype().ByteSize());
293 
294  try {
295  ::ipp::iwiFilterSobel(ipp_src_im, ipp_dst_im_dx,
296  IwiDerivativeType::iwiDerivVerFirst, it->second);
297  ::ipp::iwiFilterSobel(ipp_src_im, ipp_dst_im_dy,
298  IwiDerivativeType::iwiDerivHorFirst, it->second);
299  // IPP uses a "left minus right" kernel,
300  // https://software.intel.com/content/www/us/en/develop/documentation/ipp-dev-reference/top/volume-2-image-processing/filtering-functions-2/fixed-filters/filtersobel.html
301  // so we need to negate it in-place.
302  dst_im_dx.Neg_();
303  } catch (const ::ipp::IwException &e) {
304  // See comments in ipp/ipptypes.h for m_status meaning
305  utility::LogError("IPP-IW error {}: {}", e.m_status, e.m_string);
306  }
307 }
308 
309 // Plain IPP functions
310 
311 void Remap(const core::Tensor &src_im, /*{Ws, Hs, C}*/
312  const core::Tensor &dst2src_xmap, /*{Wd, Hd}, float*/
313  const core::Tensor &dst2src_ymap, /*{Wd, Hd}, float*/
314  core::Tensor &dst_im, /*{Wd, Hd, C}*/
315  Image::InterpType interp_type) {
316  auto dtype = src_im.GetDtype();
317  if (dtype != dst_im.GetDtype()) {
319  "Source ({}) and destination ({}) image dtypes are different!",
320  dtype.ToString(), dst_im.GetDtype().ToString());
321  }
322  if (dst2src_xmap.GetDtype() != core::Float32) {
323  utility::LogError("dst2src_xmap dtype ({}) must be Float32.",
324  dst2src_xmap.GetDtype().ToString());
325  }
326  if (dst2src_ymap.GetDtype() != core::Float32) {
327  utility::LogError("dst2src_ymap dtype ({}) must be Float32.",
328  dst2src_ymap.GetDtype().ToString());
329  }
330 
331  static const std::unordered_map<Image::InterpType, int> interp_dict = {
332  {Image::InterpType::Nearest, IPPI_INTER_NN},
333  {Image::InterpType::Linear, IPPI_INTER_LINEAR},
334  {Image::InterpType::Cubic, IPPI_INTER_CUBIC},
335  {Image::InterpType::Lanczos, IPPI_INTER_LANCZOS},
336  /* {Image::InterpType::Cubic2p_CatmullRom, */
337  /* IPPI_INTER_CUBIC2P_CATMULLROM}, */
338  };
339 
340  auto interp_it = interp_dict.find(interp_type);
341  if (interp_it == interp_dict.end()) {
342  utility::LogError("Unsupported interp type {}",
343  static_cast<int>(interp_type));
344  }
345 
346  IppiSize src_size{static_cast<int>(src_im.GetShape(1)),
347  static_cast<int>(src_im.GetShape(0))},
348  dst_roi_size{static_cast<int>(dst_im.GetShape(1)),
349  static_cast<int>(dst_im.GetShape(0))};
350  IppiRect src_roi{0, 0, static_cast<int>(src_im.GetShape(1)),
351  static_cast<int>(src_im.GetShape(0))};
352  IppStatus sts = ippStsNoErr;
353 
354  int src_step = src_im.GetDtype().ByteSize() * src_im.GetStride(0);
355  int dst_step = dst_im.GetDtype().ByteSize() * dst_im.GetStride(0);
356  int xmap_step =
357  dst2src_xmap.GetDtype().ByteSize() * dst2src_xmap.GetStride(0);
358  int ymap_step =
359  dst2src_ymap.GetDtype().ByteSize() * dst2src_ymap.GetStride(0);
360  if (src_im.GetDtype() == core::Float32 && src_im.GetShape(2) == 4) {
361  /* IPPAPI(IppStatus, ippiRemap_32f_C4R, (const Ipp32f* pSrc, IppiSize
362  * srcSize, */
363  /* int srcStep, IppiRect srcROI, const Ipp32f* pxMap, int xMapStep,
364  */
365  /* const Ipp32f* pyMap, int yMapStep, Ipp32f* pDst, int dstStep, */
366  /* IppiSize dstRoiSize, int interpolation)) */
367  const auto p_src_im = src_im.GetDataPtr<float>();
368  auto p_dst_im = dst_im.GetDataPtr<float>();
369  const auto p_dst2src_xmap = dst2src_xmap.GetDataPtr<float>();
370  const auto p_dst2src_ymap = dst2src_ymap.GetDataPtr<float>();
371  sts = ippiRemap_32f_C4R(p_src_im, src_size, src_step, src_roi,
372  p_dst2src_xmap, xmap_step, p_dst2src_ymap,
373  ymap_step, p_dst_im, dst_step, dst_roi_size,
374  interp_it->second);
375  } else {
377  "Remap not implemented for dtype ({}) and channels ({}).",
378  src_im.GetDtype().ToString(), src_im.GetShape(2));
379  }
380  if (sts != ippStsNoErr) {
381  // See comments in icv/include/ippicv_types.h for meaning
382  utility::LogError("IPP remap error {}", ippGetStatusString(sts));
383  }
384 }
385 } // namespace ipp
386 
387 } // namespace geometry
388 } // namespace t
389 } // namespace cloudViewer
int offset
std::string ToString() const
Definition: Dtype.h:65
int64_t ByteSize() const
Definition: Dtype.h:59
Tensor Neg_()
Element-wise negation of a tensor, in-place.
Definition: Tensor.cpp:1335
Dtype GetDtype() const
Definition: Tensor.h:1164
int64_t GetStride(int64_t dim) const
Definition: Tensor.h:1139
Device GetDevice() const override
Definition: Tensor.cpp:1435
static Tensor Ones(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor fill with ones.
Definition: Tensor.cpp:412
SizeVector GetShape() const
Definition: Tensor.h:1127
InterpType
Image interpolation algorithms.
Definition: Image.h:178
@ Super
Super sampling interpolation (only downsample).
@ Lanczos
Lanczos filter interpolation.
@ Nearest
Nearest neighbors interpolation.
#define LogError(...)
Definition: Logging.h:60
const Dtype UInt8
Definition: Dtype.cpp:48
const Dtype Float32
Definition: Dtype.cpp:42
void To(const core::Tensor &src_im, core::Tensor &dst_im, double scale, double offset)
Definition: IPPImage.cpp:42
void Dilate(const core::Tensor &src_im, core::Tensor &dst_im, int kernel_size)
Definition: IPPImage.cpp:133
void FilterBilateral(const core::Tensor &src_im, core::Tensor &dst_im, int kernel_size, float value_sigma, float distance_sigma)
Definition: IPPImage.cpp:202
void Remap(const core::Tensor &src_im, const core::Tensor &dst2src_xmap, const core::Tensor &dst2src_ymap, core::Tensor &dst_im, Image::InterpType interp_type)
Definition: IPPImage.cpp:311
void Filter(const core::Tensor &src_im, core::Tensor &dst_im, const core::Tensor &kernel)
Definition: IPPImage.cpp:170
void Resize(const core::Tensor &src_im, core::Tensor &dst_im, Image::InterpType interp_type)
Definition: IPPImage.cpp:94
void RGBToGray(const core::Tensor &src_im, core::Tensor &dst_im)
Definition: IPPImage.cpp:71
void FilterGaussian(const core::Tensor &src_im, core::Tensor &dst_im, int kernel_size, float sigma)
Definition: IPPImage.cpp:233
void FilterSobel(const core::Tensor &src_im, core::Tensor &dst_im_dx, core::Tensor &dst_im_dy, int kernel_size)
Definition: IPPImage.cpp:261
Generic file read and write utility for python interface.