mirror of
https://github.com/opencv/opencv.git
synced 2026-11-09 18:04:29 -06:00
Merge branch 4.x
This commit is contained in:
@@ -77,3 +77,13 @@
|
||||
editor={H.G. Maas and D. Schneider},
|
||||
booktitle={ISPRS 2006 : Proceedings of the ISPRS commission V symposium Vol. 35, part 6 : image engineering and vision metrology, Dresden, Germany 25-27 September 2006}
|
||||
}
|
||||
|
||||
@inproceedings{Terzakis2020SQPnP,
|
||||
title={A Consistently Fast and Globally Optimal Solution to the Perspective-n-Point Problem},
|
||||
author={George Terzakis and Manolis Lourakis},
|
||||
booktitle={European Conference on Computer Vision},
|
||||
pages={478--494},
|
||||
year={2020},
|
||||
publisher={Springer International Publishing},
|
||||
url={https://www.ecva.net/papers/eccv_2020/papers_ECCV/papers/123460460.pdf}
|
||||
}
|
||||
|
||||
@@ -417,19 +417,27 @@ static void undistortPointsInternal( const Mat& _src, Mat& _dst, const Mat& _cam
|
||||
x = srcd[i*sstep].x;
|
||||
y = srcd[i*sstep].y;
|
||||
}
|
||||
// [u, v]^T = [fx * x''' + cx, fy * y''' + cy]^T =>
|
||||
// [x''', y''']^T = [(u - cx) / fx, (v - cy) / fy]^T
|
||||
u = x; v = y;
|
||||
x = (x - cx)*ifx;
|
||||
y = (y - cy)*ify;
|
||||
|
||||
if( haveDistCoeffs ) {
|
||||
// compensate tilt distortion
|
||||
// s * [x''', y''', 1]^T = matTilt * [x'', y'', 1]^T =>
|
||||
// s * matTilt^{-1} * [x''', y''', 1]^T = [x'', y'', 1]^T =>
|
||||
// (invMatTilt := matTilt^{-1}, vecUntilt := invMatTilt * [x''', y''', 1]^T)
|
||||
// s * vecUntilt = [x'', y'', 1]^T =>
|
||||
// s * vecUntilt_1 = x'', s * vecUntilt_2 = y'', s * vecUntilt_3 = 1 =>
|
||||
// invProj := s = 1 / vecUntilt_3, x'' = invProj * vecUntilt_1, y'' = invProj * vecUntilt_2
|
||||
cv::Vec3d vecUntilt = invMatTilt * cv::Vec3d(x, y, 1);
|
||||
double invProj = vecUntilt(2) ? 1./vecUntilt(2) : 1;
|
||||
x0 = x = invProj * vecUntilt(0);
|
||||
y0 = y = invProj * vecUntilt(1);
|
||||
|
||||
double error = std::numeric_limits<double>::max();
|
||||
// compensate distortion iteratively
|
||||
// compensate distortion iteratively using fixed-point iteration
|
||||
|
||||
for( int j = 0; ; j++ )
|
||||
{
|
||||
@@ -437,7 +445,9 @@ static void undistortPointsInternal( const Mat& _src, Mat& _dst, const Mat& _cam
|
||||
break;
|
||||
if ((criteria.type & TermCriteria::EPS) && error < criteria.epsilon)
|
||||
break;
|
||||
// r^2 = x'^2 + y'^2
|
||||
double r2 = x*x + y*y;
|
||||
// icdist := (1 + k4 * r^2 + k5 * r^4 + k6 * r^6) / (1 + k1 * r^2 + k2 * r^4 + k3 * r^6)
|
||||
double icdist = (1 + ((k[7]*r2 + k[6])*r2 + k[5])*r2)/(1 + ((k[4]*r2 + k[1])*r2 + k[0])*r2);
|
||||
if (icdist < 0) // test: undistortPoints.regression_14583
|
||||
{
|
||||
@@ -445,8 +455,15 @@ static void undistortPointsInternal( const Mat& _src, Mat& _dst, const Mat& _cam
|
||||
y = (v - cy)*ify;
|
||||
break;
|
||||
}
|
||||
// deltaX := 2 * p1 * x' * y' + p2 * (r^2 + 2 * x'^2) + s1 * r^2 + s2 * r^4
|
||||
// deltaY := p1 * (r^2 + 2 * y'^2) + 2 * p2 * x' * y' + s3 * r^2 + s4 * r^4
|
||||
double deltaX = 2*k[2]*x*y + k[3]*(r2 + 2*x*x)+ k[8]*r2+k[9]*r2*r2;
|
||||
double deltaY = k[2]*(r2 + 2*y*y) + 2*k[3]*x*y+ k[10]*r2+k[11]*r2*r2;
|
||||
// [x'', y'']^T = [x' / icdist + deltaX, y' / icdist + deltaY]^T =>
|
||||
// [x', y']^T = [(x'' - deltaX) * icdist, (y'' - deltaY) * icdist]^T =>
|
||||
// x' = f1(x') := (x'' - deltaX) * icdist, y' = f2(y') := (y'' - deltaY) * icdist
|
||||
// Fixed-point iteration:
|
||||
// new_x' = f1(x') = (x'' - deltaX) * icdist, new_y' = f2(y') = (y'' - deltaY) * icdist
|
||||
x = (x0 - deltaX)*icdist;
|
||||
y = (y0 - deltaY)*icdist;
|
||||
|
||||
@@ -456,22 +473,33 @@ static void undistortPointsInternal( const Mat& _src, Mat& _dst, const Mat& _cam
|
||||
double xd, yd, xd0, yd0;
|
||||
Vec3d vecTilt;
|
||||
|
||||
// r^2 = x'^2 + y'^2
|
||||
r2 = x*x + y*y;
|
||||
r4 = r2*r2;
|
||||
r6 = r4*r2;
|
||||
a1 = 2*x*y;
|
||||
a2 = r2 + 2*x*x;
|
||||
a3 = r2 + 2*y*y;
|
||||
// cdist := 1 + k1 * r^2 + k2 * r^4 + k3 * r^6
|
||||
cdist = 1 + k[0]*r2 + k[1]*r4 + k[4]*r6;
|
||||
// icdist2 := 1 / (1 + k4 * r^2 + k5 * r^4 + k6 * r^6)
|
||||
icdist2 = 1./(1 + k[5]*r2 + k[6]*r4 + k[7]*r6);
|
||||
// x'' = x' * cdist * icdist2 + 2 * p1 * x' * y' + p2 * (r^2 + 2 * x'^2) + s1 * r^2 + s2 * r^4
|
||||
// y'' = y' * cdist * icdist2 + p1 * (r^2 + 2 * y'^2) + 2 * p2 * x' * y' + s3 * r^2 + s4 * r^4
|
||||
xd0 = x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4;
|
||||
yd0 = y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4;
|
||||
|
||||
// s * [x''', y''', 1]^T = matTilt * [x'', y'', 1]^T =>
|
||||
// (vecTilt := matTilt * [x'', y'', 1]^T)
|
||||
// s * [x''', y''', 1]^T = vecTilt =>
|
||||
// s * x''' = vecTilt_1, s * y''' = vecTilt_2, s = vecTilt_3 =>
|
||||
// invProj := 1 / s = 1 / vecTilt_3, x''' = invProj * vecTilt_1, y''' = invProj * vecTilt_2
|
||||
vecTilt = matTilt*cv::Vec3d(xd0, yd0, 1);
|
||||
invProj = vecTilt(2) ? 1./vecTilt(2) : 1;
|
||||
xd = invProj * vecTilt(0);
|
||||
yd = invProj * vecTilt(1);
|
||||
|
||||
// [u, v]^T = [fx * x''' + cx, fy * y''' + cy]^T
|
||||
double x_proj = xd*fx + cx;
|
||||
double y_proj = yd*fy + cy;
|
||||
|
||||
|
||||
@@ -4096,7 +4096,7 @@ namespace fisheye
|
||||
may additionally scale and shift the result by using a different matrix.
|
||||
@param new_size the new size
|
||||
|
||||
The function transforms an image to compensate radial and tangential lens distortion.
|
||||
The function transforms an image to compensate radial lens distortion.
|
||||
|
||||
The function is simply a combination of #fisheye::initUndistortRectifyMap (with unity R ) and #remap
|
||||
(with bilinear interpolation). See the former function for details of the transformation being
|
||||
|
||||
@@ -167,7 +167,7 @@ if(HAVE_CUDA)
|
||||
message(FATAL_ERROR "CUDA: OpenCV requires enabled 'cudev' module from 'opencv_contrib' repository: https://github.com/opencv/opencv_contrib")
|
||||
endif()
|
||||
if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE)
|
||||
ocv_module_include_directories(${CUDAToolkit_INCLUDE_DIRS})
|
||||
list(APPEND extra_libs CUDA::cudart${CUDA_LIB_EXT})
|
||||
endif()
|
||||
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wenum-compare -Wunused-function -Wshadow)
|
||||
endif()
|
||||
@@ -207,7 +207,11 @@ if(HAVE_OPENMP AND DEFINED OpenMP_CXX_LIBRARIES AND OpenMP_CXX_LIBRARIES)
|
||||
ocv_target_link_libraries(${the_module} LINK_PRIVATE "${OpenMP_CXX_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
ocv_add_accuracy_tests()
|
||||
set(test_libs "")
|
||||
if(HAVE_CUDA AND ENABLE_CUDA_FIRST_CLASS_LANGUAGE)
|
||||
list(APPEND test_libs CUDA::cudart${CUDA_LIB_EXT})
|
||||
endif()
|
||||
ocv_add_accuracy_tests(${test_libs})
|
||||
ocv_add_perf_tests()
|
||||
|
||||
if (TARGET opencv_test_core AND HAVE_CUDA)
|
||||
|
||||
@@ -1663,9 +1663,13 @@ elements.
|
||||
CV_EXPORTS_W bool checkRange(InputArray a, bool quiet = true, CV_OUT Point* pos = 0,
|
||||
double minVal = -DBL_MAX, double maxVal = DBL_MAX);
|
||||
|
||||
/** @brief Replaces NaNs by given number
|
||||
@param a input/output matrix (CV_32F or CV_64F type)
|
||||
@param val value to convert the NaNs
|
||||
/** @brief Replaces NaNs (Not-a-Number values) in a matrix with the specified value.
|
||||
|
||||
This function modifies the input matrix in-place.
|
||||
The input matrix must be of type `CV_32F` or `CV_64F`; other types are not supported.
|
||||
|
||||
@param a Input/output matrix (CV_32F or CV_64F type).
|
||||
@param val Value used to replace NaNs (defaults to 0).
|
||||
*/
|
||||
CV_EXPORTS_W void patchNaNs(InputOutputArray a, double val = 0);
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ if(OPENCV_DNN_CUDA AND HAVE_CUDA AND HAVE_CUBLAS AND HAVE_CUDNN)
|
||||
endforeach()
|
||||
unset(CC_LIST)
|
||||
if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE)
|
||||
list(APPEND libs ${CUDNN_LIBRARIES} CUDA::cublas${CUDA_LIB_EXT})
|
||||
list(APPEND libs CUDA::cudart${CUDA_LIB_EXT} ${CUDNN_LIBRARIES} CUDA::cublas${CUDA_LIB_EXT})
|
||||
if(NOT CUDA_VERSION VERSION_LESS 10.1)
|
||||
list(APPEND libs CUDA::cublasLt${CUDA_LIB_EXT})
|
||||
endif()
|
||||
|
||||
@@ -2047,6 +2047,85 @@ static inline void convBlock4x4(int np, const float* a, const float* b, float* c
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
template<int RowBase, int NR = 24>
|
||||
static inline void conv2xNR(
|
||||
int np,
|
||||
const float* a,
|
||||
const float* b,
|
||||
float* c, int ldc,
|
||||
bool init_c)
|
||||
{
|
||||
static_assert(NR % 8 == 0, "NR must be a multiple of 8");
|
||||
float* d0 = c + (RowBase + 0) * ldc;
|
||||
float* d1 = c + (RowBase + 1) * ldc;
|
||||
|
||||
// p = 0
|
||||
{
|
||||
const float* bp = b;
|
||||
const float a0 = a[RowBase + 0];
|
||||
const float a1 = a[RowBase + 1];
|
||||
|
||||
if (init_c)
|
||||
{
|
||||
for (int j = 0; j < NR; j += 8)
|
||||
{
|
||||
const float b0=bp[j+0], b1=bp[j+1], b2=bp[j+2], b3=bp[j+3];
|
||||
const float b4=bp[j+4], b5=bp[j+5], b6=bp[j+6], b7=bp[j+7];
|
||||
d0[j+0] = b0*a0; d0[j+1] = b1*a0; d0[j+2] = b2*a0; d0[j+3] = b3*a0;
|
||||
d0[j+4] = b4*a0; d0[j+5] = b5*a0; d0[j+6] = b6*a0; d0[j+7] = b7*a0;
|
||||
d1[j+0] = b0*a1; d1[j+1] = b1*a1; d1[j+2] = b2*a1; d1[j+3] = b3*a1;
|
||||
d1[j+4] = b4*a1; d1[j+5] = b5*a1; d1[j+6] = b6*a1; d1[j+7] = b7*a1;
|
||||
}
|
||||
} else
|
||||
{
|
||||
for (int j = 0; j < NR; j += 8)
|
||||
{
|
||||
const float b0=bp[j+0], b1=bp[j+1], b2=bp[j+2], b3=bp[j+3];
|
||||
const float b4=bp[j+4], b5=bp[j+5], b6=bp[j+6], b7=bp[j+7];
|
||||
d0[j+0] += b0*a0; d0[j+1] += b1*a0; d0[j+2] += b2*a0; d0[j+3] += b3*a0;
|
||||
d0[j+4] += b4*a0; d0[j+5] += b5*a0; d0[j+6] += b6*a0; d0[j+7] += b7*a0;
|
||||
d1[j+0] += b0*a1; d1[j+1] += b1*a1; d1[j+2] += b2*a1; d1[j+3] += b3*a1;
|
||||
d1[j+4] += b4*a1; d1[j+5] += b5*a1; d1[j+6] += b6*a1; d1[j+7] += b7*a1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// p = 1..np-1
|
||||
for (int p = 1; p < np; ++p)
|
||||
{
|
||||
const float* bp = b + p * NR;
|
||||
const int aoff = p * 4 + RowBase;
|
||||
const float a0 = a[aoff + 0];
|
||||
const float a1 = a[aoff + 1];
|
||||
|
||||
for (int j = 0; j < NR; j += 8)
|
||||
{
|
||||
const float b0=bp[j+0], b1=bp[j+1], b2=bp[j+2], b3=bp[j+3];
|
||||
const float b4=bp[j+4], b5=bp[j+5], b6=bp[j+6], b7=bp[j+7];
|
||||
d0[j+0] += b0*a0; d0[j+1] += b1*a0; d0[j+2] += b2*a0; d0[j+3] += b3*a0;
|
||||
d0[j+4] += b4*a0; d0[j+5] += b5*a0; d0[j+6] += b6*a0; d0[j+7] += b7*a0;
|
||||
d1[j+0] += b0*a1; d1[j+1] += b1*a1; d1[j+2] += b2*a1; d1[j+3] += b3*a1;
|
||||
d1[j+4] += b4*a1; d1[j+5] += b5*a1; d1[j+6] += b6*a1; d1[j+7] += b7*a1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MR == 4, outLen == 24, scalar (no SIMD128)
|
||||
static inline void convBlockNoSIMD4x24(
|
||||
int np,
|
||||
const float* a,
|
||||
const float* b,
|
||||
float* c, int ldc,
|
||||
bool init_c,
|
||||
int convNR)
|
||||
{
|
||||
CV_Assert(np > 0 && convNR == 24);
|
||||
conv2xNR<0, 24>(np, a, b, c, ldc, init_c); // rows 0 & 1
|
||||
conv2xNR<2, 24>(np, a, b, c, ldc, init_c); // rows 2 & 3
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void convBlockNoSIMD(int np, const float* a, const float* b, float* c, int ldc, bool init_c, const int outLen,
|
||||
const int convMR, const int convNR)
|
||||
{
|
||||
@@ -2106,8 +2185,17 @@ void convBlock_F32(int np, const float* a, const float* b, float* c, int ldc, bo
|
||||
return;
|
||||
}
|
||||
convBlockNoSIMD(np, a, b, c, ldc, init_c, outLen, convMR, convNR);
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
CV_Assert(convMR == 4);
|
||||
if (outLen == 24 && convNR == 24)
|
||||
{
|
||||
convBlockNoSIMD4x24(np, a, b, c, ldc, init_c, convNR);
|
||||
return;
|
||||
}
|
||||
convBlockNoSIMD(np, a, b, c, ldc, init_c, outLen, convMR, convNR);
|
||||
#else
|
||||
convBlockNoSIMD(np, a, b, c, ldc, init_c, outLen, convMR, convNR);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -52,11 +52,18 @@
|
||||
|
||||
#include "grfmt_tiff.hpp"
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <cstdarg>
|
||||
|
||||
#include "tiff.h"
|
||||
#include "tiffio.h"
|
||||
#include "tiffvers.h" // For TIFFLIB_VERSION
|
||||
|
||||
#ifdef TIFFLIB_AT_LEAST
|
||||
#if TIFFLIB_AT_LEAST(4, 5, 0)
|
||||
#define OCV_HAVE_TIFF_OPEN_OPTIONS
|
||||
#endif
|
||||
#endif
|
||||
namespace cv
|
||||
{
|
||||
|
||||
@@ -79,30 +86,116 @@ static void cv_tiffCloseHandle(void* handle)
|
||||
TIFFClose((TIFF*)handle);
|
||||
}
|
||||
|
||||
static std::string vformat(const char* fmt, va_list ap)
|
||||
{
|
||||
if (!fmt)
|
||||
return {};
|
||||
va_list ap_copy;
|
||||
va_copy(ap_copy, ap);
|
||||
const int len = std::vsnprintf(nullptr, 0, fmt, ap_copy);
|
||||
va_end(ap_copy);
|
||||
if (len < 0)
|
||||
return fmt;
|
||||
|
||||
std::string buf(static_cast<size_t>(len) + 1, '\0');
|
||||
std::vsnprintf(&buf[0], buf.size(), fmt, ap);
|
||||
buf.pop_back();
|
||||
return buf;
|
||||
}
|
||||
|
||||
static std::string formatTiffMessage(const char* module, const char* fmt, va_list ap)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (module && module[0] != '\0')
|
||||
ss << module << ": ";
|
||||
ss << cv::vformat(fmt, ap);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static int TIFF_Error(TIFF *, void *, const char* module, const char* fmt, va_list ap)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, formatTiffMessage(module, fmt, ap));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int TIFF_Warning(TIFF *, void *, const char* module, const char* fmt, va_list ap)
|
||||
{
|
||||
CV_LOG_WARNING(NULL, formatTiffMessage(module, fmt, ap));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef OCV_HAVE_TIFF_OPEN_OPTIONS
|
||||
static TIFFOpenOptions* cv_tiffCreateOptions()
|
||||
{
|
||||
auto opts = TIFFOpenOptionsAlloc();
|
||||
TIFFOpenOptionsSetErrorHandlerExtR(opts, &TIFF_Error, nullptr);
|
||||
TIFFOpenOptionsSetWarningHandlerExtR(opts, &TIFF_Warning, nullptr);
|
||||
#if TIFFLIB_AT_LEAST(4, 7, 1)
|
||||
TIFFOpenOptionsSetWarnAboutUnknownTags(opts, 1);
|
||||
#endif
|
||||
return opts;
|
||||
}
|
||||
#endif
|
||||
|
||||
static TIFF* cv_tiffOpen(const char* filename, const char* mode)
|
||||
{
|
||||
#ifdef OCV_HAVE_TIFF_OPEN_OPTIONS
|
||||
auto opts = cv_tiffCreateOptions();
|
||||
auto tiff = TIFFOpenExt(filename, mode, opts);
|
||||
TIFFOpenOptionsFree(opts);
|
||||
return tiff;
|
||||
#else
|
||||
return TIFFOpen(filename, mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
static TIFF* cv_tiffClientOpen(const char* name, const char* mode, thandle_t clientdata,
|
||||
TIFFReadWriteProc readproc, TIFFReadWriteProc writeproc,
|
||||
TIFFSeekProc seekproc, TIFFCloseProc closeproc,
|
||||
TIFFSizeProc sizeproc, TIFFMapFileProc mapproc,
|
||||
TIFFUnmapFileProc unmapproc)
|
||||
{
|
||||
#ifdef OCV_HAVE_TIFF_OPEN_OPTIONS
|
||||
auto opts = cv_tiffCreateOptions();
|
||||
auto tiff = TIFFClientOpenExt(name, mode, clientdata, readproc, writeproc,
|
||||
seekproc, closeproc, sizeproc, mapproc, unmapproc, opts);
|
||||
TIFFOpenOptionsFree(opts);
|
||||
return tiff;
|
||||
#else
|
||||
return TIFFClientOpen(name, mode, clientdata, readproc, writeproc,
|
||||
seekproc, closeproc, sizeproc, mapproc, unmapproc);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef OCV_HAVE_TIFF_OPEN_OPTIONS
|
||||
|
||||
static void cv_tiffErrorHandler(const char* module, const char* fmt, va_list ap)
|
||||
{
|
||||
if (cv::utils::logging::getLogLevel() < cv::utils::logging::LOG_LEVEL_DEBUG)
|
||||
return;
|
||||
// TODO cv::vformat() with va_list parameter
|
||||
fprintf(stderr, "OpenCV TIFF: ");
|
||||
if (module != NULL)
|
||||
fprintf(stderr, "%s: ", module);
|
||||
fprintf(stderr, "Warning, ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, ".\n");
|
||||
(void) TIFF_Error(nullptr, nullptr, module, fmt, ap);
|
||||
}
|
||||
|
||||
static void cv_tiffWarningHandler(const char* module, const char* fmt, va_list ap)
|
||||
{
|
||||
(void) TIFF_Warning(nullptr, nullptr, module, fmt, ap);
|
||||
}
|
||||
|
||||
static bool cv_tiffSetErrorHandler_()
|
||||
{
|
||||
TIFFSetErrorHandler(cv_tiffErrorHandler);
|
||||
TIFFSetWarningHandler(cv_tiffErrorHandler);
|
||||
TIFFSetWarningHandler(cv_tiffWarningHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool cv_tiffSetErrorHandler()
|
||||
{
|
||||
#ifndef OCV_HAVE_TIFF_OPEN_OPTIONS
|
||||
static bool v = cv_tiffSetErrorHandler_();
|
||||
return v;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char fmtSignTiffII[] = "II\x2a\x00";
|
||||
@@ -242,7 +335,7 @@ bool TiffDecoder::readHeader()
|
||||
{
|
||||
m_buf_pos = 0;
|
||||
TiffDecoderBufHelper* buf_helper = new TiffDecoderBufHelper(this->m_buf, this->m_buf_pos);
|
||||
tif = TIFFClientOpen( "", "r", reinterpret_cast<thandle_t>(buf_helper), &TiffDecoderBufHelper::read,
|
||||
tif = cv_tiffClientOpen( "", "r", reinterpret_cast<thandle_t>(buf_helper), &TiffDecoderBufHelper::read,
|
||||
&TiffDecoderBufHelper::write, &TiffDecoderBufHelper::seek,
|
||||
&TiffDecoderBufHelper::close, &TiffDecoderBufHelper::size,
|
||||
&TiffDecoderBufHelper::map, /*unmap=*/0 );
|
||||
@@ -251,7 +344,7 @@ bool TiffDecoder::readHeader()
|
||||
}
|
||||
else
|
||||
{
|
||||
tif = TIFFOpen(m_filename.c_str(), "r");
|
||||
tif = cv_tiffOpen(m_filename.c_str(), "r");
|
||||
}
|
||||
if (tif)
|
||||
m_tif.reset(tif, cv_tiffCloseHandle);
|
||||
@@ -1124,7 +1217,7 @@ public:
|
||||
{
|
||||
// do NOT put "wb" as the mode, because the b means "big endian" mode, not "binary" mode.
|
||||
// http://www.simplesystems.org/libtiff/functions/TIFFOpen.html
|
||||
return TIFFClientOpen( "", "w", reinterpret_cast<thandle_t>(this), &TiffEncoderBufHelper::read,
|
||||
return cv_tiffClientOpen( "", "w", reinterpret_cast<thandle_t>(this), &TiffEncoderBufHelper::read,
|
||||
&TiffEncoderBufHelper::write, &TiffEncoderBufHelper::seek,
|
||||
&TiffEncoderBufHelper::close, &TiffEncoderBufHelper::size,
|
||||
/*map=*/0, /*unmap=*/0 );
|
||||
@@ -1221,7 +1314,7 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
|
||||
}
|
||||
else
|
||||
{
|
||||
tif = TIFFOpen(m_filename.c_str(), "w");
|
||||
tif = cv_tiffOpen(m_filename.c_str(), "w");
|
||||
}
|
||||
if (!tif)
|
||||
{
|
||||
|
||||
@@ -1285,6 +1285,17 @@ TEST(Imgcodecs_Tiff, read_bigtiff_images)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Tiff, read_junk) {
|
||||
// Test exercises the tiff error handler integration.
|
||||
// Error messages can be seen with OPENCV_LOG_LEVEL=DEBUG
|
||||
const char junk[] = "II\x2a\x00\x08\x00\x00\x00\x00\x00\x00\x00";
|
||||
cv::Mat junkInputArray(1, sizeof(junk) - 1, CV_8UC1, (void*)junk);
|
||||
|
||||
cv::Mat img;
|
||||
ASSERT_NO_THROW(img = cv::imdecode(junkInputArray, IMREAD_UNCHANGED));
|
||||
ASSERT_TRUE(img.empty());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -849,6 +849,10 @@ class JSWrapperGenerator(object):
|
||||
# Register the smart pointer
|
||||
base_class_name = variant.rettype
|
||||
base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
|
||||
if base_class_name not in self.classes:
|
||||
new_base_class_name = '%s_%s' % (ns_id, base_class_name)
|
||||
print(base_class_name, ' not found in classes for registering smart pointer using ', new_base_class_name, 'instead')
|
||||
base_class_name = new_base_class_name
|
||||
self.classes[base_class_name].has_smart_ptr = True
|
||||
|
||||
# Adds the external constructor
|
||||
|
||||
@@ -6,6 +6,6 @@ endif()
|
||||
|
||||
ocv_define_module(photo opencv_imgproc OPTIONAL opencv_cudaarithm opencv_cudaimgproc WRAP java objc python js)
|
||||
|
||||
if(HAVE_CUDA AND ENABLE_CUDA_FIRST_CLASS_LANGUAGE AND HAVE_OPENCV_CUDAARITHM AND HAVE_OPENCV_CUDAIMGPROC)
|
||||
ocv_target_link_libraries(${the_module} PUBLIC "CUDA::cudart${CUDA_LIB_EXT}")
|
||||
if(HAVE_CUDA AND ENABLE_CUDA_FIRST_CLASS_LANGUAGE AND HAVE_opencv_cudaarithm AND HAVE_opencv_cudaimgproc)
|
||||
ocv_target_link_libraries(${the_module} PRIVATE CUDA::cudart${CUDA_LIB_EXT})
|
||||
endif()
|
||||
|
||||
@@ -46,6 +46,12 @@
|
||||
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
|
||||
#if !defined (HAVE_CUDA) || !defined(HAVE_OPENCV_CUDAARITHM) || !defined(HAVE_OPENCV_CUDAIMGPROC)
|
||||
#include "opencv2/core/private/cuda_stubs.hpp"
|
||||
#else
|
||||
#include "opencv2/core/private.cuda.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENCV_CUDAARITHM
|
||||
# include "opencv2/cudaarithm.hpp"
|
||||
#endif
|
||||
@@ -59,15 +65,12 @@ using namespace cv::cuda;
|
||||
|
||||
#if !defined (HAVE_CUDA) || !defined(HAVE_OPENCV_CUDAARITHM) || !defined(HAVE_OPENCV_CUDAIMGPROC)
|
||||
|
||||
#include "opencv2/core/private/cuda_stubs.hpp"
|
||||
void cv::cuda::nonLocalMeans(InputArray, OutputArray, float, int, int, int, Stream&) { throw_no_cuda(); }
|
||||
void cv::cuda::fastNlMeansDenoising(InputArray, OutputArray, float, int, int, Stream&) { throw_no_cuda(); }
|
||||
void cv::cuda::fastNlMeansDenoisingColored(InputArray, OutputArray, float, float, int, int, Stream&) { throw_no_cuda(); }
|
||||
|
||||
#else
|
||||
|
||||
#include "opencv2/core/private.cuda.hpp"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//// Non Local Means Denosing (brute force)
|
||||
|
||||
|
||||
@@ -277,17 +277,43 @@ enum
|
||||
MOTION_HOMOGRAPHY = 3
|
||||
};
|
||||
|
||||
/** @brief Computes the Enhanced Correlation Coefficient value between two images @cite EP08 .
|
||||
/**
|
||||
@brief Computes the Enhanced Correlation Coefficient (ECC) value between two images
|
||||
|
||||
@param templateImage single-channel template image; CV_8U or CV_32F array.
|
||||
@param inputImage single-channel input image to be warped to provide an image similar to
|
||||
templateImage, same type as templateImage.
|
||||
@param inputMask An optional mask to indicate valid values of inputImage.
|
||||
The Enhanced Correlation Coefficient (ECC) is a normalized measure of similarity between two images @cite EP08.
|
||||
The result lies in the range [-1, 1], where 1 corresponds to perfect similarity (modulo affine shift and scale),
|
||||
0 indicates no correlation, and -1 indicates perfect negative correlation.
|
||||
|
||||
@sa
|
||||
findTransformECC
|
||||
*/
|
||||
For single-channel images, the ECC is defined as:
|
||||
|
||||
\f[
|
||||
\mathrm{ECC}(I, T) = \frac{\sum_{x} (I(x) - \mu_I)(T(x) - \mu_T)}
|
||||
{\sqrt{\sum_{x} (I(x) - \mu_I)^2} \cdot \sqrt{\sum_{x} (T(x) - \mu_T)^2}}
|
||||
\f]
|
||||
|
||||
For multi-channel images (e.g., 3-channel RGB), the formula generalizes to:
|
||||
|
||||
\f[
|
||||
\mathrm{ECC}(I, T) =
|
||||
\frac{\sum_{x} \sum_{c=1}^{C} (I_c(x) - \mu_{I_c})(T_c(x) - \mu_{T_c})}
|
||||
{\sqrt{\sum_{x} \sum_{c=1}^{C} (I_c(x) - \mu_{I_c})^2} \cdot
|
||||
\sqrt{\sum_{x} \sum_{c=1}^{C} (T_c(x) - \mu_{T_c})^2}}
|
||||
\f]
|
||||
|
||||
Where:
|
||||
- \f$I_c(x), T_c(x)\f$ are the values of channel \f$c\f$ at spatial location \f$x\f$,
|
||||
- \f$\mu_{I_c}, \mu_{T_c}\f$ are the mean values of channel \f$c\f$ over the masked region (if provided),
|
||||
- \f$C\f$ is the number of channels (only 1 and 3 are currently supported),
|
||||
- The sums run over all pixels \f$x\f$ in the image domain (optionally restricted by mask).
|
||||
|
||||
@param templateImage Input template image; must have either 1 or 3 channels and be of type CV_8U, CV_16U, CV_32F, or CV_64F.
|
||||
@param inputImage Input image to be compared with the template; must have the same type and number of channels as templateImage.
|
||||
@param inputMask Optional single-channel mask to specify the valid region of interest in inputImage and templateImage.
|
||||
|
||||
@return The ECC similarity coefficient in the range [-1, 1].
|
||||
|
||||
@sa findTransformECC
|
||||
*/
|
||||
CV_EXPORTS_W double computeECC(InputArray templateImage, InputArray inputImage, InputArray inputMask = noArray());
|
||||
|
||||
/** @example samples/cpp/image_alignment.cpp
|
||||
@@ -296,8 +322,8 @@ An example using the image alignment ECC algorithm
|
||||
|
||||
/** @brief Finds the geometric transform (warp) between two images in terms of the ECC criterion @cite EP08 .
|
||||
|
||||
@param templateImage single-channel template image; CV_8U or CV_32F array.
|
||||
@param inputImage single-channel input image which should be warped with the final warpMatrix in
|
||||
@param templateImage 1 or 3 channel template image; CV_8U, CV_16U, CV_32F, CV_64F type.
|
||||
@param inputImage input image which should be warped with the final warpMatrix in
|
||||
order to provide an image similar to templateImage, same type as templateImage.
|
||||
@param warpMatrix floating-point \f$2\times 3\f$ or \f$3\times 3\f$ mapping matrix (warp).
|
||||
@param motionType parameter, specifying the type of motion:
|
||||
@@ -314,7 +340,7 @@ order to provide an image similar to templateImage, same type as templateImage.
|
||||
criteria.epsilon defines the threshold of the increment in the correlation coefficient between two
|
||||
iterations (a negative criteria.epsilon makes criteria.maxcount the only termination criterion).
|
||||
Default values are shown in the declaration above.
|
||||
@param inputMask An optional mask to indicate valid values of inputImage.
|
||||
@param inputMask An optional single channel mask to indicate valid values of inputImage.
|
||||
@param gaussFiltSize An optional value indicating size of gaussian blur filter; (DEFAULT: 5)
|
||||
|
||||
The function estimates the optimum transformation (warpMatrix) with respect to ECC criterion
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
namespace opencv_test {
|
||||
using namespace perf;
|
||||
|
||||
CV_ENUM(MotionType, MOTION_TRANSLATION, MOTION_EUCLIDEAN, MOTION_AFFINE, MOTION_HOMOGRAPHY)
|
||||
CV_ENUM(ReadFlag, IMREAD_GRAYSCALE, IMREAD_COLOR)
|
||||
|
||||
typedef tuple<MotionType> MotionType_t;
|
||||
typedef perf::TestBaseWithParam<MotionType_t> TransformationType;
|
||||
|
||||
|
||||
PERF_TEST_P(TransformationType, findTransformECC, /*testing::ValuesIn(MotionType::all())*/
|
||||
testing::Values((int) MOTION_TRANSLATION, (int) MOTION_EUCLIDEAN,
|
||||
(int) MOTION_AFFINE, (int) MOTION_HOMOGRAPHY)
|
||||
)
|
||||
{
|
||||
Mat img = imread(getDataPath("cv/shared/fruits_ecc.png"),0);
|
||||
Mat templateImage;
|
||||
typedef std::tuple<MotionType, ReadFlag> TestParams;
|
||||
typedef perf::TestBaseWithParam<TestParams> ECCPerfTest;
|
||||
|
||||
PERF_TEST_P(ECCPerfTest, findTransformECC,
|
||||
testing::Combine(testing::Values(MOTION_TRANSLATION, MOTION_EUCLIDEAN, MOTION_AFFINE, MOTION_HOMOGRAPHY),
|
||||
testing::Values(IMREAD_GRAYSCALE, IMREAD_COLOR))) {
|
||||
int transform_type = get<0>(GetParam());
|
||||
int readFlag = get<1>(GetParam());
|
||||
|
||||
Mat img = imread(getDataPath("cv/shared/fruits_ecc.png"), readFlag);
|
||||
Mat templateImage;
|
||||
|
||||
Mat warpMat;
|
||||
Mat warpGround;
|
||||
@@ -26,46 +24,38 @@ PERF_TEST_P(TransformationType, findTransformECC, /*testing::ValuesIn(MotionType
|
||||
double angle;
|
||||
switch (transform_type) {
|
||||
case MOTION_TRANSLATION:
|
||||
warpGround = (Mat_<float>(2,3) << 1.f, 0.f, 7.234f,
|
||||
0.f, 1.f, 11.839f);
|
||||
warpGround = (Mat_<float>(2, 3) << 1.f, 0.f, 7.234f, 0.f, 1.f, 11.839f);
|
||||
|
||||
warpAffine(img, templateImage, warpGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
warpAffine(img, templateImage, warpGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
angle = CV_PI/30;
|
||||
angle = CV_PI / 30;
|
||||
|
||||
warpGround = (Mat_<float>(2,3) << (float)cos(angle), (float)-sin(angle), 12.123f,
|
||||
(float)sin(angle), (float)cos(angle), 14.789f);
|
||||
warpAffine(img, templateImage, warpGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
warpGround = (Mat_<float>(2, 3) << (float)cos(angle), (float)-sin(angle), 12.123f, (float)sin(angle),
|
||||
(float)cos(angle), 14.789f);
|
||||
warpAffine(img, templateImage, warpGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_AFFINE:
|
||||
warpGround = (Mat_<float>(2,3) << 0.98f, 0.03f, 15.523f,
|
||||
-0.02f, 0.95f, 10.456f);
|
||||
warpAffine(img, templateImage, warpGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
warpGround = (Mat_<float>(2, 3) << 0.98f, 0.03f, 15.523f, -0.02f, 0.95f, 10.456f);
|
||||
warpAffine(img, templateImage, warpGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
warpGround = (Mat_<float>(3,3) << 0.98f, 0.03f, 15.523f,
|
||||
-0.02f, 0.95f, 10.456f,
|
||||
0.0002f, 0.0003f, 1.f);
|
||||
warpPerspective(img, templateImage, warpGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
warpGround = (Mat_<float>(3, 3) << 0.98f, 0.03f, 15.523f, -0.02f, 0.95f, 10.456f, 0.0002f, 0.0003f, 1.f);
|
||||
warpPerspective(img, templateImage, warpGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
}
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
if (transform_type<3)
|
||||
warpMat = Mat::eye(2,3, CV_32F);
|
||||
TEST_CYCLE() {
|
||||
if (transform_type < 3)
|
||||
warpMat = Mat::eye(2, 3, CV_32F);
|
||||
else
|
||||
warpMat = Mat::eye(3,3, CV_32F);
|
||||
warpMat = Mat::eye(3, 3, CV_32F);
|
||||
|
||||
findTransformECC(templateImage, img, warpMat, transform_type,
|
||||
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 5, -1));
|
||||
TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 5, -1));
|
||||
}
|
||||
|
||||
SANITY_CHECK(warpMat, 3e-3);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opencv_test
|
||||
|
||||
@@ -41,30 +41,24 @@
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
|
||||
/****************************************************************************************\
|
||||
* Image Alignment (ECC algorithm) *
|
||||
\****************************************************************************************/
|
||||
|
||||
using namespace cv;
|
||||
|
||||
static void image_jacobian_homo_ECC(const Mat& src1, const Mat& src2,
|
||||
const Mat& src3, const Mat& src4,
|
||||
const Mat& src5, Mat& dst)
|
||||
{
|
||||
|
||||
|
||||
static void image_jacobian_homo_ECC(const Mat& src1, const Mat& src2, const Mat& src3, const Mat& src4, const Mat& src5,
|
||||
Mat& dst) {
|
||||
CV_Assert(src1.size() == src2.size());
|
||||
CV_Assert(src1.size() == src3.size());
|
||||
CV_Assert(src1.size() == src4.size());
|
||||
|
||||
CV_Assert( src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (src1.cols*8));
|
||||
CV_Assert(dst.type() == CV_32FC1);
|
||||
CV_Assert(src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (src1.cols * 8));
|
||||
CV_Assert(dst.type() == CV_MAKETYPE(CV_32F, src1.channels()));
|
||||
|
||||
CV_Assert(src5.isContinuous());
|
||||
|
||||
|
||||
const float* hptr = src5.ptr<float>(0);
|
||||
|
||||
const float h0_ = hptr[0];
|
||||
@@ -78,19 +72,23 @@ static void image_jacobian_homo_ECC(const Mat& src1, const Mat& src2,
|
||||
|
||||
const int w = src1.cols;
|
||||
|
||||
// create denominator for all points as a block
|
||||
Mat den_;
|
||||
addWeighted(src3, h2_, src4, h5_, 1.0, den_);
|
||||
|
||||
//create denominator for all points as a block
|
||||
Mat den_ = src3*h2_ + src4*h5_ + 1.0;//check the time of this! otherwise use addWeighted
|
||||
// create projected points
|
||||
Mat hatX_, hatY_;
|
||||
addWeighted(src3, h0_, src4, h3_, 0.0, hatX_);
|
||||
hatX_ += h6_;
|
||||
|
||||
//create projected points
|
||||
Mat hatX_ = -src3*h0_ - src4*h3_ - h6_;
|
||||
divide(hatX_, den_, hatX_);
|
||||
Mat hatY_ = -src3*h1_ - src4*h4_ - h7_;
|
||||
divide(hatY_, den_, hatY_);
|
||||
addWeighted(src3, h1_, src4, h4_, 0.0, hatY_);
|
||||
hatY_ += h7_;
|
||||
|
||||
divide(-hatY_, den_, hatY_);
|
||||
divide(-hatX_, den_, hatX_);
|
||||
|
||||
//instead of dividing each block with den,
|
||||
//just pre-divide the block of gradients (it's more efficient)
|
||||
// instead of dividing each block with den,
|
||||
// just pre-divide the block of gradients (it's more efficient)
|
||||
|
||||
Mat src1Divided_;
|
||||
Mat src2Divided_;
|
||||
@@ -98,114 +96,98 @@ static void image_jacobian_homo_ECC(const Mat& src1, const Mat& src2,
|
||||
divide(src1, den_, src1Divided_);
|
||||
divide(src2, den_, src2Divided_);
|
||||
|
||||
// compute Jacobian blocks (8 blocks)
|
||||
|
||||
//compute Jacobian blocks (8 blocks)
|
||||
dst.colRange(0, w) = src1Divided_.mul(src3); // 1
|
||||
|
||||
dst.colRange(0, w) = src1Divided_.mul(src3);//1
|
||||
dst.colRange(w, 2 * w) = src2Divided_.mul(src3); // 2
|
||||
|
||||
dst.colRange(w,2*w) = src2Divided_.mul(src3);//2
|
||||
|
||||
Mat temp_ = (hatX_.mul(src1Divided_)+hatY_.mul(src2Divided_));
|
||||
dst.colRange(2*w,3*w) = temp_.mul(src3);//3
|
||||
Mat temp_ = (hatX_.mul(src1Divided_) + hatY_.mul(src2Divided_));
|
||||
dst.colRange(2 * w, 3 * w) = temp_.mul(src3); // 3
|
||||
|
||||
hatX_.release();
|
||||
hatY_.release();
|
||||
|
||||
dst.colRange(3*w, 4*w) = src1Divided_.mul(src4);//4
|
||||
dst.colRange(3 * w, 4 * w) = src1Divided_.mul(src4); // 4
|
||||
|
||||
dst.colRange(4*w, 5*w) = src2Divided_.mul(src4);//5
|
||||
dst.colRange(4 * w, 5 * w) = src2Divided_.mul(src4); // 5
|
||||
|
||||
dst.colRange(5*w, 6*w) = temp_.mul(src4);//6
|
||||
dst.colRange(5 * w, 6 * w) = temp_.mul(src4); // 6
|
||||
|
||||
src1Divided_.copyTo(dst.colRange(6*w, 7*w));//7
|
||||
src1Divided_.copyTo(dst.colRange(6 * w, 7 * w)); // 7
|
||||
|
||||
src2Divided_.copyTo(dst.colRange(7*w, 8*w));//8
|
||||
src2Divided_.copyTo(dst.colRange(7 * w, 8 * w)); // 8
|
||||
}
|
||||
|
||||
static void image_jacobian_euclidean_ECC(const Mat& src1, const Mat& src2,
|
||||
const Mat& src3, const Mat& src4,
|
||||
const Mat& src5, Mat& dst)
|
||||
{
|
||||
|
||||
CV_Assert( src1.size()==src2.size());
|
||||
CV_Assert( src1.size()==src3.size());
|
||||
CV_Assert( src1.size()==src4.size());
|
||||
|
||||
CV_Assert( src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (src1.cols*3));
|
||||
CV_Assert(dst.type() == CV_32FC1);
|
||||
|
||||
CV_Assert(src5.isContinuous());
|
||||
|
||||
const float* hptr = src5.ptr<float>(0);
|
||||
|
||||
const float h0 = hptr[0];//cos(theta)
|
||||
const float h1 = hptr[3];//sin(theta)
|
||||
|
||||
const int w = src1.cols;
|
||||
|
||||
//create -sin(theta)*X -cos(theta)*Y for all points as a block -> hatX
|
||||
Mat hatX = -(src3*h1) - (src4*h0);
|
||||
|
||||
//create cos(theta)*X -sin(theta)*Y for all points as a block -> hatY
|
||||
Mat hatY = (src3*h0) - (src4*h1);
|
||||
|
||||
|
||||
//compute Jacobian blocks (3 blocks)
|
||||
dst.colRange(0, w) = (src1.mul(hatX))+(src2.mul(hatY));//1
|
||||
|
||||
src1.copyTo(dst.colRange(w, 2*w));//2
|
||||
src2.copyTo(dst.colRange(2*w, 3*w));//3
|
||||
}
|
||||
|
||||
|
||||
static void image_jacobian_affine_ECC(const Mat& src1, const Mat& src2,
|
||||
const Mat& src3, const Mat& src4,
|
||||
Mat& dst)
|
||||
{
|
||||
|
||||
static void image_jacobian_euclidean_ECC(const Mat& src1, const Mat& src2, const Mat& src3, const Mat& src4,
|
||||
const Mat& src5, Mat& dst) {
|
||||
CV_Assert(src1.size() == src2.size());
|
||||
CV_Assert(src1.size() == src3.size());
|
||||
CV_Assert(src1.size() == src4.size());
|
||||
|
||||
CV_Assert(src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (6*src1.cols));
|
||||
CV_Assert(dst.cols == (src1.cols * 3));
|
||||
CV_Assert(dst.type() == CV_MAKETYPE(CV_32F, src1.channels()));
|
||||
|
||||
CV_Assert(dst.type() == CV_32FC1);
|
||||
CV_Assert(src5.isContinuous());
|
||||
|
||||
const float* hptr = src5.ptr<float>(0);
|
||||
|
||||
const float h0 = hptr[0]; // cos(theta)
|
||||
const float h1 = hptr[3]; // sin(theta)
|
||||
|
||||
const int w = src1.cols;
|
||||
|
||||
//compute Jacobian blocks (6 blocks)
|
||||
// create -sin(theta)*X -cos(theta)*Y for all points as a block -> hatX
|
||||
Mat hatX = -(src3 * h1) - (src4 * h0);
|
||||
|
||||
dst.colRange(0,w) = src1.mul(src3);//1
|
||||
dst.colRange(w,2*w) = src2.mul(src3);//2
|
||||
dst.colRange(2*w,3*w) = src1.mul(src4);//3
|
||||
dst.colRange(3*w,4*w) = src2.mul(src4);//4
|
||||
src1.copyTo(dst.colRange(4*w,5*w));//5
|
||||
src2.copyTo(dst.colRange(5*w,6*w));//6
|
||||
// create cos(theta)*X -sin(theta)*Y for all points as a block -> hatY
|
||||
Mat hatY = (src3 * h0) - (src4 * h1);
|
||||
|
||||
// compute Jacobian blocks (3 blocks)
|
||||
dst.colRange(0, w) = (src1.mul(hatX)) + (src2.mul(hatY)); // 1
|
||||
|
||||
src1.copyTo(dst.colRange(w, 2 * w)); // 2
|
||||
src2.copyTo(dst.colRange(2 * w, 3 * w)); // 3
|
||||
}
|
||||
|
||||
static void image_jacobian_affine_ECC(const Mat& src1, const Mat& src2, const Mat& src3, const Mat& src4, Mat& dst) {
|
||||
CV_Assert(src1.size() == src2.size());
|
||||
CV_Assert(src1.size() == src3.size());
|
||||
CV_Assert(src1.size() == src4.size());
|
||||
|
||||
static void image_jacobian_translation_ECC(const Mat& src1, const Mat& src2, Mat& dst)
|
||||
{
|
||||
CV_Assert(src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (6 * src1.cols));
|
||||
|
||||
CV_Assert( src1.size()==src2.size());
|
||||
|
||||
CV_Assert( src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (src1.cols*2));
|
||||
CV_Assert(dst.type() == CV_32FC1);
|
||||
CV_Assert(dst.type() == CV_MAKETYPE(CV_32F, src1.channels()));
|
||||
|
||||
const int w = src1.cols;
|
||||
|
||||
//compute Jacobian blocks (2 blocks)
|
||||
// compute Jacobian blocks (6 blocks)
|
||||
|
||||
dst.colRange(0, w) = src1.mul(src3); // 1
|
||||
dst.colRange(w, 2 * w) = src2.mul(src3); // 2
|
||||
dst.colRange(2 * w, 3 * w) = src1.mul(src4); // 3
|
||||
dst.colRange(3 * w, 4 * w) = src2.mul(src4); // 4
|
||||
src1.copyTo(dst.colRange(4 * w, 5 * w)); // 5
|
||||
src2.copyTo(dst.colRange(5 * w, 6 * w)); // 6
|
||||
}
|
||||
|
||||
static void image_jacobian_translation_ECC(const Mat& src1, const Mat& src2, Mat& dst) {
|
||||
CV_Assert(src1.size() == src2.size());
|
||||
|
||||
CV_Assert(src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (src1.cols * 2));
|
||||
CV_Assert(dst.type() == CV_MAKETYPE(CV_32F, src1.channels()));
|
||||
|
||||
const int w = src1.cols;
|
||||
|
||||
// compute Jacobian blocks (2 blocks)
|
||||
src1.copyTo(dst.colRange(0, w));
|
||||
src2.copyTo(dst.colRange(w, 2*w));
|
||||
src2.copyTo(dst.colRange(w, 2 * w));
|
||||
}
|
||||
|
||||
|
||||
static void project_onto_jacobian_ECC(const Mat& src1, const Mat& src2, Mat& dst)
|
||||
{
|
||||
static void project_onto_jacobian_ECC(const Mat& src1, const Mat& src2, Mat& dst) {
|
||||
/* this functions is used for two types of projections. If src1.cols ==src.cols
|
||||
it does a blockwise multiplication (like in the outer product of vectors)
|
||||
of the blocks in matrices src1 and src2 and dst
|
||||
@@ -222,59 +204,54 @@ static void project_onto_jacobian_ECC(const Mat& src1, const Mat& src2, Mat& dst
|
||||
|
||||
float* dstPtr = dst.ptr<float>(0);
|
||||
|
||||
if (src1.cols !=src2.cols){//dst.cols==1
|
||||
w = src2.cols;
|
||||
for (int i=0; i<dst.rows; i++){
|
||||
dstPtr[i] = (float) src2.dot(src1.colRange(i*w,(i+1)*w));
|
||||
if (src1.cols != src2.cols) { // dst.cols==1
|
||||
w = src2.cols;
|
||||
for (int i = 0; i < dst.rows; i++) {
|
||||
dstPtr[i] = (float)src2.dot(src1.colRange(i * w, (i + 1) * w));
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
CV_Assert(dst.cols == dst.rows); //dst is square (and symmetric)
|
||||
w = src2.cols/dst.cols;
|
||||
CV_Assert(dst.cols == dst.rows); // dst is square (and symmetric)
|
||||
w = src2.cols / dst.cols;
|
||||
Mat mat;
|
||||
for (int i=0; i<dst.rows; i++){
|
||||
for (int i = 0; i < dst.rows; i++) {
|
||||
mat = Mat(src1.colRange(i * w, (i + 1) * w));
|
||||
dstPtr[i * (dst.rows + 1)] = (float)pow(norm(mat), 2); // diagonal elements
|
||||
|
||||
mat = Mat(src1.colRange(i*w, (i+1)*w));
|
||||
dstPtr[i*(dst.rows+1)] = (float) pow(norm(mat),2); //diagonal elements
|
||||
|
||||
for (int j=i+1; j<dst.cols; j++){ //j starts from i+1
|
||||
dstPtr[i*dst.cols+j] = (float) mat.dot(src2.colRange(j*w, (j+1)*w));
|
||||
dstPtr[j*dst.cols+i] = dstPtr[i*dst.cols+j]; //due to symmetry
|
||||
for (int j = i + 1; j < dst.cols; j++) { // j starts from i+1
|
||||
dstPtr[i * dst.cols + j] = (float)mat.dot(src2.colRange(j * w, (j + 1) * w));
|
||||
dstPtr[j * dst.cols + i] = dstPtr[i * dst.cols + j]; // due to symmetry
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_warping_matrix_ECC(Mat& map_matrix, const Mat& update, const int motionType) {
|
||||
CV_Assert(map_matrix.type() == CV_32FC1);
|
||||
CV_Assert(update.type() == CV_32FC1);
|
||||
|
||||
static void update_warping_matrix_ECC (Mat& map_matrix, const Mat& update, const int motionType)
|
||||
{
|
||||
CV_Assert (map_matrix.type() == CV_32FC1);
|
||||
CV_Assert (update.type() == CV_32FC1);
|
||||
|
||||
CV_Assert (motionType == MOTION_TRANSLATION || motionType == MOTION_EUCLIDEAN ||
|
||||
motionType == MOTION_AFFINE || motionType == MOTION_HOMOGRAPHY);
|
||||
CV_Assert(motionType == MOTION_TRANSLATION || motionType == MOTION_EUCLIDEAN || motionType == MOTION_AFFINE ||
|
||||
motionType == MOTION_HOMOGRAPHY);
|
||||
|
||||
if (motionType == MOTION_HOMOGRAPHY)
|
||||
CV_Assert (map_matrix.rows == 3 && update.rows == 8);
|
||||
CV_Assert(map_matrix.rows == 3 && update.rows == 8);
|
||||
else if (motionType == MOTION_AFFINE)
|
||||
CV_Assert(map_matrix.rows == 2 && update.rows == 6);
|
||||
else if (motionType == MOTION_EUCLIDEAN)
|
||||
CV_Assert (map_matrix.rows == 2 && update.rows == 3);
|
||||
CV_Assert(map_matrix.rows == 2 && update.rows == 3);
|
||||
else
|
||||
CV_Assert (map_matrix.rows == 2 && update.rows == 2);
|
||||
CV_Assert(map_matrix.rows == 2 && update.rows == 2);
|
||||
|
||||
CV_Assert (update.cols == 1);
|
||||
|
||||
CV_Assert( map_matrix.isContinuous());
|
||||
CV_Assert( update.isContinuous() );
|
||||
CV_Assert(update.cols == 1);
|
||||
|
||||
CV_Assert(map_matrix.isContinuous());
|
||||
CV_Assert(update.isContinuous());
|
||||
|
||||
float* mapPtr = map_matrix.ptr<float>(0);
|
||||
const float* updatePtr = update.ptr<float>(0);
|
||||
|
||||
|
||||
if (motionType == MOTION_TRANSLATION){
|
||||
if (motionType == MOTION_TRANSLATION) {
|
||||
mapPtr[2] += updatePtr[0];
|
||||
mapPtr[5] += updatePtr[1];
|
||||
}
|
||||
@@ -302,23 +279,23 @@ static void update_warping_matrix_ECC (Mat& map_matrix, const Mat& update, const
|
||||
|
||||
mapPtr[2] += updatePtr[1];
|
||||
mapPtr[5] += updatePtr[2];
|
||||
mapPtr[0] = mapPtr[4] = (float) cos(new_theta);
|
||||
mapPtr[3] = (float) sin(new_theta);
|
||||
mapPtr[0] = mapPtr[4] = (float)cos(new_theta);
|
||||
mapPtr[3] = (float)sin(new_theta);
|
||||
mapPtr[1] = -mapPtr[3];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Function that computes enhanced corelation coefficient from Georgios et.al. 2008
|
||||
* See https://github.com/opencv/opencv/issues/12432
|
||||
*/
|
||||
double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArray inputMask)
|
||||
{
|
||||
* See https://github.com/opencv/opencv/issues/12432
|
||||
*/
|
||||
double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArray inputMask) {
|
||||
CV_Assert(!templateImage.empty());
|
||||
CV_Assert(!inputImage.empty());
|
||||
|
||||
if( ! (templateImage.type()==inputImage.type()))
|
||||
CV_Error( Error::StsUnmatchedFormats, "Both input images must have the same data type" );
|
||||
CV_Assert(templateImage.channels() == 1 || templateImage.channels() == 3);
|
||||
|
||||
if (!(templateImage.type() == inputImage.type()))
|
||||
CV_Error(Error::StsUnmatchedFormats, "Both input images must have the same data type");
|
||||
|
||||
Scalar meanTemplate, sdTemplate;
|
||||
|
||||
@@ -335,7 +312,7 @@ double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArra
|
||||
* ultimately results in an incorrect ECC. To circumvent this problem, if unsigned ints are provided,
|
||||
* we convert them to a signed ints with larger resolution for the subtraction step.
|
||||
*/
|
||||
if(type == CV_8U || type == CV_16U) {
|
||||
if (type == CV_8U || type == CV_16U) {
|
||||
int newType = type == CV_8U ? CV_16S : CV_32S;
|
||||
Mat templateMatConverted, inputMatConverted;
|
||||
templateMat.convertTo(templateMatConverted, newType);
|
||||
@@ -344,40 +321,36 @@ double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArra
|
||||
cv::swap(inputMat, inputMatConverted);
|
||||
}
|
||||
subtract(templateMat, meanTemplate, templateImage_zeromean, inputMask);
|
||||
double templateImagenorm = std::sqrt(active_pixels*sdTemplate.val[0]*sdTemplate.val[0]);
|
||||
double templateImagenorm = std::sqrt(active_pixels * cv::norm(sdTemplate, NORM_L2SQR));
|
||||
|
||||
Scalar meanInput, sdInput;
|
||||
|
||||
Mat inputImage_zeromean = Mat::zeros(inputImage.size(), inputImage.type());
|
||||
meanStdDev(inputImage, meanInput, sdInput, inputMask);
|
||||
subtract(inputMat, meanInput, inputImage_zeromean, inputMask);
|
||||
double inputImagenorm = std::sqrt(active_pixels*sdInput.val[0]*sdInput.val[0]);
|
||||
double inputImagenorm = std::sqrt(active_pixels * norm(sdInput, NORM_L2SQR));
|
||||
|
||||
return templateImage_zeromean.dot(inputImage_zeromean)/(templateImagenorm*inputImagenorm);
|
||||
return templateImage_zeromean.dot(inputImage_zeromean) / (templateImagenorm * inputImagenorm);
|
||||
}
|
||||
|
||||
|
||||
double cv::findTransformECC(InputArray templateImage,
|
||||
InputArray inputImage,
|
||||
InputOutputArray warpMatrix,
|
||||
int motionType,
|
||||
TermCriteria criteria,
|
||||
InputArray inputMask,
|
||||
int gaussFiltSize)
|
||||
{
|
||||
|
||||
|
||||
Mat src = templateImage.getMat();//template image
|
||||
Mat dst = inputImage.getMat(); //input image (to be warped)
|
||||
Mat map = warpMatrix.getMat(); //warp (transformation)
|
||||
double cv::findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix,
|
||||
int motionType, TermCriteria criteria, InputArray inputMask, int gaussFiltSize) {
|
||||
Mat src = templateImage.getMat(); // template image
|
||||
Mat dst = inputImage.getMat(); // input image (to be warped)
|
||||
Mat map = warpMatrix.getMat(); // warp (transformation)
|
||||
|
||||
CV_Assert(!src.empty());
|
||||
CV_Assert(!dst.empty());
|
||||
|
||||
CV_Assert(src.channels() == 1 || src.channels() == 3);
|
||||
CV_Assert(src.channels() == dst.channels());
|
||||
CV_Assert(src.depth() == dst.depth());
|
||||
CV_Assert(src.depth() == CV_8U || src.depth() == CV_16U || src.depth() == CV_32F || src.depth() == CV_64F);
|
||||
|
||||
// If the user passed an un-initialized warpMatrix, initialize to identity
|
||||
if(map.empty()) {
|
||||
if (map.empty()) {
|
||||
int rowCount = 2;
|
||||
if(motionType == MOTION_HOMOGRAPHY)
|
||||
if (motionType == MOTION_HOMOGRAPHY)
|
||||
rowCount = 3;
|
||||
|
||||
warpMatrix.create(rowCount, 3, CV_32FC1);
|
||||
@@ -385,44 +358,39 @@ double cv::findTransformECC(InputArray templateImage,
|
||||
map = Mat::eye(rowCount, 3, CV_32F);
|
||||
}
|
||||
|
||||
if( ! (src.type()==dst.type()))
|
||||
CV_Error( Error::StsUnmatchedFormats, "Both input images must have the same data type" );
|
||||
if (!(src.type() == dst.type()))
|
||||
CV_Error(Error::StsUnmatchedFormats, "Both input images must have the same data type");
|
||||
|
||||
//accept only 1-channel images
|
||||
if( src.type() != CV_8UC1 && src.type()!= CV_32FC1)
|
||||
CV_Error( Error::StsUnsupportedFormat, "Images must have 8uC1 or 32fC1 type");
|
||||
if (map.type() != CV_32FC1)
|
||||
CV_Error(Error::StsUnsupportedFormat, "warpMatrix must be single-channel floating-point matrix");
|
||||
|
||||
if( map.type() != CV_32FC1)
|
||||
CV_Error( Error::StsUnsupportedFormat, "warpMatrix must be single-channel floating-point matrix");
|
||||
CV_Assert(map.cols == 3);
|
||||
CV_Assert(map.rows == 2 || map.rows == 3);
|
||||
|
||||
CV_Assert (map.cols == 3);
|
||||
CV_Assert (map.rows == 2 || map.rows ==3);
|
||||
CV_Assert(motionType == MOTION_AFFINE || motionType == MOTION_HOMOGRAPHY || motionType == MOTION_EUCLIDEAN ||
|
||||
motionType == MOTION_TRANSLATION);
|
||||
|
||||
CV_Assert (motionType == MOTION_AFFINE || motionType == MOTION_HOMOGRAPHY ||
|
||||
motionType == MOTION_EUCLIDEAN || motionType == MOTION_TRANSLATION);
|
||||
|
||||
if (motionType == MOTION_HOMOGRAPHY){
|
||||
CV_Assert (map.rows ==3);
|
||||
if (motionType == MOTION_HOMOGRAPHY) {
|
||||
CV_Assert(map.rows == 3);
|
||||
}
|
||||
|
||||
CV_Assert (criteria.type & TermCriteria::COUNT || criteria.type & TermCriteria::EPS);
|
||||
const int numberOfIterations = (criteria.type & TermCriteria::COUNT) ? criteria.maxCount : 200;
|
||||
const double termination_eps = (criteria.type & TermCriteria::EPS) ? criteria.epsilon : -1;
|
||||
CV_Assert(criteria.type & TermCriteria::COUNT || criteria.type & TermCriteria::EPS);
|
||||
const int numberOfIterations = (criteria.type & TermCriteria::COUNT) ? criteria.maxCount : 200;
|
||||
const double termination_eps = (criteria.type & TermCriteria::EPS) ? criteria.epsilon : -1;
|
||||
|
||||
int paramTemp = 6;//default: affine
|
||||
switch (motionType){
|
||||
case MOTION_TRANSLATION:
|
||||
paramTemp = 2;
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
paramTemp = 3;
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
paramTemp = 8;
|
||||
break;
|
||||
int paramTemp = 6; // default: affine
|
||||
switch (motionType) {
|
||||
case MOTION_TRANSLATION:
|
||||
paramTemp = 2;
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
paramTemp = 3;
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
paramTemp = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
const int numberOfParameters = paramTemp;
|
||||
|
||||
const int ws = src.cols;
|
||||
@@ -438,10 +406,8 @@ double cv::findTransformECC(InputArray templateImage,
|
||||
float* XcoPtr = Xcoord.ptr<float>(0);
|
||||
float* YcoPtr = Ycoord.ptr<float>(0);
|
||||
int j;
|
||||
for (j=0; j<ws; j++)
|
||||
XcoPtr[j] = (float) j;
|
||||
for (j=0; j<hs; j++)
|
||||
YcoPtr[j] = (float) j;
|
||||
for (j = 0; j < ws; j++) XcoPtr[j] = (float)j;
|
||||
for (j = 0; j < hs; j++) YcoPtr[j] = (float)j;
|
||||
|
||||
repeat(Xcoord, hs, 1, Xgrid);
|
||||
repeat(Ycoord, 1, ws, Ygrid);
|
||||
@@ -449,29 +415,38 @@ double cv::findTransformECC(InputArray templateImage,
|
||||
Xcoord.release();
|
||||
Ycoord.release();
|
||||
|
||||
Mat templateZM = Mat(hs, ws, CV_32F);// to store the (smoothed)zero-mean version of template
|
||||
Mat templateFloat = Mat(hs, ws, CV_32F);// to store the (smoothed) template
|
||||
Mat imageFloat = Mat(hd, wd, CV_32F);// to store the (smoothed) input image
|
||||
Mat imageWarped = Mat(hs, ws, CV_32F);// to store the warped zero-mean input image
|
||||
Mat imageMask = Mat(hs, ws, CV_8U); // to store the final mask
|
||||
const int channels = src.channels();
|
||||
int type = CV_MAKETYPE(CV_32F, channels); // используем отдельно, если нужно явно
|
||||
|
||||
std::vector<cv::Mat> XgridCh(channels, Xgrid);
|
||||
cv::merge(XgridCh, Xgrid);
|
||||
|
||||
std::vector<cv::Mat> YgridCh(channels, Ygrid);
|
||||
cv::merge(YgridCh, Ygrid);
|
||||
|
||||
Mat templateZM = Mat(hs, ws, type); // to store the (smoothed)zero-mean version of template
|
||||
Mat templateFloat = Mat(hs, ws, type); // to store the (smoothed) template
|
||||
Mat imageFloat = Mat(hd, wd, type); // to store the (smoothed) input image
|
||||
Mat imageWarped = Mat(hs, ws, type); // to store the warped zero-mean input image
|
||||
Mat imageMask = Mat(hs, ws, CV_8U); // to store the final mask
|
||||
|
||||
Mat inputMaskMat = inputMask.getMat();
|
||||
//to use it for mask warping
|
||||
// to use it for mask warping
|
||||
Mat preMask;
|
||||
if(inputMask.empty())
|
||||
if (inputMask.empty())
|
||||
preMask = Mat::ones(hd, wd, CV_8U);
|
||||
else
|
||||
threshold(inputMask, preMask, 0, 1, THRESH_BINARY);
|
||||
|
||||
//gaussian filtering is optional
|
||||
// Gaussian filtering is optional
|
||||
src.convertTo(templateFloat, templateFloat.type());
|
||||
GaussianBlur(templateFloat, templateFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
|
||||
|
||||
Mat preMaskFloat;
|
||||
preMask.convertTo(preMaskFloat, CV_32F);
|
||||
preMask.convertTo(preMaskFloat, type);
|
||||
GaussianBlur(preMaskFloat, preMaskFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
|
||||
// Change threshold.
|
||||
preMaskFloat *= (0.5/0.95);
|
||||
preMaskFloat *= (0.5 / 0.95);
|
||||
// Rounding conversion.
|
||||
preMaskFloat.convertTo(preMask, preMask.type());
|
||||
preMask.convertTo(preMaskFloat, preMaskFloat.type());
|
||||
@@ -480,11 +455,10 @@ double cv::findTransformECC(InputArray templateImage,
|
||||
GaussianBlur(imageFloat, imageFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
|
||||
|
||||
// needed matrices for gradients and warped gradients
|
||||
Mat gradientX = Mat::zeros(hd, wd, CV_32FC1);
|
||||
Mat gradientY = Mat::zeros(hd, wd, CV_32FC1);
|
||||
Mat gradientXWarped = Mat(hs, ws, CV_32FC1);
|
||||
Mat gradientYWarped = Mat(hs, ws, CV_32FC1);
|
||||
|
||||
Mat gradientX = Mat::zeros(hd, wd, type);
|
||||
Mat gradientY = Mat::zeros(hd, wd, type);
|
||||
Mat gradientXWarped = Mat(hs, ws, type);
|
||||
Mat gradientYWarped = Mat(hs, ws, type);
|
||||
|
||||
// calculate first order image derivatives
|
||||
Matx13f dx(-0.5f, 0.0f, 0.5f);
|
||||
@@ -492,60 +466,59 @@ double cv::findTransformECC(InputArray templateImage,
|
||||
filter2D(imageFloat, gradientX, -1, dx);
|
||||
filter2D(imageFloat, gradientY, -1, dx.t());
|
||||
|
||||
gradientX = gradientX.mul(preMaskFloat);
|
||||
gradientY = gradientY.mul(preMaskFloat);
|
||||
cv::Mat preMaskFloatNCh;
|
||||
std::vector<cv::Mat> maskChannels(gradientX.channels(), preMaskFloat);
|
||||
cv::merge(maskChannels, preMaskFloatNCh);
|
||||
|
||||
gradientX = gradientX.mul(preMaskFloatNCh);
|
||||
gradientY = gradientY.mul(preMaskFloatNCh);
|
||||
|
||||
// matrices needed for solving linear equation system for maximizing ECC
|
||||
Mat jacobian = Mat(hs, ws*numberOfParameters, CV_32F);
|
||||
Mat hessian = Mat(numberOfParameters, numberOfParameters, CV_32F);
|
||||
Mat hessianInv = Mat(numberOfParameters, numberOfParameters, CV_32F);
|
||||
Mat imageProjection = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat templateProjection = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat imageProjectionHessian = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat errorProjection = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat jacobian = Mat(hs, ws * numberOfParameters, type);
|
||||
Mat hessian = Mat(numberOfParameters, numberOfParameters, CV_32F);
|
||||
Mat hessianInv = Mat(numberOfParameters, numberOfParameters, CV_32F);
|
||||
Mat imageProjection = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat templateProjection = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat imageProjectionHessian = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat errorProjection = Mat(numberOfParameters, 1, CV_32F);
|
||||
|
||||
Mat deltaP = Mat(numberOfParameters, 1, CV_32F);//transformation parameter correction
|
||||
Mat error = Mat(hs, ws, CV_32F);//error as 2D matrix
|
||||
|
||||
const int imageFlags = INTER_LINEAR + WARP_INVERSE_MAP;
|
||||
const int maskFlags = INTER_NEAREST + WARP_INVERSE_MAP;
|
||||
Mat deltaP = Mat(numberOfParameters, 1, CV_32F); // transformation parameter correction
|
||||
Mat error = Mat(hs, ws, CV_32F); // error as 2D matrix
|
||||
|
||||
const int imageFlags = INTER_LINEAR + WARP_INVERSE_MAP;
|
||||
const int maskFlags = INTER_NEAREST + WARP_INVERSE_MAP;
|
||||
|
||||
// iteratively update map_matrix
|
||||
double rho = -1;
|
||||
double last_rho = - termination_eps;
|
||||
for (int i = 1; (i <= numberOfIterations) && (fabs(rho-last_rho)>= termination_eps); i++)
|
||||
{
|
||||
|
||||
double rho = -1;
|
||||
double last_rho = -termination_eps;
|
||||
for (int i = 1; (i <= numberOfIterations) && (fabs(rho - last_rho) >= termination_eps); i++) {
|
||||
// warp-back portion of the inputImage and gradients to the coordinate space of the templateImage
|
||||
if (motionType != MOTION_HOMOGRAPHY)
|
||||
{
|
||||
warpAffine(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
|
||||
warpAffine(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
|
||||
warpAffine(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
|
||||
warpAffine(preMask, imageMask, map, imageMask.size(), maskFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
warpPerspective(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
|
||||
warpPerspective(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
|
||||
warpPerspective(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
|
||||
warpPerspective(preMask, imageMask, map, imageMask.size(), maskFlags);
|
||||
if (motionType != MOTION_HOMOGRAPHY) {
|
||||
warpAffine(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
|
||||
warpAffine(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
|
||||
warpAffine(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
|
||||
warpAffine(preMask, imageMask, map, imageMask.size(), maskFlags);
|
||||
} else {
|
||||
warpPerspective(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
|
||||
warpPerspective(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
|
||||
warpPerspective(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
|
||||
warpPerspective(preMask, imageMask, map, imageMask.size(), maskFlags);
|
||||
}
|
||||
|
||||
Scalar imgMean, imgStd, tmpMean, tmpStd;
|
||||
meanStdDev(imageWarped, imgMean, imgStd, imageMask);
|
||||
meanStdDev(imageWarped, imgMean, imgStd, imageMask);
|
||||
meanStdDev(templateFloat, tmpMean, tmpStd, imageMask);
|
||||
|
||||
subtract(imageWarped, imgMean, imageWarped, imageMask);//zero-mean input
|
||||
subtract(imageWarped, imgMean, imageWarped, imageMask); // zero-mean input
|
||||
templateZM = Mat::zeros(templateZM.rows, templateZM.cols, templateZM.type());
|
||||
subtract(templateFloat, tmpMean, templateZM, imageMask);//zero-mean template
|
||||
subtract(templateFloat, tmpMean, templateZM, imageMask); // zero-mean template
|
||||
|
||||
const double tmpNorm = std::sqrt(countNonZero(imageMask)*(tmpStd.val[0])*(tmpStd.val[0]));
|
||||
const double imgNorm = std::sqrt(countNonZero(imageMask)*(imgStd.val[0])*(imgStd.val[0]));
|
||||
int validPixels = countNonZero(imageMask);
|
||||
double tmpNorm = std::sqrt(validPixels * cv::norm(tmpStd, cv::NORM_L2SQR));
|
||||
double imgNorm = std::sqrt(validPixels * cv::norm(imgStd, cv::NORM_L2SQR));
|
||||
|
||||
// calculate jacobian of image wrt parameters
|
||||
switch (motionType){
|
||||
switch (motionType) {
|
||||
case MOTION_AFFINE:
|
||||
image_jacobian_affine_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, jacobian);
|
||||
break;
|
||||
@@ -569,48 +542,42 @@ double cv::findTransformECC(InputArray templateImage,
|
||||
|
||||
// calculate enhanced correlation coefficient (ECC)->rho
|
||||
last_rho = rho;
|
||||
rho = correlation/(imgNorm*tmpNorm);
|
||||
rho = correlation / (imgNorm * tmpNorm);
|
||||
if (cvIsNaN(rho)) {
|
||||
CV_Error(Error::StsNoConv, "NaN encountered.");
|
||||
CV_Error(Error::StsNoConv, "NaN encountered.");
|
||||
}
|
||||
|
||||
// project images into jacobian
|
||||
project_onto_jacobian_ECC( jacobian, imageWarped, imageProjection);
|
||||
project_onto_jacobian_ECC(jacobian, imageWarped, imageProjection);
|
||||
project_onto_jacobian_ECC(jacobian, templateZM, templateProjection);
|
||||
|
||||
|
||||
// calculate the parameter lambda to account for illumination variation
|
||||
imageProjectionHessian = hessianInv*imageProjection;
|
||||
const double lambda_n = (imgNorm*imgNorm) - imageProjection.dot(imageProjectionHessian);
|
||||
imageProjectionHessian = hessianInv * imageProjection;
|
||||
const double lambda_n = (imgNorm * imgNorm) - imageProjection.dot(imageProjectionHessian);
|
||||
const double lambda_d = correlation - templateProjection.dot(imageProjectionHessian);
|
||||
if (lambda_d <= 0.0)
|
||||
{
|
||||
if (lambda_d <= 0.0) {
|
||||
rho = -1;
|
||||
CV_Error(Error::StsNoConv, "The algorithm stopped before its convergence. The correlation is going to be minimized. Images may be uncorrelated or non-overlapped");
|
||||
|
||||
CV_Error(Error::StsNoConv,
|
||||
"The algorithm stopped before its convergence. The correlation is going to be minimized. Images "
|
||||
"may be uncorrelated or non-overlapped");
|
||||
}
|
||||
const double lambda = (lambda_n/lambda_d);
|
||||
const double lambda = (lambda_n / lambda_d);
|
||||
|
||||
// estimate the update step delta_p
|
||||
error = lambda*templateZM - imageWarped;
|
||||
error = lambda * templateZM - imageWarped;
|
||||
project_onto_jacobian_ECC(jacobian, error, errorProjection);
|
||||
deltaP = hessianInv * errorProjection;
|
||||
|
||||
// update warping matrix
|
||||
update_warping_matrix_ECC( map, deltaP, motionType);
|
||||
|
||||
|
||||
update_warping_matrix_ECC(map, deltaP, motionType);
|
||||
}
|
||||
|
||||
// return final correlation coefficient
|
||||
return rho;
|
||||
}
|
||||
|
||||
double cv::findTransformECC(InputArray templateImage, InputArray inputImage,
|
||||
InputOutputArray warpMatrix, int motionType,
|
||||
TermCriteria criteria,
|
||||
InputArray inputMask)
|
||||
{
|
||||
double cv::findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix,
|
||||
int motionType, TermCriteria criteria, InputArray inputMask) {
|
||||
// Use default value of 5 for gaussFiltSize to maintain backward compatibility.
|
||||
return findTransformECC(templateImage, inputImage, warpMatrix, motionType, criteria, inputMask, 5);
|
||||
}
|
||||
|
||||
@@ -42,41 +42,49 @@
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
namespace opencv_test {
|
||||
namespace {
|
||||
|
||||
class CV_ECC_BaseTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
class CV_ECC_BaseTest : public cvtest::BaseTest {
|
||||
public:
|
||||
CV_ECC_BaseTest();
|
||||
virtual ~CV_ECC_BaseTest();
|
||||
|
||||
protected:
|
||||
|
||||
protected:
|
||||
double computeRMS(const Mat& mat1, const Mat& mat2);
|
||||
bool isMapCorrect(const Mat& mat);
|
||||
|
||||
virtual bool test(const Mat) { return true; }; // single test
|
||||
bool testAllTypes(const Mat img); // run test for all supported data types (U8, U16, F32, F64)
|
||||
bool testAllChNum(const Mat img); // run test for all supported channels count (gray, RGB)
|
||||
|
||||
double MAX_RMS_ECC;//upper bound for RMS error
|
||||
int ntests;//number of tests per motion type
|
||||
int ECC_iterations;//number of iterations for ECC
|
||||
double ECC_epsilon; //we choose a negative value, so that
|
||||
void run(int);
|
||||
|
||||
bool checkMap(const Mat& map, const Mat& ground);
|
||||
|
||||
double MAX_RMS_ECC; // upper bound for RMS error
|
||||
int ntests; // number of tests per motion type
|
||||
int ECC_iterations; // number of iterations for ECC
|
||||
double ECC_epsilon; // we choose a negative value, so that
|
||||
// ECC_iterations are always executed
|
||||
TermCriteria criteria;
|
||||
};
|
||||
|
||||
CV_ECC_BaseTest::CV_ECC_BaseTest()
|
||||
{
|
||||
MAX_RMS_ECC=0.1;
|
||||
CV_ECC_BaseTest::CV_ECC_BaseTest() {
|
||||
MAX_RMS_ECC = 0.1;
|
||||
ntests = 3;
|
||||
ECC_iterations = 50;
|
||||
ECC_epsilon = -1; //-> negative value means that ECC_Iterations will be executed
|
||||
ECC_epsilon = -1; //-> negative value means that ECC_Iterations will be executed
|
||||
criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, ECC_iterations, ECC_epsilon);
|
||||
}
|
||||
|
||||
CV_ECC_BaseTest::~CV_ECC_BaseTest() {}
|
||||
|
||||
bool CV_ECC_BaseTest::isMapCorrect(const Mat& map)
|
||||
{
|
||||
bool CV_ECC_BaseTest::isMapCorrect(const Mat& map) {
|
||||
bool tr = true;
|
||||
float mapVal;
|
||||
for(int i =0; i<map.rows; i++)
|
||||
for(int j=0; j<map.cols; j++){
|
||||
for (int i = 0; i < map.rows; i++)
|
||||
for (int j = 0; j < map.cols; j++) {
|
||||
mapVal = map.at<float>(i, j);
|
||||
tr = tr & (!cvIsNaN(mapVal) && (fabs(mapVal) < 1e9));
|
||||
}
|
||||
@@ -84,415 +92,326 @@ bool CV_ECC_BaseTest::isMapCorrect(const Mat& map)
|
||||
return tr;
|
||||
}
|
||||
|
||||
double CV_ECC_BaseTest::computeRMS(const Mat& mat1, const Mat& mat2){
|
||||
|
||||
double CV_ECC_BaseTest::computeRMS(const Mat& mat1, const Mat& mat2) {
|
||||
CV_Assert(mat1.rows == mat2.rows);
|
||||
CV_Assert(mat1.cols == mat2.cols);
|
||||
|
||||
Mat errorMat;
|
||||
subtract(mat1, mat2, errorMat);
|
||||
|
||||
return sqrt(errorMat.dot(errorMat)/(mat1.rows*mat1.cols));
|
||||
return sqrt(errorMat.dot(errorMat) / (mat1.rows * mat1.cols * mat1.channels()));
|
||||
}
|
||||
|
||||
class CV_ECC_Test_Translation : public CV_ECC_BaseTest
|
||||
{
|
||||
public:
|
||||
bool CV_ECC_BaseTest::checkMap(const Mat& map, const Mat& ground) {
|
||||
if (!isMapCorrect(map)) {
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (computeRMS(map, ground) > MAX_RMS_ECC) {
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf(ts->LOG, "RMS = %f", computeRMS(map, ground));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CV_ECC_BaseTest::testAllTypes(const Mat img) {
|
||||
auto types = {CV_8U, CV_16U, CV_32F, CV_64F};
|
||||
for (auto type : types) {
|
||||
Mat timg;
|
||||
img.convertTo(timg, type);
|
||||
if (!test(timg))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CV_ECC_BaseTest::testAllChNum(const Mat img) {
|
||||
if (!testAllTypes(img))
|
||||
return false;
|
||||
|
||||
Mat gray;
|
||||
cvtColor(img, gray, COLOR_RGB2GRAY);
|
||||
if (!testAllTypes(gray))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CV_ECC_BaseTest::run(int) {
|
||||
Mat img = imread(string(ts->get_data_path()) + "shared/fruits.png");
|
||||
if (img.empty()) {
|
||||
ts->printf(ts->LOG, "test image can not be read");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
Mat testImg;
|
||||
resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
|
||||
|
||||
testAllChNum(testImg);
|
||||
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
}
|
||||
|
||||
class CV_ECC_Test_Translation : public CV_ECC_BaseTest {
|
||||
public:
|
||||
CV_ECC_Test_Translation();
|
||||
protected:
|
||||
void run(int);
|
||||
|
||||
bool testTranslation(int);
|
||||
protected:
|
||||
bool test(const Mat);
|
||||
};
|
||||
|
||||
CV_ECC_Test_Translation::CV_ECC_Test_Translation(){}
|
||||
|
||||
bool CV_ECC_Test_Translation::testTranslation(int from)
|
||||
{
|
||||
Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
|
||||
|
||||
|
||||
if (img.empty())
|
||||
{
|
||||
ts->printf( ts->LOG, "test image can not be read");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return false;
|
||||
}
|
||||
Mat testImg;
|
||||
resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
|
||||
CV_ECC_Test_Translation::CV_ECC_Test_Translation() {}
|
||||
|
||||
bool CV_ECC_Test_Translation::test(const Mat testImg) {
|
||||
cv::RNG rng = ts->get_rng();
|
||||
|
||||
int progress=0;
|
||||
int progress = 0;
|
||||
|
||||
for (int k=from; k<ntests; k++){
|
||||
|
||||
ts->update_context( this, k, true );
|
||||
for (int k = 0; k < ntests; k++) {
|
||||
ts->update_context(this, k, true);
|
||||
progress = update_progress(progress, k, ntests, 0);
|
||||
|
||||
Mat translationGround = (Mat_<float>(2,3) << 1, 0, (rng.uniform(10.f, 20.f)),
|
||||
0, 1, (rng.uniform(10.f, 20.f)));
|
||||
Mat translationGround = (Mat_<float>(2, 3) << 1, 0, (rng.uniform(10.f, 20.f)), 0, 1, (rng.uniform(10.f, 20.f)));
|
||||
|
||||
Mat warpedImage;
|
||||
|
||||
warpAffine(testImg, warpedImage, translationGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
warpAffine(testImg, warpedImage, translationGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
|
||||
Mat mapTranslation = (Mat_<float>(2,3) << 1, 0, 0, 0, 1, 0);
|
||||
Mat mapTranslation = (Mat_<float>(2, 3) << 1, 0, 0, 0, 1, 0);
|
||||
|
||||
findTransformECC(warpedImage, testImg, mapTranslation, 0,
|
||||
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
|
||||
findTransformECC(warpedImage, testImg, mapTranslation, 0, criteria);
|
||||
|
||||
if (!isMapCorrect(mapTranslation)){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
|
||||
if (!checkMap(mapTranslation, translationGround))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (computeRMS(mapTranslation, translationGround)>MAX_RMS_ECC){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf( ts->LOG, "RMS = %f",
|
||||
computeRMS(mapTranslation, translationGround));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CV_ECC_Test_Translation::run(int from)
|
||||
{
|
||||
|
||||
if (!testTranslation(from))
|
||||
return;
|
||||
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CV_ECC_Test_Euclidean : public CV_ECC_BaseTest
|
||||
{
|
||||
public:
|
||||
class CV_ECC_Test_Euclidean : public CV_ECC_BaseTest {
|
||||
public:
|
||||
CV_ECC_Test_Euclidean();
|
||||
protected:
|
||||
void run(int);
|
||||
|
||||
bool testEuclidean(int);
|
||||
protected:
|
||||
bool test(const Mat);
|
||||
};
|
||||
|
||||
CV_ECC_Test_Euclidean::CV_ECC_Test_Euclidean() { }
|
||||
|
||||
bool CV_ECC_Test_Euclidean::testEuclidean(int from)
|
||||
{
|
||||
Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
|
||||
|
||||
|
||||
if (img.empty())
|
||||
{
|
||||
ts->printf( ts->LOG, "test image can not be read");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return false;
|
||||
}
|
||||
Mat testImg;
|
||||
resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
|
||||
CV_ECC_Test_Euclidean::CV_ECC_Test_Euclidean() {}
|
||||
|
||||
bool CV_ECC_Test_Euclidean::test(const Mat testImg) {
|
||||
cv::RNG rng = ts->get_rng();
|
||||
|
||||
int progress = 0;
|
||||
for (int k=from; k<ntests; k++){
|
||||
ts->update_context( this, k, true );
|
||||
for (int k = 0; k < ntests; k++) {
|
||||
ts->update_context(this, k, true);
|
||||
progress = update_progress(progress, k, ntests, 0);
|
||||
|
||||
double angle = CV_PI/30 + CV_PI*rng.uniform((double)-2.f, (double)2.f)/180;
|
||||
double angle = CV_PI / 30 + CV_PI * rng.uniform((double)-2.f, (double)2.f) / 180;
|
||||
|
||||
Mat euclideanGround = (Mat_<float>(2,3) << cos(angle), -sin(angle), (rng.uniform(10.f, 20.f)),
|
||||
sin(angle), cos(angle), (rng.uniform(10.f, 20.f)));
|
||||
Mat euclideanGround = (Mat_<float>(2, 3) << cos(angle), -sin(angle), (rng.uniform(10.f, 20.f)), sin(angle),
|
||||
cos(angle), (rng.uniform(10.f, 20.f)));
|
||||
|
||||
Mat warpedImage;
|
||||
|
||||
warpAffine(testImg, warpedImage, euclideanGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
warpAffine(testImg, warpedImage, euclideanGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
|
||||
Mat mapEuclidean = (Mat_<float>(2,3) << 1, 0, 0, 0, 1, 0);
|
||||
Mat mapEuclidean = (Mat_<float>(2, 3) << 1, 0, 0, 0, 1, 0);
|
||||
|
||||
findTransformECC(warpedImage, testImg, mapEuclidean, 1,
|
||||
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
|
||||
findTransformECC(warpedImage, testImg, mapEuclidean, 1, criteria);
|
||||
|
||||
if (!isMapCorrect(mapEuclidean)){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
|
||||
if (!checkMap(mapEuclidean, euclideanGround))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (computeRMS(mapEuclidean, euclideanGround)>MAX_RMS_ECC){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf( ts->LOG, "RMS = %f",
|
||||
computeRMS(mapEuclidean, euclideanGround));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CV_ECC_Test_Euclidean::run(int from)
|
||||
{
|
||||
|
||||
if (!testEuclidean(from))
|
||||
return;
|
||||
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
}
|
||||
|
||||
class CV_ECC_Test_Affine : public CV_ECC_BaseTest
|
||||
{
|
||||
public:
|
||||
class CV_ECC_Test_Affine : public CV_ECC_BaseTest {
|
||||
public:
|
||||
CV_ECC_Test_Affine();
|
||||
protected:
|
||||
void run(int);
|
||||
|
||||
bool testAffine(int);
|
||||
protected:
|
||||
bool test(const Mat img);
|
||||
};
|
||||
|
||||
CV_ECC_Test_Affine::CV_ECC_Test_Affine(){}
|
||||
|
||||
|
||||
bool CV_ECC_Test_Affine::testAffine(int from)
|
||||
{
|
||||
Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
|
||||
|
||||
if (img.empty())
|
||||
{
|
||||
ts->printf( ts->LOG, "test image can not be read");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return false;
|
||||
}
|
||||
Mat testImg;
|
||||
resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
|
||||
CV_ECC_Test_Affine::CV_ECC_Test_Affine() {}
|
||||
|
||||
bool CV_ECC_Test_Affine::test(const Mat testImg) {
|
||||
cv::RNG rng = ts->get_rng();
|
||||
|
||||
int progress = 0;
|
||||
for (int k=from; k<ntests; k++){
|
||||
ts->update_context( this, k, true );
|
||||
for (int k = 0; k < ntests; k++) {
|
||||
ts->update_context(this, k, true);
|
||||
progress = update_progress(progress, k, ntests, 0);
|
||||
|
||||
|
||||
Mat affineGround = (Mat_<float>(2,3) << (1-rng.uniform(-0.05f, 0.05f)),
|
||||
(rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
|
||||
(rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)),
|
||||
(rng.uniform(10.f, 20.f)));
|
||||
Mat affineGround = (Mat_<float>(2, 3) << (1 - rng.uniform(-0.05f, 0.05f)), (rng.uniform(-0.03f, 0.03f)),
|
||||
(rng.uniform(10.f, 20.f)), (rng.uniform(-0.03f, 0.03f)), (1 - rng.uniform(-0.05f, 0.05f)),
|
||||
(rng.uniform(10.f, 20.f)));
|
||||
|
||||
Mat warpedImage;
|
||||
|
||||
warpAffine(testImg, warpedImage, affineGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
warpAffine(testImg, warpedImage, affineGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
|
||||
Mat mapAffine = (Mat_<float>(2,3) << 1, 0, 0, 0, 1, 0);
|
||||
Mat mapAffine = (Mat_<float>(2, 3) << 1, 0, 0, 0, 1, 0);
|
||||
|
||||
findTransformECC(warpedImage, testImg, mapAffine, 2,
|
||||
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
|
||||
findTransformECC(warpedImage, testImg, mapAffine, 2, criteria);
|
||||
|
||||
if (!isMapCorrect(mapAffine)){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
|
||||
if (!checkMap(mapAffine, affineGround))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (computeRMS(mapAffine, affineGround)>MAX_RMS_ECC){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf( ts->LOG, "RMS = %f",
|
||||
computeRMS(mapAffine, affineGround));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CV_ECC_Test_Affine::run(int from)
|
||||
{
|
||||
|
||||
if (!testAffine(from))
|
||||
return;
|
||||
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
}
|
||||
|
||||
class CV_ECC_Test_Homography : public CV_ECC_BaseTest
|
||||
{
|
||||
public:
|
||||
class CV_ECC_Test_Homography : public CV_ECC_BaseTest {
|
||||
public:
|
||||
CV_ECC_Test_Homography();
|
||||
protected:
|
||||
void run(int);
|
||||
|
||||
bool testHomography(int);
|
||||
protected:
|
||||
bool test(const Mat testImg);
|
||||
};
|
||||
|
||||
CV_ECC_Test_Homography::CV_ECC_Test_Homography(){}
|
||||
|
||||
bool CV_ECC_Test_Homography::testHomography(int from)
|
||||
{
|
||||
Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
|
||||
|
||||
|
||||
if (img.empty())
|
||||
{
|
||||
ts->printf( ts->LOG, "test image can not be read");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return false;
|
||||
}
|
||||
Mat testImg;
|
||||
resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
|
||||
CV_ECC_Test_Homography::CV_ECC_Test_Homography() {}
|
||||
|
||||
bool CV_ECC_Test_Homography::test(const Mat testImg) {
|
||||
cv::RNG rng = ts->get_rng();
|
||||
|
||||
int progress = 0;
|
||||
for (int k=from; k<ntests; k++){
|
||||
ts->update_context( this, k, true );
|
||||
for (int k = 0; k < ntests; k++) {
|
||||
ts->update_context(this, k, true);
|
||||
progress = update_progress(progress, k, ntests, 0);
|
||||
|
||||
Mat homoGround = (Mat_<float>(3,3) << (1-rng.uniform(-0.05f, 0.05f)),
|
||||
(rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
|
||||
(rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)),(rng.uniform(10.f, 20.f)),
|
||||
(rng.uniform(0.0001f, 0.0003f)), (rng.uniform(0.0001f, 0.0003f)), 1.f);
|
||||
Mat homoGround =
|
||||
(Mat_<float>(3, 3) << (1 - rng.uniform(-0.05f, 0.05f)), (rng.uniform(-0.03f, 0.03f)),
|
||||
(rng.uniform(10.f, 20.f)), (rng.uniform(-0.03f, 0.03f)), (1 - rng.uniform(-0.05f, 0.05f)),
|
||||
(rng.uniform(10.f, 20.f)), (rng.uniform(0.0001f, 0.0003f)), (rng.uniform(0.0001f, 0.0003f)), 1.f);
|
||||
|
||||
Mat warpedImage;
|
||||
|
||||
warpPerspective(testImg, warpedImage, homoGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
warpPerspective(testImg, warpedImage, homoGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
|
||||
Mat mapHomography = Mat::eye(3, 3, CV_32F);
|
||||
|
||||
findTransformECC(warpedImage, testImg, mapHomography, 3,
|
||||
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
|
||||
findTransformECC(warpedImage, testImg, mapHomography, 3, criteria);
|
||||
|
||||
if (!isMapCorrect(mapHomography)){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
|
||||
if (!checkMap(mapHomography, homoGround))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (computeRMS(mapHomography, homoGround)>MAX_RMS_ECC){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf( ts->LOG, "RMS = %f",
|
||||
computeRMS(mapHomography, homoGround));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CV_ECC_Test_Homography::run(int from)
|
||||
{
|
||||
if (!testHomography(from))
|
||||
return;
|
||||
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
}
|
||||
|
||||
class CV_ECC_Test_Mask : public CV_ECC_BaseTest
|
||||
{
|
||||
public:
|
||||
class CV_ECC_Test_Mask : public CV_ECC_BaseTest {
|
||||
public:
|
||||
CV_ECC_Test_Mask();
|
||||
protected:
|
||||
void run(int);
|
||||
|
||||
bool testMask(int);
|
||||
protected:
|
||||
bool test(const Mat);
|
||||
};
|
||||
|
||||
CV_ECC_Test_Mask::CV_ECC_Test_Mask(){}
|
||||
|
||||
bool CV_ECC_Test_Mask::testMask(int from)
|
||||
{
|
||||
Mat img = imread( string(ts->get_data_path()) + "shared/fruits.png", 0);
|
||||
|
||||
|
||||
if (img.empty())
|
||||
{
|
||||
ts->printf( ts->LOG, "test image can not be read");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return false;
|
||||
}
|
||||
Mat scaledImage;
|
||||
resize(img, scaledImage, Size(216, 216), 0, 0, INTER_LINEAR_EXACT );
|
||||
|
||||
Mat_<float> testImg;
|
||||
scaledImage.convertTo(testImg, testImg.type());
|
||||
CV_ECC_Test_Mask::CV_ECC_Test_Mask() {}
|
||||
|
||||
bool CV_ECC_Test_Mask::test(const Mat testImg) {
|
||||
cv::RNG rng = ts->get_rng();
|
||||
|
||||
int progress=0;
|
||||
int progress = 0;
|
||||
|
||||
for (int k=from; k<ntests; k++){
|
||||
|
||||
ts->update_context( this, k, true );
|
||||
for (int k = 0; k < ntests; k++) {
|
||||
ts->update_context(this, k, true);
|
||||
progress = update_progress(progress, k, ntests, 0);
|
||||
|
||||
Mat translationGround = (Mat_<float>(2,3) << 1, 0, (rng.uniform(10.f, 20.f)),
|
||||
0, 1, (rng.uniform(10.f, 20.f)));
|
||||
Mat translationGround = (Mat_<float>(2, 3) << 1, 0, (rng.uniform(10.f, 20.f)), 0, 1, (rng.uniform(10.f, 20.f)));
|
||||
|
||||
Mat warpedImage;
|
||||
|
||||
warpAffine(testImg, warpedImage, translationGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
warpAffine(testImg, warpedImage, translationGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
|
||||
Mat mapTranslation = (Mat_<float>(2,3) << 1, 0, 0, 0, 1, 0);
|
||||
Mat mapTranslation = (Mat_<float>(2, 3) << 1, 0, 0, 0, 1, 0);
|
||||
|
||||
Mat_<unsigned char> mask = Mat_<unsigned char>::ones(testImg.rows, testImg.cols);
|
||||
for (int i=testImg.rows*2/3; i<testImg.rows; i++) {
|
||||
for (int j=testImg.cols*2/3; j<testImg.cols; j++) {
|
||||
testImg(i, j) = 0;
|
||||
mask(i, j) = 0;
|
||||
}
|
||||
}
|
||||
Rect region(testImg.cols * 2 / 3, testImg.rows * 2 / 3, testImg.cols / 3, testImg.rows / 3);
|
||||
|
||||
findTransformECC(warpedImage, testImg, mapTranslation, 0,
|
||||
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon), mask);
|
||||
rectangle(testImg, region, Scalar::all(0), FILLED);
|
||||
rectangle(mask, region, Scalar(0), FILLED);
|
||||
|
||||
if (!isMapCorrect(mapTranslation)){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
|
||||
findTransformECC(warpedImage, testImg, mapTranslation, 0, criteria, mask);
|
||||
|
||||
if (!checkMap(mapTranslation, translationGround))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (computeRMS(mapTranslation, translationGround)>MAX_RMS_ECC){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf( ts->LOG, "RMS = %f",
|
||||
computeRMS(mapTranslation, translationGround));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test with non-default gaussian blur.
|
||||
findTransformECC(warpedImage, testImg, mapTranslation, 0,
|
||||
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon), mask, 1);
|
||||
findTransformECC(warpedImage, testImg, mapTranslation, 0, criteria, mask, 1);
|
||||
|
||||
if (!isMapCorrect(mapTranslation)){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
|
||||
if (!checkMap(mapTranslation, translationGround))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (computeRMS(mapTranslation, translationGround)>MAX_RMS_ECC){
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf( ts->LOG, "RMS = %f",
|
||||
computeRMS(mapTranslation, translationGround));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CV_ECC_Test_Mask::run(int from)
|
||||
{
|
||||
if (!testMask(from))
|
||||
return;
|
||||
void testECCProperties(Mat x, float eps) {
|
||||
// The channels are independent
|
||||
Mat y = x.t();
|
||||
Mat Z = Mat::zeros(x.size(), y.type());
|
||||
Mat O = Mat::ones(x.size(), y.type());
|
||||
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
EXPECT_NEAR(computeECC(x, y), 0.0, eps);
|
||||
if (x.type() != CV_8U && x.type() != CV_8U) {
|
||||
EXPECT_NEAR(computeECC(x + y, x - y), 0.0, eps);
|
||||
}
|
||||
|
||||
EXPECT_NEAR(computeECC(x, x), 1.0, eps);
|
||||
|
||||
Mat R, G, B, X, Y;
|
||||
cv::merge(std::vector<cv::Mat>({O, Z, Z}), R);
|
||||
cv::merge(std::vector<cv::Mat>({Z, O, Z}), G);
|
||||
cv::merge(std::vector<cv::Mat>({Z, Z, O}), B);
|
||||
cv::merge(std::vector<cv::Mat>({x, x, x}), X);
|
||||
cv::merge(std::vector<cv::Mat>({y, y, y}), Y);
|
||||
|
||||
// 1. The channels are orthogonal and independent
|
||||
EXPECT_NEAR(computeECC(X.mul(R), X.mul(G)), 0, eps);
|
||||
EXPECT_NEAR(computeECC(X.mul(R), X.mul(B)), 0, eps);
|
||||
EXPECT_NEAR(computeECC(X.mul(B), X.mul(G)), 0, eps);
|
||||
|
||||
EXPECT_NEAR(computeECC(X.mul(R) + Y.mul(B), X.mul(B) + Y.mul(R)), 0, eps);
|
||||
|
||||
EXPECT_NEAR(computeECC(X.mul(R) + Y.mul(G) + (X + Y).mul(B), Y.mul(R) + X.mul(G) + (X - Y).mul(B)), 0, eps);
|
||||
|
||||
// 2. Each channel contribute equally
|
||||
EXPECT_NEAR(computeECC(X.mul(R) + Y.mul(G + B), X), 1.0 / 3, eps);
|
||||
EXPECT_NEAR(computeECC(X.mul(G) + Y.mul(R + B), X), 1.0 / 3, eps);
|
||||
EXPECT_NEAR(computeECC(X.mul(B) + Y.mul(G + R), X), 1.0 / 3, eps);
|
||||
|
||||
// 3. The coefficient is invariant with respect to the offset of channels
|
||||
EXPECT_NEAR(computeECC(X - R + 2 * G + B, X), 1.0, eps);
|
||||
if (x.type() != CV_8U && x.type() != CV_8U) {
|
||||
EXPECT_NEAR(computeECC(X + R - 2 * G + B, Y), 0.0, eps);
|
||||
}
|
||||
|
||||
// The channels are independent. Check orthogonal combinations
|
||||
// full squares norm = sum of squared norms
|
||||
EXPECT_NEAR(computeECC(X, Y + X), 1.0 / sqrt(2.0), eps);
|
||||
EXPECT_NEAR(computeECC(X, 2 * Y + X), 1.0 / sqrt(5.0), eps);
|
||||
}
|
||||
|
||||
TEST(Video_ECC_Test_Compute, accuracy)
|
||||
{
|
||||
TEST(Video_ECC_Test_Compute, properies) {
|
||||
Mat xline(1, 100, CV_32F), x;
|
||||
for (int i = 0; i < xline.cols; ++i) xline.at<float>(0, i) = (float)i;
|
||||
|
||||
repeat(xline, xline.cols, 1, x);
|
||||
|
||||
Mat x_f64, x_u8, x_u16;
|
||||
x.convertTo(x_f64, CV_64F);
|
||||
x.convertTo(x_u8, CV_8U);
|
||||
x.convertTo(x_u16, CV_16U);
|
||||
|
||||
testECCProperties(x, 1e-5f);
|
||||
testECCProperties(x_f64, 1e-5f);
|
||||
testECCProperties(x_u8, 1);
|
||||
testECCProperties(x_u16, 1);
|
||||
}
|
||||
|
||||
TEST(Video_ECC_Test_Compute, accuracy) {
|
||||
Mat testImg = (Mat_<float>(3, 3) << 1, 0, 0, 1, 0, 0, 1, 0, 0);
|
||||
Mat warpedImage = (Mat_<float>(3, 3) << 0, 1, 0, 0, 1, 0, 0, 1, 0);
|
||||
Mat_<unsigned char> mask = Mat_<unsigned char>::ones(testImg.rows, testImg.cols);
|
||||
@@ -501,8 +420,7 @@ TEST(Video_ECC_Test_Compute, accuracy)
|
||||
EXPECT_NEAR(ecc, -0.5f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Video_ECC_Test_Compute, bug_14657)
|
||||
{
|
||||
TEST(Video_ECC_Test_Compute, bug_14657) {
|
||||
/*
|
||||
* Simple test case - a 2 x 2 matrix with 10, 10, 10, 6. When the mean (36 / 4 = 9) is subtracted,
|
||||
* it results in 1, 1, 1, 0 for the unsigned int case - compare to 1, 1, 1, -3 in the signed case.
|
||||
@@ -512,11 +430,26 @@ TEST(Video_ECC_Test_Compute, bug_14657)
|
||||
EXPECT_NEAR(computeECC(img, img), 1.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Video_ECC_Translation, accuracy) {
|
||||
CV_ECC_Test_Translation test;
|
||||
test.safe_run();
|
||||
}
|
||||
TEST(Video_ECC_Euclidean, accuracy) {
|
||||
CV_ECC_Test_Euclidean test;
|
||||
test.safe_run();
|
||||
}
|
||||
TEST(Video_ECC_Affine, accuracy) {
|
||||
CV_ECC_Test_Affine test;
|
||||
test.safe_run();
|
||||
}
|
||||
TEST(Video_ECC_Homography, accuracy) {
|
||||
CV_ECC_Test_Homography test;
|
||||
test.safe_run();
|
||||
}
|
||||
TEST(Video_ECC_Mask, accuracy) {
|
||||
CV_ECC_Test_Mask test;
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
TEST(Video_ECC_Translation, accuracy) { CV_ECC_Test_Translation test; test.safe_run();}
|
||||
TEST(Video_ECC_Euclidean, accuracy) { CV_ECC_Test_Euclidean test; test.safe_run(); }
|
||||
TEST(Video_ECC_Affine, accuracy) { CV_ECC_Test_Affine test; test.safe_run(); }
|
||||
TEST(Video_ECC_Homography, accuracy) { CV_ECC_Test_Homography test; test.safe_run(); }
|
||||
TEST(Video_ECC_Mask, accuracy) { CV_ECC_Test_Mask test; test.safe_run(); }
|
||||
|
||||
}} // namespace
|
||||
} // namespace
|
||||
} // namespace opencv_test
|
||||
|
||||
@@ -1170,7 +1170,9 @@ bool CvCapture_FFMPEG::open(const char* _filename, int index, const Ptr<IStreamR
|
||||
input_format = av_find_input_format(entry->value);
|
||||
}
|
||||
|
||||
#ifdef HAVE_FFMPEG_LIBAVDEVICE
|
||||
AVDeviceInfoList* device_list = nullptr;
|
||||
#endif
|
||||
if (index >= 0)
|
||||
{
|
||||
#ifdef HAVE_FFMPEG_LIBAVDEVICE
|
||||
@@ -1267,13 +1269,13 @@ bool CvCapture_FFMPEG::open(const char* _filename, int index, const Ptr<IStreamR
|
||||
ic->pb = avio_context;
|
||||
}
|
||||
int err = avformat_open_input(&ic, _filename, input_format, &dict);
|
||||
#ifdef HAVE_FFMPEG_LIBAVDEVICE
|
||||
if (device_list)
|
||||
{
|
||||
#ifdef HAVE_FFMPEG_LIBAVDEVICE
|
||||
avdevice_free_list_devices(&device_list);
|
||||
device_list = nullptr;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (err < 0)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user