1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-31 00:03:03 +04:00

Merge remote-tracking branch 'upstream/3.4' into merge-3.4

This commit is contained in:
Alexander Alekhin
2020-02-01 17:25:39 +00:00
13 changed files with 593 additions and 82 deletions
+12 -1
View File
@@ -118,7 +118,18 @@ v = f_y \times y'' + c_y
tangential distortion coefficients. \f$s_1\f$, \f$s_2\f$, \f$s_3\f$, and \f$s_4\f$, are the thin prism distortion
coefficients. Higher-order coefficients are not considered in OpenCV.
The next figures show two common types of radial distortion: barrel distortion (typically \f$ k_1 < 0 \f$) and pincushion distortion (typically \f$ k_1 > 0 \f$).
The next figures show two common types of radial distortion: barrel distortion
(\f$ 1 + k_1 r^2 + k_2 r^4 + k_3 r^6 \f$ monotonically decreasing)
and pincushion distortion (\f$ 1 + k_1 r^2 + k_2 r^4 + k_3 r^6 \f$ monotonically increasing).
Radial distortion is always monotonic for real lenses,
and if the estimator produces a non monotonic result,
this should be considered a calibration failure.
More generally, radial distortion must be monotonic and the distortion function, must be bijective.
A failed estimation result may look deceptively good near the image center
but will work poorly in e.g. AR/SFM applications.
The optimization method used in OpenCV camera calibration does not include these constraints as
the framework does not support the required integer programming and polynomial inequalities.
See [issue #15992](https://github.com/opencv/opencv/issues/15992) for additional information.
![](pics/distortion_examples.png)
![](pics/distortion_examples2.png)
@@ -167,7 +167,12 @@ namespace cv {
#undef abs
#undef Complex
#if defined __cplusplus
#include <limits>
#else
#include <limits.h>
#endif
#include "opencv2/core/hal/interface.h"
#if defined __ICL
@@ -1338,7 +1338,7 @@ inline v_float32x4 v_load_expand(const float16_t* ptr)
return v_float32x4(vec_extract_fp_from_shorth(vf16));
#elif CV_VSX3 && !defined(CV_COMPILER_VSX_BROKEN_ASM)
vec_float4 vf32;
__asm__ __volatile__ ("xvcvhpsp %x0,%x1" : "=wf" (vf32) : "wa" (vec_mergeh(vf16, vf16)));
__asm__ __volatile__ ("xvcvhpsp %x0,%x1" : "=wa" (vf32) : "wa" (vec_mergeh(vf16, vf16)));
return v_float32x4(vf32);
#else
const vec_int4 z = vec_int4_z, delta = vec_int4_sp(0x38000000);
@@ -1363,7 +1363,7 @@ inline void v_pack_store(float16_t* ptr, const v_float32x4& v)
// fixme: Is there any builtin op or intrinsic that cover "xvcvsphp"?
#if CV_VSX3 && !defined(CV_COMPILER_VSX_BROKEN_ASM)
vec_ushort8 vf16;
__asm__ __volatile__ ("xvcvsphp %x0,%x1" : "=wa" (vf16) : "wf" (v.val));
__asm__ __volatile__ ("xvcvsphp %x0,%x1" : "=wa" (vf16) : "wa" (v.val));
vec_st_l8(vec_mergesqe(vf16, vf16), ptr);
#else
const vec_int4 signmask = vec_int4_sp(0x80000000);
+1 -1
View File
@@ -1215,7 +1215,7 @@ _Tp Point_<_Tp>::dot(const Point_& pt) const
template<typename _Tp> inline
double Point_<_Tp>::ddot(const Point_& pt) const
{
return (double)x*pt.x + (double)y*pt.y;
return (double)x*(double)(pt.x) + (double)y*(double)(pt.y);
}
template<typename _Tp> inline
+26 -28
View File
@@ -110,9 +110,9 @@ VSX_FINLINE(rt) fnm(const rg& a, const rg& b) { return fn2(a, b); }
#if defined(__GNUG__) && !defined(__clang__)
// inline asm helper
#define VSX_IMPL_1RG(rt, rto, rg, rgo, opc, fnm) \
VSX_FINLINE(rt) fnm(const rg& a) \
{ rt rs; __asm__ __volatile__(#opc" %x0,%x1" : "="#rto (rs) : #rgo (a)); return rs; }
#define VSX_IMPL_1RG(rt, rg, opc, fnm) \
VSX_FINLINE(rt) fnm(const rg& a) \
{ rt rs; __asm__ __volatile__(#opc" %x0,%x1" : "=wa" (rs) : "wa" (a)); return rs; }
#define VSX_IMPL_1VRG(rt, rg, opc, fnm) \
VSX_FINLINE(rt) fnm(const rg& a) \
@@ -233,6 +233,10 @@ VSX_FINLINE(rt) fnm(const rg& a, const rg& b) \
#if __GNUG__ < 5
// vec_xxpermdi in gcc4 missing little-endian supports just like clang
# define vec_permi(a, b, c) vec_xxpermdi(b, a, (3 ^ (((c) & 1) << 1 | (c) >> 1)))
// same as vec_xxpermdi
# undef vec_vbpermq
VSX_IMPL_2VRG(vec_udword2, vec_uchar16, vbpermq, vec_vbpermq)
VSX_IMPL_2VRG(vec_dword2, vec_char16, vbpermq, vec_vbpermq)
#else
# define vec_permi vec_xxpermdi
#endif // __GNUG__ < 5
@@ -257,44 +261,38 @@ VSX_REDIRECT_1RG(vec_float4, vec_double2, vec_cvfo, __builtin_vsx_xvcvdpsp)
VSX_REDIRECT_1RG(vec_double2, vec_float4, vec_cvfo, __builtin_vsx_xvcvspdp)
// converts word and doubleword to double-precision
#ifdef vec_ctd
# undef vec_ctd
#endif
VSX_IMPL_1RG(vec_double2, wd, vec_int4, wa, xvcvsxwdp, vec_ctdo)
VSX_IMPL_1RG(vec_double2, wd, vec_uint4, wa, xvcvuxwdp, vec_ctdo)
VSX_IMPL_1RG(vec_double2, wd, vec_dword2, wi, xvcvsxddp, vec_ctd)
VSX_IMPL_1RG(vec_double2, wd, vec_udword2, wi, xvcvuxddp, vec_ctd)
#undef vec_ctd
VSX_IMPL_1RG(vec_double2, vec_int4, xvcvsxwdp, vec_ctdo)
VSX_IMPL_1RG(vec_double2, vec_uint4, xvcvuxwdp, vec_ctdo)
VSX_IMPL_1RG(vec_double2, vec_dword2, xvcvsxddp, vec_ctd)
VSX_IMPL_1RG(vec_double2, vec_udword2, xvcvuxddp, vec_ctd)
// converts word and doubleword to single-precision
#undef vec_ctf
VSX_IMPL_1RG(vec_float4, wf, vec_int4, wa, xvcvsxwsp, vec_ctf)
VSX_IMPL_1RG(vec_float4, wf, vec_uint4, wa, xvcvuxwsp, vec_ctf)
VSX_IMPL_1RG(vec_float4, wf, vec_dword2, wi, xvcvsxdsp, vec_ctfo)
VSX_IMPL_1RG(vec_float4, wf, vec_udword2, wi, xvcvuxdsp, vec_ctfo)
VSX_IMPL_1RG(vec_float4, vec_int4, xvcvsxwsp, vec_ctf)
VSX_IMPL_1RG(vec_float4, vec_uint4, xvcvuxwsp, vec_ctf)
VSX_IMPL_1RG(vec_float4, vec_dword2, xvcvsxdsp, vec_ctfo)
VSX_IMPL_1RG(vec_float4, vec_udword2, xvcvuxdsp, vec_ctfo)
// converts single and double precision to signed word
#undef vec_cts
VSX_IMPL_1RG(vec_int4, wa, vec_double2, wd, xvcvdpsxws, vec_ctso)
VSX_IMPL_1RG(vec_int4, wa, vec_float4, wf, xvcvspsxws, vec_cts)
VSX_IMPL_1RG(vec_int4, vec_double2, xvcvdpsxws, vec_ctso)
VSX_IMPL_1RG(vec_int4, vec_float4, xvcvspsxws, vec_cts)
// converts single and double precision to unsigned word
#undef vec_ctu
VSX_IMPL_1RG(vec_uint4, wa, vec_double2, wd, xvcvdpuxws, vec_ctuo)
VSX_IMPL_1RG(vec_uint4, wa, vec_float4, wf, xvcvspuxws, vec_ctu)
VSX_IMPL_1RG(vec_uint4, vec_double2, xvcvdpuxws, vec_ctuo)
VSX_IMPL_1RG(vec_uint4, vec_float4, xvcvspuxws, vec_ctu)
// converts single and double precision to signed doubleword
#ifdef vec_ctsl
# undef vec_ctsl
#endif
VSX_IMPL_1RG(vec_dword2, wi, vec_double2, wd, xvcvdpsxds, vec_ctsl)
VSX_IMPL_1RG(vec_dword2, wi, vec_float4, wf, xvcvspsxds, vec_ctslo)
#undef vec_ctsl
VSX_IMPL_1RG(vec_dword2, vec_double2, xvcvdpsxds, vec_ctsl)
VSX_IMPL_1RG(vec_dword2, vec_float4, xvcvspsxds, vec_ctslo)
// converts single and double precision to unsigned doubleword
#ifdef vec_ctul
# undef vec_ctul
#endif
VSX_IMPL_1RG(vec_udword2, wi, vec_double2, wd, xvcvdpuxds, vec_ctul)
VSX_IMPL_1RG(vec_udword2, wi, vec_float4, wf, xvcvspuxds, vec_ctulo)
#undef vec_ctul
VSX_IMPL_1RG(vec_udword2, vec_double2, xvcvdpuxds, vec_ctul)
VSX_IMPL_1RG(vec_udword2, vec_float4, xvcvspuxds, vec_ctulo)
// just in case if GCC doesn't define it
#ifndef vec_xl
+64 -48
View File
@@ -265,6 +265,21 @@ namespace cv{
}
}
template <typename LT> static inline
LT stripeFirstLabel4Connectivity(int y, int w)
{
CV_DbgAssert((y & 1) == 0);
return (LT(y) * LT(w) /*+ 1*/) / 2 + 1;
}
template <typename LT> static inline
LT stripeFirstLabel8Connectivity(int y, int w)
{
CV_DbgAssert((y & 1) == 0);
return LT((y /*+ 1*/) / 2) * LT((w + 1) / 2) + 1;
}
//Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan array union find) variant
//using decision trees
//Kesheng Wu, et al
@@ -283,12 +298,14 @@ namespace cv{
FirstScan8Connectivity& operator=(const FirstScan8Connectivity& ) { return *this; }
void operator()(const cv::Range& range) const CV_OVERRIDE
void operator()(const cv::Range& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, img_.rows));
int r = range.start;
chunksSizeAndLabels_[r] = range.end;
LabelT label = LabelT((r + 1) / 2) * LabelT((imgLabels_.cols + 1) / 2) + 1;
LabelT label = stripeFirstLabel8Connectivity<LabelT>(r, imgLabels_.cols);
const LabelT firstLabel = label;
const int w = img_.cols;
@@ -385,12 +402,14 @@ namespace cv{
FirstScan4Connectivity& operator=(const FirstScan4Connectivity& ) { return *this; }
void operator()(const cv::Range& range) const CV_OVERRIDE
void operator()(const cv::Range& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, img_.rows));
int r = range.start;
chunksSizeAndLabels_[r] = range.end;
LabelT label = LabelT((r * imgLabels_.cols + 1) / 2 + 1);
LabelT label = stripeFirstLabel4Connectivity<LabelT>(r, imgLabels_.cols);
const LabelT firstLabel = label;
const int w = img_.cols;
@@ -462,8 +481,9 @@ namespace cv{
SecondScan& operator=(const SecondScan& ) { return *this; }
void operator()(const cv::Range& range) const CV_OVERRIDE
void operator()(const cv::Range& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, imgLabels_.rows));
int r = range.start;
const int rowBegin = r;
const int rowEnd = range.end;
@@ -595,53 +615,51 @@ namespace cv{
//Array used to store info and labeled pixel by each thread.
//Different threads affect different memory location of chunksSizeAndLabels
int *chunksSizeAndLabels = (int *)cv::fastMalloc(h * sizeof(int));
std::vector<int> chunksSizeAndLabels(roundUp(h, 2));
//Tree of labels
LabelT *P = (LabelT *)cv::fastMalloc(Plength * sizeof(LabelT));
std::vector<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//First label is for background
P[0] = 0;
//P[0] = 0;
cv::Range range(0, h);
cv::Range range2(0, divUp(h, 2));
const double nParallelStripes = std::max(1, std::min(h / 2, getNumThreads()*4));
LabelT nLabels = 1;
if (connectivity == 8){
//First scan
cv::parallel_for_(range, FirstScan8Connectivity(img, imgLabels, P, chunksSizeAndLabels), nParallelStripes);
cv::parallel_for_(range2, FirstScan8Connectivity(img, imgLabels, P, chunksSizeAndLabels.data()), nParallelStripes);
//merge labels of different chunks
mergeLabels8Connectivity(imgLabels, P, chunksSizeAndLabels);
mergeLabels8Connectivity(imgLabels, P, chunksSizeAndLabels.data());
for (int i = 0; i < h; i = chunksSizeAndLabels[i]){
flattenL(P, int((i + 1) / 2) * int((w + 1) / 2) + 1, chunksSizeAndLabels[i + 1], nLabels);
flattenL(P, stripeFirstLabel8Connectivity<int>(i, w), chunksSizeAndLabels[i + 1], nLabels);
}
}
else{
//First scan
cv::parallel_for_(range, FirstScan4Connectivity(img, imgLabels, P, chunksSizeAndLabels), nParallelStripes);
cv::parallel_for_(range2, FirstScan4Connectivity(img, imgLabels, P, chunksSizeAndLabels.data()), nParallelStripes);
//merge labels of different chunks
mergeLabels4Connectivity(imgLabels, P, chunksSizeAndLabels);
mergeLabels4Connectivity(imgLabels, P, chunksSizeAndLabels.data());
for (int i = 0; i < h; i = chunksSizeAndLabels[i]){
flattenL(P, int(i * w + 1) / 2 + 1, chunksSizeAndLabels[i + 1], nLabels);
flattenL(P, stripeFirstLabel4Connectivity<int>(i, w), chunksSizeAndLabels[i + 1], nLabels);
}
}
//Array for statistics dataof threads
StatsOp *sopArray = new StatsOp[h];
std::vector<StatsOp> sopArray(h);
sop.init(nLabels);
//Second scan
cv::parallel_for_(range, SecondScan(imgLabels, P, sop, sopArray, nLabels), nParallelStripes);
StatsOp::mergeStats(imgLabels, sopArray, sop, nLabels);
cv::parallel_for_(range2, SecondScan(imgLabels, P, sop, sopArray.data(), nLabels), nParallelStripes);
StatsOp::mergeStats(imgLabels, sopArray.data(), sop, nLabels);
sop.finish();
delete[] sopArray;
cv::fastFree(chunksSizeAndLabels);
cv::fastFree(P);
return nLabels;
}
};//End struct LabelingWuParallel
@@ -671,9 +689,10 @@ namespace cv{
//Obviously, 4-way connectivity upper bound is also good for 8-way connectivity labeling
const size_t Plength = (size_t(h) * size_t(w) + 1) / 2 + 1;
//array P for equivalences resolution
LabelT *P = (LabelT *)fastMalloc(sizeof(LabelT) *Plength);
std::vector<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//first label is for background pixels
P[0] = 0;
//P[0] = 0;
LabelT lunique = 1;
if (connectivity == 8){
@@ -811,7 +830,6 @@ namespace cv{
}
sop.finish();
fastFree(P);
return nLabels;
}//End function LabelingWu operator()
@@ -836,14 +854,14 @@ namespace cv{
FirstScan& operator=(const FirstScan&) { return *this; }
void operator()(const cv::Range& range) const CV_OVERRIDE
void operator()(const cv::Range& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, img_.rows));
int r = range.start;
r += (r % 2);
chunksSizeAndLabels_[r] = range.end + (range.end % 2);
chunksSizeAndLabels_[r] = range.end;
LabelT label = LabelT((r + 1) / 2) * LabelT((imgLabels_.cols + 1) / 2) + 1;
LabelT label = stripeFirstLabel8Connectivity<LabelT>(r, imgLabels_.cols);
const LabelT firstLabel = label;
const int h = img_.rows, w = img_.cols;
@@ -1902,14 +1920,13 @@ namespace cv{
SecondScan(const cv::Mat& img, cv::Mat& imgLabels, LabelT *P, StatsOp& sop, StatsOp *sopArray, LabelT& nLabels)
: img_(img), imgLabels_(imgLabels), P_(P), sop_(sop), sopArray_(sopArray), nLabels_(nLabels){}
SecondScan& operator=(const SecondScan& ) { return *this; }
void operator()(const cv::Range& range) const CV_OVERRIDE
void operator()(const cv::Range& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, img_.rows));
int r = range.start;
r += (r % 2);
const int rowBegin = r;
const int rowEnd = range.end + range.end % 2;
const int rowEnd = range.end;
if (rowBegin > 0){
sopArray_[rowBegin].initElement(nLabels_);
@@ -2542,36 +2559,35 @@ namespace cv{
//Array used to store info and labeled pixel by each thread.
//Different threads affect different memory location of chunksSizeAndLabels
const int chunksSizeAndLabelsSize = h + 1;
cv::AutoBuffer<int, 0> chunksSizeAndLabels(chunksSizeAndLabelsSize);
const int chunksSizeAndLabelsSize = roundUp(h, 2);
std::vector<int> chunksSizeAndLabels(chunksSizeAndLabelsSize);
//Tree of labels
cv::AutoBuffer<LabelT, 0> P(Plength);
std::vector<LabelT> P(Plength, 0);
//First label is for background
P[0] = 0;
//P[0] = 0;
cv::Range range(0, h);
cv::Range range2(0, divUp(h, 2));
const double nParallelStripes = std::max(1, std::min(h / 2, getNumThreads()*4));
//First scan, each thread works with chunk of img.rows/nThreads rows
//e.g. 300 rows, 4 threads -> each chunks is composed of 75 rows
cv::parallel_for_(range, FirstScan(img, imgLabels, P.data(), chunksSizeAndLabels.data()), nParallelStripes);
//First scan
cv::parallel_for_(range2, FirstScan(img, imgLabels, P.data(), chunksSizeAndLabels.data()), nParallelStripes);
//merge labels of different chunks
mergeLabels(img, imgLabels, P.data(), chunksSizeAndLabels.data());
LabelT nLabels = 1;
for (int i = 0; i < h; i = chunksSizeAndLabels[i]){
CV_Assert(i + 1 < chunksSizeAndLabelsSize);
flattenL(P.data(), LabelT((i + 1) / 2) * LabelT((w + 1) / 2) + 1, chunksSizeAndLabels[i + 1], nLabels);
CV_DbgAssert(i + 1 < chunksSizeAndLabelsSize);
flattenL(P.data(), stripeFirstLabel8Connectivity<LabelT>(i, w), chunksSizeAndLabels[i + 1], nLabels);
}
//Array for statistics data
cv::AutoBuffer<StatsOp, 0> sopArray(h);
std::vector<StatsOp> sopArray(h);
sop.init(nLabels);
//Second scan
cv::parallel_for_(range, SecondScan(img, imgLabels, P.data(), sop, sopArray.data(), nLabels), nParallelStripes);
cv::parallel_for_(range2, SecondScan(img, imgLabels, P.data(), sop, sopArray.data(), nLabels), nParallelStripes);
StatsOp::mergeStats(imgLabels, sopArray.data(), sop, nLabels);
sop.finish();
@@ -2602,8 +2618,9 @@ namespace cv{
//............
const size_t Plength = size_t(((h + 1) / 2) * size_t((w + 1) / 2)) + 1;
LabelT *P = (LabelT *)fastMalloc(sizeof(LabelT) *Plength);
P[0] = 0;
std::vector<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//P[0] = 0;
LabelT lunique = 1;
// First scan
@@ -3911,7 +3928,6 @@ namespace cv{
}
sop.finish();
fastFree(P);
return nLabels;
@@ -150,4 +150,80 @@ TEST(Imgproc_ConnectedComponents, grana_buffer_overflow)
EXPECT_EQ(1, nbComponents);
}
static cv::Mat createCrashMat(int numThreads) {
const int h = numThreads * 4 * 2 + 8;
const double nParallelStripes = std::max(1, std::min(h / 2, numThreads * 4));
const int w = 4;
const int nstripes = cvRound(nParallelStripes <= 0 ? h : MIN(MAX(nParallelStripes, 1.), h));
const cv::Range stripeRange(0, nstripes);
const cv::Range wholeRange(0, h);
cv::Mat m(h, w, CV_8U);
m = 0;
// Look for a range that starts with odd value and ends with even value
cv::Range bugRange;
for (int s = stripeRange.start; s < stripeRange.end; s++) {
cv::Range sr(s, s + 1);
cv::Range r;
r.start = (int) (wholeRange.start +
((uint64) sr.start * (wholeRange.end - wholeRange.start) + nstripes / 2) / nstripes);
r.end = sr.end >= nstripes ?
wholeRange.end :
(int) (wholeRange.start +
((uint64) sr.end * (wholeRange.end - wholeRange.start) + nstripes / 2) / nstripes);
if (r.start > 0 && r.start % 2 == 1 && r.end % 2 == 0 && r.end >= r.start + 2) {
bugRange = r;
break;
}
}
if (bugRange.empty()) { // Could not create a buggy range
return m;
}
// Fill in bug Range
for (int x = 1; x < w; x++) {
m.at<char>(bugRange.start - 1, x) = 1;
}
m.at<char>(bugRange.start + 0, 0) = 1;
m.at<char>(bugRange.start + 0, 1) = 1;
m.at<char>(bugRange.start + 0, 3) = 1;
m.at<char>(bugRange.start + 1, 1) = 1;
m.at<char>(bugRange.start + 2, 1) = 1;
m.at<char>(bugRange.start + 2, 3) = 1;
m.at<char>(bugRange.start + 3, 0) = 1;
m.at<char>(bugRange.start + 3, 1) = 1;
return m;
}
TEST(Imgproc_ConnectedComponents, parallel_wu_labels)
{
cv::Mat mat = createCrashMat(cv::getNumThreads());
if(mat.empty()) {
return;
}
const int nbPixels = cv::countNonZero(mat);
cv::Mat labels;
cv::Mat stats;
cv::Mat centroids;
int nb = 0;
EXPECT_NO_THROW( nb = cv::connectedComponentsWithStats(mat, labels, stats, centroids, 8, CV_32S, cv::CCL_WU) );
int area = 0;
for(int i=1; i<nb; ++i) {
area += stats.at<int32_t>(i, cv::CC_STAT_AREA);
}
EXPECT_EQ(nbPixels, area);
}
}} // namespace