Visual Servoing Platform  version 3.3.1 under development (2020-10-22)
testImageWarp.cpp

Test image warping.

/****************************************************************************
*
* ViSP, open source Visual Servoing Platform software.
* Copyright (C) 2005 - 2019 by Inria. All rights reserved.
*
* This software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* See the file LICENSE.txt at the root directory of this source
* distribution for additional information about the GNU GPL.
*
* For using ViSP with software that can not be combined with the GNU
* GPL, please contact Inria about acquiring a ViSP Professional
* Edition License.
*
* See http://visp.inria.fr for more information.
*
* This software was developed at:
* Inria Rennes - Bretagne Atlantique
* Campus Universitaire de Beaulieu
* 35042 Rennes Cedex
* France
*
* If you have questions regarding the use of this file, please contact
* Inria at visp@inria.fr
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Description:
* Test image warping.
*
*****************************************************************************/
#include <visp3/core/vpConfig.h>
#if defined(VISP_HAVE_CATCH2)
#define CATCH_CONFIG_RUNNER
#include <catch.hpp>
#include <iostream>
#include <visp3/core/vpImageTools.h>
#include <visp3/core/vpIoTools.h>
#include <visp3/io/vpImageIo.h>
namespace
{
static const double g_threshold_value = 0.5;
static const double g_threshold_percentage = 0.9;
static const double g_threshold_percentage_bilinear = 0.75;
static const double g_threshold_percentage_pers = 0.75;
static const double g_threshold_percentage_pers_bilinear = 0.65;
static const std::vector<vpImageTools::vpImageInterpolationType> interp_methods = {vpImageTools::INTERPOLATION_NEAREST,
static const std::vector<std::string> interp_names = {"Nearest Neighbor", "Bilinear"};
static const std::vector<std::string> suffixes = { "_NN.png", "_bilinear.png" };
bool almostEqual(const vpImage<unsigned char>& I1, const vpImage<unsigned char>& I2, double threshold_val,
double threshold_percentage, double& percentage)
{
double nb_valid = 0;
if (I1.getHeight() != I2.getHeight() || I1.getWidth() != I2.getWidth()) {
return false;
}
for (unsigned int i = 0; i < I1.getHeight(); i++) {
for (unsigned int j = 0; j < I1.getWidth(); j++) {
nb_valid += vpMath::abs(I1[i][j] - I2[i][j]) < threshold_val ? 1 : 0;
}
}
percentage = nb_valid / I1.getSize();
return percentage >= threshold_percentage;
}
bool almostEqual(const vpImage<vpRGBa>& I1, const vpImage<vpRGBa>& I2, double threshold_val,
double threshold_percentage, double& percentage)
{
double nb_valid = 0;
if (I1.getHeight() != I2.getHeight() || I1.getWidth() != I2.getWidth()) {
return false;
}
for (unsigned int i = 0; i < I1.getHeight(); i++) {
for (unsigned int j = 0; j < I1.getWidth(); j++) {
if (vpMath::abs(I1[i][j].R - I2[i][j].R) < threshold_val) {
nb_valid++;
}
if (vpMath::abs(I1[i][j].G - I2[i][j].G) < threshold_val) {
nb_valid++;
}
if (vpMath::abs(I1[i][j].B - I2[i][j].B) < threshold_val) {
nb_valid++;
}
}
}
percentage = nb_valid / (3*I1.getSize());
return percentage >= threshold_percentage;
}
}
TEST_CASE("Affine warp on grayscale", "[warp_image]") {
const std::string imgPath = vpIoTools::createFilePath(vpIoTools::getViSPImagesDataPath(), "Klimt/Klimt.pgm");
REQUIRE(vpIoTools::checkFilename(imgPath));
vpImageIo::read(I, imgPath);
REQUIRE(I.getSize() > 0);
SECTION("Identity")
{
vpMatrix M(2, 3);
M.eye();
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Empty destination")
{
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
CHECK((I == I_affine));
}
SECTION("Initialized destination")
{
vpImage<unsigned char> I_affine(I.getHeight(), I.getWidth(), 0);
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
CHECK((I == I_affine));
}
}
}
}
SECTION("Rotation 45 deg")
{
vpMatrix M(2, 3);
M.eye();
const double theta = vpMath::rad(45);
M[0][0] = cos(theta); M[0][1] = -sin(theta); M[0][2] = I.getWidth() / 2.0;
M[1][0] = sin(theta); M[1][1] = cos(theta); M[1][2] = I.getHeight() / 2.0;
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Against reference implementation")
{
vpImageTools::warpImage(I, M, I_ref, interp_methods[i], false);
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref, I_affine, g_threshold_value, g_threshold_percentage, percentage);
std::cout << "Percentage valid pixels (45 deg " << interp_names[i] << " Ref): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against OpenCV")
{
std::string("warp/cv_warp_affine_rot_45_gray" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImage<unsigned char> I_ref_opencv;
vpImageIo::read(I_ref_opencv, refImgPath);
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref_opencv, I_affine, g_threshold_value, (i == 0) ? g_threshold_percentage : g_threshold_percentage_bilinear, percentage);
std::cout << "Percentage valid pixels (45 deg " << interp_names[i] << " OpenCV): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against PIL")
{
std::string("warp/pil_warp_affine_rot_45_gray" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImageIo::read(I_ref_pil, refImgPath);
vpImageTools::warpImage(I, M, I_affine, interp_methods[i], false, true);
double percentage = 0.0;
bool equal = almostEqual(I_ref_pil, I_affine, g_threshold_value, (i == 0) ? g_threshold_percentage : g_threshold_percentage_bilinear, percentage);
std::cout << "Percentage valid pixels (45 deg " << interp_names[i] << " PIL): " << percentage << std::endl;
CHECK(equal);
}
}
}
}
SECTION("SRT")
{
vpMatrix M(2, 3);
M.eye();
const double theta = vpMath::rad(-67);
const double scale = 0.83;
M[0][0] = scale*cos(theta); M[0][1] = -scale*sin(theta); M[0][2] = I.getWidth() / 2.0 + 17;
M[1][0] = scale*sin(theta); M[1][1] = scale*cos(theta); M[1][2] = I.getHeight() / 2.0 - 23;
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Against reference implementation")
{
vpImageTools::warpImage(I, M, I_ref, interp_methods[i], false);
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref, I_affine, g_threshold_value, g_threshold_percentage, percentage);
std::cout << "Percentage valid pixels (SRT " << interp_names[i] << " Ref): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against OpenCV")
{
std::string("warp/cv_warp_affine_SRT_gray" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImage<unsigned char> I_ref_opencv;
vpImageIo::read(I_ref_opencv, refImgPath);
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref_opencv, I_affine, g_threshold_value, (i == 0) ? g_threshold_percentage : g_threshold_percentage_bilinear, percentage);
std::cout << "Percentage valid pixels (SRT " << interp_names[i] << " OpenCV): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against PIL")
{
std::string("warp/pil_warp_affine_SRT_gray" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImageIo::read(I_ref_pil, refImgPath);
vpImageTools::warpImage(I, M, I_affine, interp_methods[i], false, true);
double percentage = 0.0;
bool equal = almostEqual(I_ref_pil, I_affine, g_threshold_value, (i == 0) ? g_threshold_percentage : g_threshold_percentage_bilinear, percentage);
std::cout << "Percentage valid pixels (SRT " << interp_names[i] << " PIL): " << percentage << std::endl;
CHECK(equal);
}
}
}
}
}
TEST_CASE("Affine warp on color", "[warp_image]") {
const std::string imgPath = vpIoTools::createFilePath(vpIoTools::getViSPImagesDataPath(), "Klimt/Klimt.ppm");
REQUIRE(vpIoTools::checkFilename(imgPath));
vpImageIo::read(I, imgPath);
REQUIRE(I.getSize() > 0);
SECTION("Identity")
{
vpMatrix M(2, 3);
M.eye();
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Empty destination")
{
vpImage<vpRGBa> I_affine;
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
CHECK((I == I_affine));
}
SECTION("Initialized destination")
{
vpImage<vpRGBa> I_affine(I.getHeight(), I.getWidth(), vpRGBa(0));
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
CHECK((I == I_affine));
}
}
}
}
SECTION("Rotation 45 deg")
{
vpMatrix M(2, 3);
M.eye();
const double theta = vpMath::rad(45);
M[0][0] = cos(theta); M[0][1] = -sin(theta); M[0][2] = I.getWidth() / 2.0;
M[1][0] = sin(theta); M[1][1] = cos(theta); M[1][2] = I.getHeight() / 2.0;
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Against reference implementation")
{
vpImageTools::warpImage(I, M, I_ref, interp_methods[i], false);
vpImage<vpRGBa> I_affine;
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref, I_affine, g_threshold_value, g_threshold_percentage, percentage);
std::cout << "Percentage valid pixels (45 deg " << interp_names[i] << " Ref): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against OpenCV")
{
std::string("warp/cv_warp_affine_rot_45_color" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImage<vpRGBa> I_ref_opencv;
vpImageIo::read(I_ref_opencv, refImgPath);
vpImage<vpRGBa> I_affine;
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref_opencv, I_affine, g_threshold_value, (i == 0) ? g_threshold_percentage : g_threshold_percentage_bilinear, percentage);
std::cout << "Percentage valid pixels (45 deg " << interp_names[i] << " OpenCV): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against PIL")
{
std::string("warp/pil_warp_affine_rot_45_color" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImage<vpRGBa> I_ref_pil;
vpImageIo::read(I_ref_pil, refImgPath);
vpImage<vpRGBa> I_affine;
vpImageTools::warpImage(I, M, I_affine, interp_methods[i], false, true);
double percentage = 0.0;
bool equal = almostEqual(I_ref_pil, I_affine, g_threshold_value, (i == 0) ? g_threshold_percentage : g_threshold_percentage_bilinear, percentage);
std::cout << "Percentage valid pixels (45 deg " << interp_names[i] << " PIL): " << percentage << std::endl;
CHECK(equal);
}
}
}
}
SECTION("SRT")
{
vpMatrix M(2, 3);
M.eye();
const double theta = vpMath::rad(-67);
const double scale = 0.83;
M[0][0] = scale * cos(theta); M[0][1] = -scale * sin(theta); M[0][2] = I.getWidth() / 2.0 + 17;
M[1][0] = scale * sin(theta); M[1][1] = scale * cos(theta); M[1][2] = I.getHeight() / 2.0 - 23;
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Against reference implementation")
{
vpImageTools::warpImage(I, M, I_ref, interp_methods[i], false);
vpImage<vpRGBa> I_affine;
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref, I_affine, g_threshold_value, (i == 0) ? g_threshold_percentage : g_threshold_percentage_bilinear, percentage);
std::cout << "Percentage valid pixels (SRT " << interp_names[i] << " Ref): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against OpenCV")
{
std::string("warp/cv_warp_affine_SRT_color" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImage<vpRGBa> I_ref_opencv;
vpImageIo::read(I_ref_opencv, refImgPath);
vpImage<vpRGBa> I_affine;
vpImageTools::warpImage(I, M, I_affine, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref_opencv, I_affine, g_threshold_value, (i == 0) ? g_threshold_percentage : g_threshold_percentage_bilinear, percentage);
std::cout << "Percentage valid pixels (SRT " << interp_names[i] << " OpenCV): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against PIL")
{
std::string("warp/pil_warp_affine_SRT_color" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImage<vpRGBa> I_ref_pil;
vpImageIo::read(I_ref_pil, refImgPath);
vpImage<vpRGBa> I_affine;
vpImageTools::warpImage(I, M, I_affine, interp_methods[i], false, true);
double percentage = 0.0;
bool equal = almostEqual(I_ref_pil, I_affine, g_threshold_value, (i == 0) ? g_threshold_percentage : g_threshold_percentage_bilinear, percentage);
std::cout << "Percentage valid pixels (SRT " << interp_names[i] << " PIL): " << percentage << std::endl;
CHECK(equal);
}
}
}
}
}
TEST_CASE("Perspective warp on grayscale", "[warp_image]") {
const std::string imgPath = vpIoTools::createFilePath(vpIoTools::getViSPImagesDataPath(), "Klimt/Klimt.pgm");
REQUIRE(vpIoTools::checkFilename(imgPath));
vpImageIo::read(I, imgPath);
REQUIRE(I.getSize() > 0);
SECTION("Identity")
{
vpMatrix M(3, 3);
M.eye();
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Empty destination")
{
vpImage<unsigned char> I_perspective;
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i]);
CHECK((I == I_perspective));
}
SECTION("Initialized destination")
{
vpImage<unsigned char> I_perspective(I.getHeight(), I.getWidth(), 0);
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i]);
CHECK((I == I_perspective));
}
}
}
}
SECTION("Rotation 45 deg")
{
vpMatrix M(3, 3);
M.eye();
const double theta = vpMath::rad(45);
M[0][0] = cos(theta); M[0][1] = -sin(theta); M[0][2] = I.getWidth() / 2.0;
M[1][0] = sin(theta); M[1][1] = cos(theta); M[1][2] = I.getHeight() / 2.0;
SECTION("Nearest Neighbor")
{
vpImage<unsigned char> I_perspective;
double percentage = 0.0;
bool equal = almostEqual(I_ref, I_perspective, g_threshold_value, g_threshold_percentage, percentage);
std::cout << "Percentage valid pixels (persp 45 deg): " << percentage << std::endl;
CHECK(equal);
}
}
SECTION("Homography")
{
vpMatrix M(3, 3);
M.eye();
M[0][0] = 1.8548; M[0][1] = -0.0402; M[0][2] = 114.9;
M[1][0] = 1.1209; M[1][1] = 4.0106; M[1][2] = 111;
M[2][0] = 0.0022; M[2][1] = 0.0064;
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Against reference implementation")
{
vpImageTools::warpImage(I, M, I_ref, interp_methods[i], false);
vpImage<unsigned char> I_perspective;
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref, I_perspective, g_threshold_value,
(i == 0) ? g_threshold_percentage_pers : g_threshold_percentage_pers_bilinear, percentage);
std::cout << "Percentage valid pixels (Homography " << interp_names[i] << " Ref): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against OpenCV")
{
std::string("warp/cv_warp_perspective_gray" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImage<unsigned char> I_ref_opencv;
vpImageIo::read(I_ref_opencv, refImgPath);
vpImage<unsigned char> I_perspective;
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref_opencv, I_perspective, g_threshold_value,
(i == 0) ? g_threshold_percentage_pers : g_threshold_percentage_pers_bilinear, percentage);
std::cout << "Percentage valid pixels (Homography " << interp_names[i] << " OpenCV): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against PIL")
{
std::string("warp/pil_warp_perspective_gray" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImageIo::read(I_ref_pil, refImgPath);
vpImage<unsigned char> I_perspective;
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i], false, true);
double percentage = 0.0;
bool equal = almostEqual(I_ref_pil, I_perspective, g_threshold_value,
(i == 0) ? g_threshold_percentage_pers : g_threshold_percentage_pers_bilinear, percentage);
std::cout << "Percentage valid pixels (Homography " << interp_names[i] << " PIL): " << percentage << std::endl;
CHECK(equal);
}
}
}
}
}
TEST_CASE("Perspective warp on color", "[warp_image]") {
const std::string imgPath = vpIoTools::createFilePath(vpIoTools::getViSPImagesDataPath(), "Klimt/Klimt.ppm");
REQUIRE(vpIoTools::checkFilename(imgPath));
vpImageIo::read(I, imgPath);
REQUIRE(I.getSize() > 0);
SECTION("Identity")
{
vpMatrix M(3, 3);
M.eye();
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Empty destination")
{
vpImage<vpRGBa> I_perspective;
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i]);
CHECK((I == I_perspective));
}
SECTION("Initialized destination")
{
vpImage<vpRGBa> I_perspective(I.getHeight(), I.getWidth(), vpRGBa(0));
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i]);
CHECK((I == I_perspective));
}
}
}
}
SECTION("Homography")
{
vpMatrix M(3, 3);
M.eye();
M[0][0] = 1.8548; M[0][1] = -0.0402; M[0][2] = 114.9;
M[1][0] = 1.1209; M[1][1] = 4.0106; M[1][2] = 111;
M[2][0] = 0.0022; M[2][1] = 0.0064;
for (size_t i = 0; i < interp_methods.size(); i++) {
SECTION(interp_names[i])
{
SECTION("Against reference implementation")
{
vpImageTools::warpImage(I, M, I_ref, interp_methods[i], false);
vpImage<vpRGBa> I_perspective;
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref, I_perspective, g_threshold_value,
(i == 0) ? g_threshold_percentage_pers : g_threshold_percentage_pers_bilinear, percentage);
std::cout << "Percentage valid pixels (Homography " << interp_names[i] << " Ref): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against OpenCV")
{
std::string("warp/cv_warp_perspective_color" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImage<vpRGBa> I_ref_opencv;
vpImageIo::read(I_ref_opencv, refImgPath);
vpImage<vpRGBa> I_perspective;
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i]);
double percentage = 0.0;
bool equal = almostEqual(I_ref_opencv, I_perspective, g_threshold_value,
(i == 0) ? g_threshold_percentage_pers : g_threshold_percentage_pers_bilinear, percentage);
std::cout << "Percentage valid pixels (Homography " << interp_names[i] << " OpenCV): " << percentage << std::endl;
CHECK(equal);
}
SECTION("Against PIL")
{
std::string("warp/pil_warp_perspective_color" + suffixes[i]));
REQUIRE(vpIoTools::checkFilename(refImgPath));
vpImage<vpRGBa> I_ref_pil;
vpImageIo::read(I_ref_pil, refImgPath);
vpImage<vpRGBa> I_perspective;
vpImageTools::warpImage(I, M, I_perspective, interp_methods[i], false, true);
double percentage = 0.0;
bool equal = almostEqual(I_ref_pil, I_perspective, g_threshold_value,
(i == 0) ? g_threshold_percentage_pers : g_threshold_percentage_pers_bilinear, percentage);
std::cout << "Percentage valid pixels (Homography " << interp_names[i] << " PIL): " << percentage << std::endl;
CHECK(equal);
}
}
}
}
}
int main(int argc, char *argv[])
{
Catch::Session session; // There must be exactly one instance
// Let Catch (using Clara) parse the command line
session.applyCommandLine(argc, argv);
int numFailed = session.run();
// numFailed is clamped to 255 as some unices only use the lower 8 bits.
// This clamping has already been applied, so just return it here
// You can also do any post run clean-up here
return numFailed;
}
#else
int main()
{
return 0;
}
#endif