mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 15:53:03 +04:00
Merge branch 4.x
This commit is contained in:
@@ -207,12 +207,36 @@ class PatternMaker:
|
||||
square = SVG("rect", x=x_pos+ch_ar_border, y=y_pos+ch_ar_border, width=self.aruco_marker_size,
|
||||
height=self.aruco_marker_size, fill="black", stroke="none")
|
||||
self.g.append(square)
|
||||
|
||||
# BUG: https://github.com/opencv/opencv/issues/27871
|
||||
# The loop bellow merges white squares horizontally and vertically to exclude visible grid on the final pattern
|
||||
for x_ in range(len(img_mark[0])):
|
||||
for y_ in range(len(img_mark)):
|
||||
if (img_mark[y_][x_] != 0):
|
||||
square = SVG("rect", x=x_pos+ch_ar_border+(x_)*side, y=y_pos+ch_ar_border+(y_)*side, width=side,
|
||||
height=side, fill="white", stroke="white", stroke_width = spacing*0.01)
|
||||
self.g.append(square)
|
||||
y_ = 0
|
||||
while y_ < len(img_mark):
|
||||
y_start = y_
|
||||
while y_ < len(img_mark) and img_mark[y_][x_] != 0:
|
||||
y_ += 1
|
||||
|
||||
if y_ > y_start:
|
||||
rect = SVG("rect", x=x_pos+ch_ar_border+(x_)*side, y=y_pos+ch_ar_border+(y_start)*side, width=side,
|
||||
height=(y_ - y_start)*side, fill="white", stroke="none")
|
||||
self.g.append(rect)
|
||||
|
||||
y_ += 1
|
||||
|
||||
for y_ in range(len(img_mark)):
|
||||
x_ = 0
|
||||
while x_ < len(img_mark[0]):
|
||||
x_start = x_
|
||||
while x_ < len(img_mark[0]) and img_mark[y_][x_] != 0:
|
||||
x_ += 1
|
||||
|
||||
if x_ > x_start:
|
||||
rect = SVG("rect", x=x_pos+ch_ar_border+(x_start)*side, y=y_pos+ch_ar_border+(y_)*side, width=(x_-x_start)*side,
|
||||
height=side, fill="white", stroke="none")
|
||||
self.g.append(rect)
|
||||
|
||||
x_ += 1
|
||||
|
||||
def save(self):
|
||||
c = canvas(self.g, width="%d%s" % (self.width, self.units), height="%d%s" % (self.height, self.units),
|
||||
|
||||
@@ -9,10 +9,9 @@ class aruco_objdetect_test(NewOpenCVTests):
|
||||
|
||||
def test_aruco_dicts(self):
|
||||
try:
|
||||
from svglib.svglib import svg2rlg
|
||||
from reportlab.graphics import renderPM
|
||||
import cairosvg
|
||||
except:
|
||||
raise self.skipTest("libraies svglib and reportlab not found")
|
||||
raise self.skipTest("cairosvg library was not found")
|
||||
else:
|
||||
cols = 3
|
||||
rows = 5
|
||||
@@ -47,8 +46,7 @@ class aruco_objdetect_test(NewOpenCVTests):
|
||||
os.path.join(basedir, aruco_type_str[aruco_type_i]+'.json.gz'), 0)
|
||||
pm.make_charuco_board()
|
||||
pm.save()
|
||||
drawing = svg2rlg(filesvg)
|
||||
renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=72)
|
||||
cairosvg.svg2png(url=filesvg, write_to=filepng, background_color="white")
|
||||
from_svg_img = cv.imread(filepng)
|
||||
_charucoCorners, _charuco_ids_svg, marker_corners_svg, marker_ids_svg = charuco_detector.detectBoard(from_svg_img)
|
||||
_charucoCorners, _charuco_ids_cv, marker_corners_cv, marker_ids_cv = charuco_detector.detectBoard(from_cv_img)
|
||||
@@ -70,10 +68,9 @@ class aruco_objdetect_test(NewOpenCVTests):
|
||||
|
||||
def test_aruco_marker_sizes(self):
|
||||
try:
|
||||
from svglib.svglib import svg2rlg
|
||||
from reportlab.graphics import renderPM
|
||||
import cairosvg
|
||||
except:
|
||||
raise self.skipTest("libraies svglib and reportlab not found")
|
||||
raise self.skipTest("cairosvg library was not found")
|
||||
else:
|
||||
cols = 3
|
||||
rows = 5
|
||||
@@ -104,8 +101,7 @@ class aruco_objdetect_test(NewOpenCVTests):
|
||||
board_height, "charuco_checkboard", marker_size, os.path.join(basedir, aruco_type_str+'.json.gz'), 0)
|
||||
pm.make_charuco_board()
|
||||
pm.save()
|
||||
drawing = svg2rlg(filesvg)
|
||||
renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=72)
|
||||
cairosvg.svg2png(url=filesvg, write_to=filepng, background_color="white")
|
||||
from_svg_img = cv.imread(filepng)
|
||||
|
||||
#test
|
||||
|
||||
@@ -368,6 +368,16 @@ if(WITH_GDAL)
|
||||
else()
|
||||
set(HAVE_GDAL YES)
|
||||
ocv_include_directories(${GDAL_INCLUDE_DIR})
|
||||
|
||||
# GDAL_VERSION requires CMake 3.14 or later, if not found, GDAL_RELEASE_NAME is used instead.
|
||||
# See https://cmake.org/cmake/help/latest/module/FindGDAL.html
|
||||
if(NOT GDAL_VERSION)
|
||||
if(EXISTS "${GDAL_INCLUDE_DIR}/gdal_version.h")
|
||||
file(STRINGS "${GDAL_INCLUDE_DIR}/gdal_version.h" gdal_version_str REGEX "^#[\t ]+define[\t ]+GDAL_RELEASE_NAME[\t ]*")
|
||||
string(REGEX REPLACE "^#\[\t ]+define[\t ]+GDAL_RELEASE_NAME[\t ]+\"([^ \\n]*)\"" "\\1" GDAL_VERSION "${gdal_version_str}")
|
||||
unset(gdal_version_str)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && (defined _M_ARM64 || defined _M_ARM64EC))
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__))/* || (defined _MSC_VER && (defined _M_ARM64 || defined _M_ARM64EC)) */
|
||||
// Windows + ARM64 case disabled: https://github.com/opencv/opencv/issues/25052
|
||||
|
||||
#include "arm_neon.h"
|
||||
int test()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && (defined _M_ARM64 || defined _M_ARM64EC))
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) /* || (defined _MSC_VER && (defined _M_ARM64 || defined _M_ARM64EC)) */
|
||||
// Windows + ARM64 case disabled: https://github.com/opencv/opencv/issues/25052
|
||||
#include "arm_neon.h"
|
||||
|
||||
float16x8_t vld1q_as_f16(const float* src)
|
||||
@@ -36,7 +37,7 @@ void test()
|
||||
vprintreg("s1*s2[0]+s1*s2[1] + ... + s1*s2[7]", d);
|
||||
}
|
||||
#else
|
||||
#error "FP16 is not supported"
|
||||
#error "NEON FP16 is not supported"
|
||||
#endif
|
||||
|
||||
int main()
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 42 KiB |
@@ -83,10 +83,15 @@ We map the 32-bit float HDR data into the range [0..1].
|
||||
Actually, in some cases the values can be larger than 1 or lower the 0, so notice
|
||||
we will later have to clip the data in order to avoid overflow.
|
||||
|
||||
@note: The function `cv.createTonemap()` uses a default gamma value of 1.0.
|
||||
Set it explicitly to 2.2 to match standard display brightness and ensure consistent tone mapping results.
|
||||
|
||||
|
||||
@code{.py}
|
||||
# Tonemap HDR image
|
||||
# Tonemap HDR images using gamma correction (set gamma=2.2 for standard display brightness)
|
||||
tonemap1 = cv.createTonemap(gamma=2.2)
|
||||
res_debevec = tonemap1.process(hdr_debevec.copy())
|
||||
res_robertson = tonemap1.process(hdr_robertson.copy())
|
||||
@endcode
|
||||
|
||||
### 4. Merge exposures using Mertens fusion
|
||||
@@ -125,6 +130,8 @@ You can see the different results but consider that each algorithm have addition
|
||||
extra parameters that you should fit to get your desired outcome. Best practice is
|
||||
to try the different methods and see which one performs best for your scene.
|
||||
|
||||
The results below were generated with a gamma value of 2.2 during tonemapping.
|
||||
|
||||
### Debevec:
|
||||
|
||||

