Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
testHSVGradient.cpp

Test vpImageFilter::gaussianBlur() new implementation and compare it to the old one.

Test vpImageFilter::gaussianBlur() new implementation and compare it to the old one.

/*
* ViSP, open source Visual Servoing Platform software.
* Copyright (C) 2005 - 2025 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 https://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 vpImageFilter::gaussianBlur() new implementation and compare it to the old one.
*/
#include <iostream>
#include <limits>
#include <visp3/core/vpRGBa.h>
#include <visp3/core/vpHSV.h>
#include <visp3/core/vpImage.h>
#include <visp3/core/vpImageFilter.h>
#include "hsvUtils.h"
#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
#include <type_traits>
#ifdef ENABLE_VISP_NAMESPACE
using namespace VISP_NAMESPACE_NAME;
#endif
#ifndef DOXYGEN_SHOULD_SKIP_THIS
void gradientFilter(const vpImage<unsigned char> &I, const vpImage<double> &filter, vpImage<double> &GIy)
{
const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
for (unsigned int r = 1; r < nbRows - 1; ++r) {
for (unsigned int c = 1; c < nbCols - 1; ++c) {
for (unsigned int i = 0; i <= 2; ++i) {
for (unsigned int j = 0; j <= 2; ++j) {
GIy[r][c] += filter[i][j] * I[r-1 + i][c-1 + j];
}
}
}
}
}
void gradientFilter(const vpImage<unsigned char> &I, const vpImage<double> &filterX, const vpImage<double> &filterY, vpImage<double> &GIx, vpImage<double> &GIy)
{
const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
GIx.resize(nbRows, nbCols, 0.);
GIy.resize(nbRows, nbCols, 0.);
gradientFilter(I, filterX, GIx);
gradientFilter(I, filterY, GIy);
}
static bool checkBooleanMask(const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c)
{
bool computeVal = true;
if (p_mask != nullptr) {
computeVal = (*p_mask)[r][c];
}
return computeVal;
}
template <typename ArithmeticType, typename FilterType, bool useFullScale>
{
const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
GIx.resize(nbRows, nbCols, 0.);
std::vector<FilterType> filter(3);
FilterType scale;
std::string name;
switch (type) {
filter = { 1., 2., 1. };
scale = 8.;
name = "Sobel";
break;
filter = { 3., 10., 3. };
scale = 32.;
name = "Scharr";
break;
default:
throw(vpException(vpException::badValue, "Wrong type of filtering"));
}
// std::cout << "Using " << name << " filter" << std::endl;
for (unsigned char i = 0; i < 3; ++i) {
filter[i] = filter[i] / scale;
}
auto checkBooleanPatch = [](const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c, const unsigned int &h, const unsigned int &w)
{
if (!p_mask) {
return true;
}
bool hasToCompute = (*p_mask)[r][c];
if (c < w - 1) { // We do not compute gradient on the last column
hasToCompute |= (*p_mask)[r][c + 1];
if (r < h - 1) { // We do not compute gradient on the last row
hasToCompute |= (*p_mask)[r + 1][c + 1];
}
}
if (r < h - 1) { // We do not compute gradient on the last row
hasToCompute |= (*p_mask)[r + 1][c];
}
if (r > 1) { // We do not compute gradient on the first row
hasToCompute |= (*p_mask)[r - 1][c];
if (c < w - 1) { // We do not compute gradient on the last column
hasToCompute |= (*p_mask)[r - 1][c + 1];
}
}
return hasToCompute;
};
const unsigned int rStop = nbRows - 1, cStop = nbCols - 1;
vpImage<double> IabsDiff(nbRows, nbCols);
// Computation of the rest of the image
for (unsigned int r = 1; r < rStop; ++r) {
// Computation for I[r][0]
if (checkBooleanPatch(p_mask, r, 0, nbRows, nbCols)) {
IabsDiff[r][0] = I[r][1].V - I[r][0].V;
}
// Computation for all the other columns
for (unsigned int c = 1; c < cStop; ++c) {
if (checkBooleanPatch(p_mask, r, c, nbRows, nbCols)) {
// Of the absolute value of the distance
IabsDiff[r][c] = I[r][c + 1].V - I[r][c].V;
}
}
}
for (unsigned int r = 1; r < rStop; ++r) {
for (unsigned int c = 1; c < cStop; ++c) {
if (checkBooleanMask(p_mask, r, c)) {
GIx[r][c] = 0.;
for (int dr = -1; dr <= 1; ++dr) {
GIx[r][c] += filter[dr + 1] * (IabsDiff[r + dr][c - 1] + IabsDiff[r + dr][c]);
}
}
}
}
}
template <typename ArithmeticType, typename FilterType, bool useFullScale>
{
const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
std::vector<FilterType> filter(3);
FilterType scale;
switch (type) {
filter = { 1., 2., 1. };
scale = 8.;
break;
filter = { 3., 10., 3. };
scale = 32.;
break;
default:
throw(vpException(vpException::badValue, "Wrong type of filtering"));
}
for (unsigned char i = 0; i < 3; ++i) {
filter[i] = filter[i] / scale;
}
const unsigned int rStop = nbRows - 1, cStop = nbCols - 1;
vpImage<double> IabsDiff(nbRows, nbCols);
auto checkBooleanPatch = [](const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c, const unsigned int &h, const unsigned int &w)
{
if (!p_mask) {
return true;
}
bool hasToCompute = (*p_mask)[r][c];
if (c < w - 1) { // We do not compute gradient on the last column
hasToCompute |= (*p_mask)[r][c + 1];
if (r < h - 1) { // We do not compute gradient on the last row
hasToCompute |= (*p_mask)[r + 1][c + 1];
}
}
if (r < h - 1) { // We do not compute gradient on the last row
hasToCompute |= (*p_mask)[r + 1][c];
}
if (c > 1) { // We do not compute gradient on the first column
hasToCompute |= (*p_mask)[r][c - 1];
if (r < h - 1) { // We do not compute gradient on the last row
hasToCompute |= (*p_mask)[r + 1][c - 1];
}
}
return hasToCompute;
};
// Computation for the first row
for (unsigned int c = 0; c < nbCols; ++c) {
if (checkBooleanPatch(p_mask, 0, c, nbRows, nbCols)) {
IabsDiff[0][c] = I[1][c].V - I[0][c].V;
}
}
// Computation for the rest of the image of d and sign
for (unsigned int r = 1; r < rStop; ++r) {
for (unsigned int c = 0; c < nbCols; ++c) {
// Of the absolute value of the distance
if (checkBooleanPatch(p_mask, r, c, nbRows, nbCols)) {
IabsDiff[r][c] = I[r + 1][c].V - I[r][c].V;
}
}
}
// Computation of the gradient
for (unsigned int r = 1; r < rStop; ++r) {
for (unsigned int c = 1; c < cStop; ++c) {
if (checkBooleanMask(p_mask, r, c)) {
GIy[r][c] = 0.;
for (int dc = -1; dc <= 1; ++dc) {
GIy[r][c] += filter[dc + 1] * (IabsDiff[r - 1][c + dc] + IabsDiff[r][c + dc]);
}
}
}
}
}
template <typename ArithmeticType, bool useFullScale>
{
const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
GIx.resize(nbRows, nbCols, 0.);
GIy.resize(nbRows, nbCols, 0.);
gradientFilterX(I, GIx, p_mask, type);
gradientFilterY(I, GIy, p_mask, type);
}
#endif
int main()
{
bool isSuccess = true;
// Inputs
vpHSVTests::vpInputDataset dataset;
bool useSobel = false;
vpImage<double> filterX(3, 3);
filterX[0][0] = -1.; filterX[0][1] = 0.; filterX[0][2] = 1.;
filterX[1][0] = (useSobel ? -2. : -1.); filterX[1][1] = 0.; filterX[1][2] = (useSobel ? 2. : 1.);
filterX[2][0] = -1.; filterX[2][1] = 0.; filterX[2][2] = 1.;
vpImage<double> filterY(3, 3);
filterY[0][0] = -1.; filterY[0][1] = (useSobel ? -2. : -1.); filterY[0][2] = -1.;
filterY[1][0] = 0.; filterY[1][1] = 0.; filterY[1][2] = 0.;
filterY[2][0] = 1.; filterY[2][1] = (useSobel ? 2. : 1.); filterY[2][2] = 1.;
// Outputs
vpImage<double> GIx, GIx_ref, GIy, GIy_ref;
std::vector<vpImageFilter::vpCannyFilteringAndGradientType> types = {
};
std::vector<int> nbThreads = { 1, 2 };
vpImage<bool> *p_mask = nullptr;
for (unsigned int i = 0; i < 2; ++i) {
if (i == 1) {
p_mask = &dataset.m_Imask;
}
for (auto type: types) {
for (auto nbThread: nbThreads) {
for (auto input: dataset.m_hsvUCtrue) {
vpImageFilter::gradientFilter(input.second.m_I, GIx, GIy, nbThread, p_mask, type);
gradientFilter(input.second.m_I, GIx_ref, GIy_ref, p_mask, type);
bool isSuccessGIx = vpHSVTests::areAlmostEqual(GIx, "GIx", GIx_ref, "GIx_ref");
bool isSuccessGIy = vpHSVTests::areAlmostEqual(GIy, "GIy", GIy_ref, "GIy_ref");
isSuccess = isSuccess && isSuccessGIx && isSuccessGIy;
if (!isSuccessGIx) {
std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along X on HSV<uchar, true> failed ! " << std::endl;
}
if (!isSuccessGIy) {
std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along Y on HSV<uchar, true> failed ! " << std::endl;
}
if (!(isSuccessGIx && isSuccessGIy)) {
std::cout << "Type: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << std::endl;
std::cout << "nbThread: " << nbThread << std::endl;
std::cout << "mask ? : " << (p_mask ? std::string("true") : std::string("false")) << std::endl;
vpHSVTests::print(input.second.m_I, input.first);
vpHSVTests::print(GIx, "GIx");
vpHSVTests::print(GIy, "GIy");
vpHSVTests::print(GIx_ref, "GIx_ref");
vpHSVTests::print(GIy_ref, "GIy_ref");
}
}
for (auto input: dataset.m_hsvUCfalse) {
vpImageFilter::gradientFilter(input.second.m_I, GIx, GIy, nbThread, p_mask, type);
gradientFilter(input.second.m_I, GIx_ref, GIy_ref, p_mask, type);
bool isSuccessGIx = vpHSVTests::areAlmostEqual(GIx, "GIx", GIx_ref, "GIx_ref");
bool isSuccessGIy = vpHSVTests::areAlmostEqual(GIy, "GIy", GIy_ref, "GIy_ref");
isSuccess = isSuccess && isSuccessGIx && isSuccessGIy;
if (!isSuccessGIx) {
std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along X on HSV<uchar, false> failed ! " << std::endl;
}
if (!isSuccessGIy) {
std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along Y on HSV<uchar, false> failed ! " << std::endl;
}
if (!(isSuccessGIx && isSuccessGIy)) {
std::cout << "Type: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << std::endl;
std::cout << "nbThread: " << nbThread << std::endl;
std::cout << "mask ? : " << (p_mask ? std::string("true") : std::string("false")) << std::endl;
vpHSVTests::print(input.second.m_I, input.first);
vpHSVTests::print(GIx, "GIx");
vpHSVTests::print(GIy, "GIy");
vpHSVTests::print(GIx_ref, "GIx_ref");
vpHSVTests::print(GIy_ref, "GIy_ref");
}
}
for (auto input: dataset.m_hsvDouble) {
vpImageFilter::gradientFilter(input.second.m_I, GIx, GIy, nbThread, p_mask, type);
gradientFilter(input.second.m_I, GIx_ref, GIy_ref, p_mask, type);
bool isSuccessGIx = vpHSVTests::areAlmostEqual(GIx, "GIx", GIx_ref, "GIx_ref");
bool isSuccessGIy = vpHSVTests::areAlmostEqual(GIy, "GIy", GIy_ref, "GIy_ref");
isSuccess = isSuccess && isSuccessGIx && isSuccessGIy;
if (!isSuccessGIx) {
std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along X on HSV<double> failed ! " << std::endl;
}
if (!isSuccessGIy) {
std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along Y on HSV<double> failed ! " << std::endl;
}
if (!(isSuccessGIx && isSuccessGIy)) {
std::cout << "Type: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << std::endl;
std::cout << "nbThread: " << nbThread << std::endl;
std::cout << "mask ? : " << (p_mask ? std::string("true") : std::string("false")) << std::endl;
vpHSVTests::print(input.second.m_I, input.first);
vpHSVTests::print(GIx, "GIx");
vpHSVTests::print(GIy, "GIy");
vpHSVTests::print(GIx_ref, "GIx_ref");
vpHSVTests::print(GIy_ref, "GIy_ref");
}
}
}
}
}
if (isSuccess) {
std::cout << "All tests were successful !" << std::endl;
return EXIT_SUCCESS;
}
std::cerr << "ERROR: Something went wrong !" << std::endl;
return EXIT_FAILURE;
}
#else
int main()
{
std::cout << "vpHSV class is not available, please use CXX 11 standard" << std::endl;
return EXIT_SUCCESS;
}
#endif
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:73
Class implementing the HSV pixel format.
Definition vpHSV.h:135
static std::string vpCannyFiltAndGradTypeToStr(const vpCannyFilteringAndGradientType &type)
Cast a vpImageFilter::vpCannyFilteringAndGradientType into a string, to know its name.
static void gradientFilter(const vpImage< vpHSV< ArithmeticType, useFullScale > > &I, vpImage< FilterType > &GIx, vpImage< FilterType > &GIy, const int &nbThread=-1, const vpImage< bool > *p_mask=nullptr, const vpImageFilter::vpCannyFilteringAndGradientType &type=CANNY_GBLUR_SCHARR_FILTERING)
Compute the horizontal and vertical gradients for HSV images.
vpCannyFilteringAndGradientType
Canny filter and gradient operators to apply on the image before the edge detection stage.
@ CANNY_GBLUR_SOBEL_FILTERING
Apply Gaussian blur + Sobel operator on the input image.
@ CANNY_GBLUR_SCHARR_FILTERING
Apply Gaussian blur + Scharr operator on the input image.
Definition of the vpImage class member functions.
Definition vpImage.h:131
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition vpImage.h:544