1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See http://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Image tools.
33  *
34  * Authors:
35  * Fabien Spindler
36  *
37  *****************************************************************************/
38 
39 #ifndef vpImageTools_H
40 #define vpImageTools_H
41 
42 /*!
43   \file vpImageTools.h
44 
45   \brief Various image tools; sub-image extraction, modification of
46   the look up table, binarisation...
47 */
48 
49 #include <visp3/core/vpImage.h>
50 
51 #ifdef VISP_HAVE_PTHREAD
52 #include <pthread.h>
53 #endif
54 
55 #include <visp3/core/vpCameraParameters.h>
56 #include <visp3/core/vpImageException.h>
57 #include <visp3/core/vpMath.h>
58 #include <visp3/core/vpRect.h>
59 #include <visp3/core/vpRectOriented.h>
60 
61 #include <fstream>
62 #include <iostream>
63 #include <math.h>
64 #include <string.h>
65 
66 #if defined _OPENMP
67 #include <omp.h>
68 #endif
69 
70 /*!
71   \class vpImageTools
72 
73   \ingroup group_core_image
74 
75   \brief Various image tools; sub-image extraction, modification of
76   the look up table, binarisation...
77 */
78 class VISP_EXPORT vpImageTools
79 {
80 public:
81   enum vpImageInterpolationType {
82     INTERPOLATION_NEAREST, /*!< Nearest neighbor interpolation. */
83     INTERPOLATION_LINEAR,  /*!< Bi-linear interpolation (optimized by SIMD lib if enabled). */
84     INTERPOLATION_CUBIC,   /*!< Bi-cubic interpolation. */
85     INTERPOLATION_AREA     /*!< Area interpolation (optimized by SIMD lib if enabled). */
86   };
87 
88   template <class Type>
89   static inline void binarise(vpImage<Type> &I, Type threshold1, Type threshold2, Type value1, Type value2, Type value3,
90                               bool useLUT = true);
91   static void changeLUT(vpImage<unsigned char> &I, unsigned char A, unsigned char newA, unsigned char B,
92                         unsigned char newB);
93 
94   template <class Type>
95   static void crop(const vpImage<Type> &I, double roi_top, double roi_left, unsigned int roi_height,
96                    unsigned int roi_width, vpImage<Type> &crop, unsigned int v_scale = 1, unsigned int h_scale = 1);
97 
98   static void columnMean(const vpImage<double> &I, vpRowVector &result);
99 
100   template <class Type>
101   static void crop(const vpImage<Type> &I, const vpImagePoint &topLeft, unsigned int roi_height, unsigned int roi_width,
102                    vpImage<Type> &crop, unsigned int v_scale = 1, unsigned int h_scale = 1);
103   template <class Type>
104   static void crop(const vpImage<Type> &I, const vpRect &roi, vpImage<Type> &crop, unsigned int v_scale = 1,
105                    unsigned int h_scale = 1);
106   template <class Type>
107   static void crop(const unsigned char *bitmap, unsigned int width, unsigned int height, const vpRect &roi,
108                    vpImage<Type> &crop, unsigned int v_scale = 1, unsigned int h_scale = 1);
109 
110   static void extract(const vpImage<unsigned char> &Src, vpImage<unsigned char> &Dst, const vpRectOriented &r);
111   static void extract(const vpImage<unsigned char> &Src, vpImage<double> &Dst, const vpRectOriented &r);
112 
113   template <class Type> static void flip(const vpImage<Type> &I, vpImage<Type> &newI);
114 
115   template <class Type> static void flip(vpImage<Type> &I);
116 
117   static void imageDifference(const vpImage<unsigned char> &I1, const vpImage<unsigned char> &I2,
118                               vpImage<unsigned char> &Idiff);
119   static void imageDifference(const vpImage<vpRGBa> &I1, const vpImage<vpRGBa> &I2, vpImage<vpRGBa> &Idiff);
120 
121   static void imageDifferenceAbsolute(const vpImage<unsigned char> &I1, const vpImage<unsigned char> &I2,
122                                       vpImage<unsigned char> &Idiff);
123   static void imageDifferenceAbsolute(const vpImage<double> &I1, const vpImage<double> &I2, vpImage<double> &Idiff);
124   static void imageDifferenceAbsolute(const vpImage<vpRGBa> &I1, const vpImage<vpRGBa> &I2, vpImage<vpRGBa> &Idiff);
125 
126   static void imageAdd(const vpImage<unsigned char> &I1, const vpImage<unsigned char> &I2, vpImage<unsigned char> &Ires,
127                        bool saturate = false);
128 
129   static void imageSubtract(const vpImage<unsigned char> &I1, const vpImage<unsigned char> &I2,
130                             vpImage<unsigned char> &Ires, bool saturate = false);
131 
132   static void initUndistortMap(const vpCameraParameters &cam, unsigned int width, unsigned int height,
133                                vpArray2D<int> &mapU, vpArray2D<int> &mapV,
134                                vpArray2D<float> &mapDu, vpArray2D<float> &mapDv);
135 
136   static double interpolate(const vpImage<unsigned char> &I, const vpImagePoint &point,
137                             const vpImageInterpolationType &method = INTERPOLATION_NEAREST);
138 
139   static void integralImage(const vpImage<unsigned char> &I, vpImage<double> &II, vpImage<double> &IIsq);
140 
141   static double normalizedCorrelation(const vpImage<double> &I1, const vpImage<double> &I2,
142                                       bool useOptimized = true);
143 
144   static void normalize(vpImage<double> &I);
145 
146   static void remap(const vpImage<unsigned char> &I, const vpArray2D<int> &mapU, const vpArray2D<int> &mapV,
147                     const vpArray2D<float> &mapDu, const vpArray2D<float> &mapDv, vpImage<unsigned char> &Iundist);
148   static void remap(const vpImage<vpRGBa> &I, const vpArray2D<int> &mapU, const vpArray2D<int> &mapV,
149                     const vpArray2D<float> &mapDu, const vpArray2D<float> &mapDv, vpImage<vpRGBa> &Iundist);
150 
151   template <class Type>
152   static void resize(const vpImage<Type> &I, vpImage<Type> &Ires, unsigned int width, unsigned int height,
153                      const vpImageInterpolationType &method = INTERPOLATION_NEAREST, unsigned int nThreads=0);
154 
155   template <class Type>
156   static void resize(const vpImage<Type> &I, vpImage<Type> &Ires,
157                      const vpImageInterpolationType &method = INTERPOLATION_NEAREST, unsigned int nThreads=0);
158 
159   static void templateMatching(const vpImage<unsigned char> &I, const vpImage<unsigned char> &I_tpl,
160                                vpImage<double> &I_score, unsigned int step_u, unsigned int step_v,
161                                bool useOptimized = true);
162 
163   template <class Type>
164   static void undistort(const vpImage<Type> &I, const vpCameraParameters &cam, vpImage<Type> &newI,
165                         unsigned int nThreads=2);
166 
167   template <class Type>
168   static void undistort(const vpImage<Type> &I, vpArray2D<int> mapU, vpArray2D<int> mapV, vpArray2D<float> mapDu,
169                         vpArray2D<float> mapDv, vpImage<Type> &newI);
170 
171   template <class Type>
172   static void warpImage(const vpImage<Type> &src, const vpMatrix &T, vpImage<Type> &dst,
173                         const vpImageInterpolationType &interpolation=INTERPOLATION_NEAREST,
174                         bool fixedPointArithmetic=true, bool pixelCenter=false);
175 
176 #if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
177   /*!
178     @name Deprecated functions
179   */
180   //@{
181   template <class Type>
182   vp_deprecated static void createSubImage(const vpImage<Type> &I, unsigned int i_sub, unsigned int j_sub,
183                                            unsigned int nrow_sub, unsigned int ncol_sub, vpImage<Type> &S);
184 
185   template <class Type>
186   vp_deprecated static void createSubImage(const vpImage<Type> &I, const vpRect &rect, vpImage<Type> &S);
187 //@}
188 #endif
189 
190 private:
191   // Cubic interpolation
192   static float cubicHermite(const float A, const float B, const float C, const float D, const float t);
193 
194   template <class Type> static Type getPixelClamped(const vpImage<Type> &I, float u, float v);
195 
196   static int coordCast(double x);
197 
198   // Linear interpolation
199   static double lerp(double A, double B, double t);
200   static float lerp(float A, float B, float t);
201   static int64_t lerp2(int64_t A, int64_t B, int64_t t, int64_t t_1);
202 
203   static double normalizedCorrelation(const vpImage<double> &I1, const vpImage<double> &I2, const vpImage<double> &II,
204                                       const vpImage<double> &IIsq, const vpImage<double> &II_tpl,
205                                       const vpImage<double> &IIsq_tpl, unsigned int i0, unsigned int j0);
206 
207   template <class Type>
208   static void resizeBicubic(const vpImage<Type> &I, vpImage<Type> &Ires, unsigned int i, unsigned int j,
209                             float u, float v, float xFrac, float yFrac);
210 
211   template <class Type>
212   static void resizeBilinear(const vpImage<Type> &I, vpImage<Type> &Ires, unsigned int i, unsigned int j,
213                              float u, float v, float xFrac, float yFrac);
214 
215   template <class Type>
216   static void resizeNearest(const vpImage<Type> &I, vpImage<Type> &Ires, unsigned int i, unsigned int j,
217                             float u, float v);
218 
219   static void resizeSimdlib(const vpImage<vpRGBa>& Isrc, unsigned int resizeWidth, unsigned int resizeHeight,
220                             vpImage<vpRGBa>& Idst, int method);
221   static void resizeSimdlib(const vpImage<unsigned char>& Isrc, unsigned int resizeWidth, unsigned int resizeHeight,
222                             vpImage<unsigned char>& Idst, int method);
223 
224   template <class Type>
225   static void warpNN(const vpImage<Type> &src, const vpMatrix &T, vpImage<Type> &dst, bool affine, bool centerCorner, bool fixedPoint);
226 
227   template <class Type>
228   static void warpLinear(const vpImage<Type> &src, const vpMatrix &T, vpImage<Type> &dst, bool affine, bool centerCorner, bool fixedPoint);
229 
230   static bool checkFixedPoint(unsigned int x, unsigned int y, const vpMatrix &T, bool affine);
231 };
232 
233 #if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
234 /*!
235   Crop a region of interest (ROI) in an image.
236 
237   \deprecated This fonction is deprecated. You should rather use
238   crop(const vpImage<Type> &, unsigned int, unsigned int, unsigned int,
239   unsigned int, vpImage<Type> &).
240 
241   \param I : Input image from which a sub image will be extracted.
242   \param roi_top : ROI vertical position of the upper/left corner in the input
243   image.
244   \param roi_left : ROI  horizontal position of the upper/left corner
245   in the input image.
246   \param roi_height : Cropped image height corresponding to the ROI height.
247   \param roi_width : Cropped image width corresponding to the ROI height.
248   \param crop : Cropped image.
249 
250   \sa crop(const vpImage<Type> &, unsigned int, unsigned int, unsigned int,
251   unsigned int, vpImage<Type> &)
252 */
253 template <class Type>
createSubImage(const vpImage<Type> & I,unsigned int roi_top,unsigned int roi_left,unsigned int roi_height,unsigned int roi_width,vpImage<Type> & crop)254 void vpImageTools::createSubImage(const vpImage<Type> &I, unsigned int roi_top, unsigned int roi_left,
255                                   unsigned int roi_height, unsigned int roi_width, vpImage<Type> &crop)
256 {
257   vpImageTools::crop(I, roi_top, roi_left, roi_height, roi_width, crop);
258 }
259 
260 /*!
261   Crop an image region of interest.
262 
263   \deprecated This fonction is deprecated. You should rather use
264   crop(const vpImage<Type> &, const vpRect &, vpImage<Type> &).
265 
266   \param I : Input image from which a sub image will be extracted.
267 
268   \param roi : Region of interest in image \e I corresponding to the
269   cropped part of the image.
270 
271   \param crop : Cropped image.
272 
273   \sa crop(const vpImage<Type> &, const vpRect &, vpImage<Type> &)
274 */
createSubImage(const vpImage<Type> & I,const vpRect & roi,vpImage<Type> & crop)275 template <class Type> void vpImageTools::createSubImage(const vpImage<Type> &I, const vpRect &roi, vpImage<Type> &crop)
276 {
277   vpImageTools::crop(I, roi, crop);
278 }
279 
280 #endif // #if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
281 
282 /*!
283   Crop a region of interest (ROI) in an image. The ROI coordinates and
284   dimension are defined in the original image.
285 
286   Setting \e v_scale and \e h_scale to values different from 1 allows also to
287   subsample the cropped image.
288 
289   \param I : Input image from which a sub image will be extracted.
290   \param roi_top : ROI vertical position of the upper/left corner in the input
291   image.
292   \param roi_left : ROI  horizontal position of the upper/left corner
293   in the input image.
294   \param roi_height : Cropped image height corresponding
295   to the ROI height.
296   \param roi_width : Cropped image width corresponding to
297   the ROI height.
298   \param crop : Cropped image.
299   \param v_scale [in] : Vertical subsampling factor applied to the ROI.
300   \param h_scale [in] : Horizontal subsampling factor applied to the ROI.
301 
302   \sa crop(const vpImage<Type> &, const vpRect &, vpImage<Type> &)
303 */
304 template <class Type>
crop(const vpImage<Type> & I,double roi_top,double roi_left,unsigned int roi_height,unsigned int roi_width,vpImage<Type> & crop,unsigned int v_scale,unsigned int h_scale)305 void vpImageTools::crop(const vpImage<Type> &I, double roi_top, double roi_left, unsigned int roi_height,
306                         unsigned int roi_width, vpImage<Type> &crop, unsigned int v_scale, unsigned int h_scale)
307 {
308   int i_min = (std::max)((int)(ceil(roi_top / v_scale)), 0);
309   int j_min = (std::max)((int)(ceil(roi_left / h_scale)), 0);
310   int i_max = (std::min)((int)(ceil((roi_top + roi_height)) / v_scale), (int)(I.getHeight() / v_scale));
311   int j_max = (std::min)((int)(ceil((roi_left + roi_width) / h_scale)), (int)(I.getWidth() / h_scale));
312 
313   unsigned int i_min_u = (unsigned int)i_min;
314   unsigned int j_min_u = (unsigned int)j_min;
315 
316   unsigned int r_width = (unsigned int)(j_max - j_min);
317   unsigned int r_height = (unsigned int)(i_max - i_min);
318 
319   crop.resize(r_height, r_width);
320 
321   if (v_scale == 1 && h_scale == 1) {
322     for (unsigned int i = 0; i < r_height; i++) {
323       void *src = (void *)(I[i + i_min_u] + j_min_u);
324       void *dst = (void *)crop[i];
325       memcpy(dst, src, r_width * sizeof(Type));
326     }
327   } else if (h_scale == 1) {
328     for (unsigned int i = 0; i < r_height; i++) {
329       void *src = (void *)(I[(i + i_min_u) * v_scale] + j_min_u);
330       void *dst = (void *)crop[i];
331       memcpy(dst, src, r_width * sizeof(Type));
332     }
333   } else {
334     for (unsigned int i = 0; i < r_height; i++) {
335       for (unsigned int j = 0; j < r_width; j++) {
336         crop[i][j] = I[(i + i_min_u) * v_scale][(j + j_min_u) * h_scale];
337       }
338     }
339   }
340 }
341 
342 /*!
343   Crop a region of interest (ROI) in an image. The ROI coordinates and
344   dimension are defined in the original image.
345 
346   Setting \e v_scale and \e h_scale to values different from 1 allows also to
347   subsample the cropped image.
348 
349   \param I : Input image from which a sub image will be extracted.
350   \param topLeft : ROI position of the upper/left corner in the input image.
351   \param roi_height : Cropped image height corresponding to the ROI height.
352   \param roi_width : Cropped image width corresponding to the ROI height.
353   \param crop : Cropped image.
354   \param v_scale [in] : Vertical subsampling factor applied to the ROI.
355   \param h_scale [in] : Horizontal subsampling factor applied to the ROI.
356 
357   \sa crop(const vpImage<Type> &, const vpRect &, vpImage<Type> &)
358 */
359 template <class Type>
crop(const vpImage<Type> & I,const vpImagePoint & topLeft,unsigned int roi_height,unsigned int roi_width,vpImage<Type> & crop,unsigned int v_scale,unsigned int h_scale)360 void vpImageTools::crop(const vpImage<Type> &I, const vpImagePoint &topLeft, unsigned int roi_height,
361                         unsigned int roi_width, vpImage<Type> &crop, unsigned int v_scale, unsigned int h_scale)
362 {
363   vpImageTools::crop(I, topLeft.get_i(), topLeft.get_j(), roi_height, roi_width, crop, v_scale, h_scale);
364 }
365 
366 /*!
367   Crop a region of interest (ROI) in an image. The ROI coordinates and
368   dimension are defined in the original image.
369 
370   Setting \e v_scale and \e h_scale to values different from 1 allows also to
371   subsample the cropped image.
372 
373   \param I : Input image from which a sub image will be extracted.
374 
375   \param roi : Region of interest in image \e I corresponding to the
376   cropped part of the image.
377 
378   \param crop : Cropped image.
379   \param v_scale [in] : Vertical subsampling factor applied to the ROI.
380   \param h_scale [in] : Horizontal subsampling factor applied to the ROI.
381 */
382 template <class Type>
crop(const vpImage<Type> & I,const vpRect & roi,vpImage<Type> & crop,unsigned int v_scale,unsigned int h_scale)383 void vpImageTools::crop(const vpImage<Type> &I, const vpRect &roi, vpImage<Type> &crop, unsigned int v_scale,
384                         unsigned int h_scale)
385 {
386   vpImageTools::crop(I, roi.getTop(), roi.getLeft(), (unsigned int)roi.getHeight(), (unsigned int)roi.getWidth(), crop,
387                      v_scale, h_scale);
388 }
389 
390 /*!
391   Crop a region of interest (ROI) in an image. The ROI coordinates and
392   dimension are defined in the original image.
393 
394   Setting \e v_scale and \e h_scale to values different from 1 allows also to
395   subsample the cropped image.
396 
397   \param bitmap : Pointer to the input image from which a sub image will be
398   extracted. \param width, height : Size of the input image.
399 
400   \param roi : Region of interest corresponding to the cropped part of the
401   image.
402 
403   \param crop : Cropped image.
404   \param v_scale [in] : Vertical subsampling factor applied to the ROI.
405   \param h_scale [in] : Horizontal subsampling factor applied to the ROI.
406 */
407 template <class Type>
crop(const unsigned char * bitmap,unsigned int width,unsigned int height,const vpRect & roi,vpImage<Type> & crop,unsigned int v_scale,unsigned int h_scale)408 void vpImageTools::crop(const unsigned char *bitmap, unsigned int width, unsigned int height, const vpRect &roi,
409                         vpImage<Type> &crop, unsigned int v_scale, unsigned int h_scale)
410 {
411   int i_min = (std::max)((int)(ceil(roi.getTop() / v_scale)), 0);
412   int j_min = (std::max)((int)(ceil(roi.getLeft() / h_scale)), 0);
413   int i_max = (std::min)((int)(ceil((roi.getTop() + roi.getHeight()) / v_scale)), (int)(height / v_scale));
414   int j_max = (std::min)((int)(ceil((roi.getLeft() + roi.getWidth()) / h_scale)), (int)(width / h_scale));
415 
416   unsigned int i_min_u = (unsigned int)i_min;
417   unsigned int j_min_u = (unsigned int)j_min;
418 
419   unsigned int r_width = (unsigned int)(j_max - j_min);
420   unsigned int r_height = (unsigned int)(i_max - i_min);
421 
422   crop.resize(r_height, r_width);
423 
424   if (v_scale == 1 && h_scale == 1) {
425     for (unsigned int i = 0; i < r_height; i++) {
426       void *src = (void *)(bitmap + ((i + i_min_u) * width + j_min_u) * sizeof(Type));
427       void *dst = (void *)crop[i];
428       memcpy(dst, src, r_width * sizeof(Type));
429     }
430   } else if (h_scale == 1) {
431     for (unsigned int i = 0; i < r_height; i++) {
432       void *src = (void *)(bitmap + ((i + i_min_u) * width * v_scale + j_min_u) * sizeof(Type));
433       void *dst = (void *)crop[i];
434       memcpy(dst, src, r_width * sizeof(Type));
435     }
436   } else {
437     for (unsigned int i = 0; i < r_height; i++) {
438       unsigned int i_src = (i + i_min_u) * width * v_scale + j_min_u * h_scale;
439       for (unsigned int j = 0; j < r_width; j++) {
440         void *src = (void *)(bitmap + (i_src + j * h_scale) * sizeof(Type));
441         void *dst = (void *)&crop[i][j];
442         memcpy(dst, src, sizeof(Type));
443       }
444     }
445   }
446 }
447 
448 /*!
449   Binarise an image.
450 
451   - Pixels whose values are less than \e threshold1 are set to \e value1
452 
453   - Pixels whose values are greater then or equal to \e threshold1 and
454     less then or equal to \e threshold2 are set to \e value2
455 
456   - Pixels whose values are greater than \e threshold2 are set to \e value3
457 */
458 template <class Type>
binarise(vpImage<Type> & I,Type threshold1,Type threshold2,Type value1,Type value2,Type value3,bool useLUT)459 inline void vpImageTools::binarise(vpImage<Type> &I, Type threshold1, Type threshold2, Type value1, Type value2,
460                                    Type value3, bool useLUT)
461 {
462   if (useLUT) {
463     std::cerr << "LUT not available for this type ! Will use the iteration method." << std::endl;
464   }
465 
466   Type v;
467   Type *p = I.bitmap;
468   Type *pend = I.bitmap + I.getWidth() * I.getHeight();
469   for (; p < pend; p++) {
470     v = *p;
471     if (v < threshold1)
472       *p = value1;
473     else if (v > threshold2)
474       *p = value3;
475     else
476       *p = value2;
477   }
478 }
479 
480 /*!
481   Binarise an image.
482 
483   - Pixels whose values are less than \e threshold1 are set to \e value1
484 
485   - Pixels whose values are greater then or equal to \e threshold1 and
486     less then or equal to \e threshold2 are set to \e value2
487 
488   - Pixels whose values are greater than \e threshold2 are set to \e value3
489 */
490 template <>
binarise(vpImage<unsigned char> & I,unsigned char threshold1,unsigned char threshold2,unsigned char value1,unsigned char value2,unsigned char value3,bool useLUT)491 inline void vpImageTools::binarise(vpImage<unsigned char> &I, unsigned char threshold1, unsigned char threshold2,
492                                    unsigned char value1, unsigned char value2, unsigned char value3, bool useLUT)
493 {
494   if (useLUT) {
495     // Construct the LUT
496     unsigned char lut[256];
497     for (unsigned int i = 0; i < 256; i++) {
498       lut[i] = i < threshold1 ? value1 : (i > threshold2 ? value3 : value2);
499     }
500 
501     I.performLut(lut);
502   } else {
503     unsigned char *p = I.bitmap;
504     unsigned char *pend = I.bitmap + I.getWidth() * I.getHeight();
505     for (; p < pend; p++) {
506       unsigned char v = *p;
507       if (v < threshold1)
508         *p = value1;
509       else if (v > threshold2)
510         *p = value3;
511       else
512         *p = value2;
513     }
514   }
515 }
516 
517 #ifdef VISP_HAVE_PTHREAD
518 
519 #ifndef DOXYGEN_SHOULD_SKIP_THIS
520 template <class Type> class vpUndistortInternalType
521 {
522 public:
523   Type *src;
524   Type *dst;
525   unsigned int width;
526   unsigned int height;
527   vpCameraParameters cam;
528   unsigned int nthreads;
529   unsigned int threadid;
530 
531 public:
vpUndistortInternalType()532   vpUndistortInternalType() : src(NULL), dst(NULL), width(0), height(0), cam(), nthreads(0), threadid(0) {}
533 
vpUndistortInternalType(const vpUndistortInternalType<Type> & u)534   vpUndistortInternalType(const vpUndistortInternalType<Type> &u) { *this = u; }
535   vpUndistortInternalType &operator=(const vpUndistortInternalType<Type> &u)
536   {
537     src = u.src;
538     dst = u.dst;
539     width = u.width;
540     height = u.height;
541     cam = u.cam;
542     nthreads = u.nthreads;
543     threadid = u.threadid;
544 
545     return *this;
546   }
547 
548   static void *vpUndistort_threaded(void *arg);
549 };
550 
vpUndistort_threaded(void * arg)551 template <class Type> void *vpUndistortInternalType<Type>::vpUndistort_threaded(void *arg)
552 {
553   vpUndistortInternalType<Type> *undistortSharedData = static_cast<vpUndistortInternalType<Type> *>(arg);
554   int offset = (int)undistortSharedData->threadid;
555   int width = (int)undistortSharedData->width;
556   int height = (int)undistortSharedData->height;
557   int nthreads = (int)undistortSharedData->nthreads;
558 
559   double u0 = undistortSharedData->cam.get_u0();
560   double v0 = undistortSharedData->cam.get_v0();
561   double px = undistortSharedData->cam.get_px();
562   double py = undistortSharedData->cam.get_py();
563   double kud = undistortSharedData->cam.get_kud();
564 
565   double invpx = 1.0 / px;
566   double invpy = 1.0 / py;
567 
568   double kud_px2 = kud * invpx * invpx;
569   double kud_py2 = kud * invpy * invpy;
570 
571   Type *dst = undistortSharedData->dst + (height / nthreads * offset) * width;
572   Type *src = undistortSharedData->src;
573 
574   for (double v = height / nthreads * offset; v < height / nthreads * (offset + 1); v++) {
575     double deltav = v - v0;
576     // double fr1 = 1.0 + kd * (vpMath::sqr(deltav * invpy));
577     double fr1 = 1.0 + kud_py2 * deltav * deltav;
578 
579     for (double u = 0; u < width; u++) {
580       // computation of u,v : corresponding pixel coordinates in I.
581       double deltau = u - u0;
582       // double fr2 = fr1 + kd * (vpMath::sqr(deltau * invpx));
583       double fr2 = fr1 + kud_px2 * deltau * deltau;
584 
585       double u_double = deltau * fr2 + u0;
586       double v_double = deltav * fr2 + v0;
587 
588       // computation of the bilinear interpolation
589 
590       // declarations
591       int u_round = (int)(u_double);
592       int v_round = (int)(v_double);
593       if (u_round < 0.f)
594         u_round = -1;
595       if (v_round < 0.f)
596         v_round = -1;
597       double du_double = (u_double) - (double)u_round;
598       double dv_double = (v_double) - (double)v_round;
599       Type v01;
600       Type v23;
601       if ((0 <= u_round) && (0 <= v_round) && (u_round < ((width)-1)) && (v_round < ((height)-1))) {
602         // process interpolation
603         const Type *_mp = &src[v_round * width + u_round];
604         v01 = (Type)(_mp[0] + ((_mp[1] - _mp[0]) * du_double));
605         _mp += width;
606         v23 = (Type)(_mp[0] + ((_mp[1] - _mp[0]) * du_double));
607         *dst = (Type)(v01 + ((v23 - v01) * dv_double));
608       } else {
609         *dst = 0;
610       }
611       dst++;
612     }
613   }
614 
615   pthread_exit((void *)0);
616   return NULL;
617 }
618 #endif // DOXYGEN_SHOULD_SKIP_THIS
619 #endif // VISP_HAVE_PTHREAD
620 
621 /*!
622   Undistort an image
623 
624   \param I : Input image to undistort.
625 
626   \param cam : Parameters of the camera causing distortion.
627 
628   \param undistI : Undistorted output image. The size of this image
629   will be the same than the input image \e I. If the distortion
630   parameter \f$k_{ud}\f$ is null, meaning that `cam.get_kud() == 0`, \e undistI is
631   just a copy of \e I.
632 
633   \param nThreads : Number of threads to use if pthreads library is available.
634 
635   \warning This function works only with Types authorizing "+,-,
636   multiplication by a scalar" operators.
637 
638   Since this function is time consuming, if you want to undistort multiple images, you should rather
639   call initUndistortMap() once and then remap() to undistort the images.
640   This will be less time consuming.
641 
642   \sa initUndistortMap(), remap()
643 */
644 template <class Type>
undistort(const vpImage<Type> & I,const vpCameraParameters & cam,vpImage<Type> & undistI,unsigned int nThreads)645 void vpImageTools::undistort(const vpImage<Type> &I, const vpCameraParameters &cam, vpImage<Type> &undistI,
646                              unsigned int nThreads)
647 {
648 #ifdef VISP_HAVE_PTHREAD
649   //
650   // Optimized version using pthreads
651   //
652   unsigned int width = I.getWidth();
653   unsigned int height = I.getHeight();
654 
655   undistI.resize(height, width);
656 
657   double kud = cam.get_kud();
658 
659   // if (kud == 0) {
660   if (std::fabs(kud) <= std::numeric_limits<double>::epsilon()) {
661     // There is no need to undistort the image
662     undistI = I;
663     return;
664   }
665 
666   unsigned int nthreads = nThreads;
667   pthread_attr_t attr;
668   pthread_t *callThd = new pthread_t[nthreads];
669   pthread_attr_init(&attr);
670   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
671 
672   vpUndistortInternalType<Type> *undistortSharedData;
673   undistortSharedData = new vpUndistortInternalType<Type>[nthreads];
674 
675   for (unsigned int i = 0; i < nthreads; i++) {
676     // Each thread works on a different set of data.
677     //    vpTRACE("create thread %d", i);
678     undistortSharedData[i].src = I.bitmap;
679     undistortSharedData[i].dst = undistI.bitmap;
680     undistortSharedData[i].width = I.getWidth();
681     undistortSharedData[i].height = I.getHeight();
682     undistortSharedData[i].cam = cam;
683     undistortSharedData[i].nthreads = nthreads;
684     undistortSharedData[i].threadid = i;
685     pthread_create(&callThd[i], &attr, &vpUndistortInternalType<Type>::vpUndistort_threaded, &undistortSharedData[i]);
686   }
687   pthread_attr_destroy(&attr);
688   /* Wait on the other threads */
689 
690   for (unsigned int i = 0; i < nthreads; i++) {
691     //  vpTRACE("join thread %d", i);
692     pthread_join(callThd[i], NULL);
693   }
694 
695   delete[] callThd;
696   delete[] undistortSharedData;
697 #else  // VISP_HAVE_PTHREAD
698   (void)nThreads;
699   //
700   // optimized version without pthreads
701   //
702   unsigned int width = I.getWidth();
703   unsigned int height = I.getHeight();
704 
705   undistI.resize(height, width);
706 
707   double u0 = cam.get_u0();
708   double v0 = cam.get_v0();
709   double px = cam.get_px();
710   double py = cam.get_py();
711   double kud = cam.get_kud();
712 
713   // if (kud == 0) {
714   if (std::fabs(kud) <= std::numeric_limits<double>::epsilon()) {
715     // There is no need to undistort the image
716     undistI = I;
717     return;
718   }
719 
720   double invpx = 1.0 / px;
721   double invpy = 1.0 / py;
722 
723   double kud_px2 = kud * invpx * invpx;
724   double kud_py2 = kud * invpy * invpy;
725 
726   Type *dst = undistI.bitmap;
727   for (double v = 0; v < height; v++) {
728     double deltav = v - v0;
729     // double fr1 = 1.0 + kd * (vpMath::sqr(deltav * invpy));
730     double fr1 = 1.0 + kud_py2 * deltav * deltav;
731 
732     for (double u = 0; u < width; u++) {
733       // computation of u,v : corresponding pixel coordinates in I.
734       double deltau = u - u0;
735       // double fr2 = fr1 + kd * (vpMath::sqr(deltau * invpx));
736       double fr2 = fr1 + kud_px2 * deltau * deltau;
737 
738       double u_double = deltau * fr2 + u0;
739       double v_double = deltav * fr2 + v0;
740 
741       // printf("[%g][%g] %g %g : ", u, v, u_double, v_double );
742 
743       // computation of the bilinear interpolation
744 
745       // declarations
746       int u_round = (int)(u_double);
747       int v_round = (int)(v_double);
748       if (u_round < 0.f)
749         u_round = -1;
750       if (v_round < 0.f)
751         v_round = -1;
752       double du_double = (u_double) - (double)u_round;
753       double dv_double = (v_double) - (double)v_round;
754       Type v01;
755       Type v23;
756       if ((0 <= u_round) && (0 <= v_round) && (u_round < (((int)width) - 1)) && (v_round < (((int)height) - 1))) {
757         // process interpolation
758         const Type *_mp = &I[(unsigned int)v_round][(unsigned int)u_round];
759         v01 = (Type)(_mp[0] + ((_mp[1] - _mp[0]) * du_double));
760         _mp += width;
761         v23 = (Type)(_mp[0] + ((_mp[1] - _mp[0]) * du_double));
762         *dst = (Type)(v01 + ((v23 - v01) * dv_double));
763         // printf("R %d G %d B %d\n", dst->R, dst->G, dst->B);
764       } else {
765         *dst = 0;
766       }
767       dst++;
768     }
769   }
770 #endif // VISP_HAVE_PTHREAD
771 
772 #if 0
773   // non optimized version
774   int width = I.getWidth();
775   int height = I.getHeight();
776 
777   undistI.resize(height,width);
778 
779   double u0 = cam.get_u0();
780   double v0 = cam.get_v0();
781   double px = cam.get_px();
782   double py = cam.get_py();
783   double kd = cam.get_kud();
784 
785   if (kd == 0) {
786     // There is no need to undistort the image
787     undistI = I;
788     return;
789   }
790 
791   for(int v = 0 ; v < height; v++){
792     for(int u = 0; u < height; u++){
793       double r2 = vpMath::sqr(((double)u - u0)/px) +
794                   vpMath::sqr(((double)v-v0)/py);
795       double u_double = ((double)u - u0)*(1.0+kd*r2) + u0;
796       double v_double = ((double)v - v0)*(1.0+kd*r2) + v0;
797       undistI[v][u] = I.getPixelBI((float)u_double,(float)v_double);
798     }
799   }
800 #endif
801 }
802 
803 /*!
804   Undistort an image.
805 
806   \param I       : Input image to undistort.
807   \param mapU    : Map that contains at each destination coordinate the u-coordinate in the source image.
808   \param mapV    : Map that contains at each destination coordinate the v-coordinate in the source image.
809   \param mapDu   : Map that contains at each destination coordinate the \f$ \Delta u \f$ for the interpolation.
810   \param mapDv   : Map that contains at each destination coordinate the \f$ \Delta v \f$ for the interpolation.
811   \param newI    : Undistorted output image. The size of this image will be the same as the input image \e I.
812 
813   \note To undistort a fisheye image, you have to first call initUndistortMap() function to calculate maps and then
814   call undistort() with input maps.
815 
816  */
817 template <class Type>
undistort(const vpImage<Type> & I,vpArray2D<int> mapU,vpArray2D<int> mapV,vpArray2D<float> mapDu,vpArray2D<float> mapDv,vpImage<Type> & newI)818 void vpImageTools::undistort(const vpImage<Type> &I, vpArray2D<int> mapU, vpArray2D<int> mapV, vpArray2D<float> mapDu,
819                              vpArray2D<float> mapDv, vpImage<Type> &newI)
820 {
821   remap(I, mapU, mapV, mapDu, mapDv, newI);
822 }
823 
824 /*!
825   Flip vertically the input image and give the result in the output image.
826 
827   \param I : Input image to flip.
828   \param newI : Output image which is the flipped input image.
829 */
flip(const vpImage<Type> & I,vpImage<Type> & newI)830 template <class Type> void vpImageTools::flip(const vpImage<Type> &I, vpImage<Type> &newI)
831 {
832   unsigned int height = 0, width = 0;
833 
834   height = I.getHeight();
835   width = I.getWidth();
836   newI.resize(height, width);
837 
838   for (unsigned int i = 0; i < height; i++) {
839     memcpy(newI.bitmap + i * width, I.bitmap + (height - 1 - i) * width, width * sizeof(Type));
840   }
841 }
842 
843 /*!
844   Flip vertically the input image.
845 
846   \param I : Input image which is flipped and modified in output.
847 
848   The following example shows how to use this function:
849   \code
850 #include <visp3/core/vpImage.h>
851 #include <visp3/core/vpImageTools.h>
852 #include <visp3/io/vpImageIo.h>
853 
854 int main()
855 {
856   vpImage<vpRGBa> I;
857 #ifdef _WIN32
858   std::string filename("C:/temp/ViSP-images/Klimt/Klimt.ppm");
859 #else
860   std::string filename("/local/soft/ViSP/ViSP-images/Klimt/Klimt.ppm");
861 #endif
862 
863   // Read an image from the disk
864   vpImageIo::read(I, filename);
865 
866   // Flip the image
867   vpImageTools::flip(I);
868 
869   // Write the image in a PGM P5 image file format
870   vpImageIo::write(I, "Klimt-flip.ppm");
871 }
872   \endcode
873 */
flip(vpImage<Type> & I)874 template <class Type> void vpImageTools::flip(vpImage<Type> &I)
875 {
876   unsigned int height = 0, width = 0;
877   unsigned int i = 0;
878   vpImage<Type> Ibuf;
879 
880   height = I.getHeight();
881   width = I.getWidth();
882   Ibuf.resize(1, width);
883 
884   for (i = 0; i < height / 2; i++) {
885     memcpy(Ibuf.bitmap, I.bitmap + i * width, width * sizeof(Type));
886 
887     memcpy(I.bitmap + i * width, I.bitmap + (height - 1 - i) * width, width * sizeof(Type));
888     memcpy(I.bitmap + (height - 1 - i) * width, Ibuf.bitmap, width * sizeof(Type));
889   }
890 }
891 
getPixelClamped(const vpImage<Type> & I,float u,float v)892 template <class Type> Type vpImageTools::getPixelClamped(const vpImage<Type> &I, float u, float v)
893 {
894   int x = vpMath::round(u);
895   int y = vpMath::round(v);
896   x = (std::max)(0, (std::min)(x, static_cast<int>(I.getWidth())-1));
897   y = (std::max)(0, (std::min)(y, static_cast<int>(I.getHeight())-1));
898 
899   return I[y][x];
900 }
901 
902 // Reference:
903 // http://blog.demofox.org/2015/08/15/resizing-images-with-bicubic-interpolation/
904 template <class Type>
resizeBicubic(const vpImage<Type> & I,vpImage<Type> & Ires,unsigned int i,unsigned int j,float u,float v,float xFrac,float yFrac)905 void vpImageTools::resizeBicubic(const vpImage<Type> &I, vpImage<Type> &Ires, unsigned int i,
906                                  unsigned int j, float u, float v, float xFrac, float yFrac)
907 {
908   // 1st row
909   Type p00 = getPixelClamped(I, u - 1, v - 1);
910   Type p01 = getPixelClamped(I, u + 0, v - 1);
911   Type p02 = getPixelClamped(I, u + 1, v - 1);
912   Type p03 = getPixelClamped(I, u + 2, v - 1);
913 
914   // 2nd row
915   Type p10 = getPixelClamped(I, u - 1, v + 0);
916   Type p11 = getPixelClamped(I, u + 0, v + 0);
917   Type p12 = getPixelClamped(I, u + 1, v + 0);
918   Type p13 = getPixelClamped(I, u + 2, v + 0);
919 
920   // 3rd row
921   Type p20 = getPixelClamped(I, u - 1, v + 1);
922   Type p21 = getPixelClamped(I, u + 0, v + 1);
923   Type p22 = getPixelClamped(I, u + 1, v + 1);
924   Type p23 = getPixelClamped(I, u + 2, v + 1);
925 
926   // 4th row
927   Type p30 = getPixelClamped(I, u - 1, v + 2);
928   Type p31 = getPixelClamped(I, u + 0, v + 2);
929   Type p32 = getPixelClamped(I, u + 1, v + 2);
930   Type p33 = getPixelClamped(I, u + 2, v + 2);
931 
932   float col0 = cubicHermite(p00, p01, p02, p03, xFrac);
933   float col1 = cubicHermite(p10, p11, p12, p13, xFrac);
934   float col2 = cubicHermite(p20, p21, p22, p23, xFrac);
935   float col3 = cubicHermite(p30, p31, p32, p33, xFrac);
936   float value = cubicHermite(col0, col1, col2, col3, yFrac);
937   Ires[i][j] = vpMath::saturate<Type>(value);
938 }
939 
940 template <>
resizeBicubic(const vpImage<vpRGBa> & I,vpImage<vpRGBa> & Ires,unsigned int i,unsigned int j,float u,float v,float xFrac,float yFrac)941 inline void vpImageTools::resizeBicubic(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &Ires, unsigned int i,
942                                         unsigned int j, float u, float v, float xFrac, float yFrac)
943 {
944   // 1st row
945   vpRGBa p00 = getPixelClamped(I, u - 1, v - 1);
946   vpRGBa p01 = getPixelClamped(I, u + 0, v - 1);
947   vpRGBa p02 = getPixelClamped(I, u + 1, v - 1);
948   vpRGBa p03 = getPixelClamped(I, u + 2, v - 1);
949 
950   // 2nd row
951   vpRGBa p10 = getPixelClamped(I, u - 1, v + 0);
952   vpRGBa p11 = getPixelClamped(I, u + 0, v + 0);
953   vpRGBa p12 = getPixelClamped(I, u + 1, v + 0);
954   vpRGBa p13 = getPixelClamped(I, u + 2, v + 0);
955 
956   // 3rd row
957   vpRGBa p20 = getPixelClamped(I, u - 1, v + 1);
958   vpRGBa p21 = getPixelClamped(I, u + 0, v + 1);
959   vpRGBa p22 = getPixelClamped(I, u + 1, v + 1);
960   vpRGBa p23 = getPixelClamped(I, u + 2, v + 1);
961 
962   // 4th row
963   vpRGBa p30 = getPixelClamped(I, u - 1, v + 2);
964   vpRGBa p31 = getPixelClamped(I, u + 0, v + 2);
965   vpRGBa p32 = getPixelClamped(I, u + 1, v + 2);
966   vpRGBa p33 = getPixelClamped(I, u + 2, v + 2);
967 
968   for (int c = 0; c < 3; c++) {
969     float col0 = cubicHermite(static_cast<float>(reinterpret_cast<unsigned char *>(&p00)[c]),
970                               static_cast<float>(reinterpret_cast<unsigned char *>(&p01)[c]),
971                               static_cast<float>(reinterpret_cast<unsigned char *>(&p02)[c]),
972                               static_cast<float>(reinterpret_cast<unsigned char *>(&p03)[c]), xFrac);
973     float col1 = cubicHermite(static_cast<float>(reinterpret_cast<unsigned char *>(&p10)[c]),
974                               static_cast<float>(reinterpret_cast<unsigned char *>(&p11)[c]),
975                               static_cast<float>(reinterpret_cast<unsigned char *>(&p12)[c]),
976                               static_cast<float>(reinterpret_cast<unsigned char *>(&p13)[c]), xFrac);
977     float col2 = cubicHermite(static_cast<float>(reinterpret_cast<unsigned char *>(&p20)[c]),
978                               static_cast<float>(reinterpret_cast<unsigned char *>(&p21)[c]),
979                               static_cast<float>(reinterpret_cast<unsigned char *>(&p22)[c]),
980                               static_cast<float>(reinterpret_cast<unsigned char *>(&p23)[c]), xFrac);
981     float col3 = cubicHermite(static_cast<float>(reinterpret_cast<unsigned char *>(&p30)[c]),
982                               static_cast<float>(reinterpret_cast<unsigned char *>(&p31)[c]),
983                               static_cast<float>(reinterpret_cast<unsigned char *>(&p32)[c]),
984                               static_cast<float>(reinterpret_cast<unsigned char *>(&p33)[c]), xFrac);
985     float value = cubicHermite(col0, col1, col2, col3, yFrac);
986 
987     reinterpret_cast<unsigned char *>(&Ires[i][j])[c] = vpMath::saturate<unsigned char>(value);
988   }
989 }
990 
991 template <class Type>
resizeBilinear(const vpImage<Type> & I,vpImage<Type> & Ires,unsigned int i,unsigned int j,float u,float v,float xFrac,float yFrac)992 void vpImageTools::resizeBilinear(const vpImage<Type> &I, vpImage<Type> &Ires, unsigned int i,
993                                   unsigned int j, float u, float v, float xFrac, float yFrac)
994 {
995   int u0 = static_cast<int>(u);
996   int v0 = static_cast<int>(v);
997 
998   int u1 = (std::min)(static_cast<int>(I.getWidth()) - 1, u0 + 1);
999   int v1 = v0;
1000 
1001   int u2 = u0;
1002   int v2 = (std::min)(static_cast<int>(I.getHeight()) - 1, v0 + 1);
1003 
1004   int u3 = u1;
1005   int v3 = v2;
1006 
1007   float col0 = lerp(I[v0][u0], I[v1][u1], xFrac);
1008   float col1 = lerp(I[v2][u2], I[v3][u3], xFrac);
1009   float value = lerp(col0, col1, yFrac);
1010 
1011   Ires[i][j] = vpMath::saturate<Type>(value);
1012 }
1013 
1014 template <>
resizeBilinear(const vpImage<vpRGBa> & I,vpImage<vpRGBa> & Ires,unsigned int i,unsigned int j,float u,float v,float xFrac,float yFrac)1015 inline void vpImageTools::resizeBilinear(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &Ires, unsigned int i,
1016                                          unsigned int j, float u, float v, float xFrac, float yFrac)
1017 {
1018   int u0 = static_cast<int>(u);
1019   int v0 = static_cast<int>(v);
1020 
1021   int u1 = (std::min)(static_cast<int>(I.getWidth()) - 1, u0 + 1);
1022   int v1 = v0;
1023 
1024   int u2 = u0;
1025   int v2 = (std::min)(static_cast<int>(I.getHeight()) - 1, v0 + 1);
1026 
1027   int u3 = u1;
1028   int v3 = v2;
1029 
1030   for (int c = 0; c < 3; c++) {
1031     float col0 = lerp(static_cast<float>(reinterpret_cast<const unsigned char *>(&I[v0][u0])[c]),
1032                       static_cast<float>(reinterpret_cast<const unsigned char *>(&I[v1][u1])[c]), xFrac);
1033     float col1 = lerp(static_cast<float>(reinterpret_cast<const unsigned char *>(&I[v2][u2])[c]),
1034                       static_cast<float>(reinterpret_cast<const unsigned char *>(&I[v3][u3])[c]), xFrac);
1035     float value = lerp(col0, col1, yFrac);
1036 
1037     reinterpret_cast<unsigned char *>(&Ires[i][j])[c] = vpMath::saturate<unsigned char>(value);
1038   }
1039 }
1040 
1041 template <class Type>
resizeNearest(const vpImage<Type> & I,vpImage<Type> & Ires,unsigned int i,unsigned int j,float u,float v)1042 void vpImageTools::resizeNearest(const vpImage<Type> &I, vpImage<Type> &Ires, unsigned int i,
1043                                  unsigned int j, float u, float v)
1044 {
1045   Ires[i][j] = getPixelClamped(I, u, v);
1046 }
1047 
1048 /*!
1049   Resize the image using one interpolation method (by default it uses the
1050   nearest neighbor interpolation).
1051 
1052   \param I : Input image.
1053   \param Ires : Output image resized to \e width, \e height.
1054   \param width : Resized width.
1055   \param height : Resized height.
1056   \param method : Interpolation method.
1057   \param nThreads : Number of threads to use if OpenMP is available
1058   (zero will let OpenMP uses the optimal number of threads).
1059 
1060   \warning The input \e I and output \e Ires images must be different objects.
1061 
1062   \note The SIMD lib is used to accelerate processing on x86 and ARM architecture for:
1063     - unsigned char and vpRGBa image types
1064     - and only with INTERPOLATION_AREA and INTERPOLATION_LINEAR methods
1065 */
1066 template <class Type>
resize(const vpImage<Type> & I,vpImage<Type> & Ires,unsigned int width,unsigned int height,const vpImageInterpolationType & method,unsigned int nThreads)1067 void vpImageTools::resize(const vpImage<Type> &I, vpImage<Type> &Ires, unsigned int width,
1068                           unsigned int height, const vpImageInterpolationType &method,
1069                           unsigned int nThreads)
1070 {
1071   Ires.resize(height, width);
1072 
1073   vpImageTools::resize(I, Ires, method, nThreads);
1074 }
1075 
1076 /*!
1077   Resize the image using one interpolation method (by default it uses the
1078   nearest neighbor interpolation).
1079 
1080   \param I : Input image.
1081   \param Ires : Output image resized (you have to init the image \e Ires at
1082   the desired size).
1083   \param method : Interpolation method.
1084   \param nThreads : Number of threads to use if OpenMP is available
1085   (zero will let OpenMP uses the optimal number of threads).
1086 
1087   \warning The input \e I and output \e Ires images must be different objects.
1088 
1089   \note The SIMD lib is used to accelerate processing on x86 and ARM architecture for:
1090     - unsigned char and vpRGBa image types
1091     - and only with INTERPOLATION_AREA and INTERPOLATION_LINEAR methods
1092 */
1093 template <class Type>
resize(const vpImage<Type> & I,vpImage<Type> & Ires,const vpImageInterpolationType & method,unsigned int nThreads)1094 void vpImageTools::resize(const vpImage<Type> &I, vpImage<Type> &Ires, const vpImageInterpolationType &method,
1095                           unsigned int
1096                         #if defined _OPENMP
1097                           nThreads
1098                         #endif
1099                           )
1100 {
1101   if (I.getWidth() < 2 || I.getHeight() < 2 || Ires.getWidth() < 2 || Ires.getHeight() < 2) {
1102     std::cerr << "Input or output image is too small!" << std::endl;
1103     return;
1104   }
1105 
1106   if (method == INTERPOLATION_AREA) {
1107     std::cerr << "INTERPOLATION_AREA is not implemented for this type." << std::endl;
1108     return;
1109   }
1110 
1111   const float scaleY = I.getHeight() / static_cast<float>(Ires.getHeight());
1112   const float scaleX = I.getWidth() / static_cast<float>(Ires.getWidth());
1113   const float half = 0.5f;
1114 
1115 #if defined _OPENMP
1116   if (nThreads > 0) {
1117     omp_set_num_threads(static_cast<int>(nThreads));
1118   }
1119   #pragma omp parallel for schedule(dynamic)
1120 #endif
1121   for (int i = 0; i < static_cast<int>(Ires.getHeight()); i++) {
1122     const float v = (i + half) * scaleY - half;
1123     const int v0 = static_cast<int>(v);
1124     const float yFrac = v - v0;
1125 
1126     for (unsigned int j = 0; j < Ires.getWidth(); j++) {
1127       const float u = (j + half) * scaleX - half;
1128       const int u0 = static_cast<int>(u);
1129       const float xFrac = u - u0;
1130 
1131       if (method == INTERPOLATION_NEAREST) {
1132         resizeNearest(I, Ires, static_cast<unsigned int>(i), j, u, v);
1133       } else if (method == INTERPOLATION_LINEAR) {
1134         resizeBilinear(I, Ires, static_cast<unsigned int>(i), j, u0, v0, xFrac, yFrac);
1135       } else if (method == INTERPOLATION_CUBIC) {
1136         resizeBicubic(I, Ires, static_cast<unsigned int>(i), j, u, v, xFrac, yFrac);
1137       }
1138     }
1139   }
1140 }
1141 
1142 template <> inline
resize(const vpImage<unsigned char> & I,vpImage<unsigned char> & Ires,const vpImageInterpolationType & method,unsigned int nThreads)1143 void vpImageTools::resize(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ires,
1144                           const vpImageInterpolationType &method, unsigned int
1145                                                                   #if defined _OPENMP
1146                                                                     nThreads
1147                                                                   #endif
1148                           )
1149 {
1150   if (I.getWidth() < 2 || I.getHeight() < 2 || Ires.getWidth() < 2 || Ires.getHeight() < 2) {
1151     std::cerr << "Input or output image is too small!" << std::endl;
1152     return;
1153   }
1154 
1155   if (method == INTERPOLATION_AREA) {
1156     resizeSimdlib(I, Ires.getWidth(), Ires.getHeight(), Ires, INTERPOLATION_AREA);
1157   } else if (method == INTERPOLATION_LINEAR) {
1158     resizeSimdlib(I, Ires.getWidth(), Ires.getHeight(), Ires, INTERPOLATION_LINEAR);
1159   } else {
1160     const float scaleY = I.getHeight() / static_cast<float>(Ires.getHeight());
1161     const float scaleX = I.getWidth() / static_cast<float>(Ires.getWidth());
1162     const float half = 0.5f;
1163 
1164 #if defined _OPENMP
1165     if (nThreads > 0) {
1166       omp_set_num_threads(static_cast<int>(nThreads));
1167     }
1168 #pragma omp parallel for schedule(dynamic)
1169 #endif
1170     for (int i = 0; i < static_cast<int>(Ires.getHeight()); i++) {
1171       float v = (i + half) * scaleY - half;
1172       float yFrac = v - static_cast<int>(v);
1173 
1174       for (unsigned int j = 0; j < Ires.getWidth(); j++) {
1175         float u = (j + half) * scaleX - half;
1176         float xFrac = u - static_cast<int>(u);
1177 
1178         if (method == INTERPOLATION_NEAREST) {
1179           resizeNearest(I, Ires, static_cast<unsigned int>(i), j, u, v);
1180         } else if (method == INTERPOLATION_CUBIC) {
1181           resizeBicubic(I, Ires, static_cast<unsigned int>(i), j, u, v, xFrac, yFrac);
1182         }
1183       }
1184     }
1185   }
1186 }
1187 
1188 template <> inline
resize(const vpImage<vpRGBa> & I,vpImage<vpRGBa> & Ires,const vpImageInterpolationType & method,unsigned int nThreads)1189 void vpImageTools::resize(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &Ires,
1190                           const vpImageInterpolationType &method, unsigned int
1191                                                                   #if defined _OPENMP
1192                                                                     nThreads
1193                                                                   #endif
1194                           )
1195 {
1196   if (I.getWidth() < 2 || I.getHeight() < 2 || Ires.getWidth() < 2 || Ires.getHeight() < 2) {
1197     std::cerr << "Input or output image is too small!" << std::endl;
1198     return;
1199   }
1200 
1201   if (method == INTERPOLATION_AREA) {
1202     resizeSimdlib(I, Ires.getWidth(), Ires.getHeight(), Ires, INTERPOLATION_AREA);
1203   } else if (method == INTERPOLATION_LINEAR) {
1204     resizeSimdlib(I, Ires.getWidth(), Ires.getHeight(), Ires, INTERPOLATION_LINEAR);
1205   } else {
1206     const float scaleY = I.getHeight() / static_cast<float>(Ires.getHeight());
1207     const float scaleX = I.getWidth() / static_cast<float>(Ires.getWidth());
1208     const float half = 0.5f;
1209 
1210   #if defined _OPENMP
1211     if (nThreads > 0) {
1212       omp_set_num_threads(static_cast<int>(nThreads));
1213     }
1214     #pragma omp parallel for schedule(dynamic)
1215   #endif
1216     for (int i = 0; i < static_cast<int>(Ires.getHeight()); i++) {
1217       float v = (i + half) * scaleY - half;
1218       float yFrac = v - static_cast<int>(v);
1219 
1220       for (unsigned int j = 0; j < Ires.getWidth(); j++) {
1221         float u = (j + half) * scaleX - half;
1222         float xFrac = u - static_cast<int>(u);
1223 
1224         if (method == INTERPOLATION_NEAREST) {
1225           resizeNearest(I, Ires, static_cast<unsigned int>(i), j, u, v);
1226         } else if (method == INTERPOLATION_CUBIC) {
1227           resizeBicubic(I, Ires, static_cast<unsigned int>(i), j, u, v, xFrac, yFrac);
1228         }
1229       }
1230     }
1231   }
1232 }
1233 
1234 /*!
1235   Apply a warping (affine or perspective) transformation to an image.
1236 
1237   \param src : Input image.
1238   \param T : Transformation / warping matrix, a `2x3` matrix for an affine transformation
1239   or a `3x3` matrix for a perspective transformation (homography).
1240   \param dst : Output image, if empty it will be of the same size than src and zero-initialized.
1241   \param interpolation : Interpolation method (only INTERPOLATION_NEAREST and INTERPOLATION_LINEAR
1242   are accepted, if INTERPOLATION_CUBIC is passed, INTERPOLATION_NEAREST will be used instead).
1243   \param fixedPointArithmetic : If true and if `pixelCenter` is false, fixed-point arithmetic is used if
1244   possible. Otherwise (e.g. the input image is too big) it fallbacks to the default implementation.
1245   \param pixelCenter : If true, pixel coordinates are at (0.5, 0.5), otherwise at (0,0). Fixed-point
1246   arithmetic cannot be used with `pixelCenter` option.
1247 */
1248 template <class Type>
warpImage(const vpImage<Type> & src,const vpMatrix & T,vpImage<Type> & dst,const vpImageInterpolationType & interpolation,bool fixedPointArithmetic,bool pixelCenter)1249 void vpImageTools::warpImage(const vpImage<Type> &src, const vpMatrix &T, vpImage<Type> &dst,
1250                              const vpImageInterpolationType &interpolation,
1251                              bool fixedPointArithmetic, bool pixelCenter)
1252 {
1253   if ((T.getRows() != 2 && T.getRows() != 3) || T.getCols() != 3) {
1254     std::cerr << "Input transformation must be a (2x3) or (3x3) matrix." << std::endl;
1255     return;
1256   }
1257 
1258   if (src.getSize() == 0) {
1259     return;
1260   }
1261 
1262   const bool affine = (T.getRows() == 2);
1263   const bool interp_NN = (interpolation == INTERPOLATION_NEAREST) || (interpolation == INTERPOLATION_CUBIC);
1264 
1265   if (dst.getSize() == 0) {
1266     dst.resize(src.getHeight(), src.getWidth(), Type(0));
1267   }
1268 
1269   vpMatrix M = T;
1270   if (affine) {
1271     double D = M[0][0] * M[1][1] - M[0][1] * M[1][0];
1272     D = !vpMath::nul(D, std::numeric_limits<double>::epsilon()) ? 1.0 / D : 0;
1273     double A11 = M[1][1] * D, A22 = M[0][0] * D;
1274     M[0][0] = A11; M[0][1] *= -D;
1275     M[1][0] *= -D; M[1][1] = A22;
1276     double b1 = -M[0][0] * M[0][2] - M[0][1] * M[1][2];
1277     double b2 = -M[1][0] * M[0][2] - M[1][1] * M[1][2];
1278     M[0][2] = b1; M[1][2] = b2;
1279   } else {
1280     M = T.inverseByLU();
1281   }
1282 
1283   if (fixedPointArithmetic && !pixelCenter) {
1284     fixedPointArithmetic = checkFixedPoint(0, 0, M, affine) &&
1285                            checkFixedPoint(dst.getWidth()-1, 0, M, affine) &&
1286                            checkFixedPoint(0, dst.getHeight()-1, M, affine) &&
1287                            checkFixedPoint(dst.getWidth() - 1, dst.getHeight() - 1, M, affine);
1288   }
1289 
1290   if (interp_NN) {
1291     //nearest neighbor interpolation
1292     warpNN(src, M, dst, affine, pixelCenter, fixedPointArithmetic);
1293   } else {
1294     //bilinear interpolation
1295     warpLinear(src, M, dst, affine, pixelCenter, fixedPointArithmetic);
1296   }
1297 }
1298 
1299 template <class Type>
warpNN(const vpImage<Type> & src,const vpMatrix & T,vpImage<Type> & dst,bool affine,bool centerCorner,bool fixedPoint)1300 void vpImageTools::warpNN(const vpImage<Type> &src, const vpMatrix &T, vpImage<Type> &dst, bool affine,
1301                           bool centerCorner, bool fixedPoint)
1302 {
1303   if (fixedPoint && !centerCorner) {
1304     const int nbits = 16;
1305     const int32_t precision = 1 << nbits;
1306     const float precision_1 = 1 / static_cast<float>(precision);
1307 
1308     int32_t a0_i32 = static_cast<int32_t>(T[0][0] * precision);
1309     int32_t a1_i32 = static_cast<int32_t>(T[0][1] * precision);
1310     int32_t a2_i32 = static_cast<int32_t>(T[0][2] * precision);
1311     int32_t a3_i32 = static_cast<int32_t>(T[1][0] * precision);
1312     int32_t a4_i32 = static_cast<int32_t>(T[1][1] * precision);
1313     int32_t a5_i32 = static_cast<int32_t>(T[1][2] * precision);
1314     int32_t a6_i32 = T.getRows() == 3 ? static_cast<int32_t>(T[2][0] * precision) : 0;
1315     int32_t a7_i32 = T.getRows() == 3 ? static_cast<int32_t>(T[2][1] * precision) : 0;
1316     int32_t a8_i32 = T.getRows() == 3 ? static_cast<int32_t>(T[2][2] * precision) : 1;
1317 
1318     int32_t height_1_i32 = static_cast<int32_t>((src.getHeight() - 1) * precision) + 0x8000;
1319     int32_t width_1_i32 = static_cast<int32_t>((src.getWidth() - 1) * precision) + 0x8000;
1320 
1321     if (affine) {
1322       for (unsigned int i = 0; i < dst.getHeight(); i++) {
1323         int32_t xi = a2_i32;
1324         int32_t yi = a5_i32;
1325 
1326         for (unsigned int j = 0; j < dst.getWidth(); j++) {
1327           if (yi >= 0 && yi < height_1_i32 && xi >= 0 && xi < width_1_i32) {
1328             float x_ = (xi >> nbits) + (xi & 0xFFFF) * precision_1;
1329             float y_ = (yi >> nbits) + (yi & 0xFFFF) * precision_1;
1330 
1331             int x = vpMath::round(x_);
1332             int y = vpMath::round(y_);
1333             dst[i][j] = src[y][x];
1334           }
1335 
1336           xi += a0_i32;
1337           yi += a3_i32;
1338         }
1339 
1340         a2_i32 += a1_i32;
1341         a5_i32 += a4_i32;
1342       }
1343     } else {
1344       for (unsigned int i = 0; i < dst.getHeight(); i++) {
1345         int64_t xi = a2_i32;
1346         int64_t yi = a5_i32;
1347         int64_t wi = a8_i32;
1348 
1349         for (unsigned int j = 0; j < dst.getWidth(); j++) {
1350           if (wi != 0 && yi >= 0 && yi <= (static_cast<int>(src.getHeight()) - 1)*wi &&
1351               xi >= 0 && xi <= (static_cast<int>(src.getWidth()) - 1)*wi) {
1352             float w_ = (wi >> nbits) + (wi & 0xFFFF) * precision_1;
1353             float x_ = ((xi >> nbits) + (xi & 0xFFFF) * precision_1) / w_;
1354             float y_ = ((yi >> nbits) + (yi & 0xFFFF) * precision_1) / w_;
1355 
1356             int x = vpMath::round(x_);
1357             int y = vpMath::round(y_);
1358 
1359             dst[i][j] = src[y][x];
1360           }
1361 
1362           xi += a0_i32;
1363           yi += a3_i32;
1364           wi += a6_i32;
1365         }
1366 
1367         a2_i32 += a1_i32;
1368         a5_i32 += a4_i32;
1369         a8_i32 += a7_i32;
1370       }
1371     }
1372   } else {
1373     double a0 = T[0][0];  double a1 = T[0][1];  double a2 = T[0][2];
1374     double a3 = T[1][0];  double a4 = T[1][1];  double a5 = T[1][2];
1375     double a6 = affine ? 0.0 : T[2][0];
1376     double a7 = affine ? 0.0 : T[2][1];
1377     double a8 = affine ? 1.0 : T[2][2];
1378 
1379     for (unsigned int i = 0; i < dst.getHeight(); i++) {
1380       for (unsigned int j = 0; j < dst.getWidth(); j++) {
1381         double x = a0 * (centerCorner ? j + 0.5 : j) + a1 * (centerCorner ? i + 0.5 : i) + a2;
1382         double y = a3 * (centerCorner ? j + 0.5 : j) + a4 * (centerCorner ? i + 0.5 : i) + a5;
1383         double w = a6 * (centerCorner ? j + 0.5 : j) + a7 * (centerCorner ? i + 0.5 : i) + a8;
1384 
1385         if (vpMath::nul(w, std::numeric_limits<double>::epsilon())) {
1386           w = 1.0;
1387         }
1388 
1389         int x_ = centerCorner ? coordCast(x / w) : vpMath::round(x / w);
1390         int y_ = centerCorner ? coordCast(y / w) : vpMath::round(y / w);
1391 
1392         if (x_ >= 0 && x_ < static_cast<int>(src.getWidth()) &&
1393             y_ >= 0 && y_ < static_cast<int>(src.getHeight())) {
1394           dst[i][j] = src[y_][x_];
1395         }
1396       }
1397     }
1398   }
1399 }
1400 
1401 template <class Type>
warpLinear(const vpImage<Type> & src,const vpMatrix & T,vpImage<Type> & dst,bool affine,bool centerCorner,bool fixedPoint)1402 void vpImageTools::warpLinear(const vpImage<Type> &src, const vpMatrix &T, vpImage<Type> &dst, bool affine,
1403                               bool centerCorner, bool fixedPoint)
1404 {
1405   if (fixedPoint && !centerCorner) {
1406     const int nbits = 16;
1407     const int64_t precision = 1 << nbits;
1408     const float precision_1 = 1 / static_cast<float>(precision);
1409     const int64_t precision2 = 1ULL << (2 * nbits);
1410     const float precision_2 = 1 / static_cast<float>(precision2);
1411 
1412     int64_t a0_i64 = static_cast<int64_t>(T[0][0] * precision);
1413     int64_t a1_i64 = static_cast<int64_t>(T[0][1] * precision);
1414     int64_t a2_i64 = static_cast<int64_t>(T[0][2] * precision);
1415     int64_t a3_i64 = static_cast<int64_t>(T[1][0] * precision);
1416     int64_t a4_i64 = static_cast<int64_t>(T[1][1] * precision);
1417     int64_t a5_i64 = static_cast<int64_t>(T[1][2] * precision);
1418     int64_t a6_i64 = T.getRows() == 3 ? static_cast<int64_t>(T[2][0] * precision) : 0;
1419     int64_t a7_i64 = T.getRows() == 3 ? static_cast<int64_t>(T[2][1] * precision) : 0;
1420     int64_t a8_i64 = T.getRows() == 3 ? static_cast<int64_t>(T[2][2] * precision) : 1;
1421 
1422     int64_t height_i64 = static_cast<int64_t>(src.getHeight() * precision);
1423     int64_t width_i64 = static_cast<int64_t>(src.getWidth() * precision);
1424 
1425     if (affine) {
1426       for (unsigned int i = 0; i < dst.getHeight(); i++) {
1427         int64_t xi_ = a2_i64;
1428         int64_t yi_ = a5_i64;
1429 
1430         for (unsigned int j = 0; j < dst.getWidth(); j++) {
1431           if (yi_ >= 0 && yi_ < height_i64 && xi_ >= 0 && xi_ < width_i64) {
1432             const int64_t xi_lower = xi_ & (~0xFFFF);
1433             const int64_t yi_lower = yi_ & (~0xFFFF);
1434 
1435             const int64_t t = yi_ - yi_lower;
1436             const int64_t t_1 = precision - t;
1437             const int64_t s = xi_ - xi_lower;
1438             const int64_t s_1 = precision - s;
1439 
1440             const int x_ = static_cast<int>(xi_ >> nbits);
1441             const int y_ = static_cast<int>(yi_ >> nbits);
1442 
1443             if (y_ < static_cast<int>(src.getHeight())-1 && x_ < static_cast<int>(src.getWidth())-1) {
1444               const Type val00 = src[y_][x_];
1445               const Type val01 = src[y_][x_+1];
1446               const Type val10 = src[y_+1][x_];
1447               const Type val11 = src[y_+1][x_+1];
1448               const int64_t interp_i64 = static_cast<int64_t>(s_1*t_1*val00 + s*t_1*val01 + s_1*t*val10 + s*t*val11);
1449               const float interp = (interp_i64 >> (nbits*2)) + (interp_i64 & 0xFFFFFFFF) * precision_2;
1450               dst[i][j] = vpMath::saturate<Type>(interp);
1451             } else if (y_ < static_cast<int>(src.getHeight())-1) {
1452               const Type val00 = src[y_][x_];
1453               const Type val10 = src[y_+1][x_];
1454               const int64_t interp_i64 = static_cast<int64_t>(t_1*val00 + t*val10);
1455               const float interp = (interp_i64 >> nbits) + (interp_i64 & 0xFFFF) * precision_1;
1456               dst[i][j] = vpMath::saturate<Type>(interp);
1457             } else if (x_ < static_cast<int>(src.getWidth())-1) {
1458               const Type val00 = src[y_][x_];
1459               const Type val01 = src[y_][x_+1];
1460               const int64_t interp_i64 = static_cast<int64_t>(s_1*val00 + s*val01);
1461               const float interp = (interp_i64 >> nbits) + (interp_i64 & 0xFFFF) * precision_1;
1462               dst[i][j] = vpMath::saturate<Type>(interp);
1463             } else {
1464               dst[i][j] = src[y_][x_];
1465             }
1466           }
1467 
1468           xi_ += a0_i64;
1469           yi_ += a3_i64;
1470         }
1471 
1472         a2_i64 += a1_i64;
1473         a5_i64 += a4_i64;
1474       }
1475     } else {
1476       for (unsigned int i = 0; i < dst.getHeight(); i++) {
1477         int64_t xi = a2_i64;
1478         int64_t yi = a5_i64;
1479         int64_t wi = a8_i64;
1480 
1481         for (unsigned int j = 0; j < dst.getWidth(); j++) {
1482           if (wi != 0 && yi >= 0 && yi <= (static_cast<int>(src.getHeight()) - 1)*wi &&
1483               xi >= 0 && xi <= (static_cast<int>(src.getWidth()) - 1)*wi) {
1484             const float wi_ = (wi >> nbits) + (wi & 0xFFFF) * precision_1;
1485             const float xi_ = ((xi >> nbits) + (xi & 0xFFFF) * precision_1) / wi_;
1486             const float yi_ = ((yi >> nbits) + (yi & 0xFFFF) * precision_1) / wi_;
1487 
1488             const int x_ = static_cast<int>(xi_);
1489             const int y_ = static_cast<int>(yi_);
1490 
1491             const float t = yi_ - y_;
1492             const float s = xi_ - x_;
1493 
1494             if (y_ < static_cast<int>(src.getHeight()) - 1 && x_ < static_cast<int>(src.getWidth()) - 1) {
1495               const Type val00 = src[y_][x_];
1496               const Type val01 = src[y_][x_ + 1];
1497               const Type val10 = src[y_ + 1][x_];
1498               const Type val11 = src[y_ + 1][x_ + 1];
1499               const float col0 = lerp(val00, val01, s);
1500               const float col1 = lerp(val10, val11, s);
1501               const float interp = lerp(col0, col1, t);
1502               dst[i][j] = vpMath::saturate<Type>(interp);
1503             } else if (y_ < static_cast<int>(src.getHeight()) - 1) {
1504               const Type val00 = src[y_][x_];
1505               const Type val10 = src[y_ + 1][x_];
1506               const float interp = lerp(val00, val10, t);
1507               dst[i][j] = vpMath::saturate<Type>(interp);
1508             } else if (x_ < static_cast<int>(src.getWidth()) - 1) {
1509               const Type val00 = src[y_][x_];
1510               const Type val01 = src[y_][x_ + 1];
1511               const float interp = lerp(val00, val01, s);
1512               dst[i][j] = vpMath::saturate<Type>(interp);
1513             } else {
1514               dst[i][j] = src[y_][x_];
1515             }
1516           }
1517 
1518           xi += a0_i64;
1519           yi += a3_i64;
1520           wi += a6_i64;
1521         }
1522 
1523         a2_i64 += a1_i64;
1524         a5_i64 += a4_i64;
1525         a8_i64 += a7_i64;
1526       }
1527     }
1528   } else {
1529     double a0 = T[0][0];  double a1 = T[0][1];  double a2 = T[0][2];
1530     double a3 = T[1][0];  double a4 = T[1][1];  double a5 = T[1][2];
1531     double a6 = affine ? 0.0 : T[2][0];
1532     double a7 = affine ? 0.0 : T[2][1];
1533     double a8 = affine ? 1.0 : T[2][2];
1534 
1535     for (unsigned int i = 0; i < dst.getHeight(); i++) {
1536       for (unsigned int j = 0; j < dst.getWidth(); j++) {
1537         double x = a0 * (centerCorner ? j + 0.5 : j) + a1 * (centerCorner ? i + 0.5 : i) + a2;
1538         double y = a3 * (centerCorner ? j + 0.5 : j) + a4 * (centerCorner ? i + 0.5 : i) + a5;
1539         double w = a6 * (centerCorner ? j + 0.5 : j) + a7 * (centerCorner ? i + 0.5 : i) + a8;
1540         if (vpMath::nul(w, std::numeric_limits<double>::epsilon())) {
1541           w = 1;
1542         }
1543 
1544         x = x / w - (centerCorner ? 0.5 : 0);
1545         y = y / w - (centerCorner ? 0.5 : 0);
1546 
1547         int x_lower = static_cast<int>(x);
1548         int y_lower = static_cast<int>(y);
1549 
1550         if (y_lower >= static_cast<int>(src.getHeight()) || x_lower >= static_cast<int>(src.getWidth()) ||
1551             y < 0 || x < 0) {
1552           continue;
1553         }
1554 
1555         double s = x - x_lower;
1556         double t = y - y_lower;
1557 
1558         if (y_lower < static_cast<int>(src.getHeight())-1 && x_lower < static_cast<int>(src.getWidth())-1) {
1559           const Type val00 = src[y_lower][x_lower];
1560           const Type val01 = src[y_lower][x_lower + 1];
1561           const Type val10 = src[y_lower + 1][x_lower];
1562           const Type val11 = src[y_lower + 1][x_lower + 1];
1563           const double col0 = lerp(val00, val01, s);
1564           const double col1 = lerp(val10, val11, s);
1565           const double interp = lerp(col0, col1, t);
1566           dst[i][j] = vpMath::saturate<Type>(interp);
1567         } else if (y_lower < static_cast<int>(src.getHeight())-1) {
1568           const Type val00 = src[y_lower][x_lower];
1569           const Type val10 = src[y_lower + 1][x_lower];
1570           const double interp = lerp(val00, val10, t);
1571           dst[i][j] = vpMath::saturate<Type>(interp);
1572         } else if (x_lower < static_cast<int>(src.getWidth())-1) {
1573           const Type val00 = src[y_lower][x_lower];
1574           const Type val01 = src[y_lower][x_lower + 1];
1575           const double interp = lerp(val00, val01, s);
1576           dst[i][j] = vpMath::saturate<Type>(interp);
1577         } else {
1578           dst[i][j] = src[y_lower][x_lower];
1579         }
1580       }
1581     }
1582   }
1583 }
1584 
1585 template <> inline
warpLinear(const vpImage<vpRGBa> & src,const vpMatrix & T,vpImage<vpRGBa> & dst,bool affine,bool centerCorner,bool fixedPoint)1586 void vpImageTools::warpLinear(const vpImage<vpRGBa> &src, const vpMatrix &T, vpImage<vpRGBa> &dst, bool affine,
1587                               bool centerCorner, bool fixedPoint)
1588 {
1589   if (fixedPoint && !centerCorner) {
1590     const int nbits = 16;
1591     const int64_t precision = 1 << nbits;
1592     const float precision_1 = 1 / static_cast<float>(precision);
1593     const int64_t precision2 = 1ULL << (2 * nbits);
1594     const float precision_2 = 1 / static_cast<float>(precision2);
1595 
1596     int64_t a0_i64 = static_cast<int64_t>(T[0][0] * precision);
1597     int64_t a1_i64 = static_cast<int64_t>(T[0][1] * precision);
1598     int64_t a2_i64 = static_cast<int64_t>(T[0][2] * precision);
1599     int64_t a3_i64 = static_cast<int64_t>(T[1][0] * precision);
1600     int64_t a4_i64 = static_cast<int64_t>(T[1][1] * precision);
1601     int64_t a5_i64 = static_cast<int64_t>(T[1][2] * precision);
1602     int64_t a6_i64 = T.getRows() == 3 ? static_cast<int64_t>(T[2][0] * precision) : 0;
1603     int64_t a7_i64 = T.getRows() == 3 ? static_cast<int64_t>(T[2][1] * precision) : 0;
1604     int64_t a8_i64 = precision;
1605 
1606     int64_t height_i64 = static_cast<int64_t>(src.getHeight() * precision);
1607     int64_t width_i64 = static_cast<int64_t>(src.getWidth() * precision);
1608 
1609     if (affine) {
1610       for (unsigned int i = 0; i < dst.getHeight(); i++) {
1611         int64_t xi = a2_i64;
1612         int64_t yi = a5_i64;
1613 
1614         for (unsigned int j = 0; j < dst.getWidth(); j++) {
1615           if (yi >= 0 && yi < height_i64 && xi >= 0 && xi < width_i64) {
1616             const int64_t xi_lower = xi & (~0xFFFF);
1617             const int64_t yi_lower = yi & (~0xFFFF);
1618 
1619             const int64_t t = yi - yi_lower;
1620             const int64_t t_1 = precision - t;
1621             const int64_t s = xi - xi_lower;
1622             const int64_t s_1 = precision - s;
1623 
1624             const int x_ = static_cast<int>(xi >> nbits);
1625             const int y_ = static_cast<int>(yi >> nbits);
1626 
1627             if (y_ < static_cast<int>(src.getHeight())-1 && x_ < static_cast<int>(src.getWidth())-1) {
1628               const vpRGBa val00 = src[y_][x_];
1629               const vpRGBa val01 = src[y_][x_+1];
1630               const vpRGBa val10 = src[y_+1][x_];
1631               const vpRGBa val11 = src[y_+1][x_+1];
1632               const int64_t interpR_i64 = static_cast<int64_t>(s_1*t_1*val00.R + s * t_1*val01.R + s_1 * t*val10.R + s * t*val11.R);
1633               const float interpR = (interpR_i64 >> (nbits*2)) + (interpR_i64 & 0xFFFFFFFF) * precision_2;
1634 
1635               const int64_t interpG_i64 = static_cast<int64_t>(s_1*t_1*val00.G + s * t_1*val01.G + s_1 * t*val10.G + s * t*val11.G);
1636               const float interpG = (interpG_i64 >> (nbits * 2)) + (interpG_i64 & 0xFFFFFFFF) * precision_2;
1637 
1638               const int64_t interpB_i64 = static_cast<int64_t>(s_1*t_1*val00.B + s * t_1*val01.B + s_1 * t*val10.B + s * t*val11.B);
1639               const float interpB = (interpB_i64 >> (nbits * 2)) + (interpB_i64 & 0xFFFFFFFF) * precision_2;
1640 
1641               dst[i][j] = vpRGBa(vpMath::saturate<unsigned char>(interpR),
1642                                  vpMath::saturate<unsigned char>(interpG),
1643                                  vpMath::saturate<unsigned char>(interpB),
1644                                  255);
1645             } else if (y_ < static_cast<int>(src.getHeight())-1) {
1646               const vpRGBa val00 = src[y_][x_];
1647               const vpRGBa val10 = src[y_+1][x_];
1648               const int64_t interpR_i64 = static_cast<int64_t>(t_1*val00.R + t*val10.R);
1649               const float interpR = (interpR_i64 >> nbits) + (interpR_i64 & 0xFFFF) * precision_1;
1650 
1651               const int64_t interpG_i64 = static_cast<int64_t>(t_1*val00.G + t * val10.G);
1652               const float interpG = (interpG_i64 >> nbits) + (interpG_i64 & 0xFFFF) * precision_1;
1653 
1654               const int64_t interpB_i64 = static_cast<int64_t>(t_1*val00.B + t * val10.B);
1655               const float interpB = (interpB_i64 >> nbits) + (interpB_i64 & 0xFFFF) * precision_1;
1656 
1657               dst[i][j] = vpRGBa(vpMath::saturate<unsigned char>(interpR),
1658                                  vpMath::saturate<unsigned char>(interpG),
1659                                  vpMath::saturate<unsigned char>(interpB),
1660                                  255);
1661             } else if (x_ < static_cast<int>(src.getWidth())-1) {
1662               const vpRGBa val00 = src[y_][x_];
1663               const vpRGBa val01 = src[y_][x_+1];
1664               const int64_t interpR_i64 = static_cast<int64_t>(s_1*val00.R + s*val01.R);
1665               const float interpR = (interpR_i64 >> nbits) + (interpR_i64 & 0xFFFF) * precision_1;
1666 
1667               const int64_t interpG_i64 = static_cast<int64_t>(s_1*val00.G + s * val01.G);
1668               const float interpG = (interpG_i64 >> nbits) + (interpG_i64 & 0xFFFF) * precision_1;
1669 
1670               const int64_t interpB_i64 = static_cast<int64_t>(s_1*val00.B + s * val01.B);
1671               const float interpB = (interpB_i64 >> nbits) + (interpB_i64 & 0xFFFF) * precision_1;
1672 
1673               dst[i][j] = vpRGBa(vpMath::saturate<unsigned char>(interpR),
1674                                  vpMath::saturate<unsigned char>(interpG),
1675                                  vpMath::saturate<unsigned char>(interpB),
1676                                  255);
1677             } else {
1678               dst[i][j] = src[y_][x_];
1679             }
1680           }
1681 
1682           xi += a0_i64;
1683           yi += a3_i64;
1684         }
1685 
1686         a2_i64 += a1_i64;
1687         a5_i64 += a4_i64;
1688       }
1689     } else {
1690       for (unsigned int i = 0; i < dst.getHeight(); i++) {
1691         int64_t xi = a2_i64;
1692         int64_t yi = a5_i64;
1693         int64_t wi = a8_i64;
1694 
1695         for (unsigned int j = 0; j < dst.getWidth(); j++) {
1696           if (yi >= 0 && yi <= (static_cast<int>(src.getHeight()) - 1)*wi &&
1697               xi >= 0 && xi <= (static_cast<int>(src.getWidth()) - 1)*wi) {
1698             const float wi_ = (wi >> nbits) + (wi & 0xFFFF) * precision_1;
1699             const float xi_ = ((xi >> nbits) + (xi & 0xFFFF) * precision_1) / wi_;
1700             const float yi_ = ((yi >> nbits) + (yi & 0xFFFF) * precision_1) / wi_;
1701 
1702             const int x_ = static_cast<int>(xi_);
1703             const int y_ = static_cast<int>(yi_);
1704 
1705             const float t = yi_ - y_;
1706             const float s = xi_ - x_;
1707 
1708             if (y_ < static_cast<int>(src.getHeight()) - 1 && x_ < static_cast<int>(src.getWidth()) - 1) {
1709               const vpRGBa val00 = src[y_][x_];
1710               const vpRGBa val01 = src[y_][x_ + 1];
1711               const vpRGBa val10 = src[y_ + 1][x_];
1712               const vpRGBa val11 = src[y_ + 1][x_ + 1];
1713               const float colR0 = lerp(val00.R, val01.R, s);
1714               const float colR1 = lerp(val10.R, val11.R, s);
1715               const float interpR = lerp(colR0, colR1, t);
1716 
1717               const float colG0 = lerp(val00.G, val01.G, s);
1718               const float colG1 = lerp(val10.G, val11.G, s);
1719               const float interpG = lerp(colG0, colG1, t);
1720 
1721               const float colB0 = lerp(val00.B, val01.B, s);
1722               const float colB1 = lerp(val10.B, val11.B, s);
1723               const float interpB = lerp(colB0, colB1, t);
1724 
1725               dst[i][j] = vpRGBa(vpMath::saturate<unsigned char>(interpR),
1726                                  vpMath::saturate<unsigned char>(interpG),
1727                                  vpMath::saturate<unsigned char>(interpB),
1728                                  255);
1729             } else if (y_ < static_cast<int>(src.getHeight()) - 1) {
1730               const vpRGBa val00 = src[y_][x_];
1731               const vpRGBa val10 = src[y_ + 1][x_];
1732               const float interpR = lerp(val00.R, val10.R, t);
1733               const float interpG = lerp(val00.G, val10.G, t);
1734               const float interpB = lerp(val00.B, val10.B, t);
1735 
1736               dst[i][j] = vpRGBa(vpMath::saturate<unsigned char>(interpR),
1737                                  vpMath::saturate<unsigned char>(interpG),
1738                                  vpMath::saturate<unsigned char>(interpB),
1739                                  255);
1740             } else if (x_ < static_cast<int>(src.getWidth()) - 1) {
1741               const vpRGBa val00 = src[y_][x_];
1742               const vpRGBa val01 = src[y_][x_ + 1];
1743               const float interpR = lerp(val00.R, val01.R, s);
1744               const float interpG = lerp(val00.G, val01.G, s);
1745               const float interpB = lerp(val00.B, val01.B, s);
1746 
1747               dst[i][j] = vpRGBa(vpMath::saturate<unsigned char>(interpR),
1748                                  vpMath::saturate<unsigned char>(interpG),
1749                                  vpMath::saturate<unsigned char>(interpB),
1750                                  255);
1751             } else {
1752               dst[i][j] = src[y_][x_];
1753             }
1754           }
1755 
1756           xi += a0_i64;
1757           yi += a3_i64;
1758           wi += a6_i64;
1759         }
1760 
1761         a2_i64 += a1_i64;
1762         a5_i64 += a4_i64;
1763         a8_i64 += a7_i64;
1764       }
1765     }
1766   } else {
1767     double a0 = T[0][0];  double a1 = T[0][1];  double a2 = T[0][2];
1768     double a3 = T[1][0];  double a4 = T[1][1];  double a5 = T[1][2];
1769     double a6 = affine ? 0.0 : T[2][0];
1770     double a7 = affine ? 0.0 : T[2][1];
1771     double a8 = affine ? 1.0 : T[2][2];
1772 
1773     for (unsigned int i = 0; i < dst.getHeight(); i++) {
1774       for (unsigned int j = 0; j < dst.getWidth(); j++) {
1775         double x = a0 * (centerCorner ? j + 0.5 : j) + a1 * (centerCorner ? i + 0.5 : i) + a2;
1776         double y = a3 * (centerCorner ? j + 0.5 : j) + a4 * (centerCorner ? i + 0.5 : i) + a5;
1777         double w = a6 * (centerCorner ? j + 0.5 : j) + a7 * (centerCorner ? i + 0.5 : i) + a8;
1778 
1779         x = x / w - (centerCorner ? 0.5 : 0);
1780         y = y / w - (centerCorner ? 0.5 : 0);
1781 
1782         int x_lower = static_cast<int>(x);
1783         int y_lower = static_cast<int>(y);
1784 
1785         if (y_lower >= static_cast<int>(src.getHeight()) || x_lower >= static_cast<int>(src.getWidth()) ||
1786             y < 0 || x < 0) {
1787           continue;
1788         }
1789 
1790         double s = x - x_lower;
1791         double t = y - y_lower;
1792 
1793         if (y_lower < static_cast<int>(src.getHeight())-1 && x_lower < static_cast<int>(src.getWidth())-1) {
1794           const vpRGBa val00 = src[y_lower][x_lower];
1795           const vpRGBa val01 = src[y_lower][x_lower +1];
1796           const vpRGBa val10 = src[y_lower +1][x_lower];
1797           const vpRGBa val11 = src[y_lower +1][x_lower +1];
1798           const double colR0 = lerp(val00.R, val01.R, s);
1799           const double colR1 = lerp(val10.R, val11.R, s);
1800           const double interpR = lerp(colR0, colR1, t);
1801 
1802           const double colG0 = lerp(val00.G, val01.G, s);
1803           const double colG1 = lerp(val10.G, val11.G, s);
1804           const double interpG = lerp(colG0, colG1, t);
1805 
1806           const double colB0 = lerp(val00.B, val01.B, s);
1807           const double colB1 = lerp(val10.B, val11.B, s);
1808           const double interpB = lerp(colB0, colB1, t);
1809 
1810           dst[i][j] = vpRGBa(vpMath::saturate<unsigned char>(interpR),
1811                              vpMath::saturate<unsigned char>(interpG),
1812                              vpMath::saturate<unsigned char>(interpB),
1813                              255);
1814         } else if (y_lower < static_cast<int>(src.getHeight())-1) {
1815           const vpRGBa val00 = src[y_lower][x_lower];
1816           const vpRGBa val10 = src[y_lower +1][x_lower];
1817           const double interpR = lerp(val00.R, val10.R, t);
1818           const double interpG = lerp(val00.G, val10.G, t);
1819           const double interpB = lerp(val00.B, val10.B, t);
1820 
1821           dst[i][j] = vpRGBa(vpMath::saturate<unsigned char>(interpR),
1822                               vpMath::saturate<unsigned char>(interpG),
1823                               vpMath::saturate<unsigned char>(interpB),
1824                              255);
1825         } else if (x_lower < static_cast<int>(src.getWidth())-1) {
1826           const vpRGBa val00 = src[y_lower][x_lower];
1827           const vpRGBa val01 = src[y_lower][x_lower +1];
1828           const double interpR = lerp(val00.R, val01.R, s);
1829           const double interpG = lerp(val00.G, val01.G, s);
1830           const double interpB = lerp(val00.B, val01.B, s);
1831 
1832           dst[i][j] = vpRGBa(vpMath::saturate<unsigned char>(interpR),
1833                              vpMath::saturate<unsigned char>(interpG),
1834                              vpMath::saturate<unsigned char>(interpB),
1835                              255);
1836         } else {
1837           dst[i][j] = src[y_lower][x_lower];
1838         }
1839       }
1840     }
1841   }
1842 }
1843 
1844 #endif
1845