|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
#include <opencv2/core/base.hpp>
|
||||
#include "ipp_utils.hpp"
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
#if IPP_VERSION_X100 >= 810
|
||||
|
||||
#if defined(HAVE_IPP_IW)
|
||||
int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width,
|
||||
int dst_height, const double M[6], int interpolation, int borderType, const double borderValue[4]);
|
||||
|
||||
@@ -18,15 +19,12 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
|
||||
//#define cv_hal_warpAffine ipp_hal_warpAffine
|
||||
#endif
|
||||
|
||||
#if IPP_VERSION_X100 >= 810
|
||||
int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width,
|
||||
int dst_height, const double M[9], int interpolation, int borderType, const double borderValue[4]);
|
||||
|
||||
// Does not pass tests in 5.x branch
|
||||
//#undef cv_hal_warpPerspective
|
||||
//#define cv_hal_warpPerspective ipp_hal_warpPerspective
|
||||
#endif
|
||||
|
||||
|
||||
int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height,
|
||||
uchar *dst_data, size_t dst_step, int dst_width, int dst_height,
|
||||
@@ -35,5 +33,6 @@ int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int s
|
||||
#undef cv_hal_remap32f
|
||||
#define cv_hal_remap32f ipp_hal_remap32f
|
||||
|
||||
#endif //IPP_VERSION_X100 >= 810
|
||||
|
||||
#endif //__IPP_HAL_IMGPROC_HPP__
|
||||
|
||||
@@ -73,11 +73,37 @@ static inline int ippiSuggestThreadsNum(size_t width, size_t height, size_t elem
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int ippiSuggestRowThreadsNum(size_t width, size_t height, size_t elemSize, size_t payloadSize)
|
||||
{
|
||||
int num_threads = cv::getNumThreads();
|
||||
if(num_threads > 1)
|
||||
{
|
||||
long rowThreads = static_cast<long>(height);
|
||||
|
||||
// row-based range shall not allow to split rows
|
||||
num_threads = (rowThreads < num_threads) ? rowThreads : num_threads;
|
||||
long rows_per_thread = (rowThreads + num_threads - 1) / num_threads;
|
||||
size_t item_size = width * elemSize; // row size in bytes
|
||||
|
||||
if(static_cast<size_t>(item_size * rows_per_thread) < payloadSize)
|
||||
{
|
||||
long items_per_thread = IPP_MAX(1L, static_cast<long>(payloadSize / item_size ));
|
||||
num_threads = static_cast<int>((height + items_per_thread - 1L) / items_per_thread);
|
||||
}
|
||||
}
|
||||
return num_threads;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
static inline int ippiSuggestThreadsNum(const ::ipp::IwiImage &image, double multiplier)
|
||||
{
|
||||
return ippiSuggestThreadsNum(image.m_size.width, image.m_size.height, image.m_typeSize*image.m_channels, multiplier);
|
||||
}
|
||||
|
||||
static inline int ippiSuggestRowThreadsNum(const ::ipp::IwiImage &image, size_t payloadSize)
|
||||
{
|
||||
return ippiSuggestRowThreadsNum(image.m_size.width, image.m_size.height, image.m_typeSize*image.m_channels, payloadSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__PRECOMP_IPP_HPP__
|
||||
|
||||
+249
-159
@@ -4,24 +4,28 @@
|
||||
|
||||
#include "ipp_hal_imgproc.hpp"
|
||||
|
||||
#if IPP_VERSION_X100 >= 810 // integrated IPP warping/remap ABI is available since IPP v8.1
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/core/base.hpp>
|
||||
#include "precomp_ipp.hpp"
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
#include "iw++/iw.hpp"
|
||||
#endif
|
||||
// Uncomment to enforce IPP calls for all supported by IPP configurations
|
||||
// #define IPP_CALLS_ENFORCED
|
||||
|
||||
#define IPP_WARPAFFINE_PARALLEL 1
|
||||
#define CV_IPP_SAFE_CALL(pFunc, pFlag, ...) if (pFunc(__VA_ARGS__) != ippStsNoErr) {*pFlag = false; return;}
|
||||
#define CV_TYPE(src_type) (src_type & (CV_DEPTH_MAX - 1))
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
// Warp affine section
|
||||
|
||||
#include "iw++/iw.hpp"
|
||||
|
||||
class ipp_warpAffineParallel: public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
ipp_warpAffineParallel(::ipp::IwiImage &src, ::ipp::IwiImage &dst, IppiInterpolationType _inter, double (&_coeffs)[2][3], ::ipp::IwiBorderType _borderType, IwTransDirection _iwTransDirection, bool *_ok):m_src(src), m_dst(dst)
|
||||
{
|
||||
pOk = _ok;
|
||||
ok = _ok;
|
||||
|
||||
inter = _inter;
|
||||
borderType = _borderType;
|
||||
@@ -31,15 +35,14 @@ public:
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = _coeffs[i][j];
|
||||
|
||||
*pOk = true;
|
||||
*ok = true;
|
||||
}
|
||||
~ipp_warpAffineParallel() {}
|
||||
|
||||
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
if(*pOk == false)
|
||||
if(*ok == false)
|
||||
return;
|
||||
|
||||
try
|
||||
@@ -49,9 +52,10 @@ public:
|
||||
}
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
*pOk = false;
|
||||
*ok = false;
|
||||
return;
|
||||
}
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
}
|
||||
private:
|
||||
::ipp::IwiImage &m_src;
|
||||
@@ -62,20 +66,29 @@ private:
|
||||
::ipp::IwiBorderType borderType;
|
||||
IwTransDirection iwTransDirection;
|
||||
|
||||
bool *pOk;
|
||||
bool *ok;
|
||||
const ipp_warpAffineParallel& operator= (const ipp_warpAffineParallel&);
|
||||
};
|
||||
|
||||
#if (IPP_VERSION_X100 >= 700)
|
||||
int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width,
|
||||
int dst_height, const double M[6], int interpolation, int borderType, const double borderValue[4])
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
|
||||
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
|
||||
if((int)ippInter < 0 || interpolation > 2)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
#if defined(IPP_CALLS_ENFORCED)
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][3]={{{1, 1, 0}, {0, 0, 0}, {1, 1, 0}, {1, 1, 0}}, //8U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8S
|
||||
{{1, 1, 0}, {0, 0, 0}, {1, 1, 0}, {1, 1, 0}}, //16U
|
||||
{{1, 1, 0}, {0, 0, 0}, {1, 1, 0}, {1, 1, 0}}, //16S
|
||||
{{1, 1, 0}, {0, 0, 0}, {1, 1, 0}, {1, 1, 0}}, //32S
|
||||
{{1, 1, 0}, {0, 0, 0}, {1, 1, 0}, {1, 1, 0}}, //32F
|
||||
{{1, 1, 0}, {0, 0, 0}, {1, 1, 0}, {1, 1, 0}}}; //64F
|
||||
#else // IPP_CALLS_ENFORCED is not defined, results are strictly aligned to OpenCV implementation
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][3]={{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8S
|
||||
@@ -84,6 +97,7 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32F
|
||||
{{1, 0, 0}, {0, 0, 0}, {1, 0, 0}, {1, 0, 0}}}; //64F
|
||||
#endif
|
||||
|
||||
if(impl[CV_TYPE(src_type)][CV_MAT_CN(src_type)-1][interpolation] == 0)
|
||||
{
|
||||
@@ -107,21 +121,24 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = M[i*3 + j];
|
||||
|
||||
const int threads = ippiSuggestThreadsNum(iwDst, 2);
|
||||
int min_payload = 1 << 16; // 64KB shall be minimal per thread to maximize scalability for warping functions
|
||||
const int threads = ippiSuggestRowThreadsNum(iwDst, min_payload);
|
||||
|
||||
if(IPP_WARPAFFINE_PARALLEL && threads > 1)
|
||||
if (threads > 1)
|
||||
{
|
||||
bool ok = true;
|
||||
bool ok = true;
|
||||
cv::Range range(0, (int)iwDst.m_size.height);
|
||||
ipp_warpAffineParallel invoker(iwSrc, iwDst, ippInter, coeffs, ippBorder, iwTransDirection, &ok);
|
||||
if(!ok)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
parallel_for_(range, invoker, threads*4);
|
||||
parallel_for_(range, invoker, threads);
|
||||
|
||||
if(!ok)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiWarpAffine, iwSrc, iwDst, coeffs, iwTransDirection, ippInter, ::ipp::IwiWarpAffineParams(), ippBorder);
|
||||
}
|
||||
}
|
||||
@@ -132,8 +149,10 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
|
||||
|
||||
return CV_HAL_ERROR_OK;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // HAVE_IPP_IW
|
||||
|
||||
// End of Warp affine section
|
||||
|
||||
typedef IppStatus (CV_STDCALL* ippiSetFunc)(const void*, void *, int, IppiSize);
|
||||
|
||||
@@ -194,18 +213,46 @@ static bool IPPSet(const double value[4], void *dataPointer, int step, IppiSize
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (IPP_VERSION_X100 >= 810)
|
||||
// Warp perspective section
|
||||
|
||||
typedef IppStatus (CV_STDCALL* ippiWarpPerspectiveFunc)(const Ipp8u*, int, Ipp8u*, int,IppiPoint, IppiSize, const IppiWarpSpec*,Ipp8u*);
|
||||
typedef IppStatus (CV_STDCALL* ippiWarpPerspectiveInitFunc)(IppiSize, IppiRect, IppiSize, IppDataType,const double [3][3], IppiWarpDirection, int, IppiBorderType, const Ipp64f *, int, IppiWarpSpec*);
|
||||
|
||||
class IPPWarpPerspectiveInvoker :
|
||||
public cv::ParallelLoopBody
|
||||
{
|
||||
// Mem object ot simplify IPP memory lifetime control
|
||||
struct IPPWarpPerspectiveMem
|
||||
{
|
||||
IppiWarpSpec* pSpec = nullptr;
|
||||
Ipp8u* pBuffer = nullptr;
|
||||
|
||||
IPPWarpPerspectiveMem() = default;
|
||||
IPPWarpPerspectiveMem (const IPPWarpPerspectiveMem&) = delete;
|
||||
IPPWarpPerspectiveMem& operator= (const IPPWarpPerspectiveMem&) = delete;
|
||||
|
||||
void AllocateSpec(int size)
|
||||
{
|
||||
pSpec = (IppiWarpSpec*)ippMalloc_L(size);
|
||||
}
|
||||
|
||||
void AllocateBuffer(int size)
|
||||
{
|
||||
pBuffer = (Ipp8u*)ippMalloc_L(size);
|
||||
}
|
||||
|
||||
~IPPWarpPerspectiveMem()
|
||||
{
|
||||
if (nullptr != pSpec) ippFree(pSpec);
|
||||
if (nullptr != pBuffer) ippFree(pBuffer);
|
||||
}
|
||||
};
|
||||
public:
|
||||
IPPWarpPerspectiveInvoker(int _src_type, cv::Mat &_src, size_t _src_step, cv::Mat &_dst, size_t _dst_step, IppiInterpolationType _interpolation,
|
||||
double (&_coeffs)[3][3], int &_borderType, const double _borderValue[4], ippiWarpPerspectiveFunc _func,
|
||||
double (&_coeffs)[3][3], int &_borderType, const double _borderValue[4], ippiWarpPerspectiveFunc _func, ippiWarpPerspectiveInitFunc _initFunc,
|
||||
bool *_ok) :
|
||||
ParallelLoopBody(), src_type(_src_type), src(_src), src_step(_src_step), dst(_dst), dst_step(_dst_step), inter(_interpolation), coeffs(_coeffs),
|
||||
borderType(_borderType), func(_func), ok(_ok)
|
||||
borderType(_borderType), func(_func), initFunc(_initFunc), ok(_ok)
|
||||
{
|
||||
memcpy(this->borderValue, _borderValue, sizeof(this->borderValue));
|
||||
*ok = true;
|
||||
@@ -214,9 +261,13 @@ public:
|
||||
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
IppiWarpSpec* pSpec = 0;
|
||||
int specSize = 0, initSize = 0, bufSize = 0; Ipp8u* pBuffer = 0;
|
||||
IppiPoint dstRoiOffset = {0, 0};
|
||||
if (*ok == false)
|
||||
return;
|
||||
|
||||
IPPWarpPerspectiveMem mem;
|
||||
|
||||
int specSize = 0, initSize = 0, bufSize = 0;
|
||||
|
||||
IppiWarpDirection direction = ippWarpBackward; //fixed for IPP
|
||||
const Ipp32u numChannels = CV_MAT_CN(src_type);
|
||||
|
||||
@@ -225,48 +276,34 @@ public:
|
||||
IppiRect srcroi = {0, 0, src.cols, src.rows};
|
||||
|
||||
/* Spec and init buffer sizes */
|
||||
IppStatus status = ippiWarpPerspectiveGetSize(srcsize, srcroi, dstsize, ippiGetDataType(src_type), coeffs, inter, ippWarpBackward, ippiGetBorderType(borderType), &specSize, &initSize);
|
||||
CV_IPP_SAFE_CALL(ippiWarpPerspectiveGetSize, ok, srcsize, srcroi, dstsize, ippiGetDataType(src_type), coeffs, inter, ippWarpBackward, ippiGetBorderType(borderType), &specSize, &initSize);
|
||||
|
||||
pSpec = (IppiWarpSpec*)ippMalloc_L(specSize);
|
||||
mem.AllocateSpec(specSize);
|
||||
|
||||
if (inter == ippLinear)
|
||||
CV_IPP_SAFE_CALL(initFunc, ok, srcsize, srcroi, dstsize, ippiGetDataType(src_type), coeffs, direction, numChannels, ippiGetBorderType(borderType),
|
||||
borderValue, 0, mem.pSpec);
|
||||
|
||||
CV_IPP_SAFE_CALL(ippiWarpGetBufferSize, ok, mem.pSpec, dstsize, &bufSize);
|
||||
|
||||
mem.AllocateBuffer(bufSize);
|
||||
|
||||
IppiPoint dstRoiOffset = {0, range.start};
|
||||
IppiSize dstRoiSize = {dst.cols, range.size()};
|
||||
auto* pDst = dst.ptr(range.start);
|
||||
if (borderType == cv::BorderTypes::BORDER_CONSTANT &&
|
||||
!IPPSet(borderValue, pDst, (int)dst_step, dstRoiSize, src.channels(), src.depth()))
|
||||
{
|
||||
status = ippiWarpPerspectiveLinearInit(srcsize, srcroi, dstsize, ippiGetDataType(src_type), coeffs, direction, numChannels, ippiGetBorderType(borderType),
|
||||
borderValue, 0, pSpec);
|
||||
} else
|
||||
{
|
||||
status = ippiWarpPerspectiveNearestInit(srcsize, srcroi, dstsize, ippiGetDataType(src_type), coeffs, direction, numChannels, ippiGetBorderType(borderType),
|
||||
borderValue, 0, pSpec);
|
||||
}
|
||||
|
||||
status = ippiWarpGetBufferSize(pSpec, dstsize, &bufSize);
|
||||
pBuffer = (Ipp8u*)ippMalloc_L(bufSize);
|
||||
IppiSize dstRoiSize = dstsize;
|
||||
|
||||
int cnn = src.channels();
|
||||
|
||||
if( borderType == cv::BorderTypes::BORDER_CONSTANT )
|
||||
{
|
||||
IppiSize setSize = {dst.cols, range.end - range.start};
|
||||
void *dataPointer = dst.ptr(range.start);
|
||||
if( !IPPSet( borderValue, dataPointer, (int)dst.step[0], setSize, cnn, src.depth() ) )
|
||||
{
|
||||
*ok = false;
|
||||
ippsFree(pBuffer);
|
||||
ippsFree(pSpec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
status = CV_INSTRUMENT_FUN_IPP(func, src.ptr(), (int)src_step, dst.ptr(), (int)dst_step, dstRoiOffset, dstRoiSize, pSpec, pBuffer);
|
||||
if (status != ippStsNoErr)
|
||||
*ok = false;
|
||||
else
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
return;
|
||||
}
|
||||
ippsFree(pBuffer);
|
||||
ippsFree(pSpec);
|
||||
|
||||
if (ippStsNoErr != CV_INSTRUMENT_FUN_IPP(func, src.ptr(), (int)src_step, pDst, (int)dst_step, dstRoiOffset, dstRoiSize, mem.pSpec, mem.pBuffer))
|
||||
{
|
||||
*ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
}
|
||||
private:
|
||||
int src_type;
|
||||
@@ -279,8 +316,8 @@ private:
|
||||
int borderType;
|
||||
double borderValue[4];
|
||||
ippiWarpPerspectiveFunc func;
|
||||
ippiWarpPerspectiveInitFunc initFunc;
|
||||
bool *ok;
|
||||
|
||||
const IPPWarpPerspectiveInvoker& operator= (const IPPWarpPerspectiveInvoker&);
|
||||
};
|
||||
|
||||
@@ -288,87 +325,114 @@ int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step
|
||||
int dst_width, int dst_height, const double M[9], int interpolation, int borderType, const double borderValue[4])
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
ippiWarpPerspectiveFunc ippFunc = 0;
|
||||
|
||||
if (src_height <= 1 || src_width <= 1)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ippiWarpPerspectiveFunc ippFunc = nullptr;
|
||||
ippiWarpPerspectiveInitFunc ippInitFunc = nullptr;
|
||||
|
||||
if (interpolation == cv::InterpolationFlags::INTER_NEAREST)
|
||||
{
|
||||
ippFunc = src_type == CV_8UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_8u_C1R :
|
||||
src_type == CV_8UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_8u_C3R :
|
||||
src_type == CV_8UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_8u_C4R :
|
||||
src_type == CV_16UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16u_C1R :
|
||||
src_type == CV_16UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16u_C3R :
|
||||
src_type == CV_16UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16u_C4R :
|
||||
src_type == CV_16SC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16s_C1R :
|
||||
src_type == CV_16SC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16s_C3R :
|
||||
src_type == CV_16SC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16s_C4R :
|
||||
src_type == CV_32FC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_32f_C1R :
|
||||
src_type == CV_32FC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_32f_C3R :
|
||||
src_type == CV_32FC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_32f_C4R : 0;
|
||||
ippInitFunc = ippiWarpPerspectiveNearestInit;
|
||||
ippFunc =
|
||||
src_type == CV_8UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_8u_C1R :
|
||||
src_type == CV_8UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_8u_C3R :
|
||||
src_type == CV_8UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_8u_C4R :
|
||||
src_type == CV_16UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16u_C1R :
|
||||
src_type == CV_16UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16u_C3R :
|
||||
src_type == CV_16UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16u_C4R :
|
||||
src_type == CV_16SC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16s_C1R :
|
||||
src_type == CV_16SC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16s_C3R :
|
||||
src_type == CV_16SC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16s_C4R :
|
||||
src_type == CV_32FC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_32f_C1R :
|
||||
src_type == CV_32FC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_32f_C3R :
|
||||
src_type == CV_32FC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_32f_C4R : nullptr;
|
||||
}
|
||||
else if (interpolation == cv::InterpolationFlags::INTER_LINEAR)
|
||||
{
|
||||
ippFunc = src_type == CV_8UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_8u_C1R :
|
||||
src_type == CV_8UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_8u_C3R :
|
||||
src_type == CV_8UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_8u_C4R :
|
||||
src_type == CV_16UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16u_C1R :
|
||||
src_type == CV_16UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16u_C3R :
|
||||
src_type == CV_16UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16u_C4R :
|
||||
src_type == CV_16SC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16s_C1R :
|
||||
src_type == CV_16SC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16s_C3R :
|
||||
src_type == CV_16SC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16s_C4R :
|
||||
src_type == CV_32FC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_32f_C1R :
|
||||
src_type == CV_32FC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_32f_C3R :
|
||||
src_type == CV_32FC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_32f_C4R : 0;
|
||||
ippInitFunc = ippiWarpPerspectiveLinearInit;
|
||||
ippFunc =
|
||||
src_type == CV_8UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_8u_C1R :
|
||||
src_type == CV_8UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_8u_C3R :
|
||||
src_type == CV_8UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_8u_C4R :
|
||||
src_type == CV_16UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16u_C1R :
|
||||
src_type == CV_16UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16u_C3R :
|
||||
src_type == CV_16UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16u_C4R :
|
||||
src_type == CV_16SC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16s_C1R :
|
||||
src_type == CV_16SC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16s_C3R :
|
||||
src_type == CV_16SC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16s_C4R :
|
||||
src_type == CV_32FC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_32f_C1R :
|
||||
src_type == CV_32FC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_32f_C3R :
|
||||
src_type == CV_32FC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_32f_C4R : nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if(src_height == 1 || src_width == 1) return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
int mode =
|
||||
interpolation == cv::InterpolationFlags::INTER_NEAREST ? IPPI_INTER_NN :
|
||||
interpolation == cv::InterpolationFlags::INTER_LINEAR ? IPPI_INTER_LINEAR : 0;
|
||||
|
||||
if (mode == 0 || ippFunc == 0)
|
||||
if (ippFunc == nullptr)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
#if defined(IPP_CALLS_ENFORCED)
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][2]={{{0, 0}, {1, 1}, {0, 0}, {0, 0}}, //8U
|
||||
{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, //8S
|
||||
{{0, 0}, {1, 1}, {0, 1}, {0, 1}}, //16U
|
||||
{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, //16S
|
||||
{{1, 1}, {1, 1}, {1, 0}, {1, 1}}, //32S
|
||||
{{1, 0}, {1, 0}, {0, 0}, {1, 0}}, //32F
|
||||
{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}; //64F
|
||||
char impl[CV_DEPTH_MAX][4][2]={{{1, 1}, {0, 0}, {1, 1}, {1, 1}}, //8U
|
||||
{{0, 0}, {0, 0}, {0, 0}, {0, 0}}, //8S
|
||||
{{1, 1}, {0, 0}, {1, 1}, {1, 1}}, //16U
|
||||
{{1, 1}, {0, 0}, {1, 1}, {1, 1}}, //16S
|
||||
{{1, 1}, {0, 0}, {1, 1}, {1, 1}}, //32S
|
||||
{{1, 1}, {0, 0}, {1, 1}, {1, 1}}, //32F
|
||||
{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}; //64F
|
||||
#else // IPP_CALLS_ENFORCED is not defined, results are strictly aligned to OpenCV implementation
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][2]={{{0, 0}, {0, 0}, {0, 0}, {0, 0}}, //8U
|
||||
{{0, 0}, {0, 0}, {0, 0}, {0, 0}}, //8S
|
||||
{{0, 0}, {0, 0}, {0, 1}, {0, 1}}, //16U
|
||||
{{1, 1}, {0, 0}, {1, 1}, {1, 1}}, //16S
|
||||
{{1, 1}, {0, 0}, {1, 0}, {1, 1}}, //32S
|
||||
{{1, 0}, {0, 0}, {0, 0}, {1, 0}}, //32F
|
||||
{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}; //64F
|
||||
#endif
|
||||
|
||||
const char type_size[CV_DEPTH_MAX] = {1,1,2,2,4,4,8};
|
||||
if(impl[CV_TYPE(src_type)][CV_MAT_CN(src_type)-1][interpolation] == 0)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
double coeffs[3][3];
|
||||
for( int i = 0; i < 3; i++ )
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = M[i*3 + j];
|
||||
|
||||
bool ok;
|
||||
bool ok = true;
|
||||
cv::Range range(0, dst_height);
|
||||
cv::Mat src(cv::Size(src_width, src_height), src_type, const_cast<uchar*>(src_data), src_step);
|
||||
cv::Mat dst(cv::Size(dst_width, dst_height), src_type, dst_data, dst_step);
|
||||
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
|
||||
IPPWarpPerspectiveInvoker invoker(src_type, src, src_step, dst, dst_step, ippInter, coeffs, borderType, borderValue, ippFunc, &ok);
|
||||
parallel_for_(range, invoker, dst.total()/(double)(1<<16));
|
||||
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
|
||||
|
||||
if( ok )
|
||||
int min_payload = 1 << 16; // 64KB shall be minimal per thread to maximize scalability for warping functions
|
||||
int num_threads = ippiSuggestRowThreadsNum(dst_width, dst_height, type_size[CV_TYPE(src_type)]*CV_MAT_CN(src_type), min_payload);
|
||||
|
||||
IPPWarpPerspectiveInvoker invoker(src_type, src, src_step, dst, dst_step, ippInter, coeffs, borderType, borderValue, ippFunc, ippInitFunc, &ok);
|
||||
|
||||
(num_threads > 1) ? parallel_for_(range, invoker, num_threads) : invoker(range);
|
||||
|
||||
if (ok)
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
CV_IMPL_ADD(CV_IMPL_IPP | CV_IMPL_MT);
|
||||
return CV_HAL_ERROR_OK;
|
||||
}
|
||||
return CV_HAL_ERROR_OK;
|
||||
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
|
||||
// End of Warp perspective section
|
||||
|
||||
// Remap section
|
||||
|
||||
typedef IppStatus(CV_STDCALL *ippiRemap)(const void *pSrc, IppiSize srcSize, int srcStep, IppiRect srcRoi,
|
||||
const Ipp32f *pxMap, int xMapStep, const Ipp32f *pyMap, int yMapStep,
|
||||
@@ -391,6 +455,10 @@ public:
|
||||
|
||||
virtual void operator()(const cv::Range &range) const
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
if (*ok == false)
|
||||
return;
|
||||
|
||||
IppiRect srcRoiRect = {0, 0, src_width, src_height};
|
||||
uchar *dst_roi_data = dst + range.start * dst_step;
|
||||
IppiSize dstRoiSize = ippiSize(dst_width, range.size());
|
||||
@@ -403,14 +471,15 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (CV_INSTRUMENT_FUN_IPP(ippFunc, src, {src_width, src_height}, (int)src_step, srcRoiRect,
|
||||
if (ippStsNoErr != CV_INSTRUMENT_FUN_IPP(ippFunc, src, {src_width, src_height}, (int)src_step, srcRoiRect,
|
||||
mapx, (int)mapx_step, mapy, (int)mapy_step,
|
||||
dst_roi_data, (int)dst_step, dstRoiSize, ippInterpolation) < 0)
|
||||
*ok = false;
|
||||
else
|
||||
dst_roi_data, (int)dst_step, dstRoiSize, ippInterpolation))
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP | CV_IMPL_MT);
|
||||
*ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
CV_IMPL_ADD(CV_IMPL_IPP | CV_IMPL_MT);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -436,53 +505,74 @@ int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int s
|
||||
float *mapx, size_t mapx_step, float *mapy, size_t mapy_step,
|
||||
int interpolation, int border_type, const double border_value[4])
|
||||
{
|
||||
if ((interpolation == cv::INTER_LINEAR || interpolation == cv::INTER_CUBIC || interpolation == cv::INTER_NEAREST) &&
|
||||
(border_type == cv::BORDER_CONSTANT || border_type == cv::BORDER_TRANSPARENT))
|
||||
if (!((interpolation == cv::INTER_LINEAR || interpolation == cv::INTER_CUBIC || interpolation == cv::INTER_NEAREST) &&
|
||||
(border_type == cv::BORDER_CONSTANT || border_type == cv::BORDER_TRANSPARENT)))
|
||||
{
|
||||
int ippInterpolation =
|
||||
interpolation == cv::INTER_NEAREST ? IPPI_INTER_NN : interpolation == cv::INTER_LINEAR ? IPPI_INTER_LINEAR
|
||||
: IPPI_INTER_CUBIC;
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][3]={{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //16U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //16S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32F
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; //64F
|
||||
int ippInterpolation = ippiGetInterpolation(interpolation);
|
||||
|
||||
if (impl[CV_TYPE(src_type)][CV_MAT_CN(src_type) - 1][interpolation] == 0)
|
||||
#if defined(IPP_CALLS_ENFORCED)
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][3] = {{{1, 1, 1}, {0, 0, 0}, {1, 1, 1}, {1, 1, 1}}, //8U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8S
|
||||
{{1, 1, 1}, {0, 0, 0}, {1, 1, 1}, {1, 1, 1}}, //16U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //16S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32S
|
||||
{{1, 1, 1}, {0, 0, 0}, {1, 1, 1}, {1, 1, 1}}, //32F
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; //64F
|
||||
#else // IPP_CALLS_ENFORCED is not defined, results are strictly aligned to OpenCV implementation
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][3] = {{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //16U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //16S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32F
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; //64F
|
||||
#endif
|
||||
const char type_size[CV_DEPTH_MAX] = {1,1,2,2,4,4,8};
|
||||
|
||||
if (impl[CV_TYPE(src_type)][CV_MAT_CN(src_type) - 1][interpolation] == 0)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ippiRemap ippFunc =
|
||||
src_type == CV_8UC1 ? (ippiRemap)ippiRemap_8u_C1R :
|
||||
src_type == CV_8UC3 ? (ippiRemap)ippiRemap_8u_C3R :
|
||||
src_type == CV_8UC4 ? (ippiRemap)ippiRemap_8u_C4R :
|
||||
src_type == CV_16UC1 ? (ippiRemap)ippiRemap_16u_C1R :
|
||||
src_type == CV_16UC3 ? (ippiRemap)ippiRemap_16u_C3R :
|
||||
src_type == CV_16UC4 ? (ippiRemap)ippiRemap_16u_C4R :
|
||||
src_type == CV_32FC1 ? (ippiRemap)ippiRemap_32f_C1R :
|
||||
src_type == CV_32FC3 ? (ippiRemap)ippiRemap_32f_C3R :
|
||||
src_type == CV_32FC4 ? (ippiRemap)ippiRemap_32f_C4R : 0;
|
||||
|
||||
if (ippFunc)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
IPPRemapInvoker invoker(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width,
|
||||
mapx, mapx_step, mapy, mapy_step, ippFunc, ippInterpolation, border_type, border_value, &ok);
|
||||
cv::Range range(0, dst_height);
|
||||
|
||||
int min_payload = 1 << 16; // 64KB shall be minimal per thread to maximize scalability for warping functions
|
||||
int num_threads = ippiSuggestRowThreadsNum(dst_width, dst_height, type_size[CV_TYPE(src_type)]*CV_MAT_CN(src_type), min_payload);
|
||||
|
||||
cv::parallel_for_(range, invoker, num_threads);
|
||||
|
||||
if (ok)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ippiRemap ippFunc =
|
||||
src_type == CV_8UC1 ? (ippiRemap)ippiRemap_8u_C1R : src_type == CV_8UC3 ? (ippiRemap)ippiRemap_8u_C3R
|
||||
: src_type == CV_8UC4 ? (ippiRemap)ippiRemap_8u_C4R
|
||||
: src_type == CV_16UC1 ? (ippiRemap)ippiRemap_16u_C1R
|
||||
: src_type == CV_16UC3 ? (ippiRemap)ippiRemap_16u_C3R
|
||||
: src_type == CV_16UC4 ? (ippiRemap)ippiRemap_16u_C4R
|
||||
: src_type == CV_32FC1 ? (ippiRemap)ippiRemap_32f_C1R
|
||||
: src_type == CV_32FC3 ? (ippiRemap)ippiRemap_32f_C3R
|
||||
: src_type == CV_32FC4 ? (ippiRemap)ippiRemap_32f_C4R
|
||||
: 0;
|
||||
|
||||
if (ippFunc)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
IPPRemapInvoker invoker(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width,
|
||||
mapx, mapx_step, mapy, mapy_step, ippFunc, ippInterpolation, border_type, border_value, &ok);
|
||||
cv::Range range(0, dst_height);
|
||||
cv::parallel_for_(range, invoker, dst_width * dst_height / (double)(1 << 16));
|
||||
|
||||
if (ok)
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP | CV_IMPL_MT);
|
||||
return CV_HAL_ERROR_OK;
|
||||
}
|
||||
CV_IMPL_ADD(CV_IMPL_IPP | CV_IMPL_MT);
|
||||
return CV_HAL_ERROR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// End of Remap section
|
||||
|
||||
#endif // IPP_VERSION_X100 >= 810
|
||||
|
||||
+12
-22
@@ -1,3 +1,12 @@
|
||||
@inproceedings{ding2023revisiting,
|
||||
title={Revisiting the P3P Problem},
|
||||
author={Ding, Yaqing and Yang, Jian and Larsson, Viktor and Olsson, Carl and {\AA}str{\"o}m, Kalle},
|
||||
booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
|
||||
pages={4872--4880},
|
||||
year={2023},
|
||||
url={https://openaccess.thecvf.com/content/CVPR2023/papers/Ding_Revisiting_the_P3P_Problem_CVPR_2023_paper.pdf}
|
||||
}
|
||||
|
||||
@article{lepetit2009epnp,
|
||||
title={Epnp: An accurate o (n) solution to the pnp problem},
|
||||
author={Lepetit, Vincent and Moreno-Noguer, Francesc and Fua, Pascal},
|
||||
@@ -6,27 +15,8 @@
|
||||
number={2},
|
||||
pages={155--166},
|
||||
year={2009},
|
||||
publisher={Springer}
|
||||
}
|
||||
|
||||
@article{gao2003complete,
|
||||
title={Complete solution classification for the perspective-three-point problem},
|
||||
author={Gao, Xiao-Shan and Hou, Xiao-Rong and Tang, Jianliang and Cheng, Hang-Fei},
|
||||
journal={Pattern Analysis and Machine Intelligence, IEEE Transactions on},
|
||||
volume={25},
|
||||
number={8},
|
||||
pages={930--943},
|
||||
year={2003},
|
||||
publisher={IEEE}
|
||||
}
|
||||
|
||||
@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}
|
||||
publisher={Springer},
|
||||
url={https://www.tugraz.at/fileadmin/user_upload/Institute/ICG/Images/team_lepetit/publications/lepetit_ijcv08.pdf}
|
||||
}
|
||||
|
||||
@inproceedings{strobl2011iccv,
|
||||
@@ -86,4 +76,4 @@
|
||||
pages={248--253},
|
||||
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}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ this case the function finds such a pose that minimizes reprojection error, that
|
||||
of squared distances between the observed projections "imagePoints" and the projected (using
|
||||
cv::projectPoints ) "objectPoints". Initial solution for non-planar "objectPoints" needs at least 6 points and uses the DLT algorithm.
|
||||
Initial solution for planar "objectPoints" needs at least 4 points and uses pose from homography decomposition.
|
||||
- cv::SOLVEPNP_P3P Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
"Complete Solution Classification for the Perspective-Three-Point Problem" (@cite gao2003complete).
|
||||
- cv::SOLVEPNP_P3P Method is based on the paper of Ding, Y., Yang, J., Larsson, V., Olsson, C., & Åstrom, K.
|
||||
"Revisiting the P3P Problem" (@cite ding2023revisiting).
|
||||
In this case the function requires exactly four object and image points.
|
||||
- cv::SOLVEPNP_AP3P Method is based on the paper of T. Ke, S. Roumeliotis
|
||||
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||
|
||||
@@ -391,7 +391,7 @@ enum SolvePnPMethod {
|
||||
//!< Initial solution for non-planar "objectPoints" needs at least 6 points and uses the DLT algorithm. \n
|
||||
//!< Initial solution for planar "objectPoints" needs at least 4 points and uses pose from homography decomposition.
|
||||
SOLVEPNP_EPNP = 1, //!< EPnP: Efficient Perspective-n-Point Camera Pose Estimation @cite lepetit2009epnp
|
||||
SOLVEPNP_P3P = 2, //!< Complete Solution Classification for the Perspective-Three-Point Problem @cite gao2003complete
|
||||
SOLVEPNP_P3P = 2, //!< Revisiting the P3P Problem @cite ding2023revisiting
|
||||
SOLVEPNP_AP3P = 3, //!< An Efficient Algebraic Solution to the Perspective-Three-Point Problem @cite Ke17
|
||||
SOLVEPNP_IPPE = 4, //!< Infinitesimal Plane-Based Pose Estimation @cite Collins14 \n
|
||||
//!< Object points must be coplanar.
|
||||
@@ -1117,8 +1117,8 @@ assumed.
|
||||
the model coordinate system to the camera coordinate system. A P3P problem has up to 4 solutions.
|
||||
@param tvecs Output translation vectors.
|
||||
@param flags Method for solving a P3P problem:
|
||||
- @ref SOLVEPNP_P3P Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
"Complete Solution Classification for the Perspective-Three-Point Problem" (@cite gao2003complete).
|
||||
- @ref SOLVEPNP_P3P Method is based on the paper of Ding, Y., Yang, J., Larsson, V., Olsson, C., & Åstrom, K.
|
||||
"Revisiting the P3P Problem" (@cite ding2023revisiting).
|
||||
- @ref SOLVEPNP_AP3P Method is based on the paper of T. Ke and S. Roumeliotis.
|
||||
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||
|
||||
|
||||
@@ -377,8 +377,15 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted
|
||||
for(size_t i = 0; i < n; i++ )
|
||||
{
|
||||
Vec2d pi = sdepth == CV_32F ? (Vec2d)srcf[i] : srcd[i]; // image point
|
||||
// u = fx * x' + cx (alpha = 0), v = fy * y' + cy =>
|
||||
// x' = (u - cx) / fx, y' = (v - cy) / fy
|
||||
Vec2d pw((pi[0] - c[0])/f[0], (pi[1] - c[1])/f[1]); // world point
|
||||
|
||||
// x' = (theta_d / r) * a, y' = (theta_d / r) * b =>
|
||||
// x'^2 + y'^2 = theta_d^2 * (a^2 + b^2) / r^2 =>
|
||||
// (r^2 = a^2 + b^2)
|
||||
// x'^2 + y'^2 = theta_d^2 =>
|
||||
// theta_d = sqrt(x'^2 + y'^2)
|
||||
double theta_d = sqrt(pw[0]*pw[0] + pw[1]*pw[1]);
|
||||
|
||||
// the current camera model is only valid up to 180 FOV
|
||||
@@ -397,9 +404,14 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted
|
||||
|
||||
for (int j = 0; j < maxCount; j++)
|
||||
{
|
||||
// theta_d = theta * (1 + k1 * theta^2 + k2 * theta^4 + k3 * theta^6 + k4 * theta^8) =>
|
||||
// f(theta) := theta * (1 + k1 * theta^2 + k2 * theta^4 + k3 * theta^6 + k4 * theta^8) - theta_d = 0
|
||||
// Newton's method: new_theta = theta - theta_fix, theta_fix := f(theta) / f'(theta)
|
||||
// f'(theta) = (theta * (1 + k1 * theta^2 + k2 * theta^4 + k3 * theta^6 + k4 * theta^8) - theta_d)' =
|
||||
// (theta + k1 * theta^3 + k2 * theta^5 + k3 * theta^7 + k4 * theta^9 - theta_d)' =
|
||||
// 1 + 3 * k1 * theta^2 + 5 * k2 * theta^4 + 7 * k3 * theta^6 + 9 * k4 * theta^8
|
||||
double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta6*theta2;
|
||||
double k0_theta2 = k[0] * theta2, k1_theta4 = k[1] * theta4, k2_theta6 = k[2] * theta6, k3_theta8 = k[3] * theta8;
|
||||
/* new_theta = theta - theta_fix, theta_fix = f0(theta) / f0'(theta) */
|
||||
double theta_fix = (theta * (1 + k0_theta2 + k1_theta4 + k2_theta6 + k3_theta8) - theta_d) /
|
||||
(1 + 3*k0_theta2 + 5*k1_theta4 + 7*k2_theta6 + 9*k3_theta8);
|
||||
theta = theta - theta_fix;
|
||||
@@ -411,6 +423,10 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted
|
||||
}
|
||||
}
|
||||
|
||||
// x' = (theta_d / r) * a, y' = (theta_d / r) * b =>
|
||||
// a = x' * r / theta_d, b = y' * r / theta_d =>
|
||||
// (theta = atan(r) => r = tan(theta), scale := r / theta_d = tan(theta) / theta_d)
|
||||
// a = x' * scale, b = y' * scale
|
||||
scale = std::tan(theta) / theta_d;
|
||||
}
|
||||
else
|
||||
@@ -425,6 +441,7 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted
|
||||
|
||||
if ((converged || !isEps) && !theta_flipped)
|
||||
{
|
||||
// a = x' * scale, b = y' * scale
|
||||
Vec2d pu = pw * scale; //undistorted point
|
||||
Vec2d fi;
|
||||
|
||||
|
||||
+372
-453
@@ -6,469 +6,388 @@
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#include "polynom_solver.h"
|
||||
#include "p3p.h"
|
||||
|
||||
|
||||
namespace cv {
|
||||
|
||||
void p3p::init_inverse_parameters()
|
||||
// Copyright (c) 2020, Viktor Larsson
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the copyright holder nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: Yaqing Ding
|
||||
// Mark Shachkov
|
||||
|
||||
// https://github.com/PoseLib/PoseLib/blob/79fe59ada3122c50383ac06e043a5e04072c6711/PoseLib/solvers/p3p.cc
|
||||
namespace yaqding
|
||||
{
|
||||
inv_fx = 1. / fx;
|
||||
inv_fy = 1. / fy;
|
||||
cx_fx = cx / fx;
|
||||
cy_fy = cy / fy;
|
||||
}
|
||||
|
||||
p3p::p3p(cv::Mat cameraMatrix)
|
||||
{
|
||||
if (cameraMatrix.depth() == CV_32F)
|
||||
init_camera_parameters<float>(cameraMatrix);
|
||||
else
|
||||
init_camera_parameters<double>(cameraMatrix);
|
||||
init_inverse_parameters();
|
||||
}
|
||||
|
||||
p3p::p3p(double _fx, double _fy, double _cx, double _cy)
|
||||
{
|
||||
fx = _fx;
|
||||
fy = _fy;
|
||||
cx = _cx;
|
||||
cy = _cy;
|
||||
init_inverse_parameters();
|
||||
}
|
||||
|
||||
bool p3p::solve(cv::Mat& R, cv::Mat& tvec, const cv::Mat& opoints, const cv::Mat& ipoints)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
double rotation_matrix[3][3] = {}, translation[3] = {};
|
||||
std::vector<double> points;
|
||||
if (opoints.depth() == ipoints.depth())
|
||||
{
|
||||
if (opoints.depth() == CV_32F)
|
||||
extract_points<cv::Point3f,cv::Point2f>(opoints, ipoints, points);
|
||||
else
|
||||
extract_points<cv::Point3d,cv::Point2d>(opoints, ipoints, points);
|
||||
}
|
||||
else if (opoints.depth() == CV_32F)
|
||||
extract_points<cv::Point3f,cv::Point2d>(opoints, ipoints, points);
|
||||
else
|
||||
extract_points<cv::Point3d,cv::Point2f>(opoints, ipoints, points);
|
||||
|
||||
bool result = solve(rotation_matrix, translation,
|
||||
points[0], points[1], points[2], points[3], points[4],
|
||||
points[5], points[6], points[7], points[8], points[9],
|
||||
points[10], points[11], points[12], points[13], points[14],
|
||||
points[15], points[16], points[17], points[18], points[19]);
|
||||
cv::Mat(3, 1, CV_64F, translation).copyTo(tvec);
|
||||
cv::Mat(3, 3, CV_64F, rotation_matrix).copyTo(R);
|
||||
return result;
|
||||
}
|
||||
|
||||
int p3p::solve(std::vector<cv::Mat>& Rs, std::vector<cv::Mat>& tvecs, const cv::Mat& opoints, const cv::Mat& ipoints)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
double rotation_matrix[4][3][3] = {}, translation[4][3] = {};
|
||||
std::vector<double> points;
|
||||
if (opoints.depth() == ipoints.depth())
|
||||
{
|
||||
if (opoints.depth() == CV_32F)
|
||||
extract_points<cv::Point3f,cv::Point2f>(opoints, ipoints, points);
|
||||
else
|
||||
extract_points<cv::Point3d,cv::Point2d>(opoints, ipoints, points);
|
||||
}
|
||||
else if (opoints.depth() == CV_32F)
|
||||
extract_points<cv::Point3f,cv::Point2d>(opoints, ipoints, points);
|
||||
else
|
||||
extract_points<cv::Point3d,cv::Point2f>(opoints, ipoints, points);
|
||||
|
||||
const bool p4p = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F)) == 4;
|
||||
int solutions = solve(rotation_matrix, translation,
|
||||
points[0], points[1], points[2], points[3], points[4],
|
||||
points[5], points[6], points[7], points[8], points[9],
|
||||
points[10], points[11], points[12], points[13], points[14],
|
||||
points[15], points[16], points[17], points[18], points[19],
|
||||
p4p);
|
||||
|
||||
for (int i = 0; i < solutions; i++) {
|
||||
cv::Mat R, tvec;
|
||||
cv::Mat(3, 1, CV_64F, translation[i]).copyTo(tvec);
|
||||
cv::Mat(3, 3, CV_64F, rotation_matrix[i]).copyTo(R);
|
||||
|
||||
Rs.push_back(R);
|
||||
tvecs.push_back(tvec);
|
||||
}
|
||||
|
||||
return solutions;
|
||||
}
|
||||
|
||||
bool p3p::solve(double R[3][3], double t[3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2,
|
||||
double mu3, double mv3, double X3, double Y3, double Z3)
|
||||
{
|
||||
double Rs[4][3][3] = {}, ts[4][3] = {};
|
||||
|
||||
const bool p4p = true;
|
||||
int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2, mu3, mv3, X3, Y3, Z3, p4p);
|
||||
|
||||
if (n == 0)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < 3; i++) {
|
||||
for(int j = 0; j < 3; j++)
|
||||
R[i][j] = Rs[0][i][j];
|
||||
t[i] = ts[0][i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int p3p::solve(double R[4][3][3], double t[4][3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2,
|
||||
double mu3, double mv3, double X3, double Y3, double Z3,
|
||||
bool p4p)
|
||||
{
|
||||
double mk0, mk1, mk2;
|
||||
double norm;
|
||||
|
||||
mu0 = inv_fx * mu0 - cx_fx;
|
||||
mv0 = inv_fy * mv0 - cy_fy;
|
||||
norm = sqrt(mu0 * mu0 + mv0 * mv0 + 1);
|
||||
mk0 = 1. / norm; mu0 *= mk0; mv0 *= mk0;
|
||||
|
||||
mu1 = inv_fx * mu1 - cx_fx;
|
||||
mv1 = inv_fy * mv1 - cy_fy;
|
||||
norm = sqrt(mu1 * mu1 + mv1 * mv1 + 1);
|
||||
mk1 = 1. / norm; mu1 *= mk1; mv1 *= mk1;
|
||||
|
||||
mu2 = inv_fx * mu2 - cx_fx;
|
||||
mv2 = inv_fy * mv2 - cy_fy;
|
||||
norm = sqrt(mu2 * mu2 + mv2 * mv2 + 1);
|
||||
mk2 = 1. / norm; mu2 *= mk2; mv2 *= mk2;
|
||||
|
||||
mu3 = inv_fx * mu3 - cx_fx;
|
||||
mv3 = inv_fy * mv3 - cy_fy;
|
||||
|
||||
double distances[3];
|
||||
distances[0] = sqrt( (X1 - X2) * (X1 - X2) + (Y1 - Y2) * (Y1 - Y2) + (Z1 - Z2) * (Z1 - Z2) );
|
||||
distances[1] = sqrt( (X0 - X2) * (X0 - X2) + (Y0 - Y2) * (Y0 - Y2) + (Z0 - Z2) * (Z0 - Z2) );
|
||||
distances[2] = sqrt( (X0 - X1) * (X0 - X1) + (Y0 - Y1) * (Y0 - Y1) + (Z0 - Z1) * (Z0 - Z1) );
|
||||
|
||||
// Calculate angles
|
||||
double cosines[3];
|
||||
cosines[0] = mu1 * mu2 + mv1 * mv2 + mk1 * mk2;
|
||||
cosines[1] = mu0 * mu2 + mv0 * mv2 + mk0 * mk2;
|
||||
cosines[2] = mu0 * mu1 + mv0 * mv1 + mk0 * mk1;
|
||||
|
||||
double lengths[4][3] = {};
|
||||
int n = solve_for_lengths(lengths, distances, cosines);
|
||||
|
||||
int nb_solutions = 0;
|
||||
double reproj_errors[4];
|
||||
for(int i = 0; i < n; i++) {
|
||||
double M_orig[3][3];
|
||||
|
||||
M_orig[0][0] = lengths[i][0] * mu0;
|
||||
M_orig[0][1] = lengths[i][0] * mv0;
|
||||
M_orig[0][2] = lengths[i][0] * mk0;
|
||||
|
||||
M_orig[1][0] = lengths[i][1] * mu1;
|
||||
M_orig[1][1] = lengths[i][1] * mv1;
|
||||
M_orig[1][2] = lengths[i][1] * mk1;
|
||||
|
||||
M_orig[2][0] = lengths[i][2] * mu2;
|
||||
M_orig[2][1] = lengths[i][2] * mv2;
|
||||
M_orig[2][2] = lengths[i][2] * mk2;
|
||||
|
||||
if (!align(M_orig, X0, Y0, Z0, X1, Y1, Z1, X2, Y2, Z2, R[nb_solutions], t[nb_solutions]))
|
||||
continue;
|
||||
|
||||
if (p4p) {
|
||||
double X3p = R[nb_solutions][0][0] * X3 + R[nb_solutions][0][1] * Y3 + R[nb_solutions][0][2] * Z3 + t[nb_solutions][0];
|
||||
double Y3p = R[nb_solutions][1][0] * X3 + R[nb_solutions][1][1] * Y3 + R[nb_solutions][1][2] * Z3 + t[nb_solutions][1];
|
||||
double Z3p = R[nb_solutions][2][0] * X3 + R[nb_solutions][2][1] * Y3 + R[nb_solutions][2][2] * Z3 + t[nb_solutions][2];
|
||||
double mu3p = X3p / Z3p;
|
||||
double mv3p = Y3p / Z3p;
|
||||
reproj_errors[nb_solutions] = (mu3p - mu3) * (mu3p - mu3) + (mv3p - mv3) * (mv3p - mv3);
|
||||
}
|
||||
|
||||
nb_solutions++;
|
||||
}
|
||||
|
||||
if (p4p) {
|
||||
//sort the solutions
|
||||
for (int i = 1; i < nb_solutions; i++) {
|
||||
for (int j = i; j > 0 && reproj_errors[j-1] > reproj_errors[j]; j--) {
|
||||
std::swap(reproj_errors[j], reproj_errors[j-1]);
|
||||
std::swap(R[j], R[j-1]);
|
||||
std::swap(t[j], t[j-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nb_solutions;
|
||||
}
|
||||
|
||||
/// Given 3D distances between three points and cosines of 3 angles at the apex, calculates
|
||||
/// the lengths of the line segments connecting projection center (P) and the three 3D points (A, B, C).
|
||||
/// Returned distances are for |PA|, |PB|, |PC| respectively.
|
||||
/// Only the solution to the main branch.
|
||||
/// Reference : X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang; "Complete Solution Classification for the Perspective-Three-Point Problem"
|
||||
/// IEEE Trans. on PAMI, vol. 25, No. 8, August 2003
|
||||
/// \param lengths Lengths of line segments up to four solutions.
|
||||
/// \param distances Distance between 3D points in pairs |BC|, |AC|, |AB|.
|
||||
/// \param cosines Cosine of the angles /_BPC, /_APC, /_APB.
|
||||
/// \returns Number of solutions.
|
||||
/// WARNING: NOT ALL THE DEGENERATE CASES ARE IMPLEMENTED
|
||||
|
||||
int p3p::solve_for_lengths(double lengths[4][3], double distances[3], double cosines[3])
|
||||
{
|
||||
double p = cosines[0] * 2;
|
||||
double q = cosines[1] * 2;
|
||||
double r = cosines[2] * 2;
|
||||
|
||||
double inv_d22 = 1. / (distances[2] * distances[2]);
|
||||
double a = inv_d22 * (distances[0] * distances[0]);
|
||||
double b = inv_d22 * (distances[1] * distances[1]);
|
||||
|
||||
double a2 = a * a, b2 = b * b, p2 = p * p, q2 = q * q, r2 = r * r;
|
||||
double pr = p * r, pqr = q * pr;
|
||||
|
||||
// Check reality condition (the four points should not be coplanar)
|
||||
if (p2 + q2 + r2 - pqr - 1 == 0)
|
||||
return 0;
|
||||
|
||||
double ab = a * b, a_2 = 2*a;
|
||||
|
||||
double A = -2 * b + b2 + a2 + 1 + ab*(2 - r2) - a_2;
|
||||
|
||||
// Check reality condition
|
||||
if (A == 0) return 0;
|
||||
|
||||
double a_4 = 4*a;
|
||||
|
||||
double B = q*(-2*(ab + a2 + 1 - b) + r2*ab + a_4) + pr*(b - b2 + ab);
|
||||
double C = q2 + b2*(r2 + p2 - 2) - b*(p2 + pqr) - ab*(r2 + pqr) + (a2 - a_2)*(2 + q2) + 2;
|
||||
double D = pr*(ab-b2+b) + q*((p2-2)*b + 2 * (ab - a2) + a_4 - 2);
|
||||
double E = 1 + 2*(b - a - ab) + b2 - b*p2 + a2;
|
||||
|
||||
double temp = (p2*(a-1+b) + r2*(a-1-b) + pqr - a*pqr);
|
||||
double b0 = b * temp * temp;
|
||||
// Check reality condition
|
||||
if (b0 == 0)
|
||||
return 0;
|
||||
|
||||
double real_roots[4];
|
||||
int n = solve_deg4(A, B, C, D, E, real_roots[0], real_roots[1], real_roots[2], real_roots[3]);
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
int nb_solutions = 0;
|
||||
double r3 = r2*r, pr2 = p*r2, r3q = r3 * q;
|
||||
double inv_b0 = 1. / b0;
|
||||
|
||||
// For each solution of x
|
||||
for(int i = 0; i < n; i++) {
|
||||
double x = real_roots[i];
|
||||
|
||||
// Check reality condition
|
||||
if (x <= 0)
|
||||
continue;
|
||||
|
||||
double x2 = x*x;
|
||||
|
||||
double b1 =
|
||||
((1-a-b)*x2 + (q*a-q)*x + 1 - a + b) *
|
||||
(((r3*(a2 + ab*(2 - r2) - a_2 + b2 - 2*b + 1)) * x +
|
||||
|
||||
(r3q*(2*(b-a2) + a_4 + ab*(r2 - 2) - 2) + pr2*(1 + a2 + 2*(ab-a-b) + r2*(b - b2) + b2))) * x2 +
|
||||
|
||||
(r3*(q2*(1-2*a+a2) + r2*(b2-ab) - a_4 + 2*(a2 - b2) + 2) + r*p2*(b2 + 2*(ab - b - a) + 1 + a2) + pr2*q*(a_4 + 2*(b - ab - a2) - 2 - r2*b)) * x +
|
||||
|
||||
2*r3q*(a_2 - b - a2 + ab - 1) + pr2*(q2 - a_4 + 2*(a2 - b2) + r2*b + q2*(a2 - a_2) + 2) +
|
||||
p2*(p*(2*(ab - a - b) + a2 + b2 + 1) + 2*q*r*(b + a_2 - a2 - ab - 1)));
|
||||
|
||||
// Check reality condition
|
||||
if (b1 <= 0)
|
||||
continue;
|
||||
|
||||
double y = inv_b0 * b1;
|
||||
double v = x2 + y*y - x*y*r;
|
||||
|
||||
if (v <= 0)
|
||||
continue;
|
||||
|
||||
double Z = distances[2] / sqrt(v);
|
||||
double X = x * Z;
|
||||
double Y = y * Z;
|
||||
|
||||
lengths[nb_solutions][0] = X;
|
||||
lengths[nb_solutions][1] = Y;
|
||||
lengths[nb_solutions][2] = Z;
|
||||
|
||||
nb_solutions++;
|
||||
}
|
||||
|
||||
return nb_solutions;
|
||||
}
|
||||
|
||||
bool p3p::align(double M_end[3][3],
|
||||
double X0, double Y0, double Z0,
|
||||
double X1, double Y1, double Z1,
|
||||
double X2, double Y2, double Z2,
|
||||
double R[3][3], double T[3])
|
||||
{
|
||||
// Centroids:
|
||||
double C_start[3] = {}, C_end[3] = {};
|
||||
for(int i = 0; i < 3; i++) C_end[i] = (M_end[0][i] + M_end[1][i] + M_end[2][i]) / 3;
|
||||
C_start[0] = (X0 + X1 + X2) / 3;
|
||||
C_start[1] = (Y0 + Y1 + Y2) / 3;
|
||||
C_start[2] = (Z0 + Z1 + Z2) / 3;
|
||||
|
||||
// Covariance matrix s:
|
||||
double s[3 * 3] = {};
|
||||
for(int j = 0; j < 3; j++) {
|
||||
s[0 * 3 + j] = (X0 * M_end[0][j] + X1 * M_end[1][j] + X2 * M_end[2][j]) / 3 - C_end[j] * C_start[0];
|
||||
s[1 * 3 + j] = (Y0 * M_end[0][j] + Y1 * M_end[1][j] + Y2 * M_end[2][j]) / 3 - C_end[j] * C_start[1];
|
||||
s[2 * 3 + j] = (Z0 * M_end[0][j] + Z1 * M_end[1][j] + Z2 * M_end[2][j]) / 3 - C_end[j] * C_start[2];
|
||||
}
|
||||
|
||||
double Qs[16] = {}, evs[4] = {}, U[16] = {};
|
||||
|
||||
Qs[0 * 4 + 0] = s[0 * 3 + 0] + s[1 * 3 + 1] + s[2 * 3 + 2];
|
||||
Qs[1 * 4 + 1] = s[0 * 3 + 0] - s[1 * 3 + 1] - s[2 * 3 + 2];
|
||||
Qs[2 * 4 + 2] = s[1 * 3 + 1] - s[2 * 3 + 2] - s[0 * 3 + 0];
|
||||
Qs[3 * 4 + 3] = s[2 * 3 + 2] - s[0 * 3 + 0] - s[1 * 3 + 1];
|
||||
|
||||
Qs[1 * 4 + 0] = Qs[0 * 4 + 1] = s[1 * 3 + 2] - s[2 * 3 + 1];
|
||||
Qs[2 * 4 + 0] = Qs[0 * 4 + 2] = s[2 * 3 + 0] - s[0 * 3 + 2];
|
||||
Qs[3 * 4 + 0] = Qs[0 * 4 + 3] = s[0 * 3 + 1] - s[1 * 3 + 0];
|
||||
Qs[2 * 4 + 1] = Qs[1 * 4 + 2] = s[1 * 3 + 0] + s[0 * 3 + 1];
|
||||
Qs[3 * 4 + 1] = Qs[1 * 4 + 3] = s[2 * 3 + 0] + s[0 * 3 + 2];
|
||||
Qs[3 * 4 + 2] = Qs[2 * 4 + 3] = s[2 * 3 + 1] + s[1 * 3 + 2];
|
||||
|
||||
jacobi_4x4(Qs, evs, U);
|
||||
|
||||
// Looking for the largest eigen value:
|
||||
int i_ev = 0;
|
||||
double ev_max = evs[i_ev];
|
||||
for(int i = 1; i < 4; i++)
|
||||
if (evs[i] > ev_max)
|
||||
ev_max = evs[i_ev = i];
|
||||
|
||||
// Quaternion:
|
||||
double q[4];
|
||||
for(int i = 0; i < 4; i++)
|
||||
q[i] = U[i * 4 + i_ev];
|
||||
|
||||
double q02 = q[0] * q[0], q12 = q[1] * q[1], q22 = q[2] * q[2], q32 = q[3] * q[3];
|
||||
double q0_1 = q[0] * q[1], q0_2 = q[0] * q[2], q0_3 = q[0] * q[3];
|
||||
double q1_2 = q[1] * q[2], q1_3 = q[1] * q[3];
|
||||
double q2_3 = q[2] * q[3];
|
||||
|
||||
R[0][0] = q02 + q12 - q22 - q32;
|
||||
R[0][1] = 2. * (q1_2 - q0_3);
|
||||
R[0][2] = 2. * (q1_3 + q0_2);
|
||||
|
||||
R[1][0] = 2. * (q1_2 + q0_3);
|
||||
R[1][1] = q02 + q22 - q12 - q32;
|
||||
R[1][2] = 2. * (q2_3 - q0_1);
|
||||
|
||||
R[2][0] = 2. * (q1_3 - q0_2);
|
||||
R[2][1] = 2. * (q2_3 + q0_1);
|
||||
R[2][2] = q02 + q32 - q12 - q22;
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
T[i] = C_end[i] - (R[i][0] * C_start[0] + R[i][1] * C_start[1] + R[i][2] * C_start[2]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool p3p::jacobi_4x4(double * A, double * D, double * U)
|
||||
{
|
||||
double B[4] = {}, Z[4] = {};
|
||||
double Id[16] = {1., 0., 0., 0.,
|
||||
0., 1., 0., 0.,
|
||||
0., 0., 1., 0.,
|
||||
0., 0., 0., 1.};
|
||||
|
||||
memcpy(U, Id, 16 * sizeof(double));
|
||||
|
||||
B[0] = A[0]; B[1] = A[5]; B[2] = A[10]; B[3] = A[15];
|
||||
memcpy(D, B, 4 * sizeof(double));
|
||||
|
||||
for(int iter = 0; iter < 50; iter++) {
|
||||
double sum = fabs(A[1]) + fabs(A[2]) + fabs(A[3]) + fabs(A[6]) + fabs(A[7]) + fabs(A[11]);
|
||||
|
||||
if (sum == 0.0)
|
||||
static bool solve_cubic_single_real(double c2, double c1, double c0, double &root) {
|
||||
double a = c1 - c2 * c2 / 3.0;
|
||||
double b = (2.0 * c2 * c2 * c2 - 9.0 * c2 * c1) / 27.0 + c0;
|
||||
double c = b * b / 4.0 + a * a * a / 27.0;
|
||||
if (c != 0) {
|
||||
if (c > 0) {
|
||||
c = std::sqrt(c);
|
||||
b *= -0.5;
|
||||
root = std::cbrt(b + c) + std::cbrt(b - c) - c2 / 3.0;
|
||||
return true;
|
||||
|
||||
double tresh = (iter < 3) ? 0.2 * sum / 16. : 0.0;
|
||||
for(int i = 0; i < 3; i++) {
|
||||
double * pAij = A + 5 * i + 1;
|
||||
for(int j = i + 1 ; j < 4; j++) {
|
||||
double Aij = *pAij;
|
||||
double eps_machine = 100.0 * fabs(Aij);
|
||||
|
||||
if ( iter > 3 && fabs(D[i]) + eps_machine == fabs(D[i]) && fabs(D[j]) + eps_machine == fabs(D[j]) )
|
||||
*pAij = 0.0;
|
||||
else if (fabs(Aij) > tresh) {
|
||||
double hh = D[j] - D[i], t;
|
||||
if (fabs(hh) + eps_machine == fabs(hh))
|
||||
t = Aij / hh;
|
||||
else {
|
||||
double theta = 0.5 * hh / Aij;
|
||||
t = 1.0 / (fabs(theta) + sqrt(1.0 + theta * theta));
|
||||
if (theta < 0.0) t = -t;
|
||||
}
|
||||
|
||||
hh = t * Aij;
|
||||
Z[i] -= hh;
|
||||
Z[j] += hh;
|
||||
D[i] -= hh;
|
||||
D[j] += hh;
|
||||
*pAij = 0.0;
|
||||
|
||||
double c = 1.0 / sqrt(1 + t * t);
|
||||
double s = t * c;
|
||||
double tau = s / (1.0 + c);
|
||||
for(int k = 0; k <= i - 1; k++) {
|
||||
double g = A[k * 4 + i], h = A[k * 4 + j];
|
||||
A[k * 4 + i] = g - s * (h + g * tau);
|
||||
A[k * 4 + j] = h + s * (g - h * tau);
|
||||
}
|
||||
for(int k = i + 1; k <= j - 1; k++) {
|
||||
double g = A[i * 4 + k], h = A[k * 4 + j];
|
||||
A[i * 4 + k] = g - s * (h + g * tau);
|
||||
A[k * 4 + j] = h + s * (g - h * tau);
|
||||
}
|
||||
for(int k = j + 1; k < 4; k++) {
|
||||
double g = A[i * 4 + k], h = A[j * 4 + k];
|
||||
A[i * 4 + k] = g - s * (h + g * tau);
|
||||
A[j * 4 + k] = h + s * (g - h * tau);
|
||||
}
|
||||
for(int k = 0; k < 4; k++) {
|
||||
double g = U[k * 4 + i], h = U[k * 4 + j];
|
||||
U[k * 4 + i] = g - s * (h + g * tau);
|
||||
U[k * 4 + j] = h + s * (g - h * tau);
|
||||
}
|
||||
}
|
||||
pAij++;
|
||||
}
|
||||
} else {
|
||||
c = 3.0 * b / (2.0 * a) * std::sqrt(-3.0 / a);
|
||||
root = 2.0 * std::sqrt(-a / 3.0) * std::cos(std::acos(c) / 3.0) - c2 / 3.0;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++) B[i] += Z[i];
|
||||
memcpy(D, B, 4 * sizeof(double));
|
||||
memset(Z, 0, 4 * sizeof(double));
|
||||
} else {
|
||||
root = -c2 / 3.0 + (a != 0 ? (3.0 * b / a) : 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool root2real(double b, double c, double &r1, double &r2) {
|
||||
const double THRESHOLD = -1.0e-12;
|
||||
double v = b * b - 4.0 * c;
|
||||
if (v < THRESHOLD) {
|
||||
r1 = r2 = -0.5 * b;
|
||||
return v >= 0;
|
||||
}
|
||||
if (v > THRESHOLD && v < 0.0) {
|
||||
r1 = -0.5 * b;
|
||||
r2 = -2;
|
||||
return true;
|
||||
}
|
||||
|
||||
double y = std::sqrt(v);
|
||||
if (b < 0) {
|
||||
r1 = 0.5 * (-b + y);
|
||||
r2 = 0.5 * (-b - y);
|
||||
} else {
|
||||
r1 = 2.0 * c / (-b + y);
|
||||
r2 = 2.0 * c / (-b - y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::array<Vec3d, 2> compute_pq(Matx33d C) {
|
||||
std::array<Vec3d, 2> pq;
|
||||
Matx33d C_adj;
|
||||
|
||||
C_adj(0, 0) = C(1, 2) * C(2, 1) - C(1, 1) * C(2, 2);
|
||||
C_adj(1, 1) = C(0, 2) * C(2, 0) - C(0, 0) * C(2, 2);
|
||||
C_adj(2, 2) = C(0, 1) * C(1, 0) - C(0, 0) * C(1, 1);
|
||||
C_adj(0, 1) = C(0, 1) * C(2, 2) - C(0, 2) * C(2, 1);
|
||||
C_adj(0, 2) = C(0, 2) * C(1, 1) - C(0, 1) * C(1, 2);
|
||||
C_adj(1, 0) = C_adj(0, 1);
|
||||
C_adj(1, 2) = C(0, 0) * C(1, 2) - C(0, 2) * C(1, 0);
|
||||
C_adj(2, 0) = C_adj(0, 2);
|
||||
C_adj(2, 1) = C_adj(1, 2);
|
||||
|
||||
Matx31d v;
|
||||
if (C_adj(0, 0) > C_adj(1, 1)) {
|
||||
if (C_adj(0, 0) > C_adj(2, 2)) {
|
||||
v = C_adj.col(0) / std::sqrt(C_adj(0, 0));
|
||||
} else {
|
||||
v = C_adj.col(2) / std::sqrt(C_adj(2, 2));
|
||||
}
|
||||
} else if (C_adj(1, 1) > C_adj(2, 2)) {
|
||||
v = C_adj.col(1) / std::sqrt(C_adj(1, 1));
|
||||
} else {
|
||||
v = C_adj.col(2) / std::sqrt(C_adj(2, 2));
|
||||
}
|
||||
|
||||
C(0, 1) -= v(2);
|
||||
C(0, 2) += v(1);
|
||||
C(1, 2) -= v(0);
|
||||
C(1, 0) += v(2);
|
||||
C(2, 0) -= v(1);
|
||||
C(2, 1) += v(0);
|
||||
|
||||
pq[0](0) = C.col(0)(0);
|
||||
pq[0](1) = C.col(0)(1);
|
||||
pq[0](2) = C.col(0)(2);
|
||||
pq[1](0) = C.row(0)(0);
|
||||
pq[1](1) = C.row(0)(1);
|
||||
pq[1](2) = C.row(0)(2);
|
||||
|
||||
return pq;
|
||||
}
|
||||
|
||||
// Performs a few Newton steps on the equations
|
||||
static void refine_lambda(double &lambda1, double &lambda2, double &lambda3, const double a12, const double a13,
|
||||
const double a23, const double b12, const double b13, const double b23) {
|
||||
for (int iter = 0; iter < 5; ++iter) {
|
||||
double r1 = (lambda1 * lambda1 - 2.0 * lambda1 * lambda2 * b12 + lambda2 * lambda2 - a12);
|
||||
double r2 = (lambda1 * lambda1 - 2.0 * lambda1 * lambda3 * b13 + lambda3 * lambda3 - a13);
|
||||
double r3 = (lambda2 * lambda2 - 2.0 * lambda2 * lambda3 * b23 + lambda3 * lambda3 - a23);
|
||||
|
||||
if (std::abs(r1) + std::abs(r2) + std::abs(r3) < 1e-10)
|
||||
return;
|
||||
|
||||
double x11 = lambda1 - lambda2 * b12;
|
||||
double x12 = lambda2 - lambda1 * b12;
|
||||
double x21 = lambda1 - lambda3 * b13;
|
||||
double x23 = lambda3 - lambda1 * b13;
|
||||
double x32 = lambda2 - lambda3 * b23;
|
||||
double x33 = lambda3 - lambda2 * b23;
|
||||
double detJ = 0.5 / (x11 * x23 * x32 + x12 * x21 * x33); // half minus inverse determinant
|
||||
|
||||
// This uses the closed form of the inverse for the jacobian.
|
||||
// Due to the zero elements this actually becomes quite nice.
|
||||
lambda1 += (-x23 * x32 * r1 - x12 * x33 * r2 + x12 * x23 * r3) * detJ;
|
||||
lambda2 += (-x21 * x33 * r1 + x11 * x33 * r2 - x11 * x23 * r3) * detJ;
|
||||
lambda3 += (x21 * x32 * r1 - x11 * x32 * r2 - x12 * x21 * r3) * detJ;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void p3p::calibrateAndNormalizePointsPnP(const Mat &opoints_, const Mat &ipoints_) {
|
||||
auto convertPoints = [] (const Mat &points_input, Mat &points, int pt_dim) {
|
||||
points_input.convertTo(points, CV_64F); // convert points to have float precision
|
||||
if (points.channels() > 1)
|
||||
points = points.reshape(1, (int)points.total()); // convert point to have 1 channel
|
||||
if (points.rows < points.cols)
|
||||
transpose(points, points); // transpose so points will be in rows
|
||||
CV_CheckGE(points.cols, pt_dim, "Invalid dimension of point");
|
||||
if (points.cols != pt_dim) // in case when image points are 3D convert them to 2D
|
||||
points = points.colRange(0, pt_dim);
|
||||
};
|
||||
|
||||
Mat ipoints;
|
||||
convertPoints(ipoints_, ipoints, 2);
|
||||
for (int i = 0; i < ipoints.rows; i++) {
|
||||
const double k_inv_u = ipoints.at<double>(i, 0);
|
||||
const double k_inv_v = ipoints.at<double>(i, 1);
|
||||
double x_norm = 1.0 / sqrt(k_inv_u*k_inv_u + k_inv_v*k_inv_v + 1);
|
||||
x_copy[i](0) = k_inv_u * x_norm;
|
||||
x_copy[i](1) = k_inv_v * x_norm;
|
||||
x_copy[i](2) = x_norm;
|
||||
}
|
||||
|
||||
Mat opoints;
|
||||
convertPoints(opoints_, opoints, 3);
|
||||
X_copy[0](0) = opoints.at<double>(0, 0);
|
||||
X_copy[0](1) = opoints.at<double>(0, 1);
|
||||
X_copy[0](2) = opoints.at<double>(0, 2);
|
||||
|
||||
X_copy[1](0) = opoints.at<double>(1, 0);
|
||||
X_copy[1](1) = opoints.at<double>(1, 1);
|
||||
X_copy[1](2) = opoints.at<double>(1, 2);
|
||||
|
||||
X_copy[2](0) = opoints.at<double>(2, 0);
|
||||
X_copy[2](1) = opoints.at<double>(2, 1);
|
||||
X_copy[2](2) = opoints.at<double>(2, 2);
|
||||
}
|
||||
|
||||
p3p::p3p() :
|
||||
x_copy(), X_copy()
|
||||
{
|
||||
}
|
||||
|
||||
int p3p::estimate(std::vector<Mat>& Rs, std::vector<Mat>& ts, const cv::Mat& opoints, const cv::Mat& ipoints) {
|
||||
CV_INSTRUMENT_REGION();
|
||||
calibrateAndNormalizePointsPnP(opoints, ipoints);
|
||||
|
||||
Rs.reserve(4);
|
||||
ts.reserve(4);
|
||||
|
||||
Vec3d X01 = X_copy[0] - X_copy[1];
|
||||
Vec3d X02 = X_copy[0] - X_copy[2];
|
||||
Vec3d X12 = X_copy[1] - X_copy[2];
|
||||
|
||||
double a01 = norm(X01, NORM_L2SQR);
|
||||
double a02 = norm(X02, NORM_L2SQR);
|
||||
double a12 = norm(X12, NORM_L2SQR);
|
||||
|
||||
std::array<Vec3d, 3> X = {X_copy[0], X_copy[1], X_copy[2]};
|
||||
std::array<Vec3d, 3> x = {x_copy[0], x_copy[1], x_copy[2]};
|
||||
|
||||
// Switch X,x so that BC is the largest distance among {X01, X02, X12}
|
||||
if (a01 > a02) {
|
||||
if (a01 > a12) {
|
||||
std::swap(x[0], x[2]);
|
||||
std::swap(X[0], X[2]);
|
||||
std::swap(a01, a12);
|
||||
X01 = -X12;
|
||||
X02 = -X02;
|
||||
}
|
||||
} else if (a02 > a12) {
|
||||
std::swap(x[0], x[1]);
|
||||
std::swap(X[0], X[1]);
|
||||
std::swap(a02, a12);
|
||||
X01 = -X01;
|
||||
X02 = X12;
|
||||
}
|
||||
|
||||
const double a12d = 1.0 / a12;
|
||||
const double a = a01 * a12d;
|
||||
const double b = a02 * a12d;
|
||||
|
||||
const double m01 = x[0].dot(x[1]);
|
||||
const double m02 = x[0].dot(x[2]);
|
||||
const double m12 = x[1].dot(x[2]);
|
||||
|
||||
// Ugly parameters to simplify the calculation
|
||||
const double m12sq = -m12 * m12 + 1.0;
|
||||
const double m02sq = -1.0 + m02 * m02;
|
||||
const double m01sq = -1.0 + m01 * m01;
|
||||
const double ab = a * b;
|
||||
const double bsq = b * b;
|
||||
const double asq = a * a;
|
||||
const double m013 = -2.0 + 2.0 * m01 * m02 * m12;
|
||||
const double bsqm12sq = bsq * m12sq;
|
||||
const double asqm12sq = asq * m12sq;
|
||||
const double abm12sq = 2.0 * ab * m12sq;
|
||||
|
||||
const double k3_inv = 1.0 / (bsqm12sq + b * m02sq);
|
||||
const double k2 = k3_inv * ((-1.0 + a) * m02sq + abm12sq + bsqm12sq + b * m013);
|
||||
const double k1 = k3_inv * (asqm12sq + abm12sq + a * m013 + (-1.0 + b) * m01sq);
|
||||
const double k0 = k3_inv * (asqm12sq + a * m01sq);
|
||||
|
||||
double s;
|
||||
bool G = yaqding::solve_cubic_single_real(k2, k1, k0, s);
|
||||
|
||||
Matx33d C;
|
||||
C(0, 0) = -a + s * (1 - b);
|
||||
C(0, 1) = -m02 * s;
|
||||
C(0, 2) = a * m12 + b * m12 * s;
|
||||
C(1, 0) = C(0, 1);
|
||||
C(1, 1) = s + 1;
|
||||
C(1, 2) = -m01;
|
||||
C(2, 0) = C(0, 2);
|
||||
C(2, 1) = C(1, 2);
|
||||
C(2, 2) = -a - b * s + 1;
|
||||
|
||||
std::array<Vec3d, 2> pq = yaqding::compute_pq(C);
|
||||
|
||||
// XX << X01, X02, X01.cross(X02);
|
||||
// XX = XX.inverse().eval();
|
||||
Matx33d XX;
|
||||
XX(0,0) = X01(0); XX(1,0) = X01(1); XX(2,0) = X01(2);
|
||||
XX(0,1) = X02(0); XX(1,1) = X02(1); XX(2,1) = X02(2);
|
||||
Vec3d X01_X02 = X01.cross(X02);
|
||||
XX(0,2) = X01_X02(0); XX(1,2) = X01_X02(1); XX(2,2) = X01_X02(2);
|
||||
XX = XX.inv();
|
||||
|
||||
int n_sols = 0;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// [p0 p1 p2] * [1; x; y] = 0, or [p0 p1 p2] * [d2; d0; d1] = 0
|
||||
double p0 = pq[i](0);
|
||||
double p1 = pq[i](1);
|
||||
double p2 = pq[i](2);
|
||||
// here we run into trouble if p0 is zero,
|
||||
// so depending on which is larger, we solve for either d0 or d1
|
||||
// The case p0 = p1 = 0 is degenerate and can be ignored
|
||||
bool switch_12 = std::abs(p0) <= std::abs(p1);
|
||||
|
||||
if (switch_12) {
|
||||
// eliminate d0
|
||||
double w0 = -p0 / p1;
|
||||
double w1 = -p2 / p1;
|
||||
double ca = 1.0 / (w1 * w1 - b);
|
||||
double cb = 2.0 * (b * m12 - m02 * w1 + w0 * w1) * ca;
|
||||
double cc = (w0 * w0 - 2 * m02 * w0 - b + 1.0) * ca;
|
||||
double taus[2];
|
||||
|
||||
if (!yaqding::root2real(cb, cc, taus[0], taus[1]))
|
||||
continue;
|
||||
|
||||
for (double tau : taus) {
|
||||
if (tau <= 0)
|
||||
continue;
|
||||
|
||||
// positive only
|
||||
double d2 = std::sqrt(a12 / (tau * (tau - 2.0 * m12) + 1.0));
|
||||
double d1 = tau * d2;
|
||||
double d0 = (w0 * d2 + w1 * d1);
|
||||
if (d0 < 0)
|
||||
continue;
|
||||
|
||||
yaqding::refine_lambda(d0, d1, d2, a01, a02, a12, m01, m02, m12);
|
||||
Vec3d v1 = d0 * x[0] - d1 * x[1];
|
||||
Vec3d v2 = d0 * x[0] - d2 * x[2];
|
||||
// YY << v1, v2, v1.cross(v2);
|
||||
Matx33d YY;
|
||||
YY(0,0) = v1(0); YY(1,0) = v1(1); YY(2,0) = v1(2);
|
||||
YY(0,1) = v2(0); YY(1,1) = v2(1); YY(2,1) = v2(2);
|
||||
Vec3d v1_v2 = v1.cross(v2);
|
||||
YY(0,2) = v1_v2(0); YY(1,2) = v1_v2(1); YY(2,2) = v1_v2(2);
|
||||
|
||||
// output->emplace_back(R, d0 * x[0] - R * X[0]);
|
||||
Matx33d R = (YY * XX);
|
||||
Rs.push_back(Mat(R));
|
||||
Vec3d trans = (d0 * x[0] - R * X[0]);
|
||||
ts.push_back(Mat(trans));
|
||||
++n_sols;
|
||||
}
|
||||
} else {
|
||||
double w0 = -p1 / p0;
|
||||
double w1 = -p2 / p0;
|
||||
double ca = 1.0 / (-a * w1 * w1 + 2 * a * m12 * w1 - a + 1);
|
||||
double cb = 2 * (a * m12 * w0 - m01 - a * w0 * w1) * ca;
|
||||
double cc = (1 - a * w0 * w0) * ca;
|
||||
|
||||
double taus[2];
|
||||
if (!yaqding::root2real(cb, cc, taus[0], taus[1]))
|
||||
continue;
|
||||
|
||||
for (double tau : taus) {
|
||||
if (tau <= 0)
|
||||
continue;
|
||||
|
||||
double d0 = std::sqrt(a01 / (tau * (tau - 2.0 * m01) + 1.0));
|
||||
double d1 = tau * d0;
|
||||
double d2 = w0 * d0 + w1 * d1;
|
||||
|
||||
if (d2 < 0)
|
||||
continue;
|
||||
|
||||
yaqding::refine_lambda(d0, d1, d2, a01, a02, a12, m01, m02, m12);
|
||||
Vec3d v1 = d0 * x[0] - d1 * x[1];
|
||||
Vec3d v2 = d0 * x[0] - d2 * x[2];
|
||||
// YY << v1, v2, v1.cross(v2);
|
||||
Matx33d YY;
|
||||
YY(0,0) = v1(0); YY(1,0) = v1(1); YY(2,0) = v1(2);
|
||||
YY(0,1) = v2(0); YY(1,1) = v2(1); YY(2,1) = v2(2);
|
||||
Vec3d v1_v2 = v1.cross(v2);
|
||||
YY(0,2) = v1_v2(0); YY(1,2) = v1_v2(1); YY(2,2) = v1_v2(2);
|
||||
|
||||
// output->emplace_back(R, d0 * x[0] - R * X[0]);
|
||||
Matx33d R = (YY * XX);
|
||||
Rs.push_back(Mat(R));
|
||||
Vec3d trans = (d0 * x[0] - R * X[0]);
|
||||
ts.push_back(Mat(trans));
|
||||
++n_sols;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_sols > 0 && G)
|
||||
break;
|
||||
}
|
||||
|
||||
return n_sols;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-61
@@ -9,70 +9,17 @@
|
||||
|
||||
namespace cv {
|
||||
|
||||
class p3p
|
||||
{
|
||||
public:
|
||||
p3p(double fx, double fy, double cx, double cy);
|
||||
p3p(cv::Mat cameraMatrix);
|
||||
class p3p {
|
||||
public:
|
||||
p3p();
|
||||
int estimate(std::vector<cv::Mat>& Rs, std::vector<cv::Mat>& ts, const cv::Mat& opoints, const cv::Mat& ipoints);
|
||||
|
||||
bool solve(cv::Mat& R, cv::Mat& tvec, const cv::Mat& opoints, const cv::Mat& ipoints);
|
||||
int solve(std::vector<cv::Mat>& Rs, std::vector<cv::Mat>& tvecs, const cv::Mat& opoints, const cv::Mat& ipoints);
|
||||
int solve(double R[4][3][3], double t[4][3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2,
|
||||
double mu3, double mv3, double X3, double Y3, double Z3,
|
||||
bool p4p);
|
||||
bool solve(double R[3][3], double t[3],
|
||||
double mu0, double mv0, double X0, double Y0, double Z0,
|
||||
double mu1, double mv1, double X1, double Y1, double Z1,
|
||||
double mu2, double mv2, double X2, double Y2, double Z2,
|
||||
double mu3, double mv3, double X3, double Y3, double Z3);
|
||||
private:
|
||||
void calibrateAndNormalizePointsPnP(const cv::Mat& opoints, const cv::Mat& ipoints);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void init_camera_parameters(const cv::Mat& cameraMatrix)
|
||||
{
|
||||
cx = cameraMatrix.at<T> (0, 2);
|
||||
cy = cameraMatrix.at<T> (1, 2);
|
||||
fx = cameraMatrix.at<T> (0, 0);
|
||||
fy = cameraMatrix.at<T> (1, 1);
|
||||
}
|
||||
template <typename OpointType, typename IpointType>
|
||||
void extract_points(const cv::Mat& opoints, const cv::Mat& ipoints, std::vector<double>& points)
|
||||
{
|
||||
points.clear();
|
||||
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
|
||||
points.resize(5*4); //resize vector to fit for p4p case
|
||||
for(int i = 0; i < npoints; i++)
|
||||
{
|
||||
points[i*5] = ipoints.at<IpointType>(i).x*fx + cx;
|
||||
points[i*5+1] = ipoints.at<IpointType>(i).y*fy + cy;
|
||||
points[i*5+2] = opoints.at<OpointType>(i).x;
|
||||
points[i*5+3] = opoints.at<OpointType>(i).y;
|
||||
points[i*5+4] = opoints.at<OpointType>(i).z;
|
||||
}
|
||||
//Fill vectors with unused values for p3p case
|
||||
for (int i = npoints; i < 4; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
points[i * 5 + j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
void init_inverse_parameters();
|
||||
int solve_for_lengths(double lengths[4][3], double distances[3], double cosines[3]);
|
||||
bool align(double M_start[3][3],
|
||||
double X0, double Y0, double Z0,
|
||||
double X1, double Y1, double Z1,
|
||||
double X2, double Y2, double Z2,
|
||||
double R[3][3], double T[3]);
|
||||
|
||||
bool jacobi_4x4(double * A, double * D, double * U);
|
||||
|
||||
double fx, fy, cx, cy;
|
||||
double inv_fx, inv_fy, cx_fx, cy_fy;
|
||||
std::array<cv::Vec3d, 3> x_copy;
|
||||
std::array<cv::Vec3d, 3> X_copy;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // P3P_H
|
||||
|
||||
@@ -451,8 +451,8 @@ int solveP3P( InputArray _opoints, InputArray _ipoints,
|
||||
int solutions = 0;
|
||||
if (flags == SOLVEPNP_P3P)
|
||||
{
|
||||
p3p P3Psolver(cameraMatrix);
|
||||
solutions = P3Psolver.solve(Rs, ts, opoints, undistortedPoints);
|
||||
p3p P3Psolver;
|
||||
solutions = P3Psolver.estimate(Rs, ts, opoints, undistortedPoints);
|
||||
}
|
||||
else if (flags == SOLVEPNP_AP3P)
|
||||
{
|
||||
|
||||
@@ -658,8 +658,6 @@ namespace Math {
|
||||
Matx33d getSkewSymmetric(const Vec3d &v_);
|
||||
// eliminate matrix with m rows and n columns to be upper triangular.
|
||||
bool eliminateUpperTriangular (std::vector<double> &a, int m, int n);
|
||||
Matx33d rotVec2RotMat (const Vec3d &v);
|
||||
Vec3d rotMat2RotVec (const Matx33d &R);
|
||||
}
|
||||
|
||||
class SolverPoly: public Algorithm {
|
||||
|
||||
@@ -199,7 +199,7 @@ public:
|
||||
// and translation.
|
||||
const double qi = s1, qi2 = qi*qi, qj = s2, qj2 = qj*qj, qk = s3, qk2 = qk*qk;
|
||||
const double s = 1 / (1 + qi2 + qj2 + qk2);
|
||||
const Matx33d rot_mat (1-2*s*(qj2+qk2), 2*s*(qi*qj+qk), 2*s*(qi*qk-qj),
|
||||
Matx33d rot_mat (1-2*s*(qj2+qk2), 2*s*(qi*qj+qk), 2*s*(qi*qk-qj),
|
||||
2*s*(qi*qj-qk), 1-2*s*(qi2+qk2), 2*s*(qj*qk+qi),
|
||||
2*s*(qi*qk+qj), 2*s*(qj*qk-qi), 1-2*s*(qi2+qj2));
|
||||
const Matx31d soln_translation = translation_factor * rot_mat.reshape<9,1>();
|
||||
@@ -217,8 +217,14 @@ public:
|
||||
}
|
||||
|
||||
if (all_points_in_front_of_camera) {
|
||||
// https://github.com/opencv/opencv/blob/2ba688f23c4e20754f32179d9396ba9b54b3b064/modules/calib3d/src/usac/pnp_solver.cpp#L395
|
||||
// Use directly cv::Rodrigues
|
||||
Matx31d rvec;
|
||||
Rodrigues(rot_mat, rvec);
|
||||
Rodrigues(rvec, rot_mat);
|
||||
|
||||
Mat model;
|
||||
hconcat(Math::rotVec2RotMat(Math::rotMat2RotVec(rot_mat)), soln_translation, model);
|
||||
hconcat(rot_mat, soln_translation, model);
|
||||
models_.emplace_back(K * model);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,7 +388,12 @@ public:
|
||||
zw[1] = Zw2(0); zw[4] = Zw2(1); zw[7] = Zw2(2);
|
||||
zw[2] = Z3crZ1w(0); zw[5] = Z3crZ1w(1); zw[8] = Z3crZ1w(2);
|
||||
|
||||
const Matx33d R = Math::rotVec2RotMat(Math::rotMat2RotVec(Z * Zw.inv()));
|
||||
Matx33d R = Z * Zw.inv();
|
||||
// https://github.com/opencv/opencv/blob/2ba688f23c4e20754f32179d9396ba9b54b3b064/modules/calib3d/src/usac/pnp_solver.cpp#L395
|
||||
// Use directly cv::Rodrigues
|
||||
Matx31d rvec;
|
||||
Rodrigues(R, rvec);
|
||||
Rodrigues(rvec, R);
|
||||
Matx33d KR = K * R;
|
||||
Matx34d P;
|
||||
hconcat(KR, -KR * (X1 - R.t() * nX1), P);
|
||||
|
||||
@@ -422,46 +422,6 @@ Matx33d Math::getSkewSymmetric(const Vec3d &v) {
|
||||
-v[1], v[0], 0};
|
||||
}
|
||||
|
||||
Matx33d Math::rotVec2RotMat (const Vec3d &v) {
|
||||
const double phi = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
|
||||
const double x = v[0] / phi, y = v[1] / phi, z = v[2] / phi;
|
||||
const double a = std::sin(phi), b = std::cos(phi);
|
||||
// R = I + sin(phi) * skew(v) + (1 - cos(phi) * skew(v)^2
|
||||
return {(b - 1)*y*y + (b - 1)*z*z + 1, -a*z - x*y*(b - 1), a*y - x*z*(b - 1),
|
||||
a*z - x*y*(b - 1), (b - 1)*x*x + (b - 1)*z*z + 1, -a*x - y*z*(b - 1),
|
||||
-a*y - x*z*(b - 1), a*x - y*z*(b - 1), (b - 1)*x*x + (b - 1)*y*y + 1};
|
||||
}
|
||||
|
||||
Vec3d Math::rotMat2RotVec (const Matx33d &R) {
|
||||
// https://math.stackexchange.com/questions/83874/efficient-and-accurate-numerical-implementation-of-the-inverse-rodrigues-rotatio?rq=1
|
||||
Vec3d rot_vec;
|
||||
const double trace = R(0,0)+R(1,1)+R(2,2);
|
||||
if (trace >= 3 - FLT_EPSILON) {
|
||||
rot_vec = (0.5 * (trace-3)/12)*Vec3d(R(2,1)-R(1,2),
|
||||
R(0,2)-R(2,0),
|
||||
R(1,0)-R(0,1));
|
||||
} else if (3 - FLT_EPSILON > trace && trace > -1 + FLT_EPSILON) {
|
||||
double theta = std::acos((trace - 1) / 2);
|
||||
rot_vec = (theta / (2 * std::sin(theta))) * Vec3d(R(2,1)-R(1,2),
|
||||
R(0,2)-R(2,0),
|
||||
R(1,0)-R(0,1));
|
||||
} else {
|
||||
int a;
|
||||
if (R(0,0) > R(1,1))
|
||||
a = R(0,0) > R(2,2) ? 0 : 2;
|
||||
else
|
||||
a = R(1,1) > R(2,2) ? 1 : 2;
|
||||
Vec3d v;
|
||||
int b = (a + 1) % 3, c = (a + 2) % 3;
|
||||
double s = sqrt(R(a,a) - R(b,b) - R(c,c) + 1);
|
||||
v[a] = s / 2;
|
||||
v[b] = (R(b,a) + R(a,b)) / (2 * s);
|
||||
v[c] = (R(c,a) + R(a,c)) / (2 * s);
|
||||
rot_vec = M_PI * v / norm(v);
|
||||
}
|
||||
return rot_vec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Eliminate matrix of m rows and n columns to be upper triangular.
|
||||
*/
|
||||
|
||||
@@ -458,7 +458,7 @@ public:
|
||||
{
|
||||
eps[SOLVEPNP_ITERATIVE] = 1.0e-6;
|
||||
eps[SOLVEPNP_EPNP] = 1.0e-6;
|
||||
eps[SOLVEPNP_P3P] = 2.0e-4;
|
||||
eps[SOLVEPNP_P3P] = 1.0e-4;
|
||||
eps[SOLVEPNP_AP3P] = 1.0e-4;
|
||||
eps[SOLVEPNP_IPPE] = 1.0e-6;
|
||||
eps[SOLVEPNP_IPPE_SQUARE] = 1.0e-6;
|
||||
@@ -572,7 +572,7 @@ class CV_solveP3P_Test : public CV_solvePnPRansac_Test
|
||||
public:
|
||||
CV_solveP3P_Test()
|
||||
{
|
||||
eps[SOLVEPNP_P3P] = 2.0e-4;
|
||||
eps[SOLVEPNP_P3P] = 1.0e-4;
|
||||
eps[SOLVEPNP_AP3P] = 1.0e-4;
|
||||
totalTestsCount = 1000;
|
||||
}
|
||||
@@ -1472,7 +1472,7 @@ TEST(Calib3d_SolvePnP, generic)
|
||||
for (size_t i = 0; i < rvecs_est.size() && !isTestSuccess; i++) {
|
||||
double rvecDiff = cvtest::norm(rvecs_est[i], rvec_ground_truth, NORM_L2);
|
||||
double tvecDiff = cvtest::norm(tvecs_est[i], tvec_ground_truth, NORM_L2);
|
||||
const double threshold = method == SOLVEPNP_P3P ? 1e-2 : 1e-4;
|
||||
const double threshold = 1e-4;
|
||||
isTestSuccess = rvecDiff < threshold && tvecDiff < threshold;
|
||||
}
|
||||
|
||||
@@ -1540,7 +1540,7 @@ TEST(Calib3d_SolvePnP, generic)
|
||||
for (size_t i = 0; i < rvecs_est.size() && !isTestSuccess; i++) {
|
||||
double rvecDiff = cvtest::norm(rvecs_est[i], rvec_ground_truth, NORM_L2);
|
||||
double tvecDiff = cvtest::norm(tvecs_est[i], tvec_ground_truth, NORM_L2);
|
||||
const double threshold = method == SOLVEPNP_P3P ? 1e-2 : 1e-4;
|
||||
const double threshold = 1e-4;
|
||||
isTestSuccess = rvecDiff < threshold && tvecDiff < threshold;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
@inproceedings{ding2023revisiting,
|
||||
title={Revisiting the P3P Problem},
|
||||
author={Ding, Yaqing and Yang, Jian and Larsson, Viktor and Olsson, Carl and {\AA}str{\"o}m, Kalle},
|
||||
booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
|
||||
pages={4872--4880},
|
||||
year={2023},
|
||||
url={https://openaccess.thecvf.com/content/CVPR2023/papers/Ding_Revisiting_the_P3P_Problem_CVPR_2023_paper.pdf}
|
||||
}
|
||||
@article{lepetit2009epnp,
|
||||
title={Epnp: An accurate o (n) solution to the pnp problem},
|
||||
author={Lepetit, Vincent and Moreno-Noguer, Francesc and Fua, Pascal},
|
||||
journal={International journal of computer vision},
|
||||
volume={81},
|
||||
number={2},
|
||||
pages={155--166},
|
||||
year={2009},
|
||||
publisher={Springer},
|
||||
url={https://www.tugraz.at/fileadmin/user_upload/Institute/ICG/Images/team_lepetit/publications/lepetit_ijcv08.pdf}
|
||||
}
|
||||
|
||||
@inproceedings{hesch2011direct,
|
||||
title={A direct least-squares (DLS) method for PnP},
|
||||
author={Hesch, Joel and Roumeliotis, Stergios and others},
|
||||
booktitle={Computer Vision (ICCV), 2011 IEEE International Conference on},
|
||||
pages={383--390},
|
||||
year={2011},
|
||||
organization={IEEE},
|
||||
url={https://www-users.cse.umn.edu/~stergios/papers/ICCV-11-DLS-PnP.pdf}
|
||||
}
|
||||
|
||||
@article{penate2013exhaustive,
|
||||
title={Exhaustive linearization for robust camera pose and focal length estimation},
|
||||
author={Penate-Sanchez, Adrian and Andrade-Cetto, Juan and Moreno-Noguer, Francesc},
|
||||
journal={Pattern Analysis and Machine Intelligence, IEEE Transactions on},
|
||||
volume={35},
|
||||
number={10},
|
||||
pages={2387--2400},
|
||||
year={2013},
|
||||
publisher={IEEE},
|
||||
url={https://www.researchgate.net/publication/235402233_Exhaustive_Linearization_for_Robust_Camera_Pose_and_Focal_Length_Estimation}
|
||||
}
|
||||
|
||||
@inproceedings{strobl2011iccv,
|
||||
title={More accurate pinhole camera calibration with imperfect planar target},
|
||||
author={Strobl, Klaus H. and Hirzinger, Gerd},
|
||||
booktitle={2011 IEEE International Conference on Computer Vision (ICCV)},
|
||||
pages={1068-1075},
|
||||
month={Nov},
|
||||
year={2011},
|
||||
address={Barcelona, Spain},
|
||||
publisher={IEEE},
|
||||
url={https://elib.dlr.de/71888/1/strobl_2011iccv.pdf},
|
||||
doi={10.1109/ICCVW.2011.6130369}
|
||||
}
|
||||
|
||||
@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}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,7 @@
|
||||
#include "opencv2/core/base.hpp"
|
||||
|
||||
#include "opencv2/core/cuda.hpp"
|
||||
#include "opencv2/core/private/cuda_stubs.hpp"
|
||||
|
||||
#ifdef HAVE_CUDA
|
||||
# include <cuda.h>
|
||||
@@ -101,16 +102,10 @@ namespace cv { namespace cuda {
|
||||
CV_EXPORTS void syncOutput(const GpuMat& dst, OutputArray _dst, Stream& stream);
|
||||
}}
|
||||
|
||||
#ifndef HAVE_CUDA
|
||||
|
||||
static inline CV_NORETURN void throw_no_cuda() { CV_Error(cv::Error::GpuNotSupported, "The library is compiled without CUDA support"); }
|
||||
|
||||
#else // HAVE_CUDA
|
||||
#ifdef HAVE_CUDA
|
||||
|
||||
#define nppSafeSetStream(oldStream, newStream) { if(oldStream != newStream) { cudaStreamSynchronize(oldStream); nppSetStream(newStream); } }
|
||||
|
||||
static inline CV_NORETURN void throw_no_cuda() { CV_Error(cv::Error::StsNotImplemented, "The called functionality is disabled for current build or platform"); }
|
||||
|
||||
namespace cv { namespace cuda
|
||||
{
|
||||
static inline void checkNppError(int code, const char* file, const int line, const char* func)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_CORE_PRIVATE_CUDA_STUBS_HPP
|
||||
#define OPENCV_CORE_PRIVATE_CUDA_STUBS_HPP
|
||||
|
||||
#ifndef __OPENCV_BUILD
|
||||
# error this is a private header which should not be used from outside of the OpenCV library
|
||||
#endif
|
||||
|
||||
#include "opencv2/core/cvdef.h"
|
||||
#include "opencv2/core/base.hpp"
|
||||
|
||||
#ifndef HAVE_CUDA
|
||||
static inline CV_NORETURN void throw_no_cuda() { CV_Error(cv::Error::GpuNotSupported, "The library is compiled without CUDA support"); }
|
||||
#else
|
||||
static inline CV_NORETURN void throw_no_cuda() { CV_Error(cv::Error::StsNotImplemented, "The called functionality is disabled for current build or platform"); }
|
||||
#endif
|
||||
|
||||
#endif // OPENCV_CORE_PRIVATE_CUDA_STUBS_HPP
|
||||
@@ -1931,7 +1931,7 @@ template<typename _Tp> static inline
|
||||
Rect_<_Tp>& operator &= ( Rect_<_Tp>& a, const Rect_<_Tp>& b )
|
||||
{
|
||||
if (a.empty() || b.empty()) {
|
||||
a = Rect();
|
||||
a = Rect_<_Tp>();
|
||||
return a;
|
||||
}
|
||||
const Rect_<_Tp>& Rx_min = (a.x < b.x) ? a : b;
|
||||
@@ -1945,7 +1945,7 @@ Rect_<_Tp>& operator &= ( Rect_<_Tp>& a, const Rect_<_Tp>& b )
|
||||
// Let us first deal with the following case.
|
||||
if ((Rx_min.x < 0 && Rx_min.x + Rx_min.width < Rx_max.x) ||
|
||||
(Ry_min.y < 0 && Ry_min.y + Ry_min.height < Ry_max.y)) {
|
||||
a = Rect();
|
||||
a = Rect_<_Tp>();
|
||||
return a;
|
||||
}
|
||||
// We now know that either Rx_min.x >= 0, or
|
||||
@@ -1957,7 +1957,7 @@ Rect_<_Tp>& operator &= ( Rect_<_Tp>& a, const Rect_<_Tp>& b )
|
||||
a.x = Rx_max.x;
|
||||
a.y = Ry_max.y;
|
||||
if (a.empty())
|
||||
a = Rect();
|
||||
a = Rect_<_Tp>();
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ elseif(HAVE_QT)
|
||||
endif()
|
||||
|
||||
foreach(dt_dep ${qt_deps})
|
||||
add_definitions(${Qt${QT_VERSION_MAJOR}${dt_dep}_DEFINITIONS})
|
||||
link_libraries(${Qt${QT_VERSION_MAJOR}${dt_dep}})
|
||||
include_directories(${Qt${QT_VERSION_MAJOR}${dt_dep}_INCLUDE_DIRS})
|
||||
list(APPEND HIGHGUI_LIBRARIES ${Qt${QT_VERSION_MAJOR}${dt_dep}_LIBRARIES})
|
||||
endforeach()
|
||||
|
||||
@@ -1089,9 +1089,9 @@ const std::string cv::currentUIFramework()
|
||||
return std::string("COCOA");
|
||||
#elif defined (HAVE_WAYLAND)
|
||||
return std::string("WAYLAND");
|
||||
#else
|
||||
return std::string();
|
||||
#endif
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
//========================= OpenGL fallback =========================
|
||||
|
||||
@@ -425,8 +425,18 @@ bool GdalDecoder::readData( Mat& img ){
|
||||
// create a temporary scanline pointer to store data
|
||||
double* scanline = new double[nCols];
|
||||
|
||||
#if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,3,0)
|
||||
// FITS drivers on version GDAL prior to v3.3.0 return vertically mirrored results.
|
||||
// See https://github.com/OSGeo/gdal/pull/3520
|
||||
// See https://github.com/OSGeo/gdal/commit/ef0f86696d163e065943b27f50dcff77790a1311
|
||||
const bool isNeedVerticallyFlip = strncmp(m_dataset->GetDriverName(), "FITS", 4) == 0;
|
||||
#else
|
||||
const bool isNeedVerticallyFlip = false;
|
||||
#endif
|
||||
|
||||
// iterate over each row and column
|
||||
for( int y=0; y<nRows; y++ ){
|
||||
for( int y=0; y<nRows; y++ ){ // for GDAL
|
||||
const int yCv = isNeedVerticallyFlip ? (nRows - 1) - y : y ; // for OpenCV
|
||||
|
||||
// get the entire row
|
||||
CPLErr err = band->RasterIO( GF_Read, 0, y, nCols, 1, scanline, nCols, 1, GDT_Float64, 0, 0);
|
||||
@@ -438,10 +448,10 @@ bool GdalDecoder::readData( Mat& img ){
|
||||
// set depending on image types
|
||||
// given boost, I would use enable_if to speed up. Avoid for now.
|
||||
if( hasColorTable == false ){
|
||||
write_pixel( scanline[x], gdalType, nChannels, img, y, x, color );
|
||||
write_pixel( scanline[x], gdalType, nChannels, img, yCv, x, color );
|
||||
}
|
||||
else{
|
||||
write_ctable_pixel( scanline[x], gdalType, gdalColorTable, img, y, x, color );
|
||||
write_ctable_pixel( scanline[x], gdalType, gdalColorTable, img, yCv, x, color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,7 +764,7 @@ bool GifEncoder::lzwEncode() {
|
||||
//initialize
|
||||
int32_t prev = imgCodeStream[0];
|
||||
|
||||
for (int64_t i = 1; i < height * width; i++) {
|
||||
for (size_t i = 1; i < size_t(height * width); i++) {
|
||||
// add the output code to the output buffer
|
||||
while (bitLeft >= 8) {
|
||||
buffer[bufferLen++] = (uchar)output;
|
||||
|
||||
@@ -51,10 +51,10 @@ int validateToInt(size_t sz)
|
||||
return valueInt;
|
||||
}
|
||||
|
||||
int64_t validateToInt64(size_t sz)
|
||||
int64_t validateToInt64(ptrdiff_t sz)
|
||||
{
|
||||
int64_t valueInt = static_cast<int64_t>(sz);
|
||||
CV_Assert((size_t)valueInt == sz);
|
||||
CV_Assert((ptrdiff_t)valueInt == sz);
|
||||
return valueInt;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
namespace cv {
|
||||
|
||||
int validateToInt(size_t step);
|
||||
int64_t validateToInt64(size_t step);
|
||||
int64_t validateToInt64(ptrdiff_t step);
|
||||
|
||||
template <typename _Tp> static inline
|
||||
size_t safeCastToSizeT(const _Tp v_origin, const char* msg)
|
||||
|
||||
@@ -62,25 +62,8 @@ static void findCircle3pts(Point2f *pts, Point2f ¢er, float &radius)
|
||||
float det = v1.x * v2.y - v1.y * v2.x;
|
||||
if (fabs(det) <= EPS)
|
||||
{
|
||||
// v1 and v2 are colinear, so the longest distance between any 2 points
|
||||
// is the diameter of the minimum enclosing circle.
|
||||
float d1 = normL2Sqr<float>(pts[0] - pts[1]);
|
||||
float d2 = normL2Sqr<float>(pts[0] - pts[2]);
|
||||
float d3 = normL2Sqr<float>(pts[1] - pts[2]);
|
||||
radius = sqrt(std::max(d1, std::max(d2, d3))) * 0.5f + EPS;
|
||||
if (d1 >= d2 && d1 >= d3)
|
||||
{
|
||||
center = (pts[0] + pts[1]) * 0.5f;
|
||||
}
|
||||
else if (d2 >= d1 && d2 >= d3)
|
||||
{
|
||||
center = (pts[0] + pts[2]) * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_DbgAssert(d3 >= d1 && d3 >= d2);
|
||||
center = (pts[1] + pts[2]) * 0.5f;
|
||||
}
|
||||
// triangle is degenerate, so this is 2-points case
|
||||
// 2-points case should be taken into account in previous step
|
||||
return;
|
||||
}
|
||||
float cx = (c1 * v2.y - c2 * v1.y) / det;
|
||||
@@ -115,13 +98,7 @@ static void findThirdPoint(const PT *pts, int i, int j, Point2f ¢er, float &
|
||||
ptsf[0] = (Point2f)pts[i];
|
||||
ptsf[1] = (Point2f)pts[j];
|
||||
ptsf[2] = (Point2f)pts[k];
|
||||
Point2f new_center; float new_radius = 0;
|
||||
findCircle3pts(ptsf, new_center, new_radius);
|
||||
if (new_radius > 0)
|
||||
{
|
||||
radius = new_radius;
|
||||
center = new_center;
|
||||
}
|
||||
findCircle3pts(ptsf, center, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,13 +123,7 @@ void findSecondPoint(const PT *pts, int i, Point2f ¢er, float &radius)
|
||||
}
|
||||
else
|
||||
{
|
||||
Point2f new_center; float new_radius = 0;
|
||||
findThirdPoint(pts, i, j, new_center, new_radius);
|
||||
if (new_radius > 0)
|
||||
{
|
||||
radius = new_radius;
|
||||
center = new_center;
|
||||
}
|
||||
findThirdPoint(pts, i, j, center, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,13 +149,7 @@ static void findMinEnclosingCircle(const PT *pts, int count, Point2f ¢er, fl
|
||||
}
|
||||
else
|
||||
{
|
||||
Point2f new_center; float new_radius = 0;
|
||||
findSecondPoint(pts, i, new_center, new_radius);
|
||||
if (new_radius > 0)
|
||||
{
|
||||
radius = new_radius;
|
||||
center = new_center;
|
||||
}
|
||||
findSecondPoint(pts, i, center, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,61 +175,42 @@ void cv::minEnclosingCircle( InputArray _points, Point2f& _center, float& _radiu
|
||||
const Point* ptsi = points.ptr<Point>();
|
||||
const Point2f* ptsf = points.ptr<Point2f>();
|
||||
|
||||
switch (count)
|
||||
if( count == 1 )
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
_center = (is_float) ? ptsf[0] : Point2f((float)ptsi[0].x, (float)ptsi[0].y);
|
||||
_radius = EPS;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Point2f p1 = (is_float) ? ptsf[0] : Point2f((float)ptsi[0].x, (float)ptsi[0].y);
|
||||
Point2f p2 = (is_float) ? ptsf[1] : Point2f((float)ptsi[1].x, (float)ptsi[1].y);
|
||||
_center.x = (p1.x + p2.x) / 2.0f;
|
||||
_center.y = (p1.y + p2.y) / 2.0f;
|
||||
_radius = (float)(norm(p1 - p2) / 2.0) + EPS;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Point2f center;
|
||||
float radius = 0.f;
|
||||
if (is_float)
|
||||
_center = (is_float) ? ptsf[0] : Point2f((float)ptsi[0].x, (float)ptsi[0].y);
|
||||
_radius = EPS;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_float)
|
||||
{
|
||||
findMinEnclosingCircle<Point2f>(ptsf, count, _center, _radius);
|
||||
#if 0
|
||||
for (int m = 0; m < count; ++m)
|
||||
{
|
||||
findMinEnclosingCircle<Point2f>(ptsf, count, center, radius);
|
||||
#if 0
|
||||
for (size_t m = 0; m < count; ++m)
|
||||
{
|
||||
float d = (float)norm(ptsf[m] - center);
|
||||
if (d > radius)
|
||||
{
|
||||
printf("error!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
float d = (float)norm(ptsf[m] - _center);
|
||||
if (d > _radius)
|
||||
{
|
||||
printf("error!\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
findMinEnclosingCircle<Point>(ptsi, count, _center, _radius);
|
||||
#if 0
|
||||
for (int m = 0; m < count; ++m)
|
||||
{
|
||||
findMinEnclosingCircle<Point>(ptsi, count, center, radius);
|
||||
#if 0
|
||||
for (size_t m = 0; m < count; ++m)
|
||||
{
|
||||
double dx = ptsi[m].x - center.x;
|
||||
double dy = ptsi[m].y - center.y;
|
||||
double d = std::sqrt(dx * dx + dy * dy);
|
||||
if (d > radius)
|
||||
{
|
||||
printf("error!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
double dx = ptsi[m].x - _center.x;
|
||||
double dy = ptsi[m].y - _center.y;
|
||||
double d = std::sqrt(dx * dx + dy * dy);
|
||||
if (d > _radius)
|
||||
{
|
||||
printf("error!\n");
|
||||
}
|
||||
}
|
||||
_center = center;
|
||||
_radius = radius;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,31 +52,48 @@ namespace opencv_test { namespace {
|
||||
|
||||
TEST(minEnclosingCircle, basic_test)
|
||||
{
|
||||
vector<Point2f> pts;
|
||||
pts.push_back(Point2f(0, 0));
|
||||
pts.push_back(Point2f(10, 0));
|
||||
pts.push_back(Point2f(5, 1));
|
||||
const float EPS = 1.0e-3f;
|
||||
Point2f center;
|
||||
float radius;
|
||||
|
||||
{
|
||||
const vector<Point2f> pts = { {5, 10} };
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 10, EPS);
|
||||
EXPECT_NEAR(radius, 0, EPS);
|
||||
}
|
||||
|
||||
{
|
||||
const vector<Point2f> pts = { {5, 10}, {11, 18} };
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 8, EPS);
|
||||
EXPECT_NEAR(center.y, 14, EPS);
|
||||
EXPECT_NEAR(radius, 5, EPS);
|
||||
}
|
||||
|
||||
// pts[2] is within the circle with diameter pts[0] - pts[1].
|
||||
// 2
|
||||
// 0 1
|
||||
// NB: The triangle is obtuse, so the only pts[0] and pts[1] are on the circle.
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
{
|
||||
const vector<Point2f> pts = { {0, 0}, {10, 0}, {5, 1} };
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
}
|
||||
|
||||
// pts[2] is on the circle with diameter pts[0] - pts[1].
|
||||
// 2
|
||||
// 0 1
|
||||
pts[2] = Point2f(5, 5);
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
{
|
||||
const vector<Point2f> pts = { {0, 0}, {10, 0}, {5, 5} };
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
}
|
||||
|
||||
// pts[2] is outside the circle with diameter pts[0] - pts[1].
|
||||
// 2
|
||||
@@ -84,32 +101,40 @@ TEST(minEnclosingCircle, basic_test)
|
||||
//
|
||||
// 0 1
|
||||
// NB: The triangle is acute, so all 3 points are on the circle.
|
||||
pts[2] = Point2f(5, 10);
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 3.75, EPS);
|
||||
EXPECT_NEAR(6.25f, radius, EPS);
|
||||
{
|
||||
const vector<Point2f> pts = { {0, 0}, {10, 0}, {5, 10} };
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 3.75, EPS);
|
||||
EXPECT_NEAR(6.25f, radius, EPS);
|
||||
}
|
||||
|
||||
// The 3 points are colinear.
|
||||
pts[2] = Point2f(3, 0);
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
{
|
||||
const vector<Point2f> pts = { {0, 0}, {10, 0}, {3, 0} };
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
}
|
||||
|
||||
// 2 points are the same.
|
||||
pts[2] = pts[1];
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
{
|
||||
const vector<Point2f> pts = { {0, 0}, {10, 0}, {10, 0} };
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
}
|
||||
|
||||
// 3 points are the same.
|
||||
pts[0] = pts[1];
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 10, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(0, radius, EPS);
|
||||
{
|
||||
const vector<Point2f> pts = { {10, 0}, {10, 0}, {10, 0} };
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 10, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(0, radius, EPS);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Imgproc_minEnclosingCircle, regression_16051) {
|
||||
@@ -127,6 +152,44 @@ TEST(Imgproc_minEnclosingCircle, regression_16051) {
|
||||
EXPECT_NEAR(2.1024551f, radius, 1e-3);
|
||||
}
|
||||
|
||||
TEST(Imgproc_minEnclosingCircle, regression_27891) {
|
||||
{
|
||||
const vector<Point2f> pts = { {219, 301}, {639, 635}, {740, 569}, {740, 569}, {309, 123}, {349, 88} };
|
||||
|
||||
Point2f center;
|
||||
float radius;
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
|
||||
EXPECT_NEAR(center.x, 522.476f, 1e-3f);
|
||||
EXPECT_NEAR(center.y, 346.4029f, 1e-3f);
|
||||
EXPECT_NEAR(radius, 311.2331f, 1e-3f);
|
||||
}
|
||||
|
||||
{
|
||||
const vector<Point2f> pts = { {219, 301}, {639, 635}, {740, 569}, {740, 569}, {349, 88} };
|
||||
|
||||
Point2f center;
|
||||
float radius;
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
|
||||
EXPECT_NEAR(center.x, 522.476f, 1e-3f);
|
||||
EXPECT_NEAR(center.y, 346.4029f, 1e-3f);
|
||||
EXPECT_NEAR(radius, 311.2331f, 1e-3f);
|
||||
}
|
||||
|
||||
{
|
||||
const vector<Point2f> pts = { {639, 635}, {740, 569}, {740, 569}, {349, 88} };
|
||||
|
||||
Point2f center;
|
||||
float radius;
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
|
||||
EXPECT_NEAR(center.x, 522.476f, 1e-3f);
|
||||
EXPECT_NEAR(center.y, 346.4029f, 1e-3f);
|
||||
EXPECT_NEAR(radius, 311.2331f, 1e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
PARAM_TEST_CASE(ConvexityDefects_regression_5908, bool, int)
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -5,3 +5,7 @@ if(HAVE_CUDA)
|
||||
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}")
|
||||
endif()
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace cv { namespace cuda { namespace device
|
||||
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float> > op()
|
||||
{
|
||||
plus<float> op;
|
||||
return thrust::make_tuple(op, op);
|
||||
return { op, op };
|
||||
}
|
||||
};
|
||||
template <> struct Unroll<2>
|
||||
@@ -218,7 +218,7 @@ namespace cv { namespace cuda { namespace device
|
||||
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float>, plus<float> > op()
|
||||
{
|
||||
plus<float> op;
|
||||
return thrust::make_tuple(op, op, op);
|
||||
return { op, op, op };
|
||||
}
|
||||
};
|
||||
template <> struct Unroll<3>
|
||||
@@ -237,7 +237,7 @@ namespace cv { namespace cuda { namespace device
|
||||
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float>, plus<float>, plus<float> > op()
|
||||
{
|
||||
plus<float> op;
|
||||
return thrust::make_tuple(op, op, op, op);
|
||||
return { op, op, op, op };
|
||||
}
|
||||
};
|
||||
template <> struct Unroll<4>
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "opencv2/photo/cuda.hpp"
|
||||
#include "opencv2/core/private.cuda.hpp"
|
||||
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
|
||||
@@ -60,12 +59,15 @@ 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)
|
||||
|
||||
|
||||
@@ -11,3 +11,7 @@ endif()
|
||||
ocv_define_module(stitching opencv_imgproc opencv_features opencv_3d opencv_flann
|
||||
OPTIONAL opencv_cudaarithm opencv_cudawarping opencv_cudafeatures2d opencv_cudalegacy opencv_cudaimgproc ${STITCHING_CONTRIB_DEPS}
|
||||
WRAP python)
|
||||
|
||||
if(HAVE_CUDA AND ENABLE_CUDA_FIRST_CLASS_LANGUAGE)
|
||||
ocv_target_link_libraries(${the_module} PUBLIC "CUDA::cudart${CUDA_LIB_EXT}")
|
||||
endif()
|
||||
|
||||
@@ -71,6 +71,21 @@ public:
|
||||
*/
|
||||
CV_WRAP virtual void apply(InputArray image, OutputArray fgmask, double learningRate=-1) = 0;
|
||||
|
||||
/** @brief Computes a foreground mask with known foreground mask input.
|
||||
|
||||
@param image Next video frame. Floating point frame will be used without scaling and should be in range \f$[0,255]\f$.
|
||||
@param fgmask The output foreground mask as an 8-bit binary image.
|
||||
@param knownForegroundMask The mask for inputting already known foreground, allows model to ignore pixels.
|
||||
@param learningRate The value between 0 and 1 that indicates how fast the background model is
|
||||
learnt. Negative parameter value makes the algorithm to use some automatically chosen learning
|
||||
rate. 0 means that the background model is not updated at all, 1 means that the background model
|
||||
is completely reinitialized from the last frame.
|
||||
|
||||
@note This method has a default virtual implementation that throws a "not impemented" error.
|
||||
Foreground masking may not be supported by all background subtractors.
|
||||
*/
|
||||
CV_WRAP virtual void apply(InputArray image, InputArray knownForegroundMask, OutputArray fgmask, double learningRate=-1) = 0;
|
||||
|
||||
/** @brief Computes a background image.
|
||||
|
||||
@param backgroundImage The output background image.
|
||||
@@ -206,6 +221,18 @@ public:
|
||||
is completely reinitialized from the last frame.
|
||||
*/
|
||||
CV_WRAP virtual void apply(InputArray image, OutputArray fgmask, double learningRate=-1) CV_OVERRIDE = 0;
|
||||
|
||||
/** @brief Computes a foreground mask and skips known foreground in evaluation.
|
||||
|
||||
@param image Next video frame. Floating point frame will be used without scaling and should be in range \f$[0,255]\f$.
|
||||
@param fgmask The output foreground mask as an 8-bit binary image.
|
||||
@param knownForegroundMask The mask for inputting already known foreground, allows model to ignore pixels.
|
||||
@param learningRate The value between 0 and 1 that indicates how fast the background model is
|
||||
learnt. Negative parameter value makes the algorithm to use some automatically chosen learning
|
||||
rate. 0 means that the background model is not updated at all, 1 means that the background model
|
||||
is completely reinitialized from the last frame.
|
||||
*/
|
||||
CV_WRAP virtual void apply(InputArray image, InputArray knownForegroundMask, OutputArray fgmask, double learningRate=-1) CV_OVERRIDE = 0;
|
||||
};
|
||||
|
||||
/** @brief Creates MOG2 Background Subtractor
|
||||
|
||||
@@ -132,6 +132,8 @@ public:
|
||||
//! the update operator
|
||||
void apply(InputArray image, OutputArray fgmask, double learningRate) CV_OVERRIDE;
|
||||
|
||||
void apply(InputArray image, InputArray knownForegroundMask, OutputArray fgmask, double learningRate) CV_OVERRIDE;
|
||||
|
||||
//! computes a background image which are the mean of all background gaussians
|
||||
virtual void getBackgroundImage(OutputArray backgroundImage) const CV_OVERRIDE;
|
||||
|
||||
@@ -526,7 +528,9 @@ public:
|
||||
int _nkNN,
|
||||
float _fTau,
|
||||
bool _bShadowDetection,
|
||||
uchar _nShadowDetection)
|
||||
uchar _nShadowDetection,
|
||||
const Mat& _knownForegroundMask)
|
||||
: knownForegroundMask(_knownForegroundMask)
|
||||
{
|
||||
src = &_src;
|
||||
dst = &_dst;
|
||||
@@ -587,6 +591,17 @@ public:
|
||||
m_nShortCounter,
|
||||
include
|
||||
);
|
||||
// Check that foreground mask exists
|
||||
if (!knownForegroundMask.empty()) {
|
||||
// If input mask states pixel is foreground
|
||||
if (knownForegroundMask.at<uchar>(y, x) > 0)
|
||||
{
|
||||
mask[x] = 255; // ensure output mask marks this pixel as FG
|
||||
data += nchannels;
|
||||
m_aModel += m_nN*3*ndata;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
switch (result)
|
||||
{
|
||||
case 0:
|
||||
@@ -626,6 +641,7 @@ public:
|
||||
int m_nkNN;
|
||||
bool m_bShadowDetection;
|
||||
uchar m_nShadowDetection;
|
||||
const Mat& knownForegroundMask;
|
||||
};
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
@@ -728,7 +744,12 @@ void BackgroundSubtractorKNNImpl::create_ocl_apply_kernel()
|
||||
|
||||
#endif
|
||||
|
||||
void BackgroundSubtractorKNNImpl::apply(InputArray _image, OutputArray _fgmask, double learningRate)
|
||||
// Base 3 version class
|
||||
void BackgroundSubtractorKNNImpl::apply(InputArray _image, OutputArray _fgmask, double learningRate) {
|
||||
apply(_image, noArray(), _fgmask, learningRate);
|
||||
}
|
||||
|
||||
void BackgroundSubtractorKNNImpl::apply(InputArray _image, InputArray _knownForegroundMask, OutputArray _fgmask, double learningRate)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
@@ -757,6 +778,14 @@ void BackgroundSubtractorKNNImpl::apply(InputArray _image, OutputArray _fgmask,
|
||||
_fgmask.create( image.size(), CV_8U );
|
||||
Mat fgmask = _fgmask.getMat();
|
||||
|
||||
Mat knownForegroundMask = _knownForegroundMask.getMat();
|
||||
|
||||
if(!knownForegroundMask.empty())
|
||||
{
|
||||
CV_Assert(knownForegroundMask.type() == CV_8UC1);
|
||||
CV_Assert(knownForegroundMask.size() == image.size());
|
||||
}
|
||||
|
||||
++nframes;
|
||||
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
|
||||
CV_Assert(learningRate >= 0);
|
||||
@@ -791,7 +820,8 @@ void BackgroundSubtractorKNNImpl::apply(InputArray _image, OutputArray _fgmask,
|
||||
nkNN,
|
||||
fTau,
|
||||
bShadowDetection,
|
||||
nShadowDetection),
|
||||
nShadowDetection,
|
||||
knownForegroundMask),
|
||||
image.total()/(double)(1 << 16));
|
||||
|
||||
nShortCounter++;//0,1,...,nShortUpdate-1
|
||||
|
||||
@@ -178,6 +178,8 @@ public:
|
||||
//! the update operator
|
||||
void apply(InputArray image, OutputArray fgmask, double learningRate) CV_OVERRIDE;
|
||||
|
||||
void apply(InputArray image, InputArray knownForegroundMask, OutputArray fgmask, double learningRate) CV_OVERRIDE;
|
||||
|
||||
//! computes a background image which are the mean of all background gaussians
|
||||
virtual void getBackgroundImage(OutputArray backgroundImage) const CV_OVERRIDE;
|
||||
|
||||
@@ -546,7 +548,8 @@ public:
|
||||
float _Tb, float _TB, float _Tg,
|
||||
float _varInit, float _varMin, float _varMax,
|
||||
float _prune, float _tau, bool _detectShadows,
|
||||
uchar _shadowVal)
|
||||
uchar _shadowVal, const Mat& _knownForegroundMask)
|
||||
: knownForegroundMask(_knownForegroundMask)
|
||||
{
|
||||
src = &_src;
|
||||
dst = &_dst;
|
||||
@@ -590,6 +593,18 @@ public:
|
||||
|
||||
for( int x = 0; x < ncols; x++, data += nchannels, gmm += nmixtures, mean += nmixtures*nchannels )
|
||||
{
|
||||
|
||||
// Check that foreground mask exists
|
||||
if (!knownForegroundMask.empty())
|
||||
{
|
||||
// If input mask states pixel is foreground
|
||||
if (knownForegroundMask.at<uchar>(y, x) > 0)
|
||||
{
|
||||
mask[x] = 255; // ensure output mask marks this pixel as FG
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//calculate distances to the modes (+ sort)
|
||||
//here we need to go in descending order!!!
|
||||
bool background = false;//return value -> true - the pixel classified as background
|
||||
@@ -766,6 +781,7 @@ public:
|
||||
|
||||
bool detectShadows;
|
||||
uchar shadowVal;
|
||||
const Mat& knownForegroundMask;
|
||||
};
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
@@ -844,7 +860,12 @@ void BackgroundSubtractorMOG2Impl::create_ocl_apply_kernel()
|
||||
|
||||
#endif
|
||||
|
||||
void BackgroundSubtractorMOG2Impl::apply(InputArray _image, OutputArray _fgmask, double learningRate)
|
||||
// Base 3 version class
|
||||
void BackgroundSubtractorMOG2Impl::apply(InputArray _image, OutputArray _fgmask, double learningRate) {
|
||||
apply(_image, noArray(), _fgmask, learningRate);
|
||||
}
|
||||
|
||||
void BackgroundSubtractorMOG2Impl::apply(InputArray _image, InputArray _knownForegroundMask, OutputArray _fgmask, double learningRate)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
@@ -867,6 +888,14 @@ void BackgroundSubtractorMOG2Impl::apply(InputArray _image, OutputArray _fgmask,
|
||||
_fgmask.create( image.size(), CV_8U );
|
||||
Mat fgmask = _fgmask.getMat();
|
||||
|
||||
Mat knownForegroundMask = _knownForegroundMask.getMat();
|
||||
|
||||
if(!knownForegroundMask.empty())
|
||||
{
|
||||
CV_Assert(knownForegroundMask.type() == CV_8UC1);
|
||||
CV_Assert(knownForegroundMask.size() == image.size());
|
||||
}
|
||||
|
||||
++nframes;
|
||||
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
|
||||
CV_Assert(learningRate >= 0);
|
||||
@@ -879,7 +908,7 @@ void BackgroundSubtractorMOG2Impl::apply(InputArray _image, OutputArray _fgmask,
|
||||
(float)varThreshold,
|
||||
backgroundRatio, varThresholdGen,
|
||||
fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau,
|
||||
bShadowDetection, nShadowDetection),
|
||||
bShadowDetection, nShadowDetection, knownForegroundMask),
|
||||
image.total()/(double)(1 << 16));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include "opencv2/video/background_segm.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
using namespace cv;
|
||||
|
||||
///////////////////////// MOG2 //////////////////////////////
|
||||
TEST(BackgroundSubtractorMOG2, KnownForegroundMaskShadowsTrue)
|
||||
{
|
||||
Ptr<BackgroundSubtractorMOG2> mog2 = createBackgroundSubtractorMOG2(500, 16, true);
|
||||
|
||||
//Black Frame
|
||||
Mat input = Mat::zeros(480,640 , CV_8UC3);
|
||||
|
||||
//White Rectangle
|
||||
Mat knownFG = Mat::zeros(input.size(), CV_8U);
|
||||
|
||||
rectangle(knownFG, Rect(3,3,5,5), Scalar(255,255,255), -1);
|
||||
|
||||
Mat output;
|
||||
mog2->apply(input, knownFG, output);
|
||||
|
||||
for(int y = 3; y < 8; y++)
|
||||
{
|
||||
for (int x = 3; x < 8; x++){
|
||||
EXPECT_EQ(255,output.at<uchar>(y,x)) << "Expected foreground at (" << x << "," << y << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BackgroundSubtractorMOG2, KnownForegroundMaskShadowsFalse)
|
||||
{
|
||||
Ptr<BackgroundSubtractorMOG2> mog2 = createBackgroundSubtractorMOG2(500, 16, false);
|
||||
|
||||
//Black Frame
|
||||
Mat input = Mat::zeros(480,640 , CV_8UC3);
|
||||
|
||||
//White Rectangle
|
||||
Mat knownFG = Mat::zeros(input.size(), CV_8U);
|
||||
|
||||
rectangle(knownFG, Rect(3,3,5,5), Scalar(255,255,255), FILLED);
|
||||
|
||||
Mat output;
|
||||
mog2->apply(input, knownFG, output);
|
||||
|
||||
for(int y = 3; y < 8; y++)
|
||||
{
|
||||
for (int x = 3; x < 8; x++){
|
||||
EXPECT_EQ(255,output.at<uchar>(y,x)) << "Expected foreground at (" << x << "," << y << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////// KNN //////////////////////////////
|
||||
|
||||
TEST(BackgroundSubtractorKNN, KnownForegroundMaskShadowsTrue)
|
||||
{
|
||||
Ptr<BackgroundSubtractorKNN> knn = createBackgroundSubtractorKNN(500, 400.0, true);
|
||||
|
||||
//Black Frame
|
||||
Mat input = Mat::zeros(480,640 , CV_8UC3);
|
||||
|
||||
//White Rectangle
|
||||
Mat knownFG = Mat::zeros(input.size(), CV_8U);
|
||||
|
||||
rectangle(knownFG, Rect(3,3,5,5), Scalar(255,255,255), FILLED);
|
||||
|
||||
Mat output;
|
||||
knn->apply(input, knownFG, output);
|
||||
|
||||
for(int y = 3; y < 8; y++)
|
||||
{
|
||||
for (int x = 3; x < 8; x++){
|
||||
EXPECT_EQ(255,output.at<uchar>(y,x)) << "Expected foreground at (" << x << "," << y << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BackgroundSubtractorKNN, KnownForegroundMaskShadowsFalse)
|
||||
{
|
||||
Ptr<BackgroundSubtractorKNN> knn = createBackgroundSubtractorKNN(500, 400.0, false);
|
||||
|
||||
//Black Frame
|
||||
Mat input = Mat::zeros(480,640 , CV_8UC3);
|
||||
|
||||
//White Rectangle
|
||||
Mat knownFG = Mat::zeros(input.size(), CV_8U);
|
||||
|
||||
rectangle(knownFG, Rect(3,3,5,5), Scalar(255,255,255), FILLED);
|
||||
|
||||
Mat output;
|
||||
knn->apply(input, knownFG, output);
|
||||
|
||||
for(int y = 3; y < 8; y++)
|
||||
{
|
||||
for (int x = 3; x < 8; x++){
|
||||
EXPECT_EQ(255,output.at<uchar>(y,x)) << "Expected foreground at (" << x << "," << y << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
/* End of file. */
|
||||
@@ -1,5 +1,5 @@
|
||||
# --- FFMPEG ---
|
||||
OCV_OPTION(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE "Include FFMPEG/libavdevice library support." OFF
|
||||
OCV_OPTION(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE "Include FFMPEG/libavdevice library support." ON
|
||||
VISIBLE_IF WITH_FFMPEG)
|
||||
|
||||
if(NOT HAVE_FFMPEG AND OPENCV_FFMPEG_USE_FIND_PACKAGE)
|
||||
|
||||
@@ -74,6 +74,11 @@ public:
|
||||
{
|
||||
open(filename, params);
|
||||
}
|
||||
CvCapture_FFMPEG_proxy(int index, const cv::VideoCaptureParameters& params)
|
||||
: ffmpegCapture(NULL)
|
||||
{
|
||||
open(index, params);
|
||||
}
|
||||
CvCapture_FFMPEG_proxy(const Ptr<IStreamReader>& stream, const cv::VideoCaptureParameters& params)
|
||||
: ffmpegCapture(NULL)
|
||||
{
|
||||
@@ -127,6 +132,13 @@ public:
|
||||
ffmpegCapture = cvCreateFileCaptureWithParams_FFMPEG(filename.c_str(), params);
|
||||
return ffmpegCapture != 0;
|
||||
}
|
||||
bool open(int index, const cv::VideoCaptureParameters& params)
|
||||
{
|
||||
close();
|
||||
|
||||
ffmpegCapture = cvCreateFileCaptureWithParams_FFMPEG(index, params);
|
||||
return ffmpegCapture != 0;
|
||||
}
|
||||
bool open(const Ptr<IStreamReader>& stream, const cv::VideoCaptureParameters& params)
|
||||
{
|
||||
close();
|
||||
@@ -161,6 +173,14 @@ cv::Ptr<cv::IVideoCapture> cvCreateFileCapture_FFMPEG_proxy(const std::string &f
|
||||
return cv::Ptr<cv::IVideoCapture>();
|
||||
}
|
||||
|
||||
cv::Ptr<cv::IVideoCapture> cvCreateCameraCapture_FFMPEG_proxy(int index, const cv::VideoCaptureParameters& params)
|
||||
{
|
||||
cv::Ptr<CvCapture_FFMPEG_proxy> capture = cv::makePtr<CvCapture_FFMPEG_proxy>(index, params);
|
||||
if (capture && capture->isOpened())
|
||||
return capture;
|
||||
return cv::Ptr<cv::IVideoCapture>();
|
||||
}
|
||||
|
||||
cv::Ptr<cv::IVideoCapture> cvCreateStreamCapture_FFMPEG_proxy(const Ptr<IStreamReader>& stream, const cv::VideoCaptureParameters& params)
|
||||
{
|
||||
cv::Ptr<CvCapture_FFMPEG_proxy> capture = std::make_shared<CvCapture_FFMPEG_proxy>(stream, params);
|
||||
@@ -272,13 +292,15 @@ CvResult CV_API_CALL cv_capture_open(const char* filename, int camera_index, CV_
|
||||
if (!handle)
|
||||
return CV_ERROR_FAIL;
|
||||
*handle = NULL;
|
||||
if (!filename)
|
||||
if (!filename && camera_index < 0)
|
||||
return CV_ERROR_FAIL;
|
||||
CV_UNUSED(camera_index);
|
||||
CvCapture_FFMPEG_proxy *cap = 0;
|
||||
try
|
||||
{
|
||||
cap = new CvCapture_FFMPEG_proxy(String(filename), cv::VideoCaptureParameters());
|
||||
if (filename)
|
||||
cap = new CvCapture_FFMPEG_proxy(String(filename), cv::VideoCaptureParameters());
|
||||
else
|
||||
cap = new CvCapture_FFMPEG_proxy(camera_index, cv::VideoCaptureParameters());
|
||||
if (cap->isOpened())
|
||||
{
|
||||
*handle = (CvPluginCapture)cap;
|
||||
@@ -308,14 +330,16 @@ CvResult CV_API_CALL cv_capture_open_with_params(
|
||||
if (!handle)
|
||||
return CV_ERROR_FAIL;
|
||||
*handle = NULL;
|
||||
if (!filename)
|
||||
if (!filename && camera_index < 0)
|
||||
return CV_ERROR_FAIL;
|
||||
CV_UNUSED(camera_index);
|
||||
CvCapture_FFMPEG_proxy *cap = 0;
|
||||
try
|
||||
{
|
||||
cv::VideoCaptureParameters parameters(params, n_params);
|
||||
cap = new CvCapture_FFMPEG_proxy(String(filename), parameters);
|
||||
if (filename)
|
||||
cap = new CvCapture_FFMPEG_proxy(String(filename), parameters);
|
||||
else
|
||||
cap = new CvCapture_FFMPEG_proxy(camera_index, parameters);
|
||||
if (cap->isOpened())
|
||||
{
|
||||
*handle = (CvPluginCapture)cap;
|
||||
|
||||
@@ -526,7 +526,7 @@ inline static std::string _opencv_ffmpeg_get_error_string(int error_code)
|
||||
|
||||
struct CvCapture_FFMPEG
|
||||
{
|
||||
bool open(const char* filename, const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params);
|
||||
bool open(const char* filename, int index, const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params);
|
||||
void close();
|
||||
|
||||
double getProperty(int) const;
|
||||
@@ -1043,7 +1043,7 @@ static bool isThreadSafe() {
|
||||
return threadSafe;
|
||||
}
|
||||
|
||||
bool CvCapture_FFMPEG::open(const char* _filename, const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params)
|
||||
bool CvCapture_FFMPEG::open(const char* _filename, int index, const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params)
|
||||
{
|
||||
const bool threadSafe = isThreadSafe();
|
||||
InternalFFMpegRegister::init(threadSafe);
|
||||
@@ -1145,16 +1145,6 @@ bool CvCapture_FFMPEG::open(const char* _filename, const Ptr<IStreamReader>& str
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_AV_INTERRUPT_CALLBACK
|
||||
/* interrupt callback */
|
||||
interrupt_metadata.timeout_after_ms = open_timeout;
|
||||
get_monotonic_time(&interrupt_metadata.value);
|
||||
|
||||
ic = avformat_alloc_context();
|
||||
ic->interrupt_callback.callback = _opencv_ffmpeg_interrupt_callback;
|
||||
ic->interrupt_callback.opaque = &interrupt_metadata;
|
||||
#endif
|
||||
|
||||
std::string options = utils::getConfigurationParameterString("OPENCV_FFMPEG_CAPTURE_OPTIONS");
|
||||
if (options.empty())
|
||||
{
|
||||
@@ -1180,7 +1170,53 @@ bool CvCapture_FFMPEG::open(const char* _filename, const Ptr<IStreamReader>& str
|
||||
input_format = av_find_input_format(entry->value);
|
||||
}
|
||||
|
||||
if (!_filename)
|
||||
AVDeviceInfoList* device_list = nullptr;
|
||||
if (index >= 0)
|
||||
{
|
||||
#ifdef HAVE_FFMPEG_LIBAVDEVICE
|
||||
entry = av_dict_get(dict, "f", NULL, 0);
|
||||
const char* backend = nullptr;
|
||||
if (entry)
|
||||
{
|
||||
backend = entry->value;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __linux__
|
||||
backend = "v4l2";
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
backend = "dshow";
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
backend = "avfoundation";
|
||||
#endif
|
||||
}
|
||||
avdevice_list_input_sources(nullptr, backend, nullptr, &device_list);
|
||||
if (!device_list)
|
||||
{
|
||||
CV_LOG_ONCE_WARNING(NULL, "VIDEOIO/FFMPEG: Failed list devices for backend " << backend);
|
||||
return false;
|
||||
}
|
||||
CV_CheckLT(index, device_list->nb_devices, "VIDEOIO/FFMPEG: Camera index out of range");
|
||||
_filename = device_list->devices[index]->device_name;
|
||||
#else
|
||||
CV_LOG_ONCE_WARNING(NULL, "VIDEOIO/FFMPEG: OpenCV should be configured with libavdevice to open a camera device");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_AV_INTERRUPT_CALLBACK
|
||||
/* interrupt callback */
|
||||
interrupt_metadata.timeout_after_ms = open_timeout;
|
||||
get_monotonic_time(&interrupt_metadata.value);
|
||||
|
||||
ic = avformat_alloc_context();
|
||||
ic->interrupt_callback.callback = _opencv_ffmpeg_interrupt_callback;
|
||||
ic->interrupt_callback.opaque = &interrupt_metadata;
|
||||
#endif
|
||||
|
||||
if (stream)
|
||||
{
|
||||
size_t avio_ctx_buffer_size = 4096;
|
||||
uint8_t* avio_ctx_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);
|
||||
@@ -1231,6 +1267,13 @@ bool CvCapture_FFMPEG::open(const char* _filename, const Ptr<IStreamReader>& str
|
||||
ic->pb = avio_context;
|
||||
}
|
||||
int err = avformat_open_input(&ic, _filename, input_format, &dict);
|
||||
if (device_list)
|
||||
{
|
||||
#ifdef HAVE_FFMPEG_LIBAVDEVICE
|
||||
avdevice_free_list_devices(&device_list);
|
||||
device_list = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
{
|
||||
@@ -3447,7 +3490,22 @@ CvCapture_FFMPEG* cvCreateFileCaptureWithParams_FFMPEG(const char* filename, con
|
||||
if (!capture)
|
||||
return 0;
|
||||
capture->init();
|
||||
if (capture->open(filename, nullptr, params))
|
||||
if (capture->open(filename, -1, nullptr, params))
|
||||
return capture;
|
||||
|
||||
capture->close();
|
||||
delete capture;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
CvCapture_FFMPEG* cvCreateFileCaptureWithParams_FFMPEG(int index, const VideoCaptureParameters& params)
|
||||
{
|
||||
CvCapture_FFMPEG* capture = new CvCapture_FFMPEG();
|
||||
if (!capture)
|
||||
return 0;
|
||||
capture->init();
|
||||
if (capture->open(nullptr, index, nullptr, params))
|
||||
return capture;
|
||||
|
||||
capture->close();
|
||||
@@ -3462,7 +3520,7 @@ CvCapture_FFMPEG* cvCreateStreamCaptureWithParams_FFMPEG(const Ptr<IStreamReader
|
||||
if (!capture)
|
||||
return 0;
|
||||
capture->init();
|
||||
if (capture->open(nullptr, stream, params))
|
||||
if (capture->open(nullptr, -1, stream, params))
|
||||
return capture;
|
||||
|
||||
capture->close();
|
||||
|
||||
@@ -303,6 +303,7 @@ protected:
|
||||
//==================================================================================================
|
||||
|
||||
Ptr<IVideoCapture> cvCreateFileCapture_FFMPEG_proxy(const std::string &filename, const VideoCaptureParameters& params);
|
||||
Ptr<IVideoCapture> cvCreateCameraCapture_FFMPEG_proxy(int index, const VideoCaptureParameters& params);
|
||||
Ptr<IVideoCapture> cvCreateStreamCapture_FFMPEG_proxy(const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params);
|
||||
Ptr<IVideoWriter> cvCreateVideoWriter_FFMPEG_proxy(const std::string& filename, int fourcc,
|
||||
double fps, const Size& frameSize,
|
||||
|
||||
@@ -112,6 +112,12 @@ static const struct VideoBackendInfo builtin_backends[] =
|
||||
DECLARE_STATIC_BACKEND(CAP_V4L, "V4L_BSD", MODE_CAPTURE_ALL, create_V4L_capture_file, create_V4L_capture_cam, 0)
|
||||
#endif
|
||||
|
||||
// FFmpeg webcamera by underlying backend (DShow, V4L2, AVFoundation)
|
||||
#ifdef HAVE_FFMPEG
|
||||
DECLARE_STATIC_BACKEND(CAP_FFMPEG, "FFMPEG", MODE_CAPTURE_BY_INDEX, 0, cvCreateCameraCapture_FFMPEG_proxy, 0)
|
||||
#elif defined(ENABLE_PLUGINS) || defined(HAVE_FFMPEG_WRAPPER)
|
||||
DECLARE_DYNAMIC_BACKEND(CAP_FFMPEG, "FFMPEG", MODE_CAPTURE_BY_INDEX)
|
||||
#endif
|
||||
|
||||
// RGB-D universal
|
||||
#ifdef HAVE_OPENNI2
|
||||
|
||||
@@ -327,4 +327,18 @@ TEST(DISABLED_videoio_camera, waitAny_V4L)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DISABLED_videoio_camera, ffmpeg_index)
|
||||
{
|
||||
int idx = (int)utils::getConfigurationParameterSizeT("OPENCV_TEST_FFMPEG_DEVICE_IDX", (size_t)-1);
|
||||
if (idx == -1)
|
||||
{
|
||||
throw SkipTestException("OPENCV_TEST_FFMPEG_DEVICE_IDX is not set");
|
||||
}
|
||||
VideoCapture cap;
|
||||
ASSERT_TRUE(cap.open(idx, CAP_FFMPEG));
|
||||
Mat frame;
|
||||
ASSERT_TRUE(cap.read(frame));
|
||||
ASSERT_FALSE(frame.empty());
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -158,6 +158,20 @@ inline static std::string param_printer(const testing::TestParamInfo<videoio_v4l
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/*videoio_v4l2*/, videoio_v4l2, ValuesIn(all_params), param_printer);
|
||||
|
||||
TEST(videoio_ffmpeg, camera_index)
|
||||
{
|
||||
utils::Paths devs = utils::getConfigurationParameterPaths("OPENCV_TEST_V4L2_VIVID_DEVICE");
|
||||
if (devs.size() != 1)
|
||||
{
|
||||
throw SkipTestException("OPENCV_TEST_V4L2_VIVID_DEVICE is not set");
|
||||
}
|
||||
VideoCapture cap;
|
||||
ASSERT_TRUE(cap.open(0, CAP_FFMPEG));
|
||||
Mat frame;
|
||||
ASSERT_TRUE(cap.read(frame));
|
||||
ASSERT_FALSE(frame.empty());
|
||||
}
|
||||
|
||||
}} // opencv_test::<anonymous>::
|
||||
|
||||
#endif // HAVE_CAMV4L2
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
'''
|
||||
Showcases the use of background subtraction from a live video feed,
|
||||
aswell as pass through of a known foreground parameter
|
||||
'''
|
||||
|
||||
# Python 2/3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
def main():
|
||||
cap = cv.VideoCapture(0)
|
||||
if not cap.isOpened:
|
||||
print("Capture source avaialable.")
|
||||
exit()
|
||||
|
||||
# Create background subtractor
|
||||
mog2_bg_subtractor = cv.createBackgroundSubtractorMOG2(history=300, varThreshold=50, detectShadows=False)
|
||||
knn_bg_subtractor = cv.createBackgroundSubtractorKNN(history=300, detectShadows=False)
|
||||
|
||||
frame_count = 0
|
||||
# Allows for a frame buffer for the mask to learn pre known foreground
|
||||
show_count = 10
|
||||
|
||||
while True:
|
||||
ret, frame = cap.read()
|
||||
if not ret:
|
||||
break
|
||||
|
||||
x = 100 + (frame_count % 10) * 3
|
||||
|
||||
frame = cv.resize(frame, (640, 480))
|
||||
aKnownForegroundMask = np.zeros(frame.shape[:2], dtype=np.uint8)
|
||||
|
||||
# Allow for models to "settle"/learn
|
||||
if frame_count > show_count:
|
||||
cv.rectangle(aKnownForegroundMask, (x,200), (x+50,300), 255, -1)
|
||||
cv.rectangle(aKnownForegroundMask, (540,180), (640,480), 255, -1)
|
||||
|
||||
#MOG2 Subtraction
|
||||
mog2_with_mask = mog2_bg_subtractor.apply(frame,knownForegroundMask=aKnownForegroundMask)
|
||||
mog2_without_mask = mog2_bg_subtractor.apply(frame)
|
||||
|
||||
#KNN Subtraction
|
||||
knn_with_mask = knn_bg_subtractor.apply(frame,knownForegroundMask=aKnownForegroundMask)
|
||||
knn_without_mask = knn_bg_subtractor.apply(frame)
|
||||
|
||||
# Display the 3 parameter apply and the 4 parameter apply for both subtractors
|
||||
cv.imshow("MOG2 With a Foreground Mask", mog2_with_mask)
|
||||
cv.imshow("MOG2 Without a Foreground Mask", mog2_without_mask)
|
||||
cv.imshow("KNN With a Foreground Mask", knn_with_mask)
|
||||
cv.imshow("KNN Without a Foreground Mask", knn_without_mask)
|
||||
|
||||
key = cv.waitKey(30)
|
||||
if key == 27: # ESC
|
||||
break
|
||||
|
||||
frame_count += 1
|
||||
|
||||
cap.release()
|
||||
cv.destroyAllWindows()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(__doc__)
|
||||
main()
|
||||
cv.destroyAllWindows()
|
||||
Reference in New Issue
Block a user