From 65487946cc4ab121dd8a56462dfce7b64e0377cc Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 24 May 2023 13:29:18 +0300 Subject: [PATCH 1/6] Added final constrants check to solveLP to filter out flating-point numeric issues. --- modules/core/include/opencv2/core/optim.hpp | 7 ++++++- modules/core/src/lpsolver.cpp | 19 +++++++++++++++++-- modules/core/test/test_lpsolver.cpp | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/modules/core/include/opencv2/core/optim.hpp b/modules/core/include/opencv2/core/optim.hpp index 5a0940037d..f659d00a72 100644 --- a/modules/core/include/opencv2/core/optim.hpp +++ b/modules/core/include/opencv2/core/optim.hpp @@ -256,6 +256,7 @@ public: //! return codes for cv::solveLP() function enum SolveLPResult { + SOLVELP_LOST = -3, //!< problem is feasible, but solver lost solution due to floating-point arithmetic errors SOLVELP_UNBOUNDED = -2, //!< problem is unbounded (target function can achieve arbitrary high values) SOLVELP_UNFEASIBLE = -1, //!< problem is unfeasible (there are no points that satisfy all the constraints imposed) SOLVELP_SINGLE = 0, //!< there is only one maximum for target function @@ -291,9 +292,13 @@ in the latter case it is understood to correspond to \f$c^T\f$. and the remaining to \f$A\f$. It should contain 32- or 64-bit floating point numbers. @param z The solution will be returned here as a column-vector - it corresponds to \f$c\f$ in the formulation above. It will contain 64-bit floating point numbers. +@param constr_eps allowed numeric disparity for constraints @return One of cv::SolveLPResult */ -CV_EXPORTS_W int solveLP(const Mat& Func, const Mat& Constr, Mat& z); +CV_EXPORTS_W int solveLP(const Mat& Func, const Mat& Constr, Mat& z, double constr_eps); + +/** @overload */ +CV_EXPORTS int solveLP(const Mat& Func, const Mat& Constr, Mat& z); //! @} diff --git a/modules/core/src/lpsolver.cpp b/modules/core/src/lpsolver.cpp index 1a1307d5b5..a00b3191b6 100644 --- a/modules/core/src/lpsolver.cpp +++ b/modules/core/src/lpsolver.cpp @@ -90,7 +90,7 @@ static void swap_columns(Mat_& A,int col1,int col2); #define SWAP(type,a,b) {type tmp=(a);(a)=(b);(b)=tmp;} //return codes:-2 (no_sol - unbdd),-1(no_sol - unfsbl), 0(single_sol), 1(multiple_sol=>least_l2_norm) -int solveLP(const Mat& Func, const Mat& Constr, Mat& z){ +int solveLP(const Mat& Func, const Mat& Constr, Mat& z, double constr_eps){ dprintf(("call to solveLP\n")); //sanity check (size, type, no. of channels) @@ -140,9 +140,24 @@ int solveLP(const Mat& Func, const Mat& Constr, Mat& z){ } } + //check constraints feasibility + Mat prod = Constr(Rect(0, 0, Constr.cols - 1, Constr.rows)) * z; + Mat constr_check = Constr.col(Constr.cols - 1) - prod; + double min_value = 0.0; + minMaxIdx(constr_check, &min_value); + if (min_value < -constr_eps) + { + return SOLVELP_LOST; + } + return res; } +CV_EXPORTS_W int solveLP(const Mat& Func, const Mat& Constr, Mat& z) +{ + return solveLP(Func, Constr, z, 1e-12); +} + static int initialize_simplex(Mat_& c, Mat_& b,double& v,vector& N,vector& B,vector& indexToRow){ N.resize(c.cols); N[0]=0; @@ -255,7 +270,7 @@ static int inner_simplex(Mat_& c, Mat_& b,double& v,vector& dprintf(("iteration #%d\n",count)); count++; - static MatIterator_ pos_ptr; + MatIterator_ pos_ptr; int e=-1,pos_ctr=0,min_var=INT_MAX; bool all_nonzero=true; for(pos_ptr=c.begin();pos_ptr!=c.end();pos_ptr++,pos_ctr++){ diff --git a/modules/core/test/test_lpsolver.cpp b/modules/core/test/test_lpsolver.cpp index 97f87f4c2f..a92782405e 100644 --- a/modules/core/test/test_lpsolver.cpp +++ b/modules/core/test/test_lpsolver.cpp @@ -151,4 +151,18 @@ TEST(Core_LPSolver, issue_12337) //need to update interface: EXPECT_ANY_THROW(Mat1b z_8u; cv::solveLP(A, B, z_8u)); } +// NOTE: Test parameters found experimentally to get numerically inaccurate result. +// The test behaviour may change after algorithm tuning and may removed. +TEST(Core_LPSolver, issue_12343) +{ + Mat A = (cv::Mat_(4, 1) << 3., 3., 3., 4.); + Mat B = (cv::Mat_(4, 5) << 0., 1., 4., 4., 3., + 3., 1., 2., 2., 3., + 4., 4., 0., 1., 4., + 4., 0., 4., 1., 4.); + Mat z; + int result = cv::solveLP(A, B, z); + EXPECT_EQ(SOLVELP_LOST, result); +} + }} // namespace From cbda161c3928fd9a5ee0540a86c909f2ac542ae3 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 24 May 2023 16:16:14 +0300 Subject: [PATCH 2/6] Fixed FPS computation on some videos for FFmpeg backend. --- modules/videoio/src/cap_ffmpeg_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 66388bcb87..b0b81c6fd0 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -1537,7 +1537,7 @@ int64_t CvCapture_FFMPEG::get_bitrate() const double CvCapture_FFMPEG::get_fps() const { -#if 0 && LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 1, 100) && LIBAVFORMAT_VERSION_MICRO >= 100 +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 1, 100) && LIBAVFORMAT_VERSION_MICRO >= 100 double fps = r2d(av_guess_frame_rate(ic, ic->streams[video_stream], NULL)); #else #if LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(54, 1, 0) From 66f86e898cd920988d42322168945f2d76175bba Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 2 Jun 2023 10:33:24 +0300 Subject: [PATCH 3/6] Fixed potential buffer overflow of user file name in create_samples_app --- apps/createsamples/utility.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/createsamples/utility.cpp b/apps/createsamples/utility.cpp index 5176f14836..e94792b1e4 100644 --- a/apps/createsamples/utility.cpp +++ b/apps/createsamples/utility.cpp @@ -70,7 +70,7 @@ using namespace cv; static int icvMkDir( const char* filename ) { - char path[PATH_MAX]; + char path[PATH_MAX+1]; char* p; int pos; @@ -83,7 +83,8 @@ static int icvMkDir( const char* filename ) mode = 0755; #endif /* _WIN32 */ - strcpy( path, filename ); + path[0] = '\0'; + strncat( path, filename, PATH_MAX ); p = path; for( ; ; ) From e1ce2146f52bf0e0b9822b321aacbd6217b3bc26 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 9 Jun 2023 09:21:55 +0000 Subject: [PATCH 4/6] build(ios): disable workaround for CMake 3.25.1+ --- .../cmake/Toolchains/common-ios-toolchain.cmake | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/platforms/ios/cmake/Toolchains/common-ios-toolchain.cmake b/platforms/ios/cmake/Toolchains/common-ios-toolchain.cmake index 735c75f1f3..3325665f91 100644 --- a/platforms/ios/cmake/Toolchains/common-ios-toolchain.cmake +++ b/platforms/ios/cmake/Toolchains/common-ios-toolchain.cmake @@ -98,8 +98,17 @@ if(NOT DEFINED IPHONEOS_DEPLOYMENT_TARGET) endif() if(NOT __IN_TRY_COMPILE) - set(_xcodebuild_wrapper "${CMAKE_BINARY_DIR}/xcodebuild_wrapper") - if(NOT EXISTS "${_xcodebuild_wrapper}") + set(_xcodebuild_wrapper "") + if(NOT (CMAKE_VERSION VERSION_LESS "3.25.1")) # >= 3.25.1 + # no workaround is required (#23156) + elseif(NOT (CMAKE_VERSION VERSION_LESS "3.25.0")) # >= 3.25.0 < 3.25.1 + if(NOT OPENCV_SKIP_MESSAGE_ISSUE_23156) + message(FATAL_ERROR "OpenCV: Please upgrade CMake to 3.25.1+. Current CMake version is ${CMAKE_VERSION}. Details: https://github.com/opencv/opencv/issues/23156") + endif() + else() # < 3.25.0, apply workaround from #13912 + set(_xcodebuild_wrapper "${CMAKE_BINARY_DIR}/xcodebuild_wrapper") + endif() + if(_xcodebuild_wrapper AND NOT EXISTS "${_xcodebuild_wrapper}") set(_xcodebuild_wrapper_tmp "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/xcodebuild_wrapper") if(NOT DEFINED CMAKE_MAKE_PROGRAM) # empty since CMake 3.10 find_program(XCODEBUILD_PATH "xcodebuild") @@ -119,7 +128,9 @@ if(NOT __IN_TRY_COMPILE) configure_file("${CMAKE_CURRENT_LIST_DIR}/xcodebuild_wrapper.in" "${_xcodebuild_wrapper_tmp}" @ONLY) file(COPY "${_xcodebuild_wrapper_tmp}" DESTINATION ${CMAKE_BINARY_DIR} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endif() - set(CMAKE_MAKE_PROGRAM "${_xcodebuild_wrapper}" CACHE INTERNAL "" FORCE) + if(_xcodebuild_wrapper) + set(CMAKE_MAKE_PROGRAM "${_xcodebuild_wrapper}" CACHE INTERNAL "" FORCE) + endif() endif() # Standard settings From e625b32841bd5e2a2db281bd1730dfe3af887959 Mon Sep 17 00:00:00 2001 From: dizcza Date: Thu, 15 Jun 2023 19:30:40 +0300 Subject: [PATCH 5/6] [opencv 3.x] back-ported tbb support ubuntu 22.04 --- 3rdparty/tbb/CMakeLists.txt | 2 +- cmake/OpenCVDetectTBB.cmake | 9 +++++---- cmake/OpenCVFindMKL.cmake | 9 ++------- modules/core/src/parallel.cpp | 1 - 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/3rdparty/tbb/CMakeLists.txt b/3rdparty/tbb/CMakeLists.txt index a085b0f3ca..50f3e6ccf1 100644 --- a/3rdparty/tbb/CMakeLists.txt +++ b/3rdparty/tbb/CMakeLists.txt @@ -170,4 +170,4 @@ ocv_install_target(tbb EXPORT OpenCVModules ocv_install_3rdparty_licenses(tbb "${tbb_src_dir}/LICENSE" "${tbb_src_dir}/README") -ocv_tbb_read_version("${tbb_src_dir}/include") +ocv_tbb_read_version("${tbb_src_dir}/include" tbb) diff --git a/cmake/OpenCVDetectTBB.cmake b/cmake/OpenCVDetectTBB.cmake index 38137f44f0..1154bb1d0f 100644 --- a/cmake/OpenCVDetectTBB.cmake +++ b/cmake/OpenCVDetectTBB.cmake @@ -19,7 +19,7 @@ # - "tbb" target exists and added to OPENCV_LINKER_LIBS function(ocv_tbb_cmake_guess _found) - find_package(TBB QUIET COMPONENTS tbb PATHS "$ENV{TBBROOT}/cmake") + find_package(TBB QUIET COMPONENTS tbb PATHS "$ENV{TBBROOT}/cmake" "$ENV{TBBROOT}/lib/cmake/tbb") if(TBB_FOUND) if(NOT TARGET TBB::tbb) message(WARNING "No TBB::tbb target found!") @@ -28,11 +28,11 @@ function(ocv_tbb_cmake_guess _found) get_target_property(_lib TBB::tbb IMPORTED_LOCATION_RELEASE) message(STATUS "Found TBB (cmake): ${_lib}") get_target_property(_inc TBB::tbb INTERFACE_INCLUDE_DIRECTORIES) - ocv_tbb_read_version("${_inc}") add_library(tbb INTERFACE IMPORTED) set_target_properties(tbb PROPERTIES INTERFACE_LINK_LIBRARIES TBB::tbb ) + ocv_tbb_read_version("${_inc}" tbb) set(${_found} TRUE PARENT_SCOPE) endif() endfunction() @@ -66,7 +66,6 @@ function(ocv_tbb_env_guess _found) find_library(TBB_ENV_LIB_DEBUG NAMES "tbb_debug") if (TBB_ENV_INCLUDE AND (TBB_ENV_LIB OR TBB_ENV_LIB_DEBUG)) ocv_tbb_env_verify() - ocv_tbb_read_version("${TBB_ENV_INCLUDE}") add_library(tbb UNKNOWN IMPORTED) set_target_properties(tbb PROPERTIES IMPORTED_LOCATION "${TBB_ENV_LIB}" @@ -82,12 +81,14 @@ function(ocv_tbb_env_guess _found) get_filename_component(_dir "${TBB_ENV_LIB}" DIRECTORY) set_target_properties(tbb PROPERTIES INTERFACE_LINK_LIBRARIES "-L${_dir}") endif() + ocv_tbb_read_version("${TBB_ENV_INCLUDE}" tbb) message(STATUS "Found TBB (env): ${TBB_ENV_LIB}") set(${_found} TRUE PARENT_SCOPE) endif() endfunction() -function(ocv_tbb_read_version _path) +function(ocv_tbb_read_version _path _tgt) + find_file(TBB_VER_FILE oneapi/tbb/version.h "${_path}" NO_DEFAULT_PATH CMAKE_FIND_ROOT_PATH_BOTH) find_file(TBB_VER_FILE tbb/tbb_stddef.h "${_path}" NO_DEFAULT_PATH CMAKE_FIND_ROOT_PATH_BOTH) ocv_parse_header("${TBB_VER_FILE}" TBB_VERSION_LINES TBB_VERSION_MAJOR TBB_VERSION_MINOR TBB_INTERFACE_VERSION CACHE) endfunction() diff --git a/cmake/OpenCVFindMKL.cmake b/cmake/OpenCVFindMKL.cmake index 0638e89e2b..1b6d47a271 100644 --- a/cmake/OpenCVFindMKL.cmake +++ b/cmake/OpenCVFindMKL.cmake @@ -118,16 +118,10 @@ if(MKL_USE_SINGLE_DYNAMIC_LIBRARY AND NOT (MKL_VERSION_STR VERSION_LESS "10.3.0" elseif(NOT (MKL_VERSION_STR VERSION_LESS "11.3.0")) - foreach(MKL_ARCH ${MKL_ARCH_LIST}) - list(APPEND mkl_lib_find_paths - ${MKL_ROOT_DIR}/../tbb/lib/${MKL_ARCH} - ) - endforeach() - set(mkl_lib_list "mkl_intel_${MKL_ARCH_SUFFIX}") if(MKL_WITH_TBB) - list(APPEND mkl_lib_list mkl_tbb_thread tbb) + list(APPEND mkl_lib_list mkl_tbb_thread) elseif(MKL_WITH_OPENMP) if(MSVC) list(APPEND mkl_lib_list mkl_intel_thread libiomp5md) @@ -155,6 +149,7 @@ if(NOT MKL_LIBRARIES) endif() list(APPEND MKL_LIBRARIES ${${lib_var_name}}) endforeach() + list(APPEND MKL_LIBRARIES ${OPENCV_EXTRA_MKL_LIBRARIES}) endif() message(STATUS "Found MKL ${MKL_VERSION_STR} at: ${MKL_ROOT_DIR}") diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index efecefc4b4..dc52820e71 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -101,7 +101,6 @@ #endif #include "tbb/tbb.h" #include "tbb/task.h" - #include "tbb/tbb_stddef.h" #if TBB_INTERFACE_VERSION >= 8000 #include "tbb/task_arena.h" #endif From ddcbd2cc268f6293bb636a41c1aa19dfb3c181ad Mon Sep 17 00:00:00 2001 From: lamm45 <96844552+lamm45@users.noreply.github.com> Date: Mon, 19 Jun 2023 08:11:01 -0400 Subject: [PATCH 6/6] Merge pull request #22798 from lamm45:distransform-large Fix distransform to work with large images #22798 This attempts to fix the following bug which was caused by storing squares of large integers into 32-bit floating point variables: https://github.com/opencv/opencv/issues/22732 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/src/distransform.cpp | 31 +++++++------- .../imgproc/test/test_distancetransform.cpp | 42 +++++++++++++++++++ 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/modules/imgproc/src/distransform.cpp b/modules/imgproc/src/distransform.cpp index a2c5a0d5fd..dd22d4f50b 100644 --- a/modules/imgproc/src/distransform.cpp +++ b/modules/imgproc/src/distransform.cpp @@ -448,7 +448,7 @@ static void getDistanceTransformMask( int maskType, float *metrics ) struct DTColumnInvoker : ParallelLoopBody { - DTColumnInvoker( const Mat* _src, Mat* _dst, const int* _sat_tab, const float* _sqr_tab) + DTColumnInvoker( const Mat* _src, Mat* _dst, const int* _sat_tab, const int* _sqr_tab) { src = _src; dst = _dst; @@ -481,7 +481,7 @@ struct DTColumnInvoker : ParallelLoopBody { dist = dist + 1 - sat_tab[dist - d[j]]; d[j] = dist; - dptr[0] = sqr_tab[dist]; + dptr[0] = (float)sqr_tab[dist]; } } } @@ -489,12 +489,12 @@ struct DTColumnInvoker : ParallelLoopBody const Mat* src; Mat* dst; const int* sat_tab; - const float* sqr_tab; + const int* sqr_tab; }; struct DTRowInvoker : ParallelLoopBody { - DTRowInvoker( Mat* _dst, const float* _sqr_tab, const float* _inv_tab ) + DTRowInvoker( Mat* _dst, const int* _sqr_tab, const float* _inv_tab ) { dst = _dst; sqr_tab = _sqr_tab; @@ -529,7 +529,7 @@ struct DTRowInvoker : ParallelLoopBody for(;;k--) { p = v[k]; - float s = (fq + sqr_tab[q] - d[p] - sqr_tab[p])*inv_tab[q - p]; + float s = (fq - d[p] + (sqr_tab[q]-sqr_tab[p]))*inv_tab[q - p]; if( s > z[k] ) { k++; @@ -552,28 +552,28 @@ struct DTRowInvoker : ParallelLoopBody } Mat* dst; - const float* sqr_tab; + const int* sqr_tab; const float* inv_tab; }; static void trueDistTrans( const Mat& src, Mat& dst ) { - const float inf = 1e15f; + const int inf = INT_MAX; CV_Assert( src.size() == dst.size() ); CV_Assert( src.type() == CV_8UC1 && dst.type() == CV_32FC1 ); int i, m = src.rows, n = src.cols; - cv::AutoBuffer _buf(std::max(m*2*sizeof(float) + (m*3+1)*sizeof(int), n*2*sizeof(float))); + cv::AutoBuffer _buf(std::max(m*2*sizeof(int) + (m*3+1)*sizeof(int), n*2*sizeof(float))); // stage 1: compute 1d distance transform of each column - float* sqr_tab = (float*)_buf.data(); + int* sqr_tab = (int*)_buf.data(); int* sat_tab = cv::alignPtr((int*)(sqr_tab + m*2), sizeof(int)); int shift = m*2; for( i = 0; i < m; i++ ) - sqr_tab[i] = (float)(i*i); + sqr_tab[i] = i*i; for( i = m; i < m*2; i++ ) sqr_tab[i] = inf; for( i = 0; i < shift; i++ ) @@ -584,13 +584,14 @@ trueDistTrans( const Mat& src, Mat& dst ) cv::parallel_for_(cv::Range(0, n), cv::DTColumnInvoker(&src, &dst, sat_tab, sqr_tab), src.total()/(double)(1<<16)); // stage 2: compute modified distance transform for each row - float* inv_tab = sqr_tab + n; + float* inv_tab = (float*)sqr_tab + n; - inv_tab[0] = sqr_tab[0] = 0.f; + inv_tab[0] = 0.f; + sqr_tab[0] = 0; for( i = 1; i < n; i++ ) { inv_tab[i] = (float)(0.5/i); - sqr_tab[i] = (float)(i*i); + sqr_tab[i] = i*i; } cv::parallel_for_(cv::Range(0, m), cv::DTRowInvoker(&dst, sqr_tab, inv_tab)); @@ -752,7 +753,9 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe CV_IPP_CHECK() { #if IPP_DISABLE_PERF_TRUE_DIST_MT - if(cv::getNumThreads()<=1 || (src.total()<(int)(1<<14))) + // IPP uses floats, but 4097 cannot be squared into a float + if((cv::getNumThreads()<=1 || (src.total()<(int)(1<<14))) && + src.rows < 4097 && src.cols < 4097) #endif { IppStatus status; diff --git a/modules/imgproc/test/test_distancetransform.cpp b/modules/imgproc/test/test_distancetransform.cpp index 32fc7aa2c3..742595631a 100644 --- a/modules/imgproc/test/test_distancetransform.cpp +++ b/modules/imgproc/test/test_distancetransform.cpp @@ -302,4 +302,46 @@ BIGDATA_TEST(Imgproc_DistanceTransform, large_image_12218) EXPECT_EQ(nz, (size.height*size.width / 2)); } +TEST(Imgproc_DistanceTransform, wide_image_22732) +{ + Mat src = Mat::zeros(1, 4099, CV_8U); // 4099 or larger used to be bad + Mat dist(src.rows, src.cols, CV_32F); + distanceTransform(src, dist, DIST_L2, DIST_MASK_PRECISE, CV_32F); + int nz = countNonZero(dist); + EXPECT_EQ(nz, 0); +} + +TEST(Imgproc_DistanceTransform, large_square_22732) +{ + Mat src = Mat::zeros(8000, 8005, CV_8U), dist; + distanceTransform(src, dist, DIST_L2, DIST_MASK_PRECISE, CV_32F); + int nz = countNonZero(dist); + EXPECT_EQ(dist.size(), src.size()); + EXPECT_EQ(dist.type(), CV_32F); + EXPECT_EQ(nz, 0); + + Point p0(src.cols-1, src.rows-1); + src.setTo(1); + src.at(p0) = 0; + distanceTransform(src, dist, DIST_L2, DIST_MASK_PRECISE, CV_32F); + EXPECT_EQ(dist.size(), src.size()); + EXPECT_EQ(dist.type(), CV_32F); + bool first = true; + int nerrs = 0; + for (int y = 0; y < dist.rows; y++) + for (int x = 0; x < dist.cols; x++) { + float d = dist.at(y, x); + double dx = (double)(x - p0.x), dy = (double)(y - p0.y); + float d0 = (float)sqrt(dx*dx + dy*dy); + if (std::abs(d0 - d) > 1) { + if (first) { + printf("y=%d, x=%d. dist_ref=%.2f, dist=%.2f\n", y, x, d0, d); + first = false; + } + nerrs++; + } + } + EXPECT_EQ(0, nerrs) << "reference distance map is different from computed one at " << nerrs << " pixels\n"; +} + }} // namespace