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

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2025-12-02 15:22:25 +03:00
56 changed files with 1639 additions and 876 deletions
+1 -1
View File
@@ -2544,7 +2544,7 @@ void undistortPoints(InputArray src, OutputArray dst,
CV_EXPORTS_W
void undistortImagePoints(InputArray src, OutputArray dst, InputArray cameraMatrix,
InputArray distCoeffs,
TermCriteria = TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 5, 0.01));
TermCriteria = TermCriteria(TermCriteria::MAX_ITER, 5, 0.01));
namespace fisheye {
+22 -10
View File
@@ -437,8 +437,12 @@ static void undistortPointsInternal( const Mat& _src, Mat& _dst, const Mat& _cam
y0 = y = invProj * vecUntilt(1);
double error = std::numeric_limits<double>::max();
double prevError = std::numeric_limits<double>::max();
// compensate distortion iteratively using fixed-point iteration
// parameter for damped fixed-point iteration
double alpha = 1.;
for( int j = 0; ; j++ )
{
if ((criteria.type & TermCriteria::COUNT) && j >= criteria.maxCount)
@@ -462,10 +466,11 @@ static void undistortPointsInternal( const Mat& _src, Mat& _dst, const Mat& _cam
// [x'', y'']^T = [x' / icdist + deltaX, y' / icdist + deltaY]^T =>
// [x', y']^T = [(x'' - deltaX) * icdist, (y'' - deltaY) * icdist]^T =>
// x' = f1(x') := (x'' - deltaX) * icdist, y' = f2(y') := (y'' - deltaY) * icdist
// Fixed-point iteration:
// new_x' = f1(x') = (x'' - deltaX) * icdist, new_y' = f2(y') = (y'' - deltaY) * icdist
x = (x0 - deltaX)*icdist;
y = (y0 - deltaY)*icdist;
// Damped fixed-point iteration:
// f1(x') = (x'' - deltaX) * icdist, f2(y') = (y'' - deltaY) * icdist
// new_x' = (1 - alpha) * x' + alpha * f1(x'), new_y' = (1 - alpha) * y' + alpha * f2(y')
double new_x = (1. - alpha)*x + alpha*(x0 - deltaX)*icdist;
double new_y = (1. - alpha)*y + alpha*(y0 - deltaY)*icdist;
if(criteria.type & TermCriteria::EPS)
{
@@ -474,20 +479,20 @@ static void undistortPointsInternal( const Mat& _src, Mat& _dst, const Mat& _cam
Vec3d vecTilt;
// r^2 = x'^2 + y'^2
r2 = x*x + y*y;
r2 = new_x*new_x + new_y*new_y;
r4 = r2*r2;
r6 = r4*r2;
a1 = 2*x*y;
a2 = r2 + 2*x*x;
a3 = r2 + 2*y*y;
a1 = 2*new_x*new_y;
a2 = r2 + 2*new_x*new_x;
a3 = r2 + 2*new_y*new_y;
// cdist := 1 + k1 * r^2 + k2 * r^4 + k3 * r^6
cdist = 1 + k[0]*r2 + k[1]*r4 + k[4]*r6;
// icdist2 := 1 / (1 + k4 * r^2 + k5 * r^4 + k6 * r^6)
icdist2 = 1./(1 + k[5]*r2 + k[6]*r4 + k[7]*r6);
// x'' = x' * cdist * icdist2 + 2 * p1 * x' * y' + p2 * (r^2 + 2 * x'^2) + s1 * r^2 + s2 * r^4
// y'' = y' * cdist * icdist2 + p1 * (r^2 + 2 * y'^2) + 2 * p2 * x' * y' + s3 * r^2 + s4 * r^4
xd0 = x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4;
yd0 = y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4;
xd0 = new_x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4;
yd0 = new_y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4;
// s * [x''', y''', 1]^T = matTilt * [x'', y'', 1]^T =>
// (vecTilt := matTilt * [x'', y'', 1]^T)
@@ -505,6 +510,13 @@ static void undistortPointsInternal( const Mat& _src, Mat& _dst, const Mat& _cam
error = sqrt( pow(x_proj - u, 2) + pow(y_proj - v, 2) );
}
if (error > prevError) {
alpha *= .5;
} else {
x = new_x;
y = new_y;
}
prevError = error;
}
}
+58
View File
@@ -16,6 +16,7 @@ protected:
void generateCameraMatrix(Mat& cameraMatrix);
void generateDistCoeffs(Mat& distCoeffs, int count);
cv::Mat generateRotationVector();
std::vector<cv::Point2d> distortPoints(const cv::Mat &cameraMatrix, const cv::Mat &dist, const std::vector<cv::Point2d> &points);
double thresh = 1.0e-2;
};
@@ -60,6 +61,34 @@ cv::Mat UndistortPointsTest::generateRotationVector()
return rvec;
}
std::vector<cv::Point2d> UndistortPointsTest::distortPoints(const cv::Mat &cameraMatrix, const cv::Mat &dist, const std::vector<cv::Point2d> &points)
{
CV_Assert(cameraMatrix.rows == 3 && cameraMatrix.cols == 3);
CV_Assert(cameraMatrix.type() == CV_64F);
CV_Assert(dist.rows * dist.cols == 12);
CV_Assert(dist.type() == CV_64F);
double *k = reinterpret_cast<double *>(dist.data);
double fx = cameraMatrix.at<double>(0, 0);
double fy = cameraMatrix.at<double>(1, 1);
double cx = cameraMatrix.at<double>(0, 2);
double cy = cameraMatrix.at<double>(1, 2);
std::vector<cv::Point2d> distortedPoints;
distortedPoints.reserve(points.size());
for (const cv::Point2d p : points) {
double x = (p.x - cx) / fx;
double y = (p.y - cy) / fy;
double r2 = x*x + y*y;
double cdist = (1 + ((k[4]*r2 + k[1])*r2 + k[0])*r2)/(1 + ((k[7]*r2 + k[6])*r2 + k[5])*r2);
CV_Assert(cdist >= 0);
double deltaX = 2*k[2]*x*y + k[3]*(r2 + 2*x*x)+ k[8]*r2+k[9]*r2*r2;
double deltaY = k[2]*(r2 + 2*y*y) + 2*k[3]*x*y+ k[10]*r2+k[11]*r2*r2;
distortedPoints.push_back(cv::Point2d((x * cdist + deltaX) * fx + cx, (y * cdist + deltaY) * fy + cy));
}
return distortedPoints;
}
TEST_F(UndistortPointsTest, accuracy)
{
Mat intrinsics, distCoeffs;
@@ -196,4 +225,33 @@ TEST_F(UndistortPointsTest, regression_14583)
<< "undistort point: " << undistort_pt;
}
TEST_F(UndistortPointsTest, regression_27916)
{
cv::Mat K = (cv::Mat_<double>(3, 3) <<
1570.8956145992222, 0., 744.87337646727406, 0.,
1570.3494207432338, 575.55087456337526, 0., 0., 1.);
cv::Mat dist = (cv::Mat_<double>(1, 12) <<
-2.8247717583453804, -0.80078070764368037,
-0.014595359484103326, 0.0018820998949700702, 1.9827795585249783,
-2.7306773773930897, -1.217725820479524, 2.4052243546080136,
-0.0020670359760441713, 3.4660880793174063e-05,
0.014100351510458799, -3.0935329736207612e-05);
const cv::TermCriteria termCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, 100, thresh / 2);
std::vector<cv::Point2d> distortedPoints, distortedPoints2;
std::vector<cv::Point2d> undistortedPoints;
for (int i = 0; i < 50; i++)
{
for (int j = 0; j < 50; j++)
{
distortedPoints.push_back(cv::Point2d(i, j));
}
}
cv::undistortPoints(distortedPoints, undistortedPoints, K, dist, cv::noArray(), K, termCriteria);
distortedPoints2 = distortPoints(K, dist, undistortedPoints);
EXPECT_MAT_NEAR(distortedPoints2, distortedPoints, thresh);
}
}} // namespace
@@ -201,7 +201,7 @@ cvRound( double value )
{
#if defined CV_INLINE_ROUND_DBL
CV_INLINE_ROUND_DBL(value);
#elif defined _MSC_VER && defined _M_ARM64
#elif defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC))
float64x1_t v = vdup_n_f64(value);
int64x1_t r = vcvtn_s64_f64(v);
return static_cast<int>(vget_lane_s64(r, 0));
@@ -327,7 +327,7 @@ CV_INLINE int cvRound(float value)
{
#if defined CV_INLINE_ROUND_FLT
CV_INLINE_ROUND_FLT(value);
#elif defined _MSC_VER && defined _M_ARM64
#elif defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC))
float32x2_t v = vdup_n_f32(value);
int32x2_t r = vcvtn_s32_f32(v);
return vget_lane_s32(r, 0);
@@ -59,6 +59,7 @@ namespace ogl
namespace cuda
{
class CV_EXPORTS GpuMat;
class CV_EXPORTS GpuMatND;
class CV_EXPORTS HostMem;
class CV_EXPORTS Stream;
class CV_EXPORTS Event;
+10 -1
View File
@@ -287,7 +287,8 @@ public:
STD_VECTOR_UMAT =11 << KIND_SHIFT,
STD_BOOL_VECTOR =12 << KIND_SHIFT,
STD_VECTOR_CUDA_GPU_MAT = 13 << KIND_SHIFT,
STD_ARRAY_MAT =15 << KIND_SHIFT
STD_ARRAY_MAT =15 << KIND_SHIFT,
CUDA_GPU_MATND =16 << KIND_SHIFT
};
_InputArray();
@@ -306,6 +307,7 @@ public:
_InputArray(const double& val);
_InputArray(const cuda::GpuMat& d_mat);
_InputArray(const std::vector<cuda::GpuMat>& d_mat_array);
_InputArray(const cuda::GpuMatND& d_mat);
_InputArray(const ogl::Buffer& buf);
_InputArray(const cuda::HostMem& cuda_mem);
template<typename _Tp> _InputArray(const cudev::GpuMat_<_Tp>& m);
@@ -325,6 +327,7 @@ public:
void getUMatVector(std::vector<UMat>& umv) const;
void getGpuMatVector(std::vector<cuda::GpuMat>& gpumv) const;
cuda::GpuMat getGpuMat() const;
cuda::GpuMatND getGpuMatND() const;
ogl::Buffer getOGlBuffer() const;
int getFlags() const;
@@ -360,6 +363,7 @@ public:
bool isVector() const;
bool isGpuMat() const;
bool isGpuMatVector() const;
bool isGpuMatND() const;
~_InputArray();
protected:
@@ -428,6 +432,7 @@ public:
_OutputArray(std::vector<Mat>& vec);
_OutputArray(cuda::GpuMat& d_mat);
_OutputArray(std::vector<cuda::GpuMat>& d_mat);
_OutputArray(cuda::GpuMatND& d_mat);
_OutputArray(ogl::Buffer& buf);
_OutputArray(cuda::HostMem& cuda_mem);
template<typename _Tp> _OutputArray(cudev::GpuMat_<_Tp>& m);
@@ -446,6 +451,7 @@ public:
_OutputArray(const std::vector<Mat>& vec);
_OutputArray(const cuda::GpuMat& d_mat);
_OutputArray(const std::vector<cuda::GpuMat>& d_mat);
_OutputArray(const cuda::GpuMatND& d_mat);
_OutputArray(const ogl::Buffer& buf);
_OutputArray(const cuda::HostMem& cuda_mem);
template<typename _Tp> _OutputArray(const cudev::GpuMat_<_Tp>& m);
@@ -476,6 +482,7 @@ public:
std::vector<Mat>& getMatVecRef() const;
std::vector<UMat>& getUMatVecRef() const;
template<typename _Tp> std::vector<std::vector<_Tp> >& getVecVecRef() const;
cuda::GpuMatND& getGpuMatNDRef() const;
ogl::Buffer& getOGlBufferRef() const;
cuda::HostMem& getHostMemRef() const;
@@ -516,6 +523,7 @@ public:
_InputOutputArray(Mat& m);
_InputOutputArray(std::vector<Mat>& vec);
_InputOutputArray(cuda::GpuMat& d_mat);
_InputOutputArray(cuda::GpuMatND& d_mat);
_InputOutputArray(ogl::Buffer& buf);
_InputOutputArray(cuda::HostMem& cuda_mem);
template<typename _Tp> _InputOutputArray(cudev::GpuMat_<_Tp>& m);
@@ -533,6 +541,7 @@ public:
_InputOutputArray(const std::vector<Mat>& vec);
_InputOutputArray(const cuda::GpuMat& d_mat);
_InputOutputArray(const std::vector<cuda::GpuMat>& d_mat);
_InputOutputArray(const cuda::GpuMatND& d_mat);
_InputOutputArray(const ogl::Buffer& buf);
_InputOutputArray(const cuda::HostMem& cuda_mem);
template<typename _Tp> _InputOutputArray(const cudev::GpuMat_<_Tp>& m);
+17 -2
View File
@@ -214,7 +214,10 @@ inline _InputArray::_InputArray(const cuda::GpuMat& d_mat)
{ init(+CUDA_GPU_MAT + ACCESS_READ, &d_mat); }
inline _InputArray::_InputArray(const std::vector<cuda::GpuMat>& d_mat)
{ init(+STD_VECTOR_CUDA_GPU_MAT + ACCESS_READ, &d_mat);}
{ init(+STD_VECTOR_CUDA_GPU_MAT + ACCESS_READ, &d_mat);}
inline _InputArray::_InputArray(const cuda::GpuMatND& d_mat)
{ init(+CUDA_GPU_MATND + ACCESS_READ, &d_mat); }
inline _InputArray::_InputArray(const ogl::Buffer& buf)
{ init(+OPENGL_BUFFER + ACCESS_READ, &buf); }
@@ -261,6 +264,7 @@ inline bool _InputArray::isVector() const { return kind() == _InputArray::STD_VE
(kind() == _InputArray::MATX && (sz.width <= 1 || sz.height <= 1)); }
inline bool _InputArray::isGpuMat() const { return kind() == _InputArray::CUDA_GPU_MAT; }
inline bool _InputArray::isGpuMatVector() const { return kind() == _InputArray::STD_VECTOR_CUDA_GPU_MAT; }
inline bool _InputArray::isGpuMatND() const { return kind() == _InputArray::CUDA_GPU_MATND; }
////////////////////////////////////////////////////////////////////////////////////////
@@ -339,7 +343,10 @@ inline _OutputArray::_OutputArray(cuda::GpuMat& d_mat)
{ init(+CUDA_GPU_MAT + ACCESS_WRITE, &d_mat); }
inline _OutputArray::_OutputArray(std::vector<cuda::GpuMat>& d_mat)
{ init(+STD_VECTOR_CUDA_GPU_MAT + ACCESS_WRITE, &d_mat);}
{ init(+STD_VECTOR_CUDA_GPU_MAT + ACCESS_WRITE, &d_mat);}
inline _OutputArray::_OutputArray(cuda::GpuMatND& d_mat)
{ init(+CUDA_GPU_MATND + ACCESS_WRITE, &d_mat); }
inline _OutputArray::_OutputArray(ogl::Buffer& buf)
{ init(+OPENGL_BUFFER + ACCESS_WRITE, &buf); }
@@ -362,6 +369,8 @@ inline _OutputArray::_OutputArray(const std::vector<UMat>& vec)
inline _OutputArray::_OutputArray(const cuda::GpuMat& d_mat)
{ init(FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MAT + ACCESS_WRITE, &d_mat); }
inline _OutputArray::_OutputArray(const cuda::GpuMatND& d_mat)
{ init(+FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MATND + ACCESS_WRITE, &d_mat); }
inline _OutputArray::_OutputArray(const ogl::Buffer& buf)
{ init(FIXED_TYPE + FIXED_SIZE + OPENGL_BUFFER + ACCESS_WRITE, &buf); }
@@ -486,6 +495,9 @@ _InputOutputArray::_InputOutputArray(const _Tp* vec, int n)
inline _InputOutputArray::_InputOutputArray(cuda::GpuMat& d_mat)
{ init(+CUDA_GPU_MAT + ACCESS_RW, &d_mat); }
inline _InputOutputArray::_InputOutputArray(cuda::GpuMatND& d_mat)
{ init(+CUDA_GPU_MATND + ACCESS_RW, &d_mat); }
inline _InputOutputArray::_InputOutputArray(ogl::Buffer& buf)
{ init(+OPENGL_BUFFER + ACCESS_RW, &buf); }
@@ -513,6 +525,9 @@ inline _InputOutputArray::_InputOutputArray(const std::vector<cuda::GpuMat>& d_m
template<> inline _InputOutputArray::_InputOutputArray(std::vector<cuda::GpuMat>& d_mat)
{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_CUDA_GPU_MAT + ACCESS_RW, &d_mat);}
inline _InputOutputArray::_InputOutputArray(const cuda::GpuMatND& d_mat)
{ init(+FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MATND + ACCESS_RW, &d_mat); }
inline _InputOutputArray::_InputOutputArray(const ogl::Buffer& buf)
{ init(FIXED_TYPE + FIXED_SIZE + OPENGL_BUFFER + ACCESS_RW, &buf); }
+138
View File
@@ -113,6 +113,12 @@ Mat _InputArray::getMat_(int i) const
CV_Error(cv::Error::StsNotImplemented, "You should explicitly call download method for cuda::GpuMat object");
}
if( k == CUDA_GPU_MATND )
{
CV_Assert( i < 0 );
CV_Error(cv::Error::StsNotImplemented, "You should explicitly call download method for cuda::GpuMatND object");
}
if( k == CUDA_HOST_MEM )
{
CV_Assert( i < 0 );
@@ -360,6 +366,22 @@ void _InputArray::getGpuMatVector(std::vector<cuda::GpuMat>& gpumv) const
CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)");
#endif
}
cuda::GpuMatND _InputArray::getGpuMatND() const
{
#ifdef HAVE_CUDA
_InputArray::KindFlag k = kind();
if (k == CUDA_GPU_MATND)
{
const cuda::GpuMatND* d_mat = (const cuda::GpuMatND*)obj;
return *d_mat;
}
CV_Error(cv::Error::StsNotImplemented, "getGpuMatND is available only for cuda::GpuMatND");
#else
CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)");
#endif
}
ogl::Buffer _InputArray::getOGlBuffer() const
{
_InputArray::KindFlag k = kind();
@@ -382,11 +404,29 @@ _InputArray::KindFlag _InputArray::kind() const
int _InputArray::rows(int i) const
{
#ifdef HAVE_CUDA
_InputArray::KindFlag k = kind();
if (k == CUDA_GPU_MATND)
{
const cuda::GpuMatND& _gpuMatND = *(const cuda::GpuMatND*)obj;
return (_gpuMatND.dims < 1) ? 0 : _gpuMatND.size[0];
}
#endif
return size(i).height;
}
int _InputArray::cols(int i) const
{
#ifdef HAVE_CUDA
_InputArray::KindFlag k = kind();
if (k == CUDA_GPU_MATND)
{
const cuda::GpuMatND& _gpuMatND = *(const cuda::GpuMatND*)obj;
return (_gpuMatND.dims < 2) ? 0 : _gpuMatND.size[1];
}
#endif
return size(i).width;
}
@@ -655,8 +695,13 @@ bool _InputArray::sameSize(const _InputArray& arr) const
return false;
sz1 = m->size();
}
else if ( (k1 == CUDA_GPU_MATND) && (k2 == CUDA_GPU_MATND))
{
return ((const cuda::GpuMatND*)obj)->size == ((const cuda::GpuMatND*)arr.obj)->size;
}
else
sz1 = size();
if( arr.dims() > 2 )
return false;
return sz1 == arr.size();
@@ -744,6 +789,12 @@ int _InputArray::dims(int i) const
return 2;
}
if( k == CUDA_GPU_MATND )
{
CV_Assert( i < 0 );
return ((const cuda::GpuMatND*)obj)->dims;
}
if( k == CUDA_HOST_MEM )
{
CV_Assert( i < 0 );
@@ -799,6 +850,21 @@ size_t _InputArray::total(int i) const
return vv[i].total();
}
if( k == CUDA_GPU_MATND )
{
CV_Assert( i < 0 );
size_t res = 0;
const cuda::GpuMatND& _gpuMatND = *((const cuda::GpuMatND*)obj);
if (_gpuMatND.dims > 0)
{
res = 1;
for(int d = 0 ; d<_gpuMatND.dims ; ++d)
res *= _gpuMatND.size[d];
return res;
}
}
return size(i).area();
}
@@ -876,6 +942,9 @@ int _InputArray::type(int i) const
if( k == CUDA_GPU_MAT )
return ((const cuda::GpuMat*)obj)->type();
if( k == CUDA_GPU_MATND )
return ((const cuda::GpuMatND*)obj)->type();
if( k == CUDA_HOST_MEM )
return ((const cuda::HostMem*)obj)->type();
@@ -955,6 +1024,9 @@ bool _InputArray::empty() const
return vv.empty();
}
if( k == CUDA_GPU_MATND )
return ((const cuda::GpuMatND*)obj)->empty();
if( k == CUDA_HOST_MEM )
return ((const cuda::HostMem*)obj)->empty();
@@ -999,6 +1071,9 @@ bool _InputArray::isContinuous(int i) const
if( k == CUDA_GPU_MAT )
return i < 0 ? ((const cuda::GpuMat*)obj)->isContinuous() : true;
if( k == CUDA_GPU_MATND )
return i < 0 ? ((const cuda::GpuMatND*)obj)->isContinuous() : true;
CV_Error(cv::Error::StsNotImplemented, "Unknown/unsupported array type");
}
@@ -1037,6 +1112,11 @@ bool _InputArray::isSubmatrix(int i) const
return vv[i].isSubmatrix();
}
if( k == CUDA_GPU_MATND )
{
return ((const cuda::GpuMatND*)obj)->isSubmatrix();
}
CV_Error(cv::Error::StsNotImplemented, "");
}
@@ -1152,6 +1232,12 @@ size_t _InputArray::step(int i) const
CV_Assert(i >= 0 && (size_t)i < vv.size());
return vv[i].step;
}
if( k == CUDA_GPU_MATND )
{
const cuda::GpuMatND& _gpuMatND = *(const cuda::GpuMatND*)obj;
CV_Assert( i >= _gpuMatND.dims );
return _gpuMatND.step[i];
}
CV_Error(Error::StsNotImplemented, "");
}
@@ -1234,6 +1320,18 @@ void _OutputArray::create(Size _sz, int mtype, int i, bool allowTransposed, _Out
return;
#else
CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)");
#endif
}
if( k == CUDA_GPU_MATND && i < 0 && !allowTransposed && fixedDepthMask == 0 )
{
CV_Assert(!fixedSize() || ((((cuda::GpuMatND*)obj)->dims == 2) && (((cuda::GpuMatND*)obj)->size[0] == _sz.height) && (((cuda::GpuMatND*)obj)->size[1] == _sz.width)));
CV_Assert(!fixedType() || ((cuda::GpuMatND*)obj)->type() == mtype);
#ifdef HAVE_CUDA
cuda::GpuMatND::SizeArray sizes = {_sz.height, _sz.width};
((cuda::GpuMatND*)obj)->create(sizes, mtype);
return;
#else
CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)");
#endif
}
if( k == OPENGL_BUFFER && i < 0 && !allowTransposed && fixedDepthMask == 0 )
@@ -1288,6 +1386,18 @@ void _OutputArray::create(int _rows, int _cols, int mtype, int i, bool allowTran
return;
#else
CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)");
#endif
}
if( k == CUDA_GPU_MATND && i < 0 && !allowTransposed && fixedDepthMask == 0 )
{
CV_Assert(!fixedSize() || ((((cuda::GpuMatND*)obj)->dims == 2) && (((cuda::GpuMatND*)obj)->size[0] == _rows) && (((cuda::GpuMatND*)obj)->size[1] == _cols)));
CV_Assert(!fixedType() || ((cuda::GpuMatND*)obj)->type() == mtype);
#ifdef HAVE_CUDA
cuda::GpuMatND::SizeArray sizes = {_rows, _cols};
((cuda::GpuMatND*)obj)->create(sizes, mtype);
return;
#else
CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)");
#endif
}
if( k == OPENGL_BUFFER && i < 0 && !allowTransposed && fixedDepthMask == 0 )
@@ -1705,6 +1815,18 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i,
return;
}
if( k == CUDA_GPU_MATND && d > 0 && i < 0 && !allowTransposed && fixedDepthMask == 0 )
{
#ifdef HAVE_CUDA
cuda::GpuMatND::SizeArray sizeArray = cuda::GpuMatND::SizeArray(sizes, sizes+d);
((cuda::GpuMatND*)obj)->create(sizeArray, mtype);
return;
#else
CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)");
#endif
}
CV_Error(Error::StsNotImplemented, "Unknown/unsupported array type");
}
@@ -1840,6 +1962,16 @@ void _OutputArray::release() const
#endif
}
if( k == CUDA_GPU_MATND )
{
#ifdef HAVE_CUDA
((cuda::GpuMatND*)obj)->release();
return;
#else
CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)");
#endif
}
if( k == CUDA_HOST_MEM )
{
#ifdef HAVE_CUDA
@@ -1971,6 +2103,12 @@ std::vector<cuda::GpuMat>& _OutputArray::getGpuMatVecRef() const
CV_Assert(k == STD_VECTOR_CUDA_GPU_MAT);
return *(std::vector<cuda::GpuMat>*)obj;
}
cuda::GpuMatND& _OutputArray::getGpuMatNDRef() const
{
_InputArray::KindFlag k = kind();
CV_Assert( k == CUDA_GPU_MATND );
return *(cuda::GpuMatND*)obj;
}
ogl::Buffer& _OutputArray::getOGlBufferRef() const
{
+16 -5
View File
@@ -1164,20 +1164,31 @@ String tempfile( const char* suffix )
fname = String(aname);
}
#else
// Use GUID-based naming to avoid race condition with GetTempFileNameA
// See issue #19648
char temp_dir2[MAX_PATH] = { 0 };
char temp_file[MAX_PATH] = { 0 };
if (temp_dir.empty())
{
::GetTempPathA(sizeof(temp_dir2), temp_dir2);
temp_dir = std::string(temp_dir2);
}
if(0 == ::GetTempFileNameA(temp_dir.c_str(), "ocv", 0, temp_file))
GUID g;
HRESULT hr = CoCreateGuid(&g);
if (FAILED(hr))
return String();
char guidStr[40];
const char* mask = "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x";
snprintf(guidStr, sizeof(guidStr), mask,
g.Data1, g.Data2, g.Data3, (unsigned int)g.Data4[0], (unsigned int)g.Data4[1],
(unsigned int)g.Data4[2], (unsigned int)g.Data4[3], (unsigned int)g.Data4[4],
(unsigned int)g.Data4[5], (unsigned int)g.Data4[6], (unsigned int)g.Data4[7]);
DeleteFileA(temp_file);
fname = temp_file;
fname = temp_dir;
if (!fname.empty() && fname[fname.size()-1] != '\\' && fname[fname.size()-1] != '/')
fname += "\\";
fname = fname + "ocv" + guidStr;
#endif
# else
# ifdef __ANDROID__
+3 -3
View File
@@ -8,9 +8,9 @@
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 23 &&
FLATBUFFERS_VERSION_MINOR == 5 &&
FLATBUFFERS_VERSION_REVISION == 9,
static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
FLATBUFFERS_VERSION_MINOR == 9 &&
FLATBUFFERS_VERSION_REVISION == 23,
"Non-compatible flatbuffers version included");
namespace opencv_tflite {
+4
View File
@@ -1666,6 +1666,10 @@ CvWindow::CvWindow(QString name, int arg2)
show();
}
CvWindow::~CvWindow()
{
delete myView;
}
void CvWindow::setMouseCallBack(CvMouseCallback callback, void* param)
{
+1
View File
@@ -298,6 +298,7 @@ class CvWindow : public CvWinModel
Q_OBJECT
public:
CvWindow(QString arg2, int flag = cv::WINDOW_NORMAL);
~CvWindow();
void setMouseCallBack(CvMouseCallback m, void* param);
@@ -113,7 +113,7 @@ enum ImwriteFlags {
IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. Default is IMWRITE_TIFF_PREDICTOR_HORIZONTAL .
IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000.
IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95.
IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_32F. Default is 8.
IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_16U. Default is 8.
IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and 10(fastest). Default is 9.
IMWRITE_JPEGXL_QUALITY = 640,//!< For JPEG XL, it can be a quality from 0 to 100 (the higher is the better). Default value is 95. If set, distance parameter is re-calicurated from quality level automatically. This parameter request libjxl v0.10 or later.
IMWRITE_JPEGXL_EFFORT = 641,//!< For JPEG XL, encoder effort/speed level without affecting decoding speed; it is between 1 (fastest) and 10 (slowest). Default is 7.
@@ -539,6 +539,11 @@ can be saved using this function, with these exceptions:
To achieve this, create an 8-bit 4-channel (CV_8UC4) BGRA image, ensuring the alpha channel is the last component.
Fully transparent pixels should have an alpha value of 0, while fully opaque pixels should have an alpha value of 255.
- 8-bit single-channel images (CV_8UC1) are not supported due to GIF's limitation to indexed color formats.
- With AVIF encoder, 8-bit unsigned (CV_8U) and 16-bit unsigned (CV_16U) images can be saved.
- CV_16U images can be saved as only 10-bit or 12-bit (not 16-bit). See IMWRITE_AVIF_DEPTH.
- AVIF images with an alpha channel can be saved using this function.
To achieve this, create an 8-bit 4-channel (CV_8UC4) / 16-bit 4-channel (CV_16UC4) BGRA image, ensuring the alpha channel is the last component.
Fully transparent pixels should have an alpha value of 0, while fully opaque pixels should have an alpha value of 255 (8-bit) / 1023 (10-bit) / 4095 (12-bit) (see the code sample below).
If the image format is not supported, the image will be converted to 8-bit unsigned (CV_8U) and saved that way.
+19 -20
View File
@@ -86,15 +86,12 @@ AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless, int bit_dept
result->yuvFormat = AVIF_PIXEL_FORMAT_YUV400;
result->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED;
result->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED;
result->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY;
result->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED;
result->yuvRange = AVIF_RANGE_FULL;
result->yuvPlanes[0] = img.data;
result->yuvRowBytes[0] = img.step[0];
result->imageOwnsYUVPlanes = AVIF_FALSE;
return AvifImageUniquePtr(result);
}
if (lossless) {
} else if (lossless) {
result =
avifImageCreate(width, height, bit_depth, AVIF_PIXEL_FORMAT_YUV444);
if (result == nullptr) return nullptr;
@@ -139,22 +136,24 @@ AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless, int bit_dept
#endif
}
avifRGBImage rgba;
avifRGBImageSetDefaults(&rgba, result);
if (img.channels() == 3) {
rgba.format = AVIF_RGB_FORMAT_BGR;
} else {
CV_Assert(img.channels() == 4);
rgba.format = AVIF_RGB_FORMAT_BGRA;
}
rgba.rowBytes = (uint32_t)img.step[0];
rgba.depth = bit_depth;
rgba.pixels =
const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(img.data));
if (img.channels() > 1) {
avifRGBImage rgba;
avifRGBImageSetDefaults(&rgba, result);
if (img.channels() == 3) {
rgba.format = AVIF_RGB_FORMAT_BGR;
} else {
CV_Assert(img.channels() == 4);
rgba.format = AVIF_RGB_FORMAT_BGRA;
}
rgba.rowBytes = (uint32_t)img.step[0];
rgba.depth = bit_depth;
rgba.pixels =
const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(img.data));
if (avifImageRGBToYUV(result, &rgba) != AVIF_RESULT_OK) {
avifImageDestroy(result);
return nullptr;
if (avifImageRGBToYUV(result, &rgba) != AVIF_RESULT_OK) {
avifImageDestroy(result);
return nullptr;
}
}
return AvifImageUniquePtr(result);
}
+3 -3
View File
@@ -337,7 +337,7 @@ bool BmpDecoder::readData( Mat& img )
}
else
{
int x_shift3 = (int)(line_end - data);
ptrdiff_t x_shift3 = line_end - data;
if( code == 2 )
{
@@ -430,7 +430,7 @@ decode_rle4_bad: ;
}
else
{
int x_shift3 = (int)(line_end - data);
ptrdiff_t x_shift3 = line_end - data;
int y_shift = m_height - y;
if( code || !line_end_flag || x_shift3 < width3 )
@@ -441,7 +441,7 @@ decode_rle4_bad: ;
y_shift = m_strm.getByte();
}
x_shift3 += (y_shift * width3) & ((code == 0) - 1);
x_shift3 += ((ptrdiff_t)y_shift * width3) & ((code == 0) - 1);
if( y >= m_height )
break;
+4 -4
View File
@@ -435,7 +435,7 @@ bool IsColorPalette( PaletteEntry* palette, int bpp )
uchar* FillUniColor( uchar* data, uchar*& line_end,
int step, int width3,
int& y, int height,
int count3, PaletteEntry clr )
ptrdiff_t count3, PaletteEntry clr )
{
do
{
@@ -444,7 +444,7 @@ uchar* FillUniColor( uchar* data, uchar*& line_end,
if( end > line_end )
end = line_end;
count3 -= (int)(end - data);
count3 -= end - data;
for( ; data < end; data += 3 )
{
@@ -467,7 +467,7 @@ uchar* FillUniColor( uchar* data, uchar*& line_end,
uchar* FillUniGray( uchar* data, uchar*& line_end,
int step, int width,
int& y, int height,
int count, uchar clr )
ptrdiff_t count, uchar clr )
{
do
{
@@ -476,7 +476,7 @@ uchar* FillUniGray( uchar* data, uchar*& line_end,
if( end > line_end )
end = line_end;
count -= (int)(end - data);
count -= end - data;
for( ; data < end; data++ )
{
+2 -2
View File
@@ -124,9 +124,9 @@ void FillGrayPalette( PaletteEntry* palette, int bpp, bool negative = false );
bool IsColorPalette( PaletteEntry* palette, int bpp );
void CvtPaletteToGray( const PaletteEntry* palette, uchar* grayPalette, int entries );
uchar* FillUniColor( uchar* data, uchar*& line_end, int step, int width3,
int& y, int height, int count3, PaletteEntry clr );
int& y, int height, ptrdiff_t count3, PaletteEntry clr );
uchar* FillUniGray( uchar* data, uchar*& line_end, int step, int width3,
int& y, int height, int count3, uchar clr );
int& y, int height, ptrdiff_t count3, uchar clr );
uchar* FillColorRow8( uchar* data, uchar* indices, int len, PaletteEntry* palette );
uchar* FillGrayRow8( uchar* data, uchar* indices, int len, uchar* palette );
+8 -3
View File
@@ -296,13 +296,15 @@ INSTANTIATE_TEST_CASE_P(Imgcodecs, Exif,
testing::ValuesIn(exif_files));
#ifdef HAVE_AVIF
TEST(Imgcodecs_Avif, ReadWriteWithExif)
typedef testing::TestWithParam<int> MatChannels;
TEST_P(MatChannels, Imgcodecs_Avif_ReadWriteWithExif)
{
int avif_nbits = 10;
int avif_speed = 10;
int avif_quality = 85;
int imgdepth = avif_nbits > 8 ? CV_16U : CV_8U;
int imgtype = CV_MAKETYPE(imgdepth, 3);
int imgtype = CV_MAKETYPE(imgdepth, GetParam());
const string outputname = cv::tempfile(".avif");
Mat img = makeCirclesImage(Size(1280, 720), imgtype, avif_nbits);
@@ -328,7 +330,7 @@ TEST(Imgcodecs_Avif, ReadWriteWithExif)
EXPECT_EQ(img2.rows, img.rows);
EXPECT_EQ(img2.type(), imgtype);
EXPECT_EQ(read_metadata_types, read_metadata_types2);
EXPECT_GE(read_metadata_types.size(), 1u);
ASSERT_GE(read_metadata_types.size(), 1u);
EXPECT_EQ(read_metadata, read_metadata2);
EXPECT_EQ(read_metadata_types[0], IMAGE_METADATA_EXIF);
EXPECT_EQ(read_metadata_types.size(), read_metadata.size());
@@ -338,6 +340,9 @@ TEST(Imgcodecs_Avif, ReadWriteWithExif)
EXPECT_LT(mse, 1500);
remove(outputname.c_str());
}
INSTANTIATE_TEST_CASE_P(Imgcodecs, MatChannels,
testing::Values(1,3,4));
#endif // HAVE_AVIF
#ifdef HAVE_WEBP
@@ -1344,8 +1344,8 @@ public class ImgprocTest extends OpenCVTestCase {
RotatedRect rrect = Imgproc.minAreaRect(points);
assertEquals(new Size(5, 2), rrect.size);
assertEquals(0., rrect.angle);
assertEquals(new Size(2, 5), rrect.size);
assertEquals(-90., rrect.angle);
assertEquals(new Point(3.5, 2), rrect.center);
}
+9 -5
View File
@@ -76,12 +76,16 @@ static int Sklansky_( Point_<_Tp>** array, int start, int end, int* stack, int n
if( CV_SIGN( by ) != nsign )
{
_Tp ax = array[pcur]->x - array[pprev]->x;
_Tp bx = array[pnext]->x - array[pcur]->x;
_Tp ay = cury - array[pprev]->y;
_DotTp convexity = (_DotTp)ay*bx - (_DotTp)ax*by; // if >0 then convex angle
Vec<_Tp, 2> a(array[pcur]->x - array[pprev]->x, cury - array[pprev]->y);
Vec<_Tp, 2> b(array[pnext]->x - array[pcur]->x, by);
if (std::is_floating_point<_Tp>::value)
{
a = normalize(a);
b = normalize(b);
}
_DotTp convexity = (_DotTp)a[1]*b[0] - (_DotTp)a[0]*b[1]; // if >0 then convex angle
if( CV_SIGN( convexity ) == sign2 && (ax != 0 || ay != 0) )
if( CV_SIGN( convexity ) == sign2 && (a[0] != 0 || a[1] != 0) )
{
pprev = pcur;
pcur = pnext;
+42 -6
View File
@@ -1174,13 +1174,31 @@ static bool replacementFilter2D(int stype, int dtype, int kernel_type,
cvhalFilter2D* ctx;
int res = cv_hal_filterInit(&ctx, kernel_data, kernel_step, kernel_type, kernel_width, kernel_height, width, height,
stype, dtype, borderType, delta, anchor_x, anchor_y, isSubmatrix, src_data == dst_data);
if (res != CV_HAL_ERROR_OK)
if (res == CV_HAL_ERROR_NOT_IMPLEMENTED)
{
return false;
} else if (res != CV_HAL_ERROR_OK)
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation filterInit ==> " CVAUX_STR(cv_hal_filterInit) " returned %d (0x%08x)", res, res));
}
res = cv_hal_filter(ctx, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y);
bool success = (res == CV_HAL_ERROR_OK);
if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED )
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation filter ==> " CVAUX_STR(cv_hal_filter) " returned %d (0x%08x)", res, res));
}
res = cv_hal_filterFree(ctx);
if (res != CV_HAL_ERROR_OK)
return false;
success &= (res == CV_HAL_ERROR_OK);
if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED )
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation filterFree ==> " CVAUX_STR(cv_hal_filterFree) " returned %d (0x%08x)", res, res));
}
return success;
}
@@ -1372,13 +1390,31 @@ static bool replacementSepFilter(int stype, int dtype, int ktype,
kernelx_data, kernelx_len,
kernely_data, kernely_len,
anchor_x, anchor_y, delta, borderType);
if (res != CV_HAL_ERROR_OK)
if (res == CV_HAL_ERROR_NOT_IMPLEMENTED)
{
return false;
} else if (res != CV_HAL_ERROR_OK)
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation sepFilterInit ==> " CVAUX_STR(cv_hal_sepFilterInit) " returned %d (0x%08x)", res, res));
}
res = cv_hal_sepFilter(ctx, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y);
bool success = (res == CV_HAL_ERROR_OK);
if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED )
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation sepFilter ==> " CVAUX_STR(cv_hal_sepFilter) " returned %d (0x%08x)", res, res));
}
res = cv_hal_sepFilterFree(ctx);
if (res != CV_HAL_ERROR_OK)
return false;
success &= (res == CV_HAL_ERROR_OK);
if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED )
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation sepFilterFree ==> " CVAUX_STR(cv_hal_sepFilterFree) " returned %d (0x%08x)", res, res));
}
return success;
}
+18 -3
View File
@@ -218,8 +218,14 @@ static bool halMorph(int op, int src_type, int dst_type,
anchor_x, anchor_y,
borderType, borderValue,
iterations, isSubmatrix, src_data == dst_data);
if (res != CV_HAL_ERROR_OK)
if (res == CV_HAL_ERROR_NOT_IMPLEMENTED)
{
return false;
} else if (res != CV_HAL_ERROR_OK)
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation morphInit ==> " CVAUX_STR(cv_hal_morphInit) " returned %d (0x%08x)", res, res));
}
res = cv_hal_morph(ctx, src_data, src_step, dst_data, dst_step, width, height,
roi_width, roi_height,
@@ -227,10 +233,19 @@ static bool halMorph(int op, int src_type, int dst_type,
roi_width2, roi_height2,
roi_x2, roi_y2);
bool success = (res == CV_HAL_ERROR_OK);
if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED )
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation morph ==> " CVAUX_STR(cv_hal_morph) " returned %d (0x%08x)", res, res));
}
res = cv_hal_morphFree(ctx);
if (res != CV_HAL_ERROR_OK)
return false;
success &= (res == CV_HAL_ERROR_OK);
if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED )
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation morphFree ==> " CVAUX_STR(cv_hal_morphFree) " returned %d (0x%08x)", res, res));
}
return success;
}
+32 -12
View File
@@ -64,6 +64,7 @@ enum { CALIPERS_MAXHEIGHT=0, CALIPERS_MINAREARECT=1, CALIPERS_MAXDIST=2 };
// Parameters:
// points - convex hull vertices ( any orientation )
// n - number of vertices
// orientation - -1 for clockwise vertices order, 1 for CCW. 0 if unknown.
// mode - concrete application of algorithm
// can be CV_CALIPERS_MAXDIST or
// CV_CALIPERS_MINAREARECT
@@ -115,7 +116,7 @@ static bool firstVecIsRight(const cv::Point2f& vec1, const cv::Point2f &vec2)
}
/* we will use usual cartesian coordinates */
static void rotatingCalipers( const Point2f* points, int n, int mode, float* out )
static void rotatingCalipers( const Point2f* points, int n, float orientation, int mode, float* out )
{
float minarea = FLT_MAX;
float max_dist = 0;
@@ -132,7 +133,6 @@ static void rotatingCalipers( const Point2f* points, int n, int mode, float* out
(a,b) (-b,a) (-a,-b) (b, -a)
*/
/* this is a first base vector (a,b) initialized by (1,0) */
float orientation = 0;
float base_a;
float base_b = 0;
@@ -171,6 +171,7 @@ static void rotatingCalipers( const Point2f* points, int n, int mode, float* out
}
// find convex hull orientation
if (orientation == 0.f)
{
double ax = vect[n-1].x;
double ay = vect[n-1].y;
@@ -364,8 +365,10 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
Mat hull;
Point2f out[3];
RotatedRect box;
box.angle = -(float)CV_PI / 2; // default angle for box without rotation and single point
convexHull(_points, hull, false, true);
static const bool clockwise = false;
convexHull(_points, hull, clockwise, true);
if( hull.depth() != CV_32F )
{
@@ -379,22 +382,37 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
if( n > 2 )
{
rotatingCalipers( hpoints, n, CALIPERS_MINAREARECT, (float*)out );
rotatingCalipers( hpoints, n, clockwise ? -1.f : 1.f, CALIPERS_MINAREARECT, (float*)out );
box.center.x = out[0].x + (out[1].x + out[2].x)*0.5f;
box.center.y = out[0].y + (out[1].y + out[2].y)*0.5f;
box.size.width = (float)std::sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y);
box.size.height = (float)std::sqrt((double)out[2].x*out[2].x + (double)out[2].y*out[2].y);
box.angle = (float)atan2( (double)out[1].y, (double)out[1].x );
box.size.width = (float)std::sqrt((double)out[2].x*out[2].x + (double)out[2].y*out[2].y);
box.size.height = (float)std::sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y);
if (out[1].x == 0.f && out[1].y > 0.f)
std::swap(box.size.width, box.size.height);
else
box.angle += (float)atan2( (double)out[1].y, (double)out[1].x );
}
else if( n == 2 )
{
box.center.x = (hpoints[0].x + hpoints[1].x)*0.5f;
box.center.y = (hpoints[0].y + hpoints[1].y)*0.5f;
double dx = hpoints[1].x - hpoints[0].x;
double dy = hpoints[1].y - hpoints[0].y;
box.size.width = (float)std::sqrt(dx*dx + dy*dy);
box.size.height = 0;
box.angle = (float)atan2( dy, dx );
double dx = hpoints[0].x - hpoints[1].x;
double dy = hpoints[0].y - hpoints[1].y;
box.size.width = 0;
box.size.height = (float)std::sqrt(dx*dx + dy*dy);
if (dx == 0)
{
std::swap(box.size.width, box.size.height);
}
else if (dy < 0)
{
box.angle = (float)atan2( dy, dx );
std::swap(box.size.width, box.size.height);
}
else if (dy > 0)
{
box.angle += (float)atan2( dy, dx );
}
}
else
{
@@ -403,6 +421,8 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
}
box.angle = (float)(box.angle*180/CV_PI);
CV_DbgCheckGE(box.angle, -90.0f, "");
CV_DbgCheckLT(box.angle, 0.0f, "");
return box;
}
+51
View File
@@ -1232,6 +1232,57 @@ TEST(minEnclosingPolygon, pentagon)
}
}
TEST(Imgproc_minAreaRect, reproducer_21482)
{
const int N = 4;
float pts_[N][2] = {
{ 188.8991f, 12.400669f },
{ 80.64467f, -49.644814f },
{ 469.59897f, 173.28242f },
{ 690.4597f, 299.86768f },
};
Mat contour(N, 1, CV_32FC2, (void*)pts_);
RotatedRect rr = cv::minAreaRect(contour);
EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
EXPECT_NEAR(min(rr.size.width, rr.size.height), 0, 1e-5);
EXPECT_GE(max(rr.size.width, rr.size.height), 702);
}
TEST(Imgproc_minAreaRect, reproducer_21482_small_values)
{
const int N = 4;
float pts_[N][2] = { { 0.f, 0.f }, { 1e-4f, 0.f }, { 1e-4f, 1e-4f }, { 0.f, 1e-4f },};
Mat contour(N, 1, CV_32FC2, (void*)pts_);
RotatedRect rr = cv::minAreaRect(contour);
EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
EXPECT_EQ(rr.size.width, 1e-4f);
EXPECT_EQ(rr.size.height, 1e-4f);
}
typedef testing::TestWithParam<tuple<Point2f, Point2f, Point2f, Size2f, float>> minAreaRect_of_line;
TEST_P(minAreaRect_of_line, accuracy)
{
Point2f p1 = get<0>(GetParam());
Point2f p2 = get<1>(GetParam());
RotatedRect out = minAreaRect(std::vector<Point2f>{p1, p2});
EXPECT_EQ(out.center, get<2>(GetParam()));
EXPECT_EQ(out.size, get<3>(GetParam()));
EXPECT_NEAR(out.angle, get<4>(GetParam()), 1e-6);
}
INSTANTIATE_TEST_CASE_P(Imgproc, minAreaRect_of_line,
testing::Values(
std::make_tuple(Point2f(10, 15), Point2f(10, 25), Point2f(10, 20), Size2f(10, 0), -90.f),
std::make_tuple(Point2f(450, 500), Point2f(508, 500), Point2f(479, 500), Size2f(0, 58), -90.f),
std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301002f),
std::make_tuple(Point2f(9, 19), Point2f(4, 7), Point2f(6.5, 13), Size2f(0, 13), -22.6198654f)
));
}} // namespace
/* End of file. */
+8 -8
View File
@@ -710,14 +710,14 @@ QUnit.test('test_rotatedRectangleIntersection', function(assert) {
assert.deepEqual(intersectionType, cv.INTERSECT_FULL);
intersectionPoints.convertTo(intersectionPoints, cv.CV_32S);
let intersectionPointsData = intersectionPoints.data32S;
assert.deepEqual(intersectionPointsData[0], 30);
assert.deepEqual(intersectionPointsData[1], 40);
assert.deepEqual(intersectionPointsData[2], 40);
assert.deepEqual(intersectionPointsData[3], 30);
assert.deepEqual(intersectionPointsData[4], 50);
assert.deepEqual(intersectionPointsData[5], 40);
assert.deepEqual(intersectionPointsData[6], 40);
assert.deepEqual(intersectionPointsData[7], 50);
assert.deepEqual(intersectionPointsData[0], 40);
assert.deepEqual(intersectionPointsData[1], 50);
assert.deepEqual(intersectionPointsData[2], 30);
assert.deepEqual(intersectionPointsData[3], 40);
assert.deepEqual(intersectionPointsData[4], 40);
assert.deepEqual(intersectionPointsData[5], 30);
assert.deepEqual(intersectionPointsData[6], 50);
assert.deepEqual(intersectionPointsData[7], 40);
intersectionType = cv.rotatedRectangleIntersection(rr1, rr3, intersectionPoints);
@@ -5,5 +5,5 @@ import sys
if sys.version_info[:2] >= (3, 0):
def exec_file_wrapper(fpath, g_vars, l_vars):
with open(fpath) as f:
code = compile(f.read(), os.path.basename(fpath), 'exec')
code = compile(f.read(), fpath, 'exec')
exec(code, g_vars, l_vars)
+12 -6
View File
@@ -714,28 +714,29 @@ bool pyopencv_to(PyObject* obj, String &value, const ArgInfo& info)
std::string str;
#if ((PY_VERSION_HEX >= 0x03060000) && !defined(Py_LIMITED_API)) || (Py_LIMITED_API >= 0x03060000)
PyObject* path_obj = NULL;
if (info.pathlike)
{
obj = PyOS_FSPath(obj);
path_obj = PyOS_FSPath(obj);
if (PyErr_Occurred())
{
failmsg("Expected '%s' to be a str or path-like object", info.name);
return false;
}
obj = path_obj;
}
#endif
bool result = false;
if (getUnicodeString(obj, str))
{
value = str;
return true;
result = true;
}
else
{
// If error hasn't been already set by Python conversion functions
if (!PyErr_Occurred())
{
// Direct access to underlying slots of PyObjectType is not allowed
// when limited API is enabled
#ifdef Py_LIMITED_API
failmsg("Can't convert object to 'str' for '%s'", info.name);
#else
@@ -744,7 +745,12 @@ bool pyopencv_to(PyObject* obj, String &value, const ArgInfo& info)
#endif
}
}
return false;
#if ((PY_VERSION_HEX >= 0x03060000) && !defined(Py_LIMITED_API)) || (Py_LIMITED_API >= 0x03060000)
Py_XDECREF(path_obj);
#endif
return result;
}
template<>
+1 -1
View File
@@ -81,7 +81,7 @@ class Hackathon244Tests(NewOpenCVTests):
mc, mr = cv.minEnclosingCircle(a)
be0 = ((150.2511749267578, 150.77322387695312), (158.024658203125, 197.57696533203125), 37.57804489135742)
br0 = ((161.2974090576172, 154.41793823242188), (207.7177734375, 199.2301483154297), 80.83544921875)
br0 = ((161.2974090576172, 154.41793823242188), (199.2301483154297, 207.7177734375), -9.164555549621582)
mc0, mr0 = (160.41790771484375, 144.55152893066406), 136.713500977
self.check_close_boxes(be, be0, 5, 15)
+3
View File
@@ -277,6 +277,9 @@ void MultiBandBlender::prepare(Rect dst_roi)
else
#endif
{
dst_pyr_laplace_.clear();
dst_band_weights_.clear();
dst_pyr_laplace_.resize(num_bands_ + 1);
dst_pyr_laplace_[0] = dst_;
@@ -383,6 +383,51 @@ double findTransformECC(InputArray templateImage, InputArray inputImage,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 50, 0.001),
InputArray inputMask = noArray());
/** @brief Finds the geometric transform (warp) between two images in terms of the ECC criterion @cite EP08
using validity masks for both the template and the input images.
This function extends findTransformECC() by adding a mask for the template image.
The Enhanced Correlation Coefficient is evaluated only over pixels that are valid in both images:
on each iteration inputMask is warped into the template frame and combined with templateMask, and
only the intersection of these masks contributes to the objective function.
@param templateImage 1 or 3 channel template image; CV_8U, CV_16U, CV_32F, CV_64F type.
@param inputImage input image which should be warped with the final warpMatrix in
order to provide an image similar to templateImage, same type as templateImage.
@param templateMask single-channel 8-bit mask for templateImage indicating valid pixels
to be used in the alignment. Must have the same size as templateImage.
@param inputMask single-channel 8-bit mask for inputImage indicating valid pixels
before warping. Must have the same size as inputImage.
@param warpMatrix floating-point \f$2\times 3\f$ or \f$3\times 3\f$ mapping matrix (warp).
@param motionType parameter, specifying the type of motion:
- **MOTION_TRANSLATION** sets a translational motion model; warpMatrix is \f$2\times 3\f$ with
the first \f$2\times 2\f$ part being the unity matrix and the rest two parameters being
estimated.
- **MOTION_EUCLIDEAN** sets a Euclidean (rigid) transformation as motion model; three
parameters are estimated; warpMatrix is \f$2\times 3\f$.
- **MOTION_AFFINE** sets an affine motion model (DEFAULT); six parameters are estimated;
warpMatrix is \f$2\times 3\f$.
- **MOTION_HOMOGRAPHY** sets a homography as a motion model; eight parameters are
estimated; warpMatrix is \f$3\times 3\f$.
@param criteria parameter, specifying the termination criteria of the ECC algorithm;
criteria.epsilon defines the threshold of the increment in the correlation coefficient between two
iterations (a negative criteria.epsilon makes criteria.maxcount the only termination criterion).
Default values are shown in the declaration above.
@param gaussFiltSize size of the Gaussian blur filter used for smoothing images and masks
before computing the alignment (DEFAULT: 5).
@sa
findTransformECC, computeECC, estimateAffine2D, estimateAffinePartial2D, findHomography
*/
CV_EXPORTS_W double findTransformECCWithMask( InputArray templateImage,
InputArray inputImage,
InputArray templateMask,
InputArray inputMask,
InputOutputArray warpMatrix,
int motionType = MOTION_AFFINE,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 50, 1e-6),
int gaussFiltSize = 5 );
/** @example samples/cpp/snippets/kalman.cpp
An example using the standard Kalman filter
*/
+72 -25
View File
@@ -333,8 +333,15 @@ double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArra
return templateImage_zeromean.dot(inputImage_zeromean) / (templateImagenorm * inputImagenorm);
}
double cv::findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix,
int motionType, TermCriteria criteria, InputArray inputMask, int gaussFiltSize) {
double cv::findTransformECCWithMask( InputArray templateImage,
InputArray inputImage,
InputArray templateMask,
InputArray inputMask,
InputOutputArray warpMatrix,
int motionType,
TermCriteria criteria,
int gaussFiltSize) {
Mat src = templateImage.getMat(); // template image
Mat dst = inputImage.getMat(); // input image (to be warped)
Mat map = warpMatrix.getMat(); // warp (transformation)
@@ -416,7 +423,7 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp
Ycoord.release();
const int channels = src.channels();
int type = CV_MAKETYPE(CV_32F, channels); // используем отдельно, если нужно явно
int type = CV_MAKETYPE(CV_32F, channels);
std::vector<cv::Mat> XgridCh(channels, Xgrid);
cv::merge(XgridCh, Xgrid);
@@ -430,27 +437,10 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp
Mat imageWarped = Mat(hs, ws, type); // to store the warped zero-mean input image
Mat imageMask = Mat(hs, ws, CV_8U); // to store the final mask
Mat inputMaskMat = inputMask.getMat();
// to use it for mask warping
Mat preMask;
if (inputMask.empty())
preMask = Mat::ones(hd, wd, CV_8U);
else
threshold(inputMask, preMask, 0, 1, THRESH_BINARY);
// Gaussian filtering is optional
src.convertTo(templateFloat, templateFloat.type());
GaussianBlur(templateFloat, templateFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
Mat preMaskFloat;
preMask.convertTo(preMaskFloat, type);
GaussianBlur(preMaskFloat, preMaskFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
// Change threshold.
preMaskFloat *= (0.5 / 0.95);
// Rounding conversion.
preMaskFloat.convertTo(preMask, preMask.type());
preMask.convertTo(preMaskFloat, preMaskFloat.type());
dst.convertTo(imageFloat, imageFloat.type());
GaussianBlur(imageFloat, imageFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
@@ -466,12 +456,48 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp
filter2D(imageFloat, gradientX, -1, dx);
filter2D(imageFloat, gradientY, -1, dx.t());
cv::Mat preMaskFloatNCh;
std::vector<cv::Mat> maskChannels(gradientX.channels(), preMaskFloat);
cv::merge(maskChannels, preMaskFloatNCh);
// To use in mask warping
Mat templtMask;
if(templateMask.empty())
{
templtMask = Mat::ones(hs, ws, CV_8U);
}
else
{
threshold(templateMask, templtMask, 0, 1, THRESH_BINARY);
templtMask.convertTo(templtMask, CV_32F);
GaussianBlur(templtMask, templtMask, Size(gaussFiltSize, gaussFiltSize), 0, 0);
templtMask *= (0.5/0.95);
templtMask.convertTo(templtMask, CV_8U);
}
gradientX = gradientX.mul(preMaskFloatNCh);
gradientY = gradientY.mul(preMaskFloatNCh);
//to use it for mask warping
Mat preMask;
if(inputMask.empty())
{
preMask = Mat::ones(hd, wd, CV_8U);
}
else
{
Mat preMaskFloat;
threshold(inputMask, preMask, 0, 1, THRESH_BINARY);
preMask.convertTo(preMaskFloat, CV_32F);
GaussianBlur(preMaskFloat, preMaskFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
// Change threshold.
preMaskFloat *= (0.5/0.95);
// Rounding conversion.
preMaskFloat.convertTo(preMask, CV_8U);
// If there's no template mask, we can apply image masks to gradients only once.
// Otherwise, we'll need to combine the template and image masks at each iteration.
if (templateMask.empty())
{
cv::Mat zeroMask = (preMask == 0);
gradientX.setTo(0, zeroMask);
gradientY.setTo(0, zeroMask);
}
}
// matrices needed for solving linear equation system for maximizing ECC
Mat jacobian = Mat(hs, ws * numberOfParameters, type);
@@ -505,6 +531,15 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp
warpPerspective(preMask, imageMask, map, imageMask.size(), maskFlags);
}
if (!templateMask.empty())
{
cv::bitwise_and(imageMask, templtMask, imageMask);
cv::Mat zeroMask = (imageMask == 0);
gradientXWarped.setTo(0, zeroMask);
gradientYWarped.setTo(0, zeroMask);
}
Scalar imgMean, imgStd, tmpMean, tmpStd;
meanStdDev(imageWarped, imgMean, imgStd, imageMask);
meanStdDev(templateFloat, tmpMean, tmpStd, imageMask);
@@ -576,6 +611,18 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp
return rho;
}
double cv::findTransformECC(InputArray templateImage,
InputArray inputImage,
InputOutputArray warpMatrix,
int motionType,
TermCriteria criteria,
InputArray inputMask,
int gaussFiltSize
) {
return findTransformECCWithMask(templateImage, inputImage, noArray(), inputMask,
warpMatrix, motionType, criteria, gaussFiltSize);
}
double cv::findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix,
int motionType, TermCriteria criteria, InputArray inputMask) {
// Use default value of 5 for gaussFiltSize to maintain backward compatibility.
+20
View File
@@ -342,6 +342,26 @@ bool CV_ECC_Test_Mask::test(const Mat testImg) {
// Test with non-default gaussian blur.
findTransformECC(warpedImage, testImg, mapTranslation, 0, criteria, mask, 1);
if (!checkMap(mapTranslation, translationGround))
return false;
// Test with template mask.
Mat_<unsigned char> warpedMask = Mat_<unsigned char>::ones(warpedImage.rows, warpedImage.cols);
for (int i=warpedImage.rows*1/3; i<warpedImage.rows*2/3; i++) {
for (int j=warpedImage.cols*1/3; j<warpedImage.cols*2/3; j++) {
warpedMask(i, j) = 0;
}
}
findTransformECCWithMask(warpedImage, testImg, warpedMask, mask, mapTranslation, 0,
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
if (!checkMap(mapTranslation, translationGround))
return false;
// Test with non-default gaussian blur.
findTransformECCWithMask(warpedImage, testImg, warpedMask, mask, mapTranslation, 0, criteria, 1);
if (!checkMap(mapTranslation, translationGround))
return false;
}
+2 -4
View File
@@ -9,12 +9,10 @@ endif()
if(NOT HAVE_ARAVIS_API)
find_path(ARAVIS_INCLUDE "arv.h"
PATHS "${ARAVIS_ROOT}" ENV ARAVIS_ROOT
PATH_SUFFIXES "include/aravis-0.8"
NO_DEFAULT_PATH)
PATH_SUFFIXES "include/aravis-0.8")
find_library(ARAVIS_LIBRARY "aravis-0.8"
PATHS "${ARAVIS_ROOT}" ENV ARAVIS_ROOT
PATH_SUFFIXES "lib"
NO_DEFAULT_PATH)
PATH_SUFFIXES "lib")
if(ARAVIS_INCLUDE AND ARAVIS_LIBRARY)
set(HAVE_ARAVIS_API TRUE)
file(STRINGS "${ARAVIS_INCLUDE}/arvversion.h" ver_strings REGEX "#define +ARAVIS_(MAJOR|MINOR|MICRO)_VERSION.*")
+37 -4
View File
@@ -127,6 +127,8 @@ protected:
void autoExposureControl(const Mat &);
double getExpectedMidGrey(ArvPixelFormat fmt) const;
ArvCamera *camera; // Camera to control.
ArvStream *stream; // Object for video stream reception.
void *framebuffer; //
@@ -269,6 +271,19 @@ bool CvCaptureCAM_Aravis::open( int index )
// get initial values
pixelFormat = arv_camera_get_pixel_format(camera, NULL);
// If camera's pixel format is not one of the supported formats, set a default
if (pixelFormat != ARV_PIXEL_FORMAT_MONO_8 &&
pixelFormat != ARV_PIXEL_FORMAT_BAYER_GR_8 &&
pixelFormat != ARV_PIXEL_FORMAT_MONO_12 &&
pixelFormat != ARV_PIXEL_FORMAT_MONO_16) {
pixelFormat = ARV_PIXEL_FORMAT_MONO_8;
arv_camera_set_pixel_format(camera, pixelFormat, NULL);
CV_LOG_WARNING(NULL, "Current camera pixel format is not supported. Failed back to MONO_8.");
}
midGrey = getExpectedMidGrey(pixelFormat);
exposure = exposureAvailable ? arv_camera_get_exposure_time(camera, NULL) : 0;
gain = gainAvailable ? arv_camera_get_gain(camera, NULL) : 0;
fps = arv_camera_get_frame_rate(camera, NULL);
@@ -489,6 +504,26 @@ double CvCaptureCAM_Aravis::getProperty( int property_id ) const
return -1.0;
}
double CvCaptureCAM_Aravis::getExpectedMidGrey(ArvPixelFormat fmt) const
{
double grey = 0.;
switch(fmt)
{
case ARV_PIXEL_FORMAT_MONO_8:
case ARV_PIXEL_FORMAT_BAYER_GR_8:
grey = 128.;
break;
case ARV_PIXEL_FORMAT_MONO_12:
grey = 2048.;
break;
case ARV_PIXEL_FORMAT_MONO_16:
grey = 32768.;
break;
}
return grey;
}
bool CvCaptureCAM_Aravis::setProperty( int property_id, double value )
{
switch(property_id) {
@@ -535,24 +570,22 @@ bool CvCaptureCAM_Aravis::setProperty( int property_id, double value )
case MODE_GREY:
case MODE_Y800:
newFormat = ARV_PIXEL_FORMAT_MONO_8;
targetGrey = 128;
break;
case MODE_Y12:
newFormat = ARV_PIXEL_FORMAT_MONO_12;
targetGrey = 2048;
break;
case MODE_Y16:
newFormat = ARV_PIXEL_FORMAT_MONO_16;
targetGrey = 32768;
break;
case MODE_GRBG:
newFormat = ARV_PIXEL_FORMAT_BAYER_GR_8;
targetGrey = 128;
break;
}
if(newFormat != pixelFormat) {
stopCapture();
arv_camera_set_pixel_format(camera, pixelFormat = newFormat, NULL);
midGrey = getExpectedMidGrey(newFormat);
startCapture();
}
}
+4 -4
View File
@@ -153,7 +153,7 @@ public:
protected:
// Known widget names
static const char * PROP_EXPOSURE_COMPENSACTION;
static const char * PROP_EXPOSURE_COMPENSATION;
static const char * PROP_SELF_TIMER_DELAY;
static const char * PROP_MANUALFOCUS;
static const char * PROP_AUTOFOCUS;
@@ -294,7 +294,7 @@ const char * DigitalCameraCapture::lineDelimiter = "\n";
* Those are actually substrings of widget name.
* ie. for VIEWFINDER, Nikon uses "viewfinder", while Canon can use "eosviewfinder".
*/
const char * DigitalCameraCapture::PROP_EXPOSURE_COMPENSACTION =
const char * DigitalCameraCapture::PROP_EXPOSURE_COMPENSATION =
"exposurecompensation";
const char * DigitalCameraCapture::PROP_SELF_TIMER_DELAY = "selftimerdelay";
const char * DigitalCameraCapture::PROP_MANUALFOCUS = "manualfocusdrive";
@@ -555,7 +555,7 @@ CameraWidget * DigitalCameraCapture::getGenericProperty(int propertyId,
return NULL;
}
case CAP_PROP_EXPOSURE:
return findWidgetByName(PROP_EXPOSURE_COMPENSACTION);
return findWidgetByName(PROP_EXPOSURE_COMPENSATION);
case CAP_PROP_TRIGGER_DELAY:
return findWidgetByName(PROP_SELF_TIMER_DELAY);
case CAP_PROP_ZOOM:
@@ -692,7 +692,7 @@ CameraWidget * DigitalCameraCapture::setGenericProperty(int propertyId,
output = false;
return NULL;
case CAP_PROP_EXPOSURE:
return findWidgetByName(PROP_EXPOSURE_COMPENSACTION);
return findWidgetByName(PROP_EXPOSURE_COMPENSATION);
case CAP_PROP_TRIGGER_DELAY:
return findWidgetByName(PROP_SELF_TIMER_DELAY);
case CAP_PROP_ZOOM:
+142 -98
View File
@@ -448,50 +448,68 @@ public:
STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) CV_OVERRIDE
{
HRESULT hr = 0;
cv::AutoLock lock(m_mutex);
if (SUCCEEDED(hrStatus))
HRESULT hr = S_OK;
try
{
if (pSample)
cv::AutoLock lock(m_mutex);
if (SUCCEEDED(hrStatus))
{
CV_LOG_DEBUG(NULL, "videoio(MSMF): got frame at " << llTimestamp);
if (m_capturedFrames.size() >= MSMF_READER_MAX_QUEUE_SIZE)
if (pSample)
{
CV_LOG_DEBUG(NULL, "videoio(MSMF): got frame at " << llTimestamp);
if (m_capturedFrames.size() >= MSMF_READER_MAX_QUEUE_SIZE)
{
#if 0
CV_LOG_DEBUG(NULL, "videoio(MSMF): drop frame (not processed). Timestamp=" << m_capturedFrames.front().timestamp);
m_capturedFrames.pop();
CV_LOG_DEBUG(NULL, "videoio(MSMF): drop frame (not processed). Timestamp=" << m_capturedFrames.front().timestamp);
m_capturedFrames.pop();
#else
// this branch reduces latency if we drop frames due to slow processing.
// avoid fetching of already outdated frames from the queue's front.
CV_LOG_DEBUG(NULL, "videoio(MSMF): drop previous frames (not processed): " << m_capturedFrames.size());
std::queue<CapturedFrameInfo>().swap(m_capturedFrames); // similar to missing m_capturedFrames.clean();
CV_LOG_DEBUG(NULL, "videoio(MSMF): drop previous frames (not processed): " << m_capturedFrames.size());
std::queue<CapturedFrameInfo>().swap(m_capturedFrames); // similar to missing m_capturedFrames.clean();
#endif
}
m_capturedFrames.emplace(CapturedFrameInfo{ llTimestamp, _ComPtr<IMFSample>(pSample), hrStatus });
}
m_capturedFrames.emplace(CapturedFrameInfo{ llTimestamp, _ComPtr<IMFSample>(pSample), hrStatus });
}
else
{
CV_LOG_WARNING(NULL, "videoio(MSMF): OnReadSample() is called with error status: " << hrStatus);
}
if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags)
{
// Reached the end of the stream.
m_bEOS = true;
}
m_hrStatus = hrStatus;
if (FAILED(hr = m_reader->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL)))
{
CV_LOG_WARNING(NULL, "videoio(MSMF): async ReadSample() call is failed with error status: " << hr);
m_bEOS = true;
}
if (pSample || m_bEOS)
{
SetEvent(m_hEvent);
}
}
else
catch (const _com_error& e)
{
CV_LOG_WARNING(NULL, "videoio(MSMF): OnReadSample() is called with error status: " << hrStatus);
std::string msg;
#ifdef _UNICODE
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
msg = conv.to_bytes(e.ErrorMessage());
#else
msg = std::string(e.ErrorMessage());
#endif
CV_LOG_WARNING(NULL, "videoio(MSMF): _com_error in OnReadSample: " << msg);
return S_OK; // Keep callback alive
}
if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags)
catch (...)
{
// Reached the end of the stream.
m_bEOS = true;
}
m_hrStatus = hrStatus;
if (FAILED(hr = m_reader->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL)))
{
CV_LOG_WARNING(NULL, "videoio(MSMF): async ReadSample() call is failed with error status: " << hr);
m_bEOS = true;
}
if (pSample || m_bEOS)
{
SetEvent(m_hEvent);
CV_LOG_WARNING(NULL, "videoio(MSMF): Unknown exception in OnReadSample");
return S_OK;
}
return S_OK;
}
@@ -1758,82 +1776,108 @@ bool CvCapture_MSMF::grabFrame()
{
CV_TRACE_FUNCTION();
if (grabIsDone)
try
{
grabIsDone = false;
CV_LOG_DEBUG(NULL, "videoio(MSMF): return pre-grabbed frame " << usedVideoSampleTime);
return true;
}
audioFrame = Mat();
if (readCallback) // async "live" capture mode
{
audioSamples.push_back(NULL);
HRESULT hr = 0;
SourceReaderCB* reader = ((SourceReaderCB*)readCallback.Get());
DWORD dwStreamIndex = 0;
if (videoStream != -1)
dwStreamIndex = dwVideoStreamIndex;
if (audioStream != -1)
dwStreamIndex = dwAudioStreamIndex;
if (!reader->m_reader)
if (grabIsDone)
{
// Initiate capturing with async callback
reader->m_reader = videoFileSource.Get();
reader->m_dwStreamIndex = dwStreamIndex;
if (FAILED(hr = videoFileSource->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL)))
grabIsDone = false;
CV_LOG_DEBUG(NULL, "videoio(MSMF): return pre-grabbed frame " << usedVideoSampleTime);
return true;
}
audioFrame = Mat();
if (readCallback) // async "live" capture mode
{
audioSamples.push_back(NULL);
HRESULT hr = 0;
SourceReaderCB* reader = ((SourceReaderCB*)readCallback.Get());
DWORD dwStreamIndex = 0;
if (videoStream != -1)
dwStreamIndex = dwVideoStreamIndex;
if (audioStream != -1)
dwStreamIndex = dwAudioStreamIndex;
if (!reader->m_reader)
{
CV_LOG_ERROR(NULL, "videoio(MSMF): can't grab frame - initial async ReadSample() call failed: " << hr);
reader->m_reader = NULL;
// Initiate capturing with async callback
reader->m_reader = videoFileSource.Get();
reader->m_dwStreamIndex = dwStreamIndex;
if (FAILED(hr = videoFileSource->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL)))
{
CV_LOG_ERROR(NULL, "videoio(MSMF): can't grab frame - initial async ReadSample() call failed: " << hr);
reader->m_reader = NULL;
return false;
}
}
BOOL bEOS = false;
LONGLONG timestamp = 0;
if (FAILED(hr = reader->Wait( videoStream == -1 ? INFINITE : 10000, (videoStream != -1) ? usedVideoSample : audioSamples[0], timestamp, bEOS))) // 10 sec
{
CV_LOG_WARNING(NULL, "videoio(MSMF): can't grab frame. Error: " << hr);
return false;
}
}
BOOL bEOS = false;
LONGLONG timestamp = 0;
if (FAILED(hr = reader->Wait( videoStream == -1 ? INFINITE : 10000, (videoStream != -1) ? usedVideoSample : audioSamples[0], timestamp, bEOS))) // 10 sec
{
CV_LOG_WARNING(NULL, "videoio(MSMF): can't grab frame. Error: " << hr);
return false;
}
if (bEOS)
{
CV_LOG_WARNING(NULL, "videoio(MSMF): EOS signal. Capture stream is lost");
return false;
}
if (videoStream != -1)
usedVideoSampleTime = timestamp;
if (audioStream != -1)
return configureAudioFrame();
CV_LOG_DEBUG(NULL, "videoio(MSMF): grabbed frame " << usedVideoSampleTime);
return true;
}
else if (isOpen)
{
if (vEOS)
return false;
bool returnFlag = true;
if (videoStream != -1)
{
if (!vEOS)
returnFlag &= grabVideoFrame();
if (!returnFlag)
if (bEOS)
{
CV_LOG_WARNING(NULL, "videoio(MSMF): EOS signal. Capture stream is lost");
return false;
}
}
if (videoStream != -1)
usedVideoSampleTime = timestamp;
if (audioStream != -1)
return configureAudioFrame();
if (audioStream != -1)
CV_LOG_DEBUG(NULL, "videoio(MSMF): grabbed frame " << usedVideoSampleTime);
return true;
}
else if (isOpen)
{
const int bytesPerSample = (captureAudioFormat.bit_per_sample/8) * captureAudioFormat.nChannels;
bufferedAudioDuration = (double)(bufferAudioData.size()/bytesPerSample)/captureAudioFormat.nSamplesPerSec;
audioFrame.release();
if (!aEOS)
returnFlag &= grabAudioFrame();
}
if (vEOS)
return false;
return returnFlag;
bool returnFlag = true;
if (videoStream != -1)
{
if (!vEOS)
returnFlag &= grabVideoFrame();
if (!returnFlag)
return false;
}
if (audioStream != -1)
{
const int bytesPerSample = (captureAudioFormat.bit_per_sample/8) * captureAudioFormat.nChannels;
bufferedAudioDuration = (double)(bufferAudioData.size()/bytesPerSample)/captureAudioFormat.nSamplesPerSec;
audioFrame.release();
if (!aEOS)
returnFlag &= grabAudioFrame();
}
return returnFlag;
}
}
catch (const _com_error& e)
{
std::string msg;
#ifdef _UNICODE
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
msg = conv.to_bytes(e.ErrorMessage());
#else
msg = std::string(e.ErrorMessage());
#endif
CV_LOG_WARNING(NULL, "videoio(MSMF): _com_error caught in grabFrame: " << msg);
return false;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "videoio(MSMF): std::exception caught in grabFrame: " << e.what());
return false;
}
catch (...)
{
CV_LOG_WARNING(NULL, "videoio(MSMF): Unknown exception caught in grabFrame");
return false;
}
return false;
}