From bfb5db886b5243e630254dcd252417c7d4046087 Mon Sep 17 00:00:00 2001 From: asantamaria <asantamaria@iri.upc.edu> Date: Mon, 25 Sep 2017 15:16:22 +0200 Subject: [PATCH] added google tests structure --- .cproject | 16 +- CMakeLists.txt | 38 ++++- src/CMakeLists.txt | 31 +++- .../activesearch/alg_activesearch.cpp | 8 +- src/test/CMakeLists.txt | 27 ++++ src/test/Test_ORB.png | Bin 0 -> 10936 bytes src/test/data/Test_ORB.png | Bin 0 -> 10936 bytes src/test/data/roi_orb.yaml | 10 ++ src/test/gtest/CMakeLists.txt | 64 ++++++++ src/test/gtest_example.cpp | 20 +++ src/test/gtest_roi_ORB.cpp | 141 ++++++++++++++++++ src/test/gtest_vision_utils.cpp | 34 +++++ src/test/utils_gtest.h | 132 ++++++++++++++++ src/vision_utils.cpp | 26 ++++ src/vision_utils.h | 24 +-- 15 files changed, 543 insertions(+), 28 deletions(-) create mode 100644 src/test/CMakeLists.txt create mode 100644 src/test/Test_ORB.png create mode 100644 src/test/data/Test_ORB.png create mode 100644 src/test/data/roi_orb.yaml create mode 100644 src/test/gtest/CMakeLists.txt create mode 100644 src/test/gtest_example.cpp create mode 100644 src/test/gtest_roi_ORB.cpp create mode 100644 src/test/gtest_vision_utils.cpp create mode 100644 src/test/utils_gtest.h diff --git a/.cproject b/.cproject index de76406..222a359 100644 --- a/.cproject +++ b/.cproject @@ -22,8 +22,8 @@ <tool id="cdt.managedbuild.tool.gnu.archiver.base.599567389" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/> <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1572359948" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"> <option id="gnu.cpp.compiler.option.include.paths.1770487778" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath"> - <listOptionValue builtIn="false" value=""${workspace_loc:/vision_utils/src}""/> <listOptionValue builtIn="false" value="/usr/local/include"/> + <listOptionValue builtIn="false" value=""${workspace_loc:/Vision Utils/src}""/> </option> <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.466329004" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/> </tool> @@ -36,7 +36,7 @@ </tool> <tool id="cdt.managedbuild.tool.gnu.c.linker.base.1546364764" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/> <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.674742229" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"> - <option id="gnu.cpp.link.option.paths.1264112011" superClass="gnu.cpp.link.option.paths" valueType="libPaths"> + <option id="gnu.cpp.link.option.paths.1264112011" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths"> <listOptionValue builtIn="false" value="/usr/local/lib"/> </option> <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.224259993" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input"> @@ -70,18 +70,18 @@ <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/> <storageModule moduleId="scannerConfiguration"> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> - <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1736073220;cdt.managedbuild.toolchain.gnu.base.1736073220.1390770886;cdt.managedbuild.tool.gnu.c.compiler.base.1459107576;cdt.managedbuild.tool.gnu.c.compiler.input.1695301683"> - <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> - </scannerConfigBuildInfo> <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1819918851;cdt.managedbuild.toolchain.gnu.base.1819918851.1318867162;cdt.managedbuild.tool.gnu.cpp.compiler.base.1649772065;cdt.managedbuild.tool.gnu.cpp.compiler.input.146329834"> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> </scannerConfigBuildInfo> - <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.207681558;cdt.managedbuild.toolchain.gnu.base.207681558.1208625085;cdt.managedbuild.tool.gnu.c.compiler.base.1439246049;cdt.managedbuild.tool.gnu.c.compiler.input.637694474"> + <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1736073220;cdt.managedbuild.toolchain.gnu.base.1736073220.1390770886;cdt.managedbuild.tool.gnu.c.compiler.base.1459107576;cdt.managedbuild.tool.gnu.c.compiler.input.1695301683"> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> </scannerConfigBuildInfo> <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.727026548;cdt.managedbuild.toolchain.gnu.base.727026548.1071101131;cdt.managedbuild.tool.gnu.c.compiler.base.61708613;cdt.managedbuild.tool.gnu.c.compiler.input.166078074"> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> </scannerConfigBuildInfo> + <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.207681558;cdt.managedbuild.toolchain.gnu.base.207681558.1208625085;cdt.managedbuild.tool.gnu.c.compiler.base.1439246049;cdt.managedbuild.tool.gnu.c.compiler.input.637694474"> + <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> + </scannerConfigBuildInfo> <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1244102678;cdt.managedbuild.toolchain.gnu.base.1244102678.1450977940;cdt.managedbuild.tool.gnu.cpp.compiler.base.1572359948;cdt.managedbuild.tool.gnu.cpp.compiler.input.466329004"> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> </scannerConfigBuildInfo> @@ -91,13 +91,13 @@ <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.727026548;cdt.managedbuild.toolchain.gnu.base.727026548.1071101131;cdt.managedbuild.tool.gnu.cpp.compiler.base.433122623;cdt.managedbuild.tool.gnu.cpp.compiler.input.341288586"> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> </scannerConfigBuildInfo> - <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1244102678;cdt.managedbuild.toolchain.gnu.base.1244102678.1450977940;cdt.managedbuild.tool.gnu.c.compiler.base.1838202852;cdt.managedbuild.tool.gnu.c.compiler.input.964584931"> + <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1736073220;cdt.managedbuild.toolchain.gnu.base.1736073220.1390770886;cdt.managedbuild.tool.gnu.cpp.compiler.base.1526891046;cdt.managedbuild.tool.gnu.cpp.compiler.input.1116382079"> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> </scannerConfigBuildInfo> <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1819918851;cdt.managedbuild.toolchain.gnu.base.1819918851.1318867162;cdt.managedbuild.tool.gnu.c.compiler.base.593702953;cdt.managedbuild.tool.gnu.c.compiler.input.392834553"> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> </scannerConfigBuildInfo> - <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1736073220;cdt.managedbuild.toolchain.gnu.base.1736073220.1390770886;cdt.managedbuild.tool.gnu.cpp.compiler.base.1526891046;cdt.managedbuild.tool.gnu.cpp.compiler.input.1116382079"> + <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1244102678;cdt.managedbuild.toolchain.gnu.base.1244102678.1450977940;cdt.managedbuild.tool.gnu.c.compiler.base.1838202852;cdt.managedbuild.tool.gnu.c.compiler.input.964584931"> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> </scannerConfigBuildInfo> </storageModule> diff --git a/CMakeLists.txt b/CMakeLists.txt index 93fe002..59f82f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,10 @@ # Pre-requisites about cmake itself -CMAKE_MINIMUM_REQUIRED(VERSION 2.4) +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +## OPTIONS ## +OPTION(BUILD_TESTS "Build Unit tests" ON) +OPTION(BUILD_EXAMPLES "Build examples" ON) +OPTION(PRINT_INFO_VU "Print vision utils information" OFF) if(COMMAND cmake_policy) cmake_policy(SET CMP0005 NEW) @@ -15,10 +20,14 @@ SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) SET(CMAKE_INSTALL_PREFIX /usr/local) IF (NOT CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE "RELEASE") + SET(CMAKE_BUILD_TYPE "DEBUG") ENDIF (NOT CMAKE_BUILD_TYPE) MESSAGE(STATUS "Compilation type: ${CMAKE_BUILD_TYPE}") +#Set Flags +SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -D_REENTRANT") +SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -D_REENTRANT") + if(UNIX) # GCC is not strict enough by default, so enable most of the warnings. set(CMAKE_CXX_FLAGS @@ -39,6 +48,16 @@ else() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif() +## Tests ## +if(BUILD_TESTS) + MESSAGE("Building tests.") + # Enables testing for this directory and below. + # Note that ctest expects to find a test file in the build directory root. + # Therefore, this command should be in the source directory root. + #include(CTest) # according to http://public.kitware.com/pipermail/cmake/2012-June/050853.html + enable_testing() +endif() + ADD_SUBDIRECTORY(src) FIND_PACKAGE(Doxygen) @@ -76,4 +95,19 @@ ELSE(UNIX) ) ENDIF(UNIX) +IF (UNIX) + SET(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-dev-${CPACK_PACKAGE_VERSION}${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") + SET(CPACK_PACKAGE_NAME "${PROJECT_NAME}-dev") + SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "...Enter something here...") + SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) + SET(CPACK_GENERATOR "DEB") + SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "asantamaria@iri.upc.edu") + SET(CPACK_SET_DESTDIR "ON") # Necessary because of the absolute install paths + INCLUDE(CPack) +ELSE(UNIX) + ADD_CUSTOM_COMMAND( + COMMENT "packaging only implemented in unix" + TARGET uninstall) +ENDIF(UNIX) + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ceae6cc..0167371 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,19 @@ -# Specific definitions -#ADD_DEFINITIONS(-DPRINT_INFO_VU) +#Start Vision Utils build +MESSAGE("Starting Vision Utils CMakeLists ...") +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +IF(BUILD_EXAMPLES OR BUILD_TESTS) + SET(_VU_ROOT_DIR ${CMAKE_SOURCE_DIR}) +ENDIF(BUILD_EXAMPLES OR BUILD_TESTS) + +IF(PRINT_INFO_VU) + ADD_DEFINITIONS(-DPRINT_INFO_VU) +ENDIF(PRINT_INFO_VU) + +IF((CMAKE_BUILD_TYPE MATCHES DEBUG) OR (CMAKE_BUILD_TYPE MATCHES debug) OR (CMAKE_BUILD_TYPE MATCHES Debug)) + SET(_VU_DEBUG true) + ADD_DEFINITIONS(-D_VU_DEBUG) +ENDIF() # library source files SET(sources @@ -277,7 +291,14 @@ INSTALL(FILES ${headers_alg_activesearch} DESTINATION include/${PROJECT_NAME}/al INSTALL(FILES ../cmake_modules/Find${PROJECT_NAME}.cmake DESTINATION ${CMAKE_ROOT}/Modules/) INSTALL(FILES "${VU_CONFIG_DIR}/config.h" DESTINATION include/${PROJECT_NAME}/_internal) -# examples of usage -ADD_SUBDIRECTORY(examples) - +## Tests ## +if(BUILD_TESTS) + MESSAGE("Building tests.") + ADD_SUBDIRECTORY(test) +endif() +## Examples ## +IF(BUILD_EXAMPLES) + MESSAGE("Building examples.") + ADD_SUBDIRECTORY(examples) +ENDIF(BUILD_EXAMPLES) diff --git a/src/algorithms/activesearch/alg_activesearch.cpp b/src/algorithms/activesearch/alg_activesearch.cpp index 1305f09..b55b0b1 100644 --- a/src/algorithms/activesearch/alg_activesearch.cpp +++ b/src/algorithms/activesearch/alg_activesearch.cpp @@ -163,13 +163,13 @@ void AlgorithmACTIVESEARCH::detectNewFeatures(FramePtr& _frame, const DetectorBa { // Keep best in cell KeyPointVector list_keypoints = kps; - unsigned int index = 0; + // cv::KeyPointsFilter keypoint_filter; // keypoint_filter.retainBest(kps,1); retainBest(kps,1); - for(unsigned int ii = 0; ii < list_keypoints.size(); ii++) - if(list_keypoints[ii].pt == kps[0].pt) - index = ii; + + // Check if point exist in list + int index = existsIn(kps[0], list_keypoints, 1.0); if(kps[0].response > params_ptr_->min_response_new_feature) { diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt new file mode 100644 index 0000000..5f9db75 --- /dev/null +++ b/src/test/CMakeLists.txt @@ -0,0 +1,27 @@ +# Retrieve googletest from github & compile +add_subdirectory(gtest) + +# Include gtest directory. +include_directories(${GTEST_INCLUDE_DIRS}) + +############# USE THIS TEST AS AN EXAMPLE ################# +# # +# Create a specific test executable for gtest_example # +vu_add_gtest(gtest_example gtest_example.cpp) # +target_link_libraries(gtest_example ${PROJECT_NAME}) # +# # +########################################################### + + +################# ADD YOUR TESTS BELOW #################### +# # +# ==== IN ALPHABETICAL ORDER! ==== # +# # + +# ------- First Core classes ---------- + +# ------- Now Derived classes ---------- + +# ROI test + vu_add_gtest(gtest_roi_ORB gtest_roi_ORB.cpp) + target_link_libraries(gtest_roi_ORB ${PROJECT_NAME}) \ No newline at end of file diff --git a/src/test/Test_ORB.png b/src/test/Test_ORB.png new file mode 100644 index 0000000000000000000000000000000000000000..016141f5309c1ed34a61d71cfa63b130ea90aa8f GIT binary patch literal 10936 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 LSU2yi!_9vK%33fW literal 0 HcmV?d00001 diff --git a/src/test/data/Test_ORB.png b/src/test/data/Test_ORB.png new file mode 100644 index 0000000000000000000000000000000000000000..016141f5309c1ed34a61d71cfa63b130ea90aa8f GIT binary patch literal 10936 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 LSU2yi!_9vK%33fW literal 0 HcmV?d00001 diff --git a/src/test/data/roi_orb.yaml b/src/test/data/roi_orb.yaml new file mode 100644 index 0000000..96331a6 --- /dev/null +++ b/src/test/data/roi_orb.yaml @@ -0,0 +1,10 @@ +detector: + type: "ORB" + nfeatures: 20 + scale factor: 1.2 + nlevels: 8 + edge threshold: 16 # 16 + first level: 0 + WTA_K: 2 # See: http://docs.opencv.org/trunk/db/d95/classcv_1_1ORB.html#a180ae17d3300cf2c619aa240d9b607e5 + score type: 0 #enum { kBytes = 32, HARRIS_SCORE=0, FAST_SCORE=1 }; + patch size: 16 # 31 diff --git a/src/test/gtest/CMakeLists.txt b/src/test/gtest/CMakeLists.txt new file mode 100644 index 0000000..1e7b400 --- /dev/null +++ b/src/test/gtest/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required(VERSION 2.8.8) +project(gtest_builder C CXX) + +# We need thread support +#find_package(Threads REQUIRED) + +# Enable ExternalProject CMake module +include(ExternalProject) + +set(GTEST_FORCE_SHARED_CRT ON) +set(GTEST_DISABLE_PTHREADS OFF) + +# For some reason I need to disable PTHREADS +# with g++ (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 +# This is a known issue for MinGW : +# https://github.com/google/shaderc/pull/174 +#if(MINGW) + set(GTEST_DISABLE_PTHREADS ON) +#endif() + +# Download GoogleTest +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + # TIMEOUT 1 # We'll try this + CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs + -DCMAKE_CXX_FLAGS=${MSVC_COMPILER_DEFS} + -Dgtest_force_shared_crt=${GTEST_FORCE_SHARED_CRT} + -Dgtest_disable_pthreads=${GTEST_DISABLE_PTHREADS} + -DBUILD_GTEST=ON + PREFIX "${CMAKE_CURRENT_BINARY_DIR}" + # Disable install step + INSTALL_COMMAND "" + # UPDATE_DISCONNECTED 1 +) + +# Get GTest source and binary directories from CMake project + +# Specify include dir +ExternalProject_Get_Property(googletest source_dir) +set(GTEST_INCLUDE_DIRS ${source_dir}/googletest/include PARENT_SCOPE) + +# Specify MainTest's link libraries +ExternalProject_Get_Property(googletest binary_dir) +set(GTEST_LIBS_DIR ${binary_dir}/googlemock/gtest PARENT_SCOPE) + +# Create a libgtest target to be used as a dependency by test programs +add_library(libgtest IMPORTED STATIC GLOBAL) +add_dependencies(libgtest googletest) + +# Set libgtest properties +set_target_properties(libgtest PROPERTIES + "IMPORTED_LOCATION" "${binary_dir}/googlemock/gtest/libgtest.a" + "IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}" +) + +function(vu_add_gtest target) + add_executable(${target} ${ARGN}) + add_dependencies(${target} libgtest) + target_link_libraries(${target} libgtest) + + #WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin + add_test(NAME ${target} COMMAND ${target}) +endfunction() diff --git a/src/test/gtest_example.cpp b/src/test/gtest_example.cpp new file mode 100644 index 0000000..9c4ae7a --- /dev/null +++ b/src/test/gtest_example.cpp @@ -0,0 +1,20 @@ +#include "utils_gtest.h" + +TEST(TestTest, DummyTestExample) +{ + EXPECT_FALSE(false); + + ASSERT_TRUE(true); + + int my_int = 5; + + ASSERT_EQ(my_int, 5); + + PRINTF("All good at TestTest::DummyTestExample !\n"); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/test/gtest_roi_ORB.cpp b/src/test/gtest_roi_ORB.cpp new file mode 100644 index 0000000..4c9bcd0 --- /dev/null +++ b/src/test/gtest_roi_ORB.cpp @@ -0,0 +1,141 @@ +/** + * \file gtest_roi_ORB.cpp + * + * Created on: Nov 28, 2016 + * \author: jsola + */ + +#include "utils_gtest.h" + +// vision utils includes +#include "../vision_utils.h" +#include "../detectors.h" + +// std include +#include <vector> +#include <map> + +TEST(RoiORB, LoadImageFromFile) +{ + cv::Mat image; + std::string filename, vu_root; + vu_root = _VU_ROOT_DIR; + filename = vu_root + "/src/test/data/Test_ORB.png"; + image = cv::imread(filename, CV_LOAD_IMAGE_GRAYSCALE); + + ASSERT_TRUE(image.data)<< "Failed to load image " << filename << std::endl; +} + +// Test_ORB.png image file has the following interest points that should be detected: +// 0 [ 65.6 , 100.6 ] +// 1 [ 164 , 100.6 ] +// 2 [ 266 , 100.6 ] +// 3 [ 365.2 , 100.6 ] +// 4 [ 467.04 , 101.32 ] +// 5 [ 565.6 , 100.6 ] +// 6 [ 71.6 , 237.8 ] +// 7 [ 164.8 , 237.8 ] +// 8 [ 251.36 , 239.24 ] +// 9 [ 330.2 , 237.8 ] +// 10 [ 349 , 270 ] // point #10 is out of the ROI scanned area and should not be detected +// 11 [ 584 , 237.8 ] +// 12 [ 467.2 , 455.4 ] +// 13 [ 566 , 455.4 ] +PointVector points_to_check({ + cv::Point2f( 65.6 , 100.6 ), + cv::Point2f( 164 , 100.6 ), + cv::Point2f( 266 , 100.6 ), + cv::Point2f( 365.2 , 100.6 ), + cv::Point2f( 467 , 101.3 ), + cv::Point2f( 565.6 , 100.6 ), + cv::Point2f( 71.6 , 237.8 ), + cv::Point2f( 164.8 , 237.8 ), + cv::Point2f( 250 , 239.2 ), + cv::Point2f( 330.2 , 237.8 ), + cv::Point2f( 349 , 270 ), + cv::Point2f( 584 , 237.8 ), + cv::Point2f( 467.2 , 455.4 ), + cv::Point2f( 566 , 455.4 ) +}); + +TEST(RoiORB, RoiBounds) +{ + cv::Mat image; + std::string filename, vu_root; + vu_root = _VU_ROOT_DIR; + filename = vu_root + "/src/test/data/Test_ORB.png"; + image = cv::imread(filename, CV_LOAD_IMAGE_GRAYSCALE); + + ASSERT_TRUE(image.data)<< "failed to load image " << filename << std::endl; + + unsigned int img_width = image.cols; + + // Define detector + std::string yaml_file_params = vu_root + "/src/test/data/roi_orb.yaml"; + std::string det_name = vision_utils::readYamlType(yaml_file_params, "detector"); + vision_utils::DetectorBasePtr det_ptr = vision_utils::setupDetector(det_name, det_name + " detector", yaml_file_params); + det_ptr = std::static_pointer_cast<vision_utils::DetectorORB>(det_ptr); + + std::vector<cv::KeyPoint> target_keypoints; + cv::KeyPointsFilter keypoint_filter; + + int roi_x; + int roi_y; + int roi_width = 50; + int roi_heigth = 50; + + Eigen::VectorXi roi_center_y(3); roi_center_y << 102 , 250 , 476; + + std::map<int, cv::Point2f> points_found; + + for (int ii = 0; ii<roi_center_y.size() ; ii++) + { + roi_y = roi_center_y(ii) - roi_width/2; + + for(roi_x = 0; roi_x < img_width; roi_x += 5) + { + cv::Rect roi(roi_x, roi_y, roi_width, roi_heigth); + cv::Rect roi_inflated = roi; + + // Detect features in ROI + target_keypoints = det_ptr->detect(image, roi_inflated); + + // Keep only one KP in ROI + if (!target_keypoints.empty()) + { + keypoint_filter.retainBest(target_keypoints,1); + cv::Point2f pnt = target_keypoints[0].pt; + + int j = vision_utils::existsIn(pnt, points_to_check, 2.0); + + ASSERT_GE(j, 0); + + // append the keypoint to the list of keypoints found + if (j >= 0) + points_found[j] = pnt; + } + +#ifdef _VU_DEBUG + cv::Mat image_graphics = image.clone(); + cv::drawKeypoints(image_graphics,target_keypoints,image_graphics); + cv::rectangle(image_graphics, roi, cv::Scalar(255.0, 0.0, 255.0), 1, 8, 0); + cv::rectangle(image_graphics, roi_inflated, cv::Scalar(255.0, 255.0, 0.0), 1, 8, 0); + cv::imshow("test",image_graphics); + cv::waitKey(1); +#endif + } + } + + // check that at least all keypoints in the list except #10 have been detected + // (note: #10 is out of the ROI) + std::vector<int> nn({0,1,2,3,4,5,6,7,8,9,11,12,13}); + for (int n : nn) + ASSERT_TRUE(points_found.count(n)); + ASSERT_FALSE(points_found.count(10)); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/test/gtest_vision_utils.cpp b/src/test/gtest_vision_utils.cpp new file mode 100644 index 0000000..3b8cf3e --- /dev/null +++ b/src/test/gtest_vision_utils.cpp @@ -0,0 +1,34 @@ +#include "utils_gtest.h" +#include "../vision_utils.h" + +TEST(VisionUtils, DummyTestExample) +{ + EXPECT_FALSE(false); + + ASSERT_TRUE(true); + + int my_int = 5; + + ASSERT_EQ(my_int, 5); + + PRINTF("All good at TestTest::DummyTestExample !\n"); +} + +TEST(VisionUtils, DummyTestExample) +{ + EXPECT_FALSE(false); + + ASSERT_TRUE(true); + + int my_int = 5; + + ASSERT_EQ(my_int, 5); + + PRINTF("All good at TestTest::DummyTestExample !\n"); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/test/utils_gtest.h b/src/test/utils_gtest.h new file mode 100644 index 0000000..7716bf0 --- /dev/null +++ b/src/test/utils_gtest.h @@ -0,0 +1,132 @@ +#ifndef VU_UTILS_GTEST_H +#define VU_UTILS_GTEST_H + +#include <gtest/gtest.h> + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// C String Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); +// +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); + + +// http://stackoverflow.com/a/29155677 + +namespace testing +{ +namespace internal +{ +enum GTestColor +{ + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +extern void ColoredPrintf(GTestColor color, const char* fmt, ...); + +#define PRINTF(...) \ + do { testing::internal::ColoredPrintf(testing::internal::COLOR_GREEN,\ + "[ ] "); \ + testing::internal::ColoredPrintf(testing::internal::COLOR_YELLOW, __VA_ARGS__); } \ + while(0) + +// C++ stream interface +class TestCout : public std::stringstream +{ +public: + ~TestCout() + { + PRINTF("%s\n", str().c_str()); + } +}; + +/* Usage : + +TEST(Test, Foo) +{ + // the following works but prints default stream + EXPECT_TRUE(false) << "Testing Stream."; + + // or you can play with AINSI color code + EXPECT_TRUE(false) << "\033[1;31m" << "Testing Stream."; + + // or use the above defined macros + + PRINTF("Hello world"); + + // or + + TEST_COUT << "Hello world"; +} + +*/ +#define TEST_COUT testing::internal::TestCout() + +} // namespace internal + +/* Macros related to testing Eigen classes: + */ +#define EXPECT_MATRIX_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXs lhs, const Eigen::MatrixXs rhs) { \ + return (lhs - rhs).isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); + +#define ASSERT_MATRIX_APPROX(C_expect, C_actual, precision) ASSERT_PRED2([](const Eigen::MatrixXs lhs, const Eigen::MatrixXs rhs) { \ + return (lhs - rhs).isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); + +#define EXPECT_QUATERNION_APPROX(C_expect, C_actual, precision) EXPECT_MATRIX_APPROX((C_expect).coeffs(), (C_actual).coeffs(), precision) + +#define ASSERT_QUATERNION_APPROX(C_expect, C_actual, precision) ASSERT_MATRIX_APPROX((C_expect).coeffs(), (C_actual).coeffs(), precision) + +#define EXPECT_POSE2D_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXs lhs, const Eigen::MatrixXs rhs) { \ + MatrixXs er = lhs - rhs; \ + er(2) = pi2pi((Scalar)er(2)); \ + return er.isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); + +#define ASSERT_POSE2D_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXs lhs, const Eigen::MatrixXs rhs) { \ + MatrixXs er = lhs - rhs; \ + er(2) = pi2pi((Scalar)er(2)); \ + return er.isMuchSmallerThan(1, precision); \ + }, \ + C_expect, C_actual); + +} // namespace testing + + +#endif /* VU_UTILS_GTEST_H */ diff --git a/src/vision_utils.cpp b/src/vision_utils.cpp index 2b68a93..6cb1fd3 100644 --- a/src/vision_utils.cpp +++ b/src/vision_utils.cpp @@ -151,6 +151,32 @@ void whoHasMoved(const PointVector& _pt_vec_1, const PointVector& _pt_vec_2, Poi } } +int existsIn(const cv::Point2f& p, const PointVector p_vec, const double& pixel_tol) +{ + double pixel_tol_squared = pixel_tol*pixel_tol; + for (int ii = 0; ii < p_vec.size(); ii++) + { + double dx = p.x - p_vec[ii].x; + double dy = p.y - p_vec[ii].y; + if ( dx*dx + dy*dy < pixel_tol_squared) // match based on Euclidean distance + return ii; + } + return -1; // -1 marks 'not found' +} + +int existsIn(const cv::KeyPoint& p, const KeyPointVector p_vec, const double& pixel_tol) +{ + double pixel_tol_squared = pixel_tol*pixel_tol; + for (int ii = 0; ii < p_vec.size(); ii++) + { + double dx = p.pt.x - p_vec[ii].pt.x; + double dy = p.pt.y - p_vec[ii].pt.y; + if ( dx*dx + dy*dy < pixel_tol_squared) // match based on Euclidean distance + return ii; + } + return -1; // -1 marks 'not found' +} + void drawKeyPoints(cv::Mat& _image, const KeyPointVector& _kp_vec, const int& _radius, const cv::Scalar& _color, const int& _thickness) { for (auto kp : _kp_vec) diff --git a/src/vision_utils.h b/src/vision_utils.h index 9f10fc4..d9e883d 100644 --- a/src/vision_utils.h +++ b/src/vision_utils.h @@ -24,17 +24,20 @@ #include <eigen3/Eigen/Sparse> // OpenCV -#include <opencv2/core/core.hpp> -#include <opencv2/core/types.hpp> +#include <opencv2/opencv.hpp> #include <opencv2/core/eigen.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/features2d/features2d.hpp> -#include <opencv2/features2d.hpp> -#include <opencv2/xfeatures2d/nonfree.hpp> +#include <opencv2/line_descriptor.hpp> #include <opencv2/xfeatures2d.hpp> -#include <opencv2/line_descriptor/descriptor.hpp> -#include <opencv2/calib3d.hpp> + +// REMOVE +//#include <opencv2/core.hpp> +//#include <opencv2/core/types.hpp> +//#include <opencv2/imgproc/imgproc.hpp> +//#include <opencv2/highgui/highgui.hpp> +//#include <opencv2/features2d/features2d.hpp> +//#include <opencv2/features2d.hpp> +//#include <opencv2/xfeatures2d/nonfree.hpp> +//#include <opencv2/calib3d.hpp> typedef std::vector<cv::Point2f> PointVector; typedef std::vector<cv::KeyPoint> KeyPointVector; @@ -153,6 +156,9 @@ PointVector KPToP(const KeyPointVector& _kp_vec); void whoHasMoved(const KeyPointVector& _kpts1, const KeyPointVector& _kpts2, KeyPointVector& _common_kpts, KeyPointVector& _new_in_kpts2); void whoHasMoved(const PointVector& _pts1, const PointVector& _pts2, PointVector& _common_pts, PointVector& _new_in_pts2); +int existsIn(const cv::Point2f& p, const PointVector p_vec, const double& pixel_tol); +int existsIn(const cv::KeyPoint& p, const KeyPointVector p_vec, const double& pixel_tol); + void drawKeyPoints(cv::Mat& _image, const KeyPointVector& _kp_vec, const int& _radius=5, const cv::Scalar& _color = cv::Scalar(0, 0, 255), const int& _thickness=-1); void drawKeyLines(cv::Mat& _image, const KeyLineVector& _kl_vec, const cv::Scalar& _color = cv::Scalar(128, 128, 255) ); -- GitLab