Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpImageTools.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Image tools.
32 */
33
34#include <visp3/core/vpCPUFeatures.h>
35#include <visp3/core/vpImageConvert.h>
36#include <visp3/core/vpImageTools.h>
37#include <visp3/core/vpImageException.h>
38
39#if defined(VISP_HAVE_SIMDLIB)
40#include <Simd/SimdLib.hpp>
41#endif
42
103void vpImageTools::changeLUT(vpImage<unsigned char> &I, unsigned char A, unsigned char A_star, unsigned char B,
104 unsigned char B_star)
105{
106 // Test if input values are valid
107 if (B <= A) {
109 }
110 unsigned char v;
111
112 double factor = static_cast<double>((B_star - A_star) / static_cast<double>((B - A)));
113
114 unsigned int i_height = I.getHeight();
115 unsigned int i_width = I.getWidth();
116 for (unsigned int i = 0; i < i_height; ++i) {
117 for (unsigned int j = 0; j < i_width; ++j) {
118 v = I[i][j];
119
120 if (v <= A) {
121 I[i][j] = A_star;
122 }
123 else if (v >= B) {
124 I[i][j] = B_star;
125 }
126 else {
127 I[i][j] = static_cast<unsigned char>(A_star + (factor * (v - A)));
128 }
129 }
130 }
131}
132
147{
148 if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
149 throw(vpException(vpException::dimensionError, "The two images have not the same size"));
150 }
151
152 if ((I1.getHeight() != Idiff.getHeight()) || (I1.getWidth() != Idiff.getWidth())) {
153 Idiff.resize(I1.getHeight(), I1.getWidth());
154 }
155
156#if defined(VISP_HAVE_SIMDLIB)
157 SimdImageDifference(I1.bitmap, I2.bitmap, I1.getSize(), Idiff.bitmap);
158#else
159 const int val_255 = 255;
160 unsigned int i1_size = I1.getSize();
161 for (unsigned int i = 0; i < i1_size; ++i) {
162 int diff = (I1.bitmap[i] - I2.bitmap[i]) + 128;
163 Idiff.bitmap[i] = static_cast<unsigned char>(std::max<unsigned char>(std::min<unsigned char>(diff, val_255), 0));
164 }
165#endif
166}
167
182{
183 if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
185 "Cannot compute image difference. The two images "
186 "(%ux%u) and (%ux%u) have not the same size",
187 I1.getWidth(), I1.getHeight(), I2.getWidth(), I2.getHeight()));
188 }
189
190 if ((I1.getHeight() != Idiff.getHeight()) || (I1.getWidth() != Idiff.getWidth())) {
191 Idiff.resize(I1.getHeight(), I1.getWidth());
192 }
193
194#if defined(VISP_HAVE_SIMDLIB)
195 SimdImageDifference(reinterpret_cast<unsigned char *>(I1.bitmap), reinterpret_cast<unsigned char *>(I2.bitmap),
196 I1.getSize() * 4, reinterpret_cast<unsigned char *>(Idiff.bitmap));
197#else
198 const unsigned int val_4 = 4;
199 const int val_255 = 255;
200 unsigned int i1_size = I1.getSize();
201 for (unsigned int i = 0; i < (i1_size * val_4); ++i) {
202 int diffR = (I1.bitmap[i].R - I2.bitmap[i].R) + 128;
203 int diffG = (I1.bitmap[i].G - I2.bitmap[i].G) + 128;
204 int diffB = (I1.bitmap[i].B - I2.bitmap[i].B) + 128;
205 int diffA = (I1.bitmap[i].A - I2.bitmap[i].A) + 128;
206 Idiff.bitmap[i].R = static_cast<unsigned char>(vpMath::maximum(vpMath::minimum(diffR, val_255), 0));
207 Idiff.bitmap[i].G = static_cast<unsigned char>(vpMath::maximum(vpMath::minimum(diffG, val_255), 0));
208 Idiff.bitmap[i].B = static_cast<unsigned char>(vpMath::maximum(vpMath::minimum(diffB, val_255), 0));
209 Idiff.bitmap[i].A = static_cast<unsigned char>(vpMath::maximum(vpMath::minimum(diffA, val_255), 0));
210 }
211#endif
212}
213
226{
227 if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
228 throw(vpException(vpException::dimensionError, "The two images do not have the same size"));
229 }
230
231 if ((I1.getHeight() != Idiff.getHeight()) || (I1.getWidth() != Idiff.getWidth())) {
232 Idiff.resize(I1.getHeight(), I1.getWidth());
233 }
234
235 unsigned int n = I1.getHeight() * I1.getWidth();
236 for (unsigned int b = 0; b < n; ++b) {
237 int diff = I1.bitmap[b] - I2.bitmap[b];
238 Idiff.bitmap[b] = static_cast<unsigned char>(vpMath::abs(diff));
239 }
240}
241
250{
251 if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
252 throw(vpException(vpException::dimensionError, "The two images do not have the same size"));
253 }
254
255 if ((I1.getHeight() != Idiff.getHeight()) || (I1.getWidth() != Idiff.getWidth())) {
256 Idiff.resize(I1.getHeight(), I1.getWidth());
257 }
258
259 unsigned int n = I1.getHeight() * I1.getWidth();
260 for (unsigned int b = 0; b < n; ++b) {
261 Idiff.bitmap[b] = vpMath::abs(I1.bitmap[b] - I2.bitmap[b]);
262 }
263}
264
279{
280 if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
281 throw(vpException(vpException::dimensionError, "The two images do not have the same size"));
282 }
283
284 if ((I1.getHeight() != Idiff.getHeight()) || (I1.getWidth() != Idiff.getWidth())) {
285 Idiff.resize(I1.getHeight(), I1.getWidth());
286 }
287
288 unsigned int n = I1.getHeight() * I1.getWidth();
289 for (unsigned int b = 0; b < n; ++b) {
290 int diffR = I1.bitmap[b].R - I2.bitmap[b].R;
291 int diffG = I1.bitmap[b].G - I2.bitmap[b].G;
292 int diffB = I1.bitmap[b].B - I2.bitmap[b].B;
293 // --comment: int diffA eq I1 dot bitmap[b] dot A minus I2 dot bitmap[b] dot A
294 Idiff.bitmap[b].R = static_cast<unsigned char>(vpMath::abs(diffR));
295 Idiff.bitmap[b].G = static_cast<unsigned char>(vpMath::abs(diffG));
296 Idiff.bitmap[b].B = static_cast<unsigned char>(vpMath::abs(diffB));
297 // --comment: Idiff dot bitmap[b] dot A eq diffA
298 Idiff.bitmap[b].A = 0;
299 }
300}
301
316 vpImage<unsigned char> &Ires, bool saturate)
317{
318 if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
319 throw(vpException(vpException::dimensionError, "The two images do not have the same size"));
320 }
321
322 if ((I1.getHeight() != Ires.getHeight()) || (I1.getWidth() != Ires.getWidth())) {
323 Ires.resize(I1.getHeight(), I1.getWidth());
324 }
325
326#if defined(VISP_HAVE_SIMDLIB)
327 typedef Simd::View<Simd::Allocator> View;
328 View img1(I1.getWidth(), I1.getHeight(), I1.getWidth(), View::Gray8, I1.bitmap);
329 View img2(I2.getWidth(), I2.getHeight(), I2.getWidth(), View::Gray8, I2.bitmap);
330 View imgAdd(Ires.getWidth(), Ires.getHeight(), Ires.getWidth(), View::Gray8, Ires.bitmap);
331
332 Simd::OperationBinary8u(img1, img2, imgAdd,
333 saturate ? SimdOperationBinary8uSaturatedAddition : SimdOperationBinary8uAddition);
334#else
335 unsigned char *ptr_I1 = I1.bitmap;
336 unsigned char *ptr_I2 = I2.bitmap;
337 unsigned char *ptr_Ires = Ires.bitmap;
338 unsigned int ires_size = Ires.getSize();
339 for (unsigned int cpt = 0; cpt < ires_size; ++cpt, ++ptr_I1, ++ptr_I2, ++ptr_Ires) {
340 *ptr_Ires = saturate ? vpMath::saturate<unsigned char>(static_cast<short int>(*ptr_I1) + static_cast<short int>(*ptr_I2)) : ((*ptr_I1) + (*ptr_I2));
341 }
342#endif
343}
344
359 vpImage<unsigned char> &Ires, bool saturate)
360{
361 if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
362 throw(vpException(vpException::dimensionError, "The two images do not have the same size"));
363 }
364
365 if ((I1.getHeight() != Ires.getHeight()) || (I1.getWidth() != Ires.getWidth())) {
366 Ires.resize(I1.getHeight(), I1.getWidth());
367 }
368
369#if defined(VISP_HAVE_SIMDLIB)
370 typedef Simd::View<Simd::Allocator> View;
371 View img1(I1.getWidth(), I1.getHeight(), I1.getWidth(), View::Gray8, I1.bitmap);
372 View img2(I2.getWidth(), I2.getHeight(), I2.getWidth(), View::Gray8, I2.bitmap);
373 View imgAdd(Ires.getWidth(), Ires.getHeight(), Ires.getWidth(), View::Gray8, Ires.bitmap);
374
375 Simd::OperationBinary8u(img1, img2, imgAdd,
376 saturate ? SimdOperationBinary8uSaturatedSubtraction : SimdOperationBinary8uSubtraction);
377#else
378 unsigned char *ptr_I1 = I1.bitmap;
379 unsigned char *ptr_I2 = I2.bitmap;
380 unsigned char *ptr_Ires = Ires.bitmap;
381 unsigned int ires_size = Ires.getSize();
382 for (unsigned int cpt = 0; cpt < ires_size; ++cpt, ++ptr_I1, ++ptr_I2, ++ptr_Ires) {
383 *ptr_Ires = saturate ?
384 vpMath::saturate<unsigned char>(static_cast<short int>(*ptr_I1) - static_cast<short int>(*ptr_I2)) :
385 ((*ptr_I1) - (*ptr_I2));
386 }
387#endif
388}
389
401void vpImageTools::initUndistortMap(const vpCameraParameters &cam, unsigned int width, unsigned int height,
403 vpArray2D<float> &mapDv)
404{
405 mapU.resize(height, width, false, false);
406 mapV.resize(height, width, false, false);
407 mapDu.resize(height, width, false, false);
408 mapDv.resize(height, width, false, false);
409
410 vpCameraParameters::vpCameraParametersProjType projModel = cam.get_projModel();
411 bool is_KannalaBrandt =
412 (projModel == vpCameraParameters::ProjWithKannalaBrandtDistortion); // Check the projection model used
413
414 float u0 = static_cast<float>(cam.get_u0());
415 float v0 = static_cast<float>(cam.get_v0());
416 float px = static_cast<float>(cam.get_px());
417 float py = static_cast<float>(cam.get_py());
418 float kud = 0;
419 std::vector<double> dist_coefs;
420
421 if (!is_KannalaBrandt) {
422 kud = static_cast<float>(cam.get_kud());
423 }
424 else {
425 dist_coefs = cam.getKannalaBrandtDistortionCoefficients();
426 }
427
428 if ((!is_KannalaBrandt) && (std::fabs(static_cast<double>(kud)) <= std::numeric_limits<double>::epsilon())) {
429 // There is no need to undistort the image (Perpective projection)
430 for (unsigned int i = 0; i < height; ++i) {
431 for (unsigned int j = 0; j < width; ++j) {
432 mapU[i][j] = static_cast<int>(j);
433 mapV[i][j] = static_cast<int>(i);
434 mapDu[i][j] = 0;
435 mapDv[i][j] = 0;
436 }
437 }
438
439 return;
440 }
441
442 float invpx, invpy;
443 float kud_px2 = 0., kud_py2 = 0., deltau_px, deltav_py = 0;
444 float fr1 = 0, fr2;
445 float deltav, deltau;
446 float u_float, v_float;
447 int u_round, v_round;
448 double r, scale;
449 double theta, theta_d;
450 double theta2, theta4, theta6, theta8;
451 const unsigned int index_0 = 0;
452 const unsigned int index_1 = 1;
453 const unsigned int index_2 = 2;
454 const unsigned int index_3 = 3;
455
456 invpx = 1.0f / px;
457 invpy = 1.0f / py;
458
459 if (!is_KannalaBrandt) {
460 kud_px2 = kud * invpx * invpx;
461 kud_py2 = kud * invpy * invpy;
462 }
463
464 for (unsigned int v = 0; v < height; ++v) {
465 deltav = v - v0;
466
467 if (!is_KannalaBrandt) {
468 fr1 = 1.0f + (kud_py2 * deltav * deltav);
469 }
470 else {
471 deltav_py = deltav * invpy;
472 }
473
474 for (unsigned int u = 0; u < width; ++u) {
475 // computation of u,v : corresponding pixel coordinates in I.
476 deltau = u - u0;
477 if (!is_KannalaBrandt) {
478 fr2 = fr1 + (kud_px2 * deltau * deltau);
479
480 u_float = (deltau * fr2) + u0;
481 v_float = (deltav * fr2) + v0;
482 }
483
484 else {
485 deltau_px = deltau * invpx;
486 r = sqrt(vpMath::sqr(deltau_px) + vpMath::sqr(deltav_py));
487 theta = atan(r);
488
489 theta2 = vpMath::sqr(theta);
490 theta4 = vpMath::sqr(theta2);
491 theta6 = theta2 * theta4;
492 theta8 = vpMath::sqr(theta4);
493
494 theta_d = theta * (1 + (dist_coefs[index_0] * theta2) + (dist_coefs[index_1] * theta4) + (dist_coefs[index_2] * theta6) +
495 (dist_coefs[index_3] * theta8));
496
497 // --comment: scale eq (r == 0) 1.0 otherwise theta_d / r
498 scale = (std::fabs(r) < std::numeric_limits<double>::epsilon()) ? 1.0 : (theta_d / r);
499 u_float = static_cast<float>((deltau * scale) + u0);
500 v_float = static_cast<float>((deltav * scale) + v0);
501 }
502
503 u_round = static_cast<int>(u_float);
504 v_round = static_cast<int>(v_float);
505
506 mapU[v][u] = u_round;
507 mapV[v][u] = v_round;
508
509 mapDu[v][u] = u_float - u_round;
510 mapDv[v][u] = v_float - v_round;
511 }
512 }
513}
514
527{
528 if (I.getSize() == 0) {
529 std::cerr << "Error, input image is empty." << std::endl;
530 return;
531 }
532
533 II.resize(I.getHeight() + 1, I.getWidth() + 1, 0.0);
534 IIsq.resize(I.getHeight() + 1, I.getWidth() + 1, 0.0);
535
536 unsigned int ii_height = II.getHeight();
537 unsigned int ii_width = II.getWidth();
538 for (unsigned int i = 1; i < ii_height; ++i) {
539 for (unsigned int j = 1; j < ii_width; ++j) {
540 II[i][j] = (I[i - 1][j - 1] + II[i - 1][j] + II[i][j - 1]) - II[i - 1][j - 1];
541 IIsq[i][j] = (vpMath::sqr(I[i - 1][j - 1]) + IIsq[i - 1][j] + IIsq[i][j - 1]) - IIsq[i - 1][j - 1];
542 }
543 }
544}
545
553double vpImageTools::normalizedCorrelation(const vpImage<double> &I1, const vpImage<double> &I2, bool useOptimized)
554{
555 if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
557 "Error: in vpImageTools::normalizedCorrelation(): "
558 "image dimension mismatch between I1=%ux%u and I2=%ux%u",
559 I1.getHeight(), I1.getWidth(), I2.getHeight(), I2.getWidth());
560 }
561
562 const double a = I1.getMeanValue();
563 const double b = I2.getMeanValue();
564
565 double ab = 0.0;
566 double a2 = 0.0;
567 double b2 = 0.0;
568
569#if defined(VISP_HAVE_SIMDLIB)
570 SimdNormalizedCorrelation(I1.bitmap, a, I2.bitmap, b, I1.getSize(), a2, b2, ab, useOptimized);
571#else
572 unsigned int i1_size = I1.getSize();
573 for (unsigned int cpt = 0; cpt < i1_size; ++cpt) {
574 ab += (I1.bitmap[cpt] - a) * (I2.bitmap[cpt] - b);
575 a2 += vpMath::sqr(I1.bitmap[cpt] - a);
576 b2 += vpMath::sqr(I2.bitmap[cpt] - b);
577 }
578 (void)useOptimized;
579#endif
580
581 return ab / sqrt(a2 * b2);
582}
583
591{
592 unsigned int height = I.getHeight(), width = I.getWidth();
593 V.resize(width); // resize and nullify
594
595 for (unsigned int i = 0; i < height; ++i) {
596 for (unsigned int j = 0; j < width; ++j) {
597 V[j] += I[i][j];
598 }
599 }
600 for (unsigned int j = 0; j < width; ++j) {
601 V[j] /= height;
602 }
603}
604
610{
611 double s = I.getSum();
612 unsigned int i_height = I.getHeight();
613 unsigned int i_width = I.getWidth();
614 for (unsigned int i = 0; i < i_height; ++i) {
615 for (unsigned int j = 0; j < i_width; ++j) {
616 I(i, j, I(i, j) / s);
617 }
618 }
619}
620
621namespace
622{
628double interpolationNearest(const vpImage<unsigned char> &I, const vpImagePoint &point)
629{
630 int x1 = static_cast<int>(floor(point.get_i()));
631 int x2 = static_cast<int>(ceil(point.get_i()));
632 int y1 = static_cast<int>(floor(point.get_j()));
633 int y2 = static_cast<int>(ceil(point.get_j()));
634 double v1, v2;
635 if (x1 == x2) {
636 v1 = I(x1, y1);
637 v2 = I(x1, y2);
638 }
639 else {
640 v1 = ((x2 - point.get_i()) * I(x1, y1)) + ((point.get_i() - x1) * I(x2, y1));
641 v2 = ((x2 - point.get_i()) * I(x1, y2)) + ((point.get_i() - x1) * I(x2, y2));
642 }
643 if (y1 == y2) {
644 return v1;
645 }
646 return ((y2 - point.get_j()) * v1) + ((point.get_j() - y1) * v2);
647}
648}
649
658 const vpImageInterpolationType &method)
659{
660 switch (method) {
662 return I(vpMath::round(point.get_i()), vpMath::round(point.get_j()));
664 return interpolationNearest(I, point);
665 }
666 case INTERPOLATION_CUBIC: {
668 "vpImageTools::interpolate(): bi-cubic interpolation is not implemented.");
669 }
670 default: {
671 throw vpException(vpException::notImplementedError, "vpImageTools::interpolate(): invalid interpolation type");
672 }
673 }
674}
675
683{
684 unsigned int x_d = vpMath::round(r.getHeight());
685 unsigned int y_d = vpMath::round(r.getWidth());
686 double x1 = r.getTopLeft().get_i();
687 double y1 = r.getTopLeft().get_j();
688 double t = r.getOrientation();
689 double cos_t = cos(t);
690 double sin_t = sin(t);
691 dst.resize(x_d, y_d);
692 for (unsigned int x = 0; x < x_d; ++x) {
693 double x_cos_t = x * cos_t;
694 double x_sin_t = x * sin_t;
695 for (unsigned int y = 0; y < y_d; ++y) {
696 dst(x, y,
697 static_cast<unsigned char>(interpolate(src, vpImagePoint(x1 + x_cos_t + (y * sin_t), (y1 - x_sin_t) + (y * cos_t)),
699 }
700 }
701}
702
710{
711 unsigned int x_d = vpMath::round(r.getHeight());
712 unsigned int y_d = vpMath::round(r.getWidth());
713 double x1 = r.getTopLeft().get_i();
714 double y1 = r.getTopLeft().get_j();
715 double t = r.getOrientation();
716 double cos_t = cos(t);
717 double sin_t = sin(t);
718 dst.resize(x_d, y_d);
719 for (unsigned int x = 0; x < x_d; ++x) {
720 double x_cos_t = x * cos_t;
721 double x_sin_t = x * sin_t;
722 for (unsigned int y = 0; y < y_d; ++y) {
723 dst(x, y,
724 interpolate(src, vpImagePoint(x1 + x_cos_t + (y * sin_t), (y1 - x_sin_t) + (y * cos_t)),
726 }
727 }
728}
729
745 vpImage<double> &I_score, unsigned int step_u, unsigned int step_v,
746 bool useOptimized)
747{
748 if (I.getSize() == 0) {
749 std::cerr << "Error, input image is empty." << std::endl;
750 return;
751 }
752
753 if (I_tpl.getSize() == 0) {
754 std::cerr << "Error, template image is empty." << std::endl;
755 return;
756 }
757
758 if ((I_tpl.getHeight() > I.getHeight()) || (I_tpl.getWidth() > I.getWidth())) {
759 std::cerr << "Error, template image is bigger than input image." << std::endl;
760 return;
761 }
762
763 vpImage<double> I_double, I_tpl_double;
764 vpImageConvert::convert(I, I_double);
765 vpImageConvert::convert(I_tpl, I_tpl_double);
766
767 unsigned int height_tpl = I_tpl.getHeight(), width_tpl = I_tpl.getWidth();
768 I_score.resize(I.getHeight() - height_tpl, I.getWidth() - width_tpl, 0.0);
769
770 if (useOptimized) {
771 vpImage<double> II, IIsq;
772 integralImage(I, II, IIsq);
773
774 vpImage<double> II_tpl, IIsq_tpl;
775 integralImage(I_tpl, II_tpl, IIsq_tpl);
776
777 // zero-mean template image
778 const double sum2 = (((II_tpl[height_tpl][width_tpl] + II_tpl[0][0]) - II_tpl[0][width_tpl]) - II_tpl[height_tpl][0]);
779 const double mean2 = sum2 / I_tpl.getSize();
780 unsigned int i_tpl_double_size = I_tpl_double.getSize();
781 for (unsigned int cpt = 0; cpt < i_tpl_double_size; ++cpt) {
782 I_tpl_double.bitmap[cpt] -= mean2;
783 }
784
785#if defined(_OPENMP) && (_OPENMP >= 200711) // OpenMP 3.1
786#pragma omp parallel for schedule(dynamic)
787 for (unsigned int i = 0; i < I.getHeight() - height_tpl; i += step_v) {
788 for (unsigned int j = 0; j < I.getWidth() - width_tpl; j += step_u) {
789 I_score[i][j] = normalizedCorrelation(I_double, I_tpl_double, II, IIsq, II_tpl, IIsq_tpl, i, j);
790 }
791 }
792#else
793 // error C3016: 'i': index variable in OpenMP 'for' statement must have signed integral type
794 int end = static_cast<int>((I.getHeight() - height_tpl) / step_v) + 1;
795 std::vector<unsigned int> vec_step_v(static_cast<size_t>(end));
796 unsigned int i_height = I.getHeight();
797 for (unsigned int cpt = 0, idx = 0; cpt < (i_height - height_tpl); cpt += step_v, ++idx) {
798 vec_step_v[static_cast<size_t>(idx)] = cpt;
799 }
800#if defined(_OPENMP) // only to disable warning: ignoring #pragma omp parallel [-Wunknown-pragmas]
801#pragma omp parallel for schedule(dynamic)
802#endif
803 for (int cpt = 0; cpt < end; ++cpt) {
804 unsigned int i_width = I.getWidth();
805 for (unsigned int j = 0; j < (i_width - width_tpl); j += step_u) {
806 I_score[vec_step_v[cpt]][j] =
807 normalizedCorrelation(I_double, I_tpl_double, II, IIsq, II_tpl, IIsq_tpl, vec_step_v[cpt], j);
808 }
809 }
810#endif
811 }
812 else {
813 vpImage<double> I_cur;
814
815 unsigned int i_height = I.getHeight();
816 unsigned int i_width = I.getWidth();
817 for (unsigned int i = 0; i < (i_height - height_tpl); i += step_v) {
818 for (unsigned int j = 0; j < (i_width - width_tpl); j += step_u) {
819 vpRect roi(vpImagePoint(i, j), vpImagePoint(((i + height_tpl) - 1), ((j + width_tpl) - 1)));
820 vpImageTools::crop(I_double, roi, I_cur);
821
822 I_score[i][j] = vpImageTools::normalizedCorrelation(I_cur, I_tpl_double, useOptimized);
823 }
824 }
825 }
826}
827
828// Reference:
829// http://blog.demofox.org/2015/08/15/resizing-images-with-bicubic-interpolation/
830// t is a value that goes from 0 to 1 to interpolate in a C1 continuous way
831// across uniformly sampled data points. when t is 0, this will return B.
832// When t is 1, this will return C. In between values will return an
833// interpolation between B and C. A and B are used to calculate the slopes at
834// the edges.
835float vpImageTools::cubicHermite(const float A, const float B, const float C, const float D, const float t)
836{
837 float a = (((-A + (3.0f * B)) - (3.0f * C)) + D) / 2.0f;
838 float b = (A + (2.0f * C)) - (((5.0f * B) + D) / 2.0f);
839 float c = (-A + C) / 2.0f;
840 float d = B;
841
842 return (a * t * t * t) + (b * t * t) + (c * t) + d;
843}
844
845int vpImageTools::coordCast(double x) { return x < 0 ? -1 : static_cast<int>(x); }
846
847double vpImageTools::lerp(double A, double B, double t) { return (A * (1.0 - t)) + (B * t); }
848
849float vpImageTools::lerp(float A, float B, float t) { return (A * (1.0f - t)) + (B * t); }
850
851int64_t vpImageTools::lerp2(int64_t A, int64_t B, int64_t t, int64_t t_1) { return (A * t_1) + (B * t); }
852
854 const vpImage<double> &II, const vpImage<double> &IIsq,
855 const vpImage<double> &II_tpl, const vpImage<double> &IIsq_tpl,
856 unsigned int i0, unsigned int j0)
857{
858 double ab = 0.0;
859
860#if defined(VISP_HAVE_SIMDLIB)
861 SimdNormalizedCorrelation2(I1.bitmap, I1.getWidth(), I2.bitmap, I2.getWidth(), I2.getHeight(), i0, j0, ab);
862#else
863 unsigned int i2_height = I2.getHeight();
864 unsigned int i2_width = I2.getWidth();
865 for (unsigned int i = 0; i < i2_height; ++i) {
866 for (unsigned int j = 0; j < i2_width; ++j) {
867 ab += (I1[i0 + i][j0 + j]) * I2[i][j];
868 }
869 }
870#endif
871
872 unsigned int height_tpl = I2.getHeight(), width_tpl = I2.getWidth();
873 const double sum1 =
874 (((II[i0 + height_tpl][j0 + width_tpl] + II[i0][j0]) - II[i0][j0 + width_tpl]) - II[i0 + height_tpl][j0]);
875 const double sum2 = (((II_tpl[height_tpl][width_tpl] + II_tpl[0][0]) - II_tpl[0][width_tpl]) - II_tpl[height_tpl][0]);
876
877 double a2 = ((((IIsq[i0 + I2.getHeight()][j0 + I2.getWidth()] + IIsq[i0][j0]) - IIsq[i0][j0 + I2.getWidth()]) -
878 IIsq[i0 + I2.getHeight()][j0]) -
879 ((1.0 / I2.getSize()) * vpMath::sqr(sum1)));
880
881 double b2 = ((((IIsq_tpl[I2.getHeight()][I2.getWidth()] + IIsq_tpl[0][0]) - IIsq_tpl[0][I2.getWidth()]) -
882 IIsq_tpl[I2.getHeight()][0]) -
883 ((1.0 / I2.getSize()) * vpMath::sqr(sum2)));
884 return ab / sqrt(a2 * b2);
885}
886
898 const vpArray2D<float> &mapDu, const vpArray2D<float> &mapDv, vpImage<unsigned char> &Iundist)
899{
900 Iundist.resize(I.getHeight(), I.getWidth());
901 const int I_height = static_cast<int>(I.getHeight());
902
903#if defined(_OPENMP) // only to disable warning: ignoring #pragma omp parallel [-Wunknown-pragmas]
904#pragma omp parallel for schedule(dynamic)
905#endif
906 for (int i_ = 0; i_ < I_height; ++i_) {
907 const unsigned int i = static_cast<unsigned int>(i_);
908 unsigned int i_width = I.getWidth();
909 for (unsigned int j = 0; j < i_width; ++j) {
910
911 int u_round = mapU[i][j];
912 int v_round = mapV[i][j];
913
914 float du = mapDu[i][j];
915 float dv = mapDv[i][j];
916
917 if ((0 <= u_round) && (0 <= v_round) && (u_round < (static_cast<int>(I.getWidth()) - 1)) &&
918 (v_round < (I_height - 1))) {
919 // process interpolation
920 float col0 = lerp(I[v_round][u_round], I[v_round][u_round + 1], du);
921 float col1 = lerp(I[v_round + 1][u_round], I[v_round + 1][u_round + 1], du);
922 float value = lerp(col0, col1, dv);
923
924 Iundist[i][j] = static_cast<unsigned char>(value);
925 }
926 else {
927 Iundist[i][j] = 0;
928 }
929 }
930 }
931}
932
943void vpImageTools::remap(const vpImage<vpRGBa> &I, const vpArray2D<int> &mapU, const vpArray2D<int> &mapV,
944 const vpArray2D<float> &mapDu, const vpArray2D<float> &mapDv, vpImage<vpRGBa> &Iundist)
945{
946 Iundist.resize(I.getHeight(), I.getWidth());
947 const int I_height = static_cast<int>(I.getHeight());
948#if defined(_OPENMP) // only to disable warning: ignoring #pragma omp parallel [-Wunknown-pragmas]
949#pragma omp parallel for schedule(dynamic)
950#endif
951 for (int i = 0; i < I_height; ++i) {
952#if defined(VISP_HAVE_SIMDLIB)
953 SimdRemap(reinterpret_cast<unsigned char *>(I.bitmap), 4, I.getWidth(), I.getHeight(), i * I.getWidth(), mapU.data,
954 mapV.data, mapDu.data, mapDv.data, reinterpret_cast<unsigned char *>(Iundist.bitmap));
955#else
956 const unsigned int i_ = static_cast<unsigned int>(i);
957 unsigned int i_width = I.getWidth();
958 for (unsigned int j = 0; j < i_width; ++j) {
959
960 int u_round = mapU[i_][j];
961 int v_round = mapV[i_][j];
962
963 float du = mapDu[i_][j];
964 float dv = mapDv[i_][j];
965
966 if ((0 <= u_round) && (0 <= v_round) && (u_round < (static_cast<int>(I.getWidth()) - 1))
967 && (v_round < (I_height - 1))) {
968 // process interpolation
969 float col0 = lerp(I[v_round][u_round].R, I[v_round][u_round + 1].R, du);
970 float col1 = lerp(I[v_round + 1][u_round].R, I[v_round + 1][u_round + 1].R, du);
971 float value = lerp(col0, col1, dv);
972
973 Iundist[i][j].R = static_cast<unsigned char>(value);
974
975 col0 = lerp(I[v_round][u_round].G, I[v_round][u_round + 1].G, du);
976 col1 = lerp(I[v_round + 1][u_round].G, I[v_round + 1][u_round + 1].G, du);
977 value = lerp(col0, col1, dv);
978
979 Iundist[i][j].G = static_cast<unsigned char>(value);
980
981 col0 = lerp(I[v_round][u_round].B, I[v_round][u_round + 1].B, du);
982 col1 = lerp(I[v_round + 1][u_round].B, I[v_round + 1][u_round + 1].B, du);
983 value = lerp(col0, col1, dv);
984
985 Iundist[i][j].B = static_cast<unsigned char>(value);
986
987 col0 = lerp(I[v_round][u_round].A, I[v_round][u_round + 1].A, du);
988 col1 = lerp(I[v_round + 1][u_round].A, I[v_round + 1][u_round + 1].A, du);
989 value = lerp(col0, col1, dv);
990
991 Iundist[i][j].A = static_cast<unsigned char>(value);
992 }
993 else {
994 Iundist[i][j] = 0;
995 }
996 }
997#endif
998 }
999}
1000
1001#if defined(VISP_HAVE_SIMDLIB)
1002void vpImageTools::resizeSimdlib(const vpImage<vpRGBa> &Isrc, unsigned int resizeWidth, unsigned int resizeHeight,
1003 vpImage<vpRGBa> &Idst, int method)
1004{
1005 Idst.resize(resizeHeight, resizeWidth);
1006
1007 typedef Simd::View<Simd::Allocator> View;
1008 View src(Isrc.getWidth(), Isrc.getHeight(), Isrc.getWidth() * sizeof(vpRGBa), View::Bgra32, Isrc.bitmap);
1009 View dst(Idst.getWidth(), Idst.getHeight(), Idst.getWidth() * sizeof(vpRGBa), View::Bgra32, Idst.bitmap);
1010
1011 Simd::Resize(src, dst, method == INTERPOLATION_LINEAR ? SimdResizeMethodBilinear : SimdResizeMethodArea);
1012}
1013
1014void vpImageTools::resizeSimdlib(const vpImage<unsigned char> &Isrc, unsigned int resizeWidth,
1015 unsigned int resizeHeight, vpImage<unsigned char> &Idst, int method)
1016{
1017 Idst.resize(resizeHeight, resizeWidth);
1018
1019 typedef Simd::View<Simd::Allocator> View;
1020 View src(Isrc.getWidth(), Isrc.getHeight(), Isrc.getWidth(), View::Gray8, Isrc.bitmap);
1021 View dst(Idst.getWidth(), Idst.getHeight(), Idst.getWidth(), View::Gray8, Idst.bitmap);
1022
1023 Simd::Resize(src, dst, method == INTERPOLATION_LINEAR ? SimdResizeMethodBilinear : SimdResizeMethodArea);
1024}
1025#endif
1026
1027bool vpImageTools::checkFixedPoint(unsigned int x, unsigned int y, const vpMatrix &T, bool affine)
1028{
1029 const unsigned int index_0 = 0;
1030 const unsigned int index_1 = 1;
1031 const unsigned int index_2 = 2;
1032 double a0 = T[index_0][index_0];
1033 double a1 = T[index_0][index_1];
1034 double a2 = T[index_0][index_2];
1035 double a3 = T[index_1][index_0];
1036 double a4 = T[index_1][index_1];
1037 double a5 = T[index_1][index_2];
1038 double a6 = affine ? 0.0 : T[index_2][index_0];
1039 double a7 = affine ? 0.0 : T[index_2][index_1];
1040 double a8 = affine ? 1.0 : T[index_2][index_2];
1041
1042 double w = (a6 * x) + (a7 * y) + a8;
1043 double x2 = ((a0 * x) + (a1 * y) + a2) / w;
1044 double y2 = ((a3 * x) + (a4 * y) + a5) / w;
1045
1046 const double limit = 1 << 15;
1047 return (vpMath::abs(x2) < limit) && (vpMath::abs(y2) < limit);
1048}
1049
1068int vpImageTools::inRange(const unsigned char *hue, const unsigned char *saturation, const unsigned char *value,
1069 const vpColVector &hsv_range, unsigned char *mask, unsigned int size)
1070{
1071 const std::size_t val_6 = 6;
1072 if ((hue == nullptr) || (saturation == nullptr) || (value == nullptr)) {
1074 "Error in vpImageTools::inRange(): hsv pointer are empty"));
1075 }
1076 else if (hsv_range.size() != val_6) {
1078 "Error in vpImageTools::inRange(): wrong values vector size (%d)", hsv_range.size()));
1079 }
1080 const unsigned int index_0 = 0;
1081 const unsigned int index_1 = 1;
1082 const unsigned int index_2 = 2;
1083 const unsigned int index_3 = 3;
1084 const unsigned int index_4 = 4;
1085 const unsigned int index_5 = 5;
1086 const unsigned char val_uchar_255 = 255;
1087 unsigned char h_low = static_cast<unsigned char>(hsv_range[index_0]);
1088 unsigned char h_high = static_cast<unsigned char>(hsv_range[index_1]);
1089 unsigned char s_low = static_cast<unsigned char>(hsv_range[index_2]);
1090 unsigned char s_high = static_cast<unsigned char>(hsv_range[index_3]);
1091 unsigned char v_low = static_cast<unsigned char>(hsv_range[index_4]);
1092 unsigned char v_high = static_cast<unsigned char>(hsv_range[index_5]);
1093 int size_ = static_cast<int>(size);
1094 int cpt_in_range = 0;
1095#if defined(_OPENMP)
1096#pragma omp parallel for reduction(+:cpt_in_range)
1097#endif
1098 for (int i = 0; i < size_; ++i) {
1099 bool check_h_low_high_hue = (h_low <= hue[i]) && (hue[i] <= h_high);
1100 bool check_s_low_high_saturation = (s_low <= saturation[i]) && (saturation[i] <= s_high);
1101 bool check_v_low_high_value = (v_low <= value[i]) && (value[i] <= v_high);
1102 if (check_h_low_high_hue && check_s_low_high_saturation && check_v_low_high_value) {
1103 mask[i] = val_uchar_255;
1104 ++cpt_in_range;
1105 }
1106 else {
1107 mask[i] = 0;
1108 }
1109 }
1110 return cpt_in_range;
1111}
1112
1132int vpImageTools::inRange(const unsigned char *hue, const unsigned char *saturation, const unsigned char *value,
1133 const std::vector<int> &hsv_range, unsigned char *mask, unsigned int size)
1134{
1135 const std::size_t val_6 = 6;
1136 if ((hue == nullptr) || (saturation == nullptr) || (value == nullptr)) {
1138 "Error in vpImageTools::inRange(): hsv pointer are empty"));
1139 }
1140 else if (hsv_range.size() != val_6) {
1142 "Error in vpImageTools::inRange(): wrong values vector size (%d)", hsv_range.size()));
1143 }
1144 const unsigned int index_0 = 0;
1145 const unsigned int index_1 = 1;
1146 const unsigned int index_2 = 2;
1147 const unsigned int index_3 = 3;
1148 const unsigned int index_4 = 4;
1149 const unsigned int index_5 = 5;
1150 unsigned char h_low = static_cast<unsigned char>(hsv_range[index_0]);
1151 unsigned char h_high = static_cast<unsigned char>(hsv_range[index_1]);
1152 unsigned char s_low = static_cast<unsigned char>(hsv_range[index_2]);
1153 unsigned char s_high = static_cast<unsigned char>(hsv_range[index_3]);
1154 unsigned char v_low = static_cast<unsigned char>(hsv_range[index_4]);
1155 unsigned char v_high = static_cast<unsigned char>(hsv_range[index_5]);
1156 int size_ = static_cast<int>(size);
1157 int cpt_in_range = 0;
1158
1159 const unsigned char val_uc_255 = 255;
1160#if defined(_OPENMP)
1161#pragma omp parallel for reduction(+:cpt_in_range)
1162#endif
1163 for (int i = 0; i < size_; ++i) {
1164 bool check_h_low_high_hue = (h_low <= hue[i]) && (hue[i] <= h_high);
1165 bool check_s_low_high_saturation = (s_low <= saturation[i]) && (saturation[i] <= s_high);
1166 bool check_v_low_high_value = (v_low <= value[i]) && (value[i] <= v_high);
1167 if (check_h_low_high_hue && check_s_low_high_saturation && check_v_low_high_value) {
1168 mask[i] = val_uc_255;
1169 ++cpt_in_range;
1170 }
1171 else {
1172 mask[i] = 0;
1173 }
1174 }
1175 return cpt_in_range;
1176}
1177END_VISP_NAMESPACE
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition vpArray2D.h:146
Type * data
Address of the first element of the data array.
Definition vpArray2D.h:149
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition vpArray2D.h:448
unsigned int size() const
Return the number of elements of the 2D array.
Definition vpArray2D.h:435
Generic class defining intrinsic camera parameters.
@ ProjWithKannalaBrandtDistortion
Projection with Kannala-Brandt distortion model.
Implementation of column vector and the associated operations.
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ dimensionError
Bad dimension.
Definition vpException.h:71
@ notImplementedError
Not implemented.
Definition vpException.h:69
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Error that can be emitted by the vpImage class and its derivatives.
@ notInitializedError
Image not initialized.
@ incorrectInitializationError
Wrong image initialization.
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
double get_j() const
double get_i() const
static void imageDifference(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Idiff)
static void templateMatching(const vpImage< unsigned char > &I, const vpImage< unsigned char > &I_tpl, vpImage< double > &I_score, unsigned int step_u, unsigned int step_v, bool useOptimized=true)
static void initUndistortMap(const vpCameraParameters &cam, unsigned int width, unsigned int height, vpArray2D< int > &mapU, vpArray2D< int > &mapV, vpArray2D< float > &mapDu, vpArray2D< float > &mapDv)
static void 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=1, unsigned int h_scale=1)
static double interpolate(const vpImage< unsigned char > &I, const vpImagePoint &point, const vpImageInterpolationType &method=INTERPOLATION_NEAREST)
static void integralImage(const vpImage< unsigned char > &I, vpImage< double > &II, vpImage< double > &IIsq)
static void extract(const vpImage< unsigned char > &src, vpImage< unsigned char > &dst, const vpRectOriented &r)
static void imageSubtract(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Ires, bool saturate=false)
static void imageAdd(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Ires, bool saturate=false)
static void columnMean(const vpImage< double > &I, vpRowVector &result)
static void normalize(vpImage< double > &I)
static void remap(const vpImage< unsigned char > &I, const vpArray2D< int > &mapU, const vpArray2D< int > &mapV, const vpArray2D< float > &mapDu, const vpArray2D< float > &mapDv, vpImage< unsigned char > &Iundist)
static void changeLUT(vpImage< unsigned char > &I, unsigned char A, unsigned char newA, unsigned char B, unsigned char newB)
static double normalizedCorrelation(const vpImage< double > &I1, const vpImage< double > &I2, bool useOptimized=true)
static int inRange(const unsigned char *hue, const unsigned char *saturation, const unsigned char *value, const vpColVector &hsv_range, unsigned char *mask, unsigned int size)
static void imageDifferenceAbsolute(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Idiff)
Definition of the vpImage class member functions.
Definition vpImage.h:131
unsigned int getWidth() const
Definition vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition vpImage.h:544
unsigned int getSize() const
Definition vpImage.h:221
Type * bitmap
points toward the bitmap
Definition vpImage.h:135
unsigned int getHeight() const
Definition vpImage.h:181
double getMeanValue(const vpImage< bool > *p_mask=nullptr, unsigned int *nbValidPoints=nullptr) const
Return the mean value of the bitmap.
static Tp saturate(unsigned char v)
Definition vpMath.h:306
static Type maximum(const Type &a, const Type &b)
Definition vpMath.h:257
static double sqr(double x)
Definition vpMath.h:203
static Type abs(const Type &x)
Definition vpMath.h:272
static int round(double x)
Definition vpMath.h:413
static Type minimum(const Type &a, const Type &b)
Definition vpMath.h:265
Implementation of a matrix and operations on matrices.
Definition vpMatrix.h:175
Defines an oriented rectangle in the plane.
Defines a rectangle in the plane.
Definition vpRect.h:79
Implementation of row vector and the associated operations.
void resize(unsigned int i, bool flagNullify=true)