1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 23:33:05 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Alekhin
2022-08-21 14:55:48 +00:00
538 changed files with 84703 additions and 5374 deletions
+29 -15
View File
@@ -1557,7 +1557,7 @@ unit length.
*/
CV_EXPORTS_W void decomposeEssentialMat( InputArray E, OutputArray R1, OutputArray R2, OutputArray t );
/** @brief Recovers the relative camera rotation and the translation from corresponding points in two images from two different cameras, using cheirality check. Returns the number of
/** @brief Recovers the relative camera rotation and the translation from corresponding points in two images from two different cameras, using chirality check. Returns the number of
inliers that pass the check.
@param points1 Array of N 2D points from the first image. The point coordinates should be
@@ -1589,11 +1589,11 @@ line in pixels, beyond which the point is considered an outlier and is not used
final fundamental matrix. It can be set to something like 1-3, depending on the accuracy of the
point localization, image resolution, and the image noise.
@param mask Input/output mask for inliers in points1 and points2. If it is not empty, then it marks
inliers in points1 and points2 for then given essential matrix E. Only these inliers will be used to
recover pose. In the output mask only inliers which pass the cheirality check.
inliers in points1 and points2 for the given essential matrix E. Only these inliers will be used to
recover pose. In the output mask only inliers which pass the chirality check.
This function decomposes an essential matrix using @ref decomposeEssentialMat and then verifies
possible pose hypotheses by doing cheirality check. The cheirality check means that the
possible pose hypotheses by doing chirality check. The chirality check means that the
triangulated 3D points should have positive depth. Some details can be found in @cite Nister03.
This function can be used to process the output E and mask from @ref findEssentialMat. In this
@@ -1628,7 +1628,7 @@ CV_EXPORTS_W int recoverPose( InputArray points1, InputArray points2,
InputOutputArray mask = noArray());
/** @brief Recovers the relative camera rotation and the translation from an estimated essential
matrix and the corresponding points in two images, using cheirality check. Returns the number of
matrix and the corresponding points in two images, using chirality check. Returns the number of
inliers that pass the check.
@param E The input essential matrix.
@@ -1646,11 +1646,11 @@ described below.
therefore is only known up to scale, i.e. t is the direction of the translation vector and has unit
length.
@param mask Input/output mask for inliers in points1 and points2. If it is not empty, then it marks
inliers in points1 and points2 for then given essential matrix E. Only these inliers will be used to
recover pose. In the output mask only inliers which pass the cheirality check.
inliers in points1 and points2 for the given essential matrix E. Only these inliers will be used to
recover pose. In the output mask only inliers which pass the chirality check.
This function decomposes an essential matrix using @ref decomposeEssentialMat and then verifies
possible pose hypotheses by doing cheirality check. The cheirality check means that the
possible pose hypotheses by doing chirality check. The chirality check means that the
triangulated 3D points should have positive depth. Some details can be found in @cite Nister03.
This function can be used to process the output E and mask from @ref findEssentialMat. In this
@@ -1697,8 +1697,8 @@ length.
are feature points from cameras with same focal length and principal point.
@param pp principal point of the camera.
@param mask Input/output mask for inliers in points1 and points2. If it is not empty, then it marks
inliers in points1 and points2 for then given essential matrix E. Only these inliers will be used to
recover pose. In the output mask only inliers which pass the cheirality check.
inliers in points1 and points2 for the given essential matrix E. Only these inliers will be used to
recover pose. In the output mask only inliers which pass the chirality check.
This function differs from the one above that it computes camera intrinsic matrix from focal length and
principal point:
@@ -1733,12 +1733,12 @@ length.
@param distanceThresh threshold distance which is used to filter out far away points (i.e. infinite
points).
@param mask Input/output mask for inliers in points1 and points2. If it is not empty, then it marks
inliers in points1 and points2 for then given essential matrix E. Only these inliers will be used to
recover pose. In the output mask only inliers which pass the cheirality check.
inliers in points1 and points2 for the given essential matrix E. Only these inliers will be used to
recover pose. In the output mask only inliers which pass the chirality check.
@param triangulatedPoints 3D points which were reconstructed by triangulation.
This function differs from the one above that it outputs the triangulated 3D point that are used for
the cheirality check.
the chirality check.
*/
CV_EXPORTS_W int recoverPose( InputArray E, InputArray points1, InputArray points2,
InputArray cameraMatrix, OutputArray R, OutputArray t,
@@ -2097,7 +2097,7 @@ Check @ref tutorial_homography "the corresponding tutorial" for more details.
This function extracts relative camera motion between two views of a planar object and returns up to
four mathematical solution tuples of rotation, translation, and plane normal. The decomposition of
the homography matrix H is described in detail in @cite Malis.
the homography matrix H is described in detail in @cite Malis2007.
If the homography H, induced by the plane, gives the constraint
\f[s_i \vecthree{x'_i}{y'_i}{1} \sim H \vecthree{x_i}{y_i}{1}\f] on the source image points
@@ -2125,7 +2125,7 @@ CV_EXPORTS_W int decomposeHomographyMat(InputArray H,
@param pointsMask optional Mat/Vector of 8u type representing the mask for the inliers as given by the #findHomography function
This function is intended to filter the output of the #decomposeHomographyMat based on additional
information as described in @cite Malis . The summary of the method: the #decomposeHomographyMat function
information as described in @cite Malis2007 . The summary of the method: the #decomposeHomographyMat function
returns 2 unique solutions and their "opposites" for a total of 4 solutions. If we have access to the
sets of points visible in the camera frame before and after the homography transformation is applied,
we can determine which are the true potential solutions and which are the opposites by verifying which
@@ -2465,6 +2465,20 @@ void undistortPoints(InputArray src, OutputArray dst,
TermCriteria criteria=TermCriteria(TermCriteria::MAX_ITER, 5, 0.01));
/**
* @brief Compute undistorted image points position
*
* @param src Observed points position, 2xN/Nx2 1-channel or 1xN/Nx1 2-channel (CV_32FC2 or CV_64FC2) (or vector\<Point2f\> ).
* @param dst Output undistorted points position (1xN/Nx1 2-channel or vector\<Point2f\> ).
* @param cameraMatrix Camera matrix \f$\vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ .
* @param distCoeffs Distortion coefficients
*/
CV_EXPORTS_W
void undistortImagePoints(InputArray src, OutputArray dst, InputArray cameraMatrix,
InputArray distCoeffs,
TermCriteria = TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 5, 0.01));
/** @brief Octree for 3D vision.
*
* In 3D vision filed, the Octree is used to process and accelerate the pointcloud data. The class Octree represents
+1 -1
View File
@@ -583,7 +583,7 @@ int recoverPose( InputArray E, InputArray _points1, InputArray _points2,
P3(Range::all(), Range(0, 3)) = R1 * 1.0; P3.col(3) = -t * 1.0;
P4(Range::all(), Range(0, 3)) = R2 * 1.0; P4.col(3) = -t * 1.0;
// Do the cheirality check.
// Do the chirality check.
// Notice here a threshold dist is used to filter
// out far away points (i.e. infinite points) since
// their depth may vary between positive and negative.
+91 -41
View File
@@ -116,7 +116,7 @@ void PoseSolver::solve(InputArray objectPoints, InputArray imagePoints, OutputAr
num_solutions_ = 0;
computeOmega(_objectPoints, _imagePoints);
solveInternal();
solveInternal(_objectPoints);
int depthRot = rvecs.fixedType() ? rvecs.depth() : CV_64F;
int depthTrans = tvecs.fixedType() ? tvecs.depth() : CV_64F;
@@ -192,37 +192,41 @@ void PoseSolver::computeOmega(InputArray objectPoints, InputArray imagePoints)
omega_(7, 7) += sq_norm * Y2; omega_(7, 8) += sq_norm * YZ;
omega_(8, 8) += sq_norm * Z2;
//Compute qa_sum
//Compute qa_sum. Certain pairs of elements are equal, so filling them outside the loop saves some operations
qa_sum(0, 0) += X; qa_sum(0, 1) += Y; qa_sum(0, 2) += Z;
qa_sum(1, 3) += X; qa_sum(1, 4) += Y; qa_sum(1, 5) += Z;
qa_sum(0, 6) += -x * X; qa_sum(0, 7) += -x * Y; qa_sum(0, 8) += -x * Z;
qa_sum(1, 6) += -y * X; qa_sum(1, 7) += -y * Y; qa_sum(1, 8) += -y * Z;
qa_sum(2, 0) += -x * X; qa_sum(2, 1) += -x * Y; qa_sum(2, 2) += -x * Z;
qa_sum(2, 3) += -y * X; qa_sum(2, 4) += -y * Y; qa_sum(2, 5) += -y * Z;
qa_sum(2, 6) += sq_norm * X; qa_sum(2, 7) += sq_norm * Y; qa_sum(2, 8) += sq_norm * Z;
}
//Complete qa_sum
qa_sum(1, 3) = qa_sum(0, 0); qa_sum(1, 4) = qa_sum(0, 1); qa_sum(1, 5) = qa_sum(0, 2);
qa_sum(2, 0) = qa_sum(0, 6); qa_sum(2, 1) = qa_sum(0, 7); qa_sum(2, 2) = qa_sum(0, 8);
qa_sum(2, 3) = qa_sum(1, 6); qa_sum(2, 4) = qa_sum(1, 7); qa_sum(2, 5) = qa_sum(1, 8);
//lower triangles of omega_'s off-diagonal blocks (0:2, 6:8), (3:5, 6:8) and (6:8, 6:8)
omega_(1, 6) = omega_(0, 7); omega_(2, 6) = omega_(0, 8); omega_(2, 7) = omega_(1, 8);
omega_(4, 6) = omega_(3, 7); omega_(5, 6) = omega_(3, 8); omega_(5, 7) = omega_(4, 8);
omega_(7, 6) = omega_(6, 7); omega_(8, 6) = omega_(6, 8); omega_(8, 7) = omega_(7, 8);
//upper triangle of omega_'s block (3:5, 3:5)
omega_(3, 3) = omega_(0, 0); omega_(3, 4) = omega_(0, 1); omega_(3, 5) = omega_(0, 2);
omega_(4, 4) = omega_(1, 1); omega_(4, 5) = omega_(1, 2);
omega_(5, 5) = omega_(2, 2);
omega_(4, 4) = omega_(1, 1); omega_(4, 5) = omega_(1, 2);
omega_(5, 5) = omega_(2, 2);
//Mirror upper triangle to lower triangle
for (int r = 0; r < 9; r++)
{
for (int c = 0; c < r; c++)
{
omega_(r, c) = omega_(c, r);
}
}
//Mirror omega_'s upper triangle to lower triangle
//Note that elements (7, 6), (8, 6) & (8, 7) have already been assigned above
omega_(1, 0) = omega_(0, 1);
omega_(2, 0) = omega_(0, 2); omega_(2, 1) = omega_(1, 2);
omega_(3, 0) = omega_(0, 3); omega_(3, 1) = omega_(1, 3); omega_(3, 2) = omega_(2, 3);
omega_(4, 0) = omega_(0, 4); omega_(4, 1) = omega_(1, 4); omega_(4, 2) = omega_(2, 4); omega_(4, 3) = omega_(3, 4);
omega_(5, 0) = omega_(0, 5); omega_(5, 1) = omega_(1, 5); omega_(5, 2) = omega_(2, 5); omega_(5, 3) = omega_(3, 5); omega_(5, 4) = omega_(4, 5);
omega_(6, 0) = omega_(0, 6); omega_(6, 1) = omega_(1, 6); omega_(6, 2) = omega_(2, 6); omega_(6, 3) = omega_(3, 6); omega_(6, 4) = omega_(4, 6); omega_(6, 5) = omega_(5, 6);
omega_(7, 0) = omega_(0, 7); omega_(7, 1) = omega_(1, 7); omega_(7, 2) = omega_(2, 7); omega_(7, 3) = omega_(3, 7); omega_(7, 4) = omega_(4, 7); omega_(7, 5) = omega_(5, 7);
omega_(8, 0) = omega_(0, 8); omega_(8, 1) = omega_(1, 8); omega_(8, 2) = omega_(2, 8); omega_(8, 3) = omega_(3, 8); omega_(8, 4) = omega_(4, 8); omega_(8, 5) = omega_(5, 8);
cv::Matx<double, 3, 3> q;
q(0, 0) = n; q(0, 1) = 0; q(0, 2) = -sum_img.x;
@@ -245,6 +249,11 @@ void PoseSolver::computeOmega(InputArray objectPoints, InputArray imagePoints)
cv::SVD omega_svd(omega_, cv::SVD::FULL_UV);
s_ = omega_svd.w;
u_ = cv::Mat(omega_svd.vt.t());
#if 0
// EVD equivalent of the SVD; less accurate
cv::eigen(omega_, s_, u_);
u_ = u_.t(); // eigenvectors were returned as rows
#endif
CV_Assert(s_(0) >= 1e-7);
@@ -255,7 +264,7 @@ void PoseSolver::computeOmega(InputArray objectPoints, InputArray imagePoints)
point_mean_ = cv::Vec3d(sum_obj.x / n, sum_obj.y / n, sum_obj.z / n);
}
void PoseSolver::solveInternal()
void PoseSolver::solveInternal(InputArray objectPoints)
{
double min_sq_err = std::numeric_limits<double>::max();
int num_eigen_points = num_null_vectors_ > 0 ? num_null_vectors_ : 1;
@@ -272,42 +281,39 @@ void PoseSolver::solveInternal()
{
solutions[0].r_hat = det3x3(e) * e;
solutions[0].t = p_ * solutions[0].r_hat;
checkSolution(solutions[0], min_sq_err);
checkSolution(solutions[0], objectPoints, min_sq_err);
}
else
{
Matx<double, 9, 1> r;
nearestRotationMatrix(e, r);
nearestRotationMatrixFOAM(e, r);
solutions[0] = runSQP(r);
solutions[0].t = p_ * solutions[0].r_hat;
checkSolution(solutions[0], min_sq_err);
checkSolution(solutions[0], objectPoints, min_sq_err);
nearestRotationMatrix(-e, r);
nearestRotationMatrixFOAM(-e, r);
solutions[1] = runSQP(r);
solutions[1].t = p_ * solutions[1].r_hat;
checkSolution(solutions[1], min_sq_err);
checkSolution(solutions[1], objectPoints, min_sq_err);
}
}
int c = 1;
while (min_sq_err > 3 * s_[9 - num_eigen_points - c] && 9 - num_eigen_points - c > 0)
int index, c = 1;
while ((index = 9 - num_eigen_points - c) > 0 && min_sq_err > 3 * s_[index])
{
int index = 9 - num_eigen_points - c;
const cv::Matx<double, 9, 1> e = u_.col(index);
SQPSolution solutions[2];
Matx<double, 9, 1> r;
nearestRotationMatrix(e, r);
nearestRotationMatrixFOAM(e, r);
solutions[0] = runSQP(r);
solutions[0].t = p_ * solutions[0].r_hat;
checkSolution(solutions[0], min_sq_err);
checkSolution(solutions[0], objectPoints, min_sq_err);
nearestRotationMatrix(-e, r);
nearestRotationMatrixFOAM(-e, r);
solutions[1] = runSQP(r);
solutions[1].t = p_ * solutions[1].r_hat;
checkSolution(solutions[1], min_sq_err);
checkSolution(solutions[1], objectPoints, min_sq_err);
c++;
}
@@ -339,7 +345,7 @@ PoseSolver::SQPSolution PoseSolver::runSQP(const cv::Matx<double, 9, 1>& r0)
if (det_r > SQP_DET_THRESHOLD)
{
nearestRotationMatrix(r, solution.r_hat);
nearestRotationMatrixFOAM(r, solution.r_hat);
}
else
{
@@ -613,12 +619,26 @@ void PoseSolver::computeRowAndNullspace(const cv::Matx<double, 9, 1>& r,
}
// faster nearest rotation computation based on FOAM (see: http://users.ics.forth.gr/~lourakis/publ/2018_iros.pdf )
// if e = u*w*vt then r=u*diag([1, 1, det(u)*det(v)])*vt
void PoseSolver::nearestRotationMatrixSVD(const cv::Matx<double, 9, 1>& e,
cv::Matx<double, 9, 1>& r)
{
cv::Matx<double, 3, 3> e33 = e.reshape<3, 3>();
cv::SVD e33_svd(e33, cv::SVD::FULL_UV);
double detuv = cv::determinant(e33_svd.u)*cv::determinant(e33_svd.vt);
cv::Matx<double, 3, 3> diag = cv::Matx33d::eye();
diag(2, 2) = detuv;
cv::Matx<double, 3, 3> r33 = cv::Mat(e33_svd.u*diag*e33_svd.vt);
r = r33.reshape<9, 1>();
}
// Faster nearest rotation computation based on FOAM. See M. Lourakis: "An Efficient Solution to Absolute Orientation", ICPR 2016
// and M. Lourakis, G. Terzakis: "Efficient Absolute Orientation Revisited", IROS 2018.
/* Solve the nearest orthogonal approximation problem
* i.e., given e, find R minimizing ||R-e||_F
*
* The computation borrows from Markley's FOAM algorithm
* "Attitude Determination Using Vector Observations: A Fast Optimal Matrix Algorithm", J. Astronaut. Sci.
* "Attitude Determination Using Vector Observations: A Fast Optimal Matrix Algorithm", J. Astronaut. Sci. 1993.
*
* See also M. Lourakis: "An Efficient Solution to Absolute Orientation", ICPR 2016
*
@@ -626,24 +646,32 @@ void PoseSolver::computeRowAndNullspace(const cv::Matx<double, 9, 1>& r,
* Institute of Computer Science, Foundation for Research & Technology - Hellas
* Heraklion, Crete, Greece.
*/
void PoseSolver::nearestRotationMatrix(const cv::Matx<double, 9, 1>& e,
void PoseSolver::nearestRotationMatrixFOAM(const cv::Matx<double, 9, 1>& e,
cv::Matx<double, 9, 1>& r)
{
int i;
double l, lprev, det_e, e_sq, adj_e_sq, adj_e[9];
// det(e)
det_e = e(0) * e(4) * e(8) - e(0) * e(5) * e(7) - e(1) * e(3) * e(8) + e(2) * e(3) * e(7) + e(1) * e(6) * e(5) - e(2) * e(6) * e(4);
if (fabs(det_e) < 1E-04) { // singular, handle it with SVD
PoseSolver::nearestRotationMatrixSVD(e, r);
return;
}
// e's adjoint
adj_e[0] = e(4) * e(8) - e(5) * e(7); adj_e[1] = e(2) * e(7) - e(1) * e(8); adj_e[2] = e(1) * e(5) - e(2) * e(4);
adj_e[3] = e(5) * e(6) - e(3) * e(8); adj_e[4] = e(0) * e(8) - e(2) * e(6); adj_e[5] = e(2) * e(3) - e(0) * e(5);
adj_e[6] = e(3) * e(7) - e(4) * e(6); adj_e[7] = e(1) * e(6) - e(0) * e(7); adj_e[8] = e(0) * e(4) - e(1) * e(3);
// det(e), ||e||^2, ||adj(e)||^2
det_e = e(0) * e(4) * e(8) - e(0) * e(5) * e(7) - e(1) * e(3) * e(8) + e(2) * e(3) * e(7) + e(1) * e(6) * e(5) - e(2) * e(6) * e(4);
// ||e||^2, ||adj(e)||^2
e_sq = e(0) * e(0) + e(1) * e(1) + e(2) * e(2) + e(3) * e(3) + e(4) * e(4) + e(5) * e(5) + e(6) * e(6) + e(7) * e(7) + e(8) * e(8);
adj_e_sq = adj_e[0] * adj_e[0] + adj_e[1] * adj_e[1] + adj_e[2] * adj_e[2] + adj_e[3] * adj_e[3] + adj_e[4] * adj_e[4] + adj_e[5] * adj_e[5] + adj_e[6] * adj_e[6] + adj_e[7] * adj_e[7] + adj_e[8] * adj_e[8];
// compute l_max with Newton-Raphson from FOAM's characteristic polynomial, i.e. eq.(23) - (26)
for (i = 200, l = 2.0, lprev = 0.0; fabs(l - lprev) > 1E-12 * fabs(lprev) && i > 0; --i) {
l = 0.5*(e_sq + 3.0); // 1/2*(trace(mat(e)*mat(e)') + trace(eye(3)))
if (det_e < 0.0) l = -l;
for (i = 15, lprev = 0.0; fabs(l - lprev) > 1E-12 * fabs(lprev) && i > 0; --i) {
double tmp, p, pp;
tmp = (l * l - e_sq);
@@ -717,9 +745,31 @@ inline bool PoseSolver::positiveDepth(const SQPSolution& solution) const
return (r(6) * mean(0) + r(7) * mean(1) + r(8) * mean(2) + t(2) > 0);
}
void PoseSolver::checkSolution(SQPSolution& solution, double& min_error)
inline bool PoseSolver::positiveMajorityDepths(const SQPSolution& solution, InputArray objectPoints) const
{
if (positiveDepth(solution))
const cv::Matx<double, 9, 1>& r = solution.r_hat;
const cv::Matx<double, 3, 1>& t = solution.t;
int npos = 0, nneg = 0;
Mat _objectPoints = objectPoints.getMat();
int n = _objectPoints.cols * _objectPoints.rows;
for (int i = 0; i < n; i++)
{
const cv::Point3d& obj_pt = _objectPoints.at<cv::Point3d>(i);
if (r(6) * obj_pt.x + r(7) * obj_pt.y + r(8) * obj_pt.z + t(2) > 0) ++npos;
else ++nneg;
}
return npos >= nneg;
}
void PoseSolver::checkSolution(SQPSolution& solution, InputArray objectPoints, double& min_error)
{
bool cheirok = positiveDepth(solution) || positiveMajorityDepths(solution, objectPoints); // check the majority if the check with centroid fails
if (cheirok)
{
solution.sq_error = (omega_ * solution.r_hat).ddot(solution.r_hat);
if (fabs(min_error - solution.sq_error) > EQUAL_SQUARED_ERRORS_DIFF)
+33 -14
View File
@@ -85,13 +85,14 @@ private:
/*
* @brief Computes the 9x9 PSD Omega matrix and supporting matrices.
* @param objectPoints The 3D points in object coordinates.
*/
void solveInternal();
void solveInternal(InputArray objectPoints);
/*
* @brief Produces the distance from being orthogonal for a given 3x3 matrix
* in row-major form.
* @param e The vector to test representing a 3x3 matrix in row major form.
* in row-major order.
* @param e The vector to test representing a 3x3 matrix in row-major order.
* @return The distance the matrix is from being orthogonal.
*/
static double orthogonalityError(const cv::Matx<double, 9, 1>& e);
@@ -99,31 +100,49 @@ private:
/*
* @brief Processes a solution and sorts it by error.
* @param solution The solution to evaluate.
* @param min_error The current minimum error.
* @param objectPoints The 3D points in object coordinates.
* @param min_error The current minimum error.
*/
void checkSolution(SQPSolution& solution, double& min_error);
void checkSolution(SQPSolution& solution, InputArray objectPoints, double& min_error);
/*
* @brief Computes the determinant of a matrix stored in row-major format.
* @param e Vector representing a 3x3 matrix stored in row-major format.
* @brief Computes the determinant of a matrix stored in row-major order.
* @param e Vector representing a 3x3 matrix stored in row-major order.
* @return The determinant of the matrix.
*/
static double det3x3(const cv::Matx<double, 9, 1>& e);
/*
* @brief Tests the cheirality for a given solution.
* @brief Tests the cheirality on the mean object point for a given solution.
* @param solution The solution to evaluate.
*/
inline bool positiveDepth(const SQPSolution& solution) const;
/*
* @brief Determines the nearest rotation matrix to a given rotaiton matrix.
* Input and output are 9x1 vector representing a vector stored in row-major
* form.
* @param e The input 3x3 matrix stored in a vector in row-major form.
* @param r The nearest rotation matrix to the input e (again in row-major form).
* @brief Tests the cheirality on all object points for a given solution.
* @param solution The solution to evaluate.
* @param objectPoints The 3D points in object coordinates.
*/
static void nearestRotationMatrix(const cv::Matx<double, 9, 1>& e,
inline bool positiveMajorityDepths(const SQPSolution& solution, InputArray objectPoints) const;
/*
* @brief Determines the nearest rotation matrix to a given rotation matrix using SVD.
* Input and output are 9x1 vector representing a matrix stored in row-major
* order.
* @param e The input 3x3 matrix stored in a vector in row-major order.
* @param r The nearest rotation matrix to the input e (again in row-major order).
*/
static void nearestRotationMatrixSVD(const cv::Matx<double, 9, 1>& e,
cv::Matx<double, 9, 1>& r);
/*
* @brief Determines the nearest rotation matrix to a given rotation matrix using the FOAM algorithm.
* Input and output are 9x1 vector representing a matrix stored in row-major
* order.
* @param e The input 3x3 matrix stored in a vector in row-major order.
* @param r The nearest rotation matrix to the input e (again in row-major order).
*/
static void nearestRotationMatrixFOAM(const cv::Matx<double, 9, 1>& e,
cv::Matx<double, 9, 1>& r);
/*
+6
View File
@@ -40,6 +40,7 @@
//
//M*/
#include "opencv2/core/types.hpp"
#include "precomp.hpp"
#include "distortion_model.hpp"
@@ -523,6 +524,11 @@ void undistortPoints(InputArray _src, OutputArray _dst,
undistortPointsInternal(src, dst, cameraMatrix, distCoeffs, R, P, criteria);
}
void undistortImagePoints(InputArray src, OutputArray dst, InputArray cameraMatrix, InputArray distCoeffs, TermCriteria termCriteria)
{
undistortPoints(src, dst, cameraMatrix, distCoeffs, noArray(), cameraMatrix, termCriteria);
}
static Point2f mapPointSpherical(const Point2f& p, float alpha, Vec4d* J, enum UndistortTypes projType)
{
double x = p.x, y = p.y;
+63 -10
View File
@@ -3,6 +3,7 @@
// of this distribution and at http://opencv.org/license.html.
#include <opencv2/ts/cuda_test.hpp> // EXPECT_MAT_NEAR
#include "opencv2/core/types.hpp"
#include "test_precomp.hpp"
namespace opencv_test { namespace {
@@ -14,6 +15,7 @@ protected:
-1, 5), Point3f pmax = Point3f(1, 1, 10));
void generateCameraMatrix(Mat& cameraMatrix);
void generateDistCoeffs(Mat& distCoeffs, int count);
cv::Mat generateRotationVector();
double thresh = 1.0e-2;
};
@@ -50,6 +52,14 @@ void UndistortPointsTest::generateDistCoeffs(Mat& distCoeffs, int count)
distCoeffs.at<double>(i,0) = theRNG().uniform(-0.1, 0.1);
}
cv::Mat UndistortPointsTest::generateRotationVector()
{
Mat rvec(1, 3, CV_64F);
theRNG().fill(rvec, RNG::UNIFORM, -0.2, 0.2);
return rvec;
}
TEST_F(UndistortPointsTest, accuracy)
{
Mat intrinsics, distCoeffs;
@@ -58,34 +68,73 @@ TEST_F(UndistortPointsTest, accuracy)
vector<Point3f> points(500);
generate3DPointCloud(points);
vector<Point2f> projectedPoints;
projectedPoints.resize(points.size());
Mat rvec = generateRotationVector();
Mat R;
cv::Rodrigues(rvec, R);
int modelMembersCount[] = {4,5,8};
for (int idx = 0; idx < 3; idx++)
{
generateDistCoeffs(distCoeffs, modelMembersCount[idx]);
/* Project points with distortion */
vector<Point2f> projectedPoints;
projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1),
Mat::zeros(3,1,CV_64FC1), intrinsics,
distCoeffs, projectedPoints);
/* Project points without distortion */
vector<Point2f> realUndistortedPoints;
projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1),
projectPoints(Mat(points), rvec,
Mat::zeros(3,1,CV_64FC1), intrinsics,
Mat::zeros(4,1,CV_64FC1), realUndistortedPoints);
/* Undistort points */
Mat undistortedPoints;
undistortPoints(Mat(projectedPoints), undistortedPoints, intrinsics, distCoeffs);
Mat p;
perspectiveTransform(undistortedPoints, p, intrinsics);
undistortedPoints = p;
undistortPoints(Mat(projectedPoints), undistortedPoints, intrinsics, distCoeffs, R, intrinsics);
EXPECT_MAT_NEAR(realUndistortedPoints, undistortedPoints.t(), thresh);
}
}
TEST_F(UndistortPointsTest, undistortImagePointsAccuracy)
{
Mat intrinsics, distCoeffs;
generateCameraMatrix(intrinsics);
vector<Point3f> points(500);
generate3DPointCloud(points);
int modelMembersCount[] = {4,5,8};
for (int idx = 0; idx < 3; idx++)
{
generateDistCoeffs(distCoeffs, modelMembersCount[idx]);
/* Project points with distortion */
vector<Point2f> projectedPoints;
projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1),
Mat::zeros(3,1,CV_64FC1), intrinsics,
distCoeffs, projectedPoints);
/* Project points without distortion */
vector<Point2f> realUndistortedPoints;
projectPoints(Mat(points), Mat::zeros(3, 1, CV_64FC1),
Mat::zeros(3,1,CV_64FC1), intrinsics,
Mat::zeros(4,1,CV_64FC1), realUndistortedPoints);
/* Undistort points */
Mat undistortedPoints;
TermCriteria termCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 5, thresh / 2);
undistortImagePoints(Mat(projectedPoints), undistortedPoints, intrinsics, distCoeffs,
termCriteria);
EXPECT_MAT_NEAR(realUndistortedPoints, undistortedPoints.t(), thresh);
}
}
TEST_F(UndistortPointsTest, stop_criteria)
{
Mat cameraMatrix = (Mat_<double>(3,3,CV_64F) << 857.48296979, 0, 968.06224829,
@@ -103,13 +152,17 @@ TEST_F(UndistortPointsTest, stop_criteria)
TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 100, maxError);
std::vector<Point2d> pt_undist_vec;
undistortPoints(pt_distorted_vec, pt_undist_vec, cameraMatrix, distCoeffs, noArray(), noArray(), criteria);
Mat rVec = Mat(Matx31d(0.1, -0.2, 0.2));
Mat R;
cv::Rodrigues(rVec, R);
undistortPoints(pt_distorted_vec, pt_undist_vec, cameraMatrix, distCoeffs, R, noArray(), criteria);
std::vector<Point3d> pt_undist_vec_homogeneous;
pt_undist_vec_homogeneous.emplace_back(pt_undist_vec[0].x, pt_undist_vec[0].y, 1.0 );
std::vector<Point2d> pt_redistorted_vec;
projectPoints(pt_undist_vec_homogeneous, Mat::zeros(3,1,CV_64F),
projectPoints(pt_undist_vec_homogeneous, -rVec,
Mat::zeros(3,1,CV_64F), cameraMatrix, distCoeffs, pt_redistorted_vec);
const double obtainedError = sqrt( pow(pt_distorted.x - pt_redistorted_vec[0].x, 2) + pow(pt_distorted.y - pt_redistorted_vec[0].y, 2) );
+6 -3
View File
@@ -6,6 +6,7 @@
#define OPENCV_CALIB_HPP
#include "opencv2/core.hpp"
#include "opencv2/core/types.hpp"
#include "opencv2/features2d.hpp"
#include "opencv2/core/affine.hpp"
@@ -390,6 +391,9 @@ R & t \\
\f[u = f_x (x' + \alpha y') + c_x \\
v = f_y y' + c_y\f]
Summary:
Generic camera model @cite Kannala2006 with perspective projection and without distortion correction
@defgroup calib3d_c C API
@}
@@ -1455,8 +1459,7 @@ the number of points in the view.
@param distorted Output array of image points, 1xN/Nx1 2-channel, or vector\<Point2f\> .
Note that the function assumes the camera intrinsic matrix of the undistorted points to be identity.
This means if you want to transform back points undistorted with undistortPoints() you have to
multiply them with \f$P^{-1}\f$.
This means if you want to distort image points you have to multiply them with \f$K^{-1}\f$.
*/
CV_EXPORTS_W void distortPoints(InputArray undistorted, OutputArray distorted, InputArray K, InputArray D, double alpha = 0);
@@ -1541,7 +1544,7 @@ length. Balance is in range of [0, 1].
CV_EXPORTS_W void estimateNewCameraMatrixForUndistortRectify(InputArray K, InputArray D, const Size &image_size, InputArray R,
OutputArray P, double balance = 0.0, const Size& new_size = Size(), double fov_scale = 1.0);
/** @brief Performs camera calibaration
/** @brief Performs camera calibration
@param objectPoints vector of vectors of calibration pattern points in the calibration pattern
coordinate space.
+9 -13
View File
@@ -5,31 +5,27 @@
namespace opencv_test { namespace {
using PerfIntType = perf::TestBaseWithParam<std::tuple<int>>;
PERF_TEST_P(PerfIntType, fisheye_undistortPoints,
(testing::Values(1e2, 1e3, 1e4)))
PERF_TEST(Undistort, fisheye_undistortPoints_100k_10iter)
{
const cv::Size imageSize(1280, 800);
const int pointsNumber = 100000;
const Size imageSize(1280, 800);
/* Set camera matrix */
const cv::Matx33d K(558.478087865323, 0, 620.458515360843,
const Matx33d K(558.478087865323, 0, 620.458515360843,
0, 560.506767351568, 381.939424848348,
0, 0, 1);
/* Set distortion coefficients */
Mat D(1, 4, CV_64F);
theRNG().fill(D, RNG::UNIFORM, -1.e-5, 1.e-5);
int pointsNumber = std::get<0>(GetParam());
const Matx14d D(2.81e-06, 1.31e-06, -4.42e-06, -1.25e-06);
/* Create two-channel points matrix */
cv::Mat xy[2] = {};
Mat xy[2] = {};
xy[0].create(pointsNumber, 1, CV_64F);
theRNG().fill(xy[0], cv::RNG::UNIFORM, 0, imageSize.width); // x
theRNG().fill(xy[0], RNG::UNIFORM, 0, imageSize.width); // x
xy[1].create(pointsNumber, 1, CV_64F);
theRNG().fill(xy[1], cv::RNG::UNIFORM, 0, imageSize.height); // y
theRNG().fill(xy[1], RNG::UNIFORM, 0, imageSize.height); // y
cv::Mat points;
Mat points;
merge(xy, 2, points);
/* Set fixed iteration number to check only c++ code, not algo convergence */
+4 -3
View File
@@ -1227,7 +1227,7 @@ int ChessBoardDetector::cleanFoundConnectedQuads(std::vector<ChessBoardQuad*>& q
centers[i] = ci;
center += ci;
}
center.x *= (1.0f / quad_count);
center *= (1.0f / quad_count);
// If we still have more quadrangles than we should,
// we try to eliminate bad ones based on minimizing the bounding box.
@@ -1251,7 +1251,7 @@ int ChessBoardDetector::cleanFoundConnectedQuads(std::vector<ChessBoardQuad*>& q
Mat points(1, quad_count, CV_32FC2, &centers[0]);
convexHull(points, hull, true);
centers[skip] = temp;
double hull_area = contourArea(hull, true);
double hull_area = contourArea(hull, false);
// remember smallest box area
if (hull_area < min_box_area)
@@ -1293,6 +1293,7 @@ int ChessBoardDetector::cleanFoundConnectedQuads(std::vector<ChessBoardQuad*>& q
quad_group[min_box_area_index] = quad_group[quad_count];
centers[min_box_area_index] = centers[quad_count];
}
quad_group.resize(quad_count);
return quad_count;
}
@@ -1464,7 +1465,7 @@ int ChessBoardDetector::checkQuadGroup(std::vector<ChessBoardQuad*>& quad_group,
first = below; // remember the first corner in the next row
// find and store the first row (or column)
for (int j = 1; ; ++j)
while( 1 )
{
right->row = 0;
out_corners.push_back(right);
+1 -1
View File
@@ -377,7 +377,7 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted
size_t n = distorted.total();
int sdepth = distorted.depth();
const bool isEps = criteria.type & TermCriteria::EPS;
const bool isEps = (criteria.type & TermCriteria::EPS) != 0;
/* Define max count for solver iterations */
int maxCount = std::numeric_limits<int>::max();
+3 -23
View File
@@ -484,7 +484,8 @@ void CV_CameraCalibrationTest::run( int start_from )
for( i = 0; i < 3; i++ )
for( j = 0; j < 3; j++ )
{
values_read = fscanf(file, "%lf", &goodRotMatrs[currImage].val[i*3+j]);
// Yes, load with transpose
values_read = fscanf(file, "%lf", &goodRotMatrs[currImage].val[j*3+i]);
CV_Assert(values_read == 1);
}
}
@@ -568,12 +569,7 @@ void CV_CameraCalibrationTest::run( int start_from )
/* ----- Compute reprojection error ----- */
double dx,dy;
double rx,ry;
double meanDx,meanDy;
double maxDx = 0.0;
double maxDy = 0.0;
meanDx = 0;
meanDy = 0;
for( currImage = 0; currImage < numImages; currImage++ )
{
double imageMeanDx = 0;
@@ -585,20 +581,8 @@ void CV_CameraCalibrationTest::run( int start_from )
dx = rx - imagePoints[currImage][currPoint].x;
dy = ry - imagePoints[currImage][currPoint].y;
meanDx += dx;
meanDy += dy;
imageMeanDx += dx*dx;
imageMeanDy += dy*dy;
dx = fabs(dx);
dy = fabs(dy);
if( dx > maxDx )
maxDx = dx;
if( dy > maxDy )
maxDy = dy;
}
goodPerViewErrors[currImage] = sqrt( (imageMeanDx + imageMeanDy) /
(etalonSize.width * etalonSize.height));
@@ -609,9 +593,6 @@ void CV_CameraCalibrationTest::run( int start_from )
perViewErrors[currImage] = goodPerViewErrors[currImage];
}
meanDx /= numImages * etalonSize.width * etalonSize.height;
meanDy /= numImages * etalonSize.width * etalonSize.height;
/* ========= Compare parameters ========= */
CV_Assert(cameraMatrix.type() == CV_64F && cameraMatrix.size() == Size(3, 3));
CV_Assert(distortion.type() == CV_64F);
@@ -681,7 +662,7 @@ void CV_CameraCalibrationTest::run( int start_from )
/* ----- Compare per view re-projection errors ----- */
CV_Assert(perViewErrors.size() == (size_t)numImages);
code = compare(&perViewErrors[0], &goodPerViewErrors[0], numImages, 1.1, "per view errors vector");
code = compare(&perViewErrors[0], &goodPerViewErrors[0], numImages, 0.1, "per view errors vector");
if( code < 0 )
break;
@@ -812,7 +793,6 @@ void CV_CameraCalibrationTest_CPP::calibrate(Size imageSize,
{
Mat r9;
Rodrigues( rvecs[i], r9 );
cv::transpose(r9, r9);
r9.convertTo(rotationMatrices[i], CV_64F);
tvecs[i].convertTo(translationVectors[i], CV_64F);
}
+7 -8
View File
@@ -131,20 +131,20 @@ TEST_F(fisheyeTest, distortUndistortPoints)
/* Test with random D set */
for (size_t i = 0; i < 10; ++i) {
cv::Mat D(1, 4, CV_64F);
theRNG().fill(D, cv::RNG::UNIFORM, -0.00001, 0.00001);
cv::Mat distortion(1, 4, CV_64F);
theRNG().fill(distortion, cv::RNG::UNIFORM, -0.00001, 0.00001);
/* Distort -> Undistort */
cv::Mat distortedPoints;
cv::fisheye::distortPoints(points0, distortedPoints, K, D);
cv::fisheye::distortPoints(points0, distortedPoints, K, distortion);
cv::Mat undistortedPoints;
cv::fisheye::undistortPoints(distortedPoints, undistortedPoints, K, D);
cv::fisheye::undistortPoints(distortedPoints, undistortedPoints, K, distortion);
EXPECT_MAT_NEAR(points0, undistortedPoints, 1e-8);
/* Undistort -> Distort */
cv::fisheye::undistortPoints(points0, undistortedPoints, K, D);
cv::fisheye::distortPoints(undistortedPoints, distortedPoints, K, D);
cv::fisheye::undistortPoints(points0, undistortedPoints, K, distortion);
cv::fisheye::distortPoints(undistortedPoints, distortedPoints, K, distortion);
EXPECT_MAT_NEAR(points0, distortedPoints, 1e-8);
}
@@ -227,7 +227,7 @@ TEST_F(fisheyeTest, undistortAndDistortImage)
cv::Mat undPointsGt(imageHeight, imageWidth, CV_32FC2);
cv::Mat imageGt(imageHeight, imageWidth, CV_8UC3);
for(int y = 0, k = 0; y < imageHeight; ++y)
for(int y = 0; y < imageHeight; ++y)
{
for(int x = 0; x < imageWidth; ++x)
{
@@ -261,7 +261,6 @@ TEST_F(fisheyeTest, undistortAndDistortImage)
pixel_gt[2] = pixel[2];
}
k++;
}
}
+2 -1
View File
@@ -6,7 +6,7 @@ ocv_add_dispatched_file(arithm SSE2 SSE4_1 AVX2 VSX3)
ocv_add_dispatched_file(convert SSE2 AVX2 VSX3)
ocv_add_dispatched_file(convert_scale SSE2 AVX2)
ocv_add_dispatched_file(count_non_zero SSE2 AVX2)
ocv_add_dispatched_file(matmul SSE2 SSE4_1 AVX2 AVX512_SKX)
ocv_add_dispatched_file(matmul SSE2 SSE4_1 AVX2 AVX512_SKX NEON_DOTPROD)
ocv_add_dispatched_file(mean SSE2 AVX2)
ocv_add_dispatched_file(merge SSE2 AVX2)
ocv_add_dispatched_file(split SSE2 AVX2)
@@ -146,6 +146,7 @@ ocv_create_module(${extra_libs})
ocv_target_link_libraries(${the_module} PRIVATE
"${ZLIB_LIBRARIES}" "${OPENCL_LIBRARIES}" "${VA_LIBRARIES}"
"${OPENGL_LIBRARIES}"
"${GLX_LIBRARIES}"
"${LAPACK_LIBRARIES}" "${CPUFEATURES_LIBRARIES}" "${HALIDE_LIBRARIES}"
"${ITT_LIBRARIES}"
"${OPENCV_HAL_LINKER_LIBS}"
+12 -12
View File
@@ -72,7 +72,7 @@ data sharing. A destructor decrements the reference counter associated with the
The buffer is deallocated if and only if the reference counter reaches zero, that is, when no other
structures refer to the same buffer. Similarly, when a Mat instance is copied, no actual data is
really copied. Instead, the reference counter is incremented to memorize that there is another owner
of the same data. There is also the Mat::clone method that creates a full copy of the matrix data.
of the same data. There is also the cv::Mat::clone method that creates a full copy of the matrix data.
See the example below:
```.cpp
// create a big 8Mb matrix
@@ -160,11 +160,11 @@ grayscale conversion. Note that frame and edges are allocated only once during t
of the loop body since all the next video frames have the same resolution. If you somehow change the
video resolution, the arrays are automatically reallocated.
The key component of this technology is the Mat::create method. It takes the desired array size and
type. If the array already has the specified size and type, the method does nothing. Otherwise, it
releases the previously allocated data, if any (this part involves decrementing the reference
The key component of this technology is the cv::Mat::create method. It takes the desired array size
and type. If the array already has the specified size and type, the method does nothing. Otherwise,
it releases the previously allocated data, if any (this part involves decrementing the reference
counter and comparing it with zero), and then allocates a new buffer of the required size. Most
functions call the Mat::create method for each output array, and so the automatic output data
functions call the cv::Mat::create method for each output array, and so the automatic output data
allocation is implemented.
Some notable exceptions from this scheme are cv::mixChannels, cv::RNG::fill, and a few other
@@ -248,9 +248,9 @@ Examples:
// matrix (10-element complex vector)
Mat img(Size(1920, 1080), CV_8UC3); // make a 3-channel (color) image
// of 1920 columns and 1080 rows.
Mat grayscale(image.size(), CV_MAKETYPE(image.depth(), 1)); // make a 1-channel image of
// the same size and same
// channel type as img
Mat grayscale(img.size(), CV_MAKETYPE(img.depth(), 1)); // make a 1-channel image of
// the same size and same
// channel type as img
```
Arrays with more complex elements cannot be constructed or processed using OpenCV. Furthermore, each
function or method can handle only a subset of all possible array types. Usually, the more complex
@@ -270,14 +270,14 @@ extended in future based on user requests.
### InputArray and OutputArray
Many OpenCV functions process dense 2-dimensional or multi-dimensional numerical arrays. Usually,
such functions take cppMat as parameters, but in some cases it's more convenient to use
such functions take `cv::Mat` as parameters, but in some cases it's more convenient to use
`std::vector<>` (for a point set, for example) or `cv::Matx<>` (for 3x3 homography matrix and such). To
avoid many duplicates in the API, special "proxy" classes have been introduced. The base "proxy"
class is cv::InputArray. It is used for passing read-only arrays on a function input. The derived from
InputArray class cv::OutputArray is used to specify an output array for a function. Normally, you should
not care of those intermediate types (and you should not declare variables of those types
explicitly) - it will all just work automatically. You can assume that instead of
InputArray/OutputArray you can always use `Mat`, `std::vector<>`, `cv::Matx<>`, `cv::Vec<>` or `cv::Scalar`. When a
InputArray/OutputArray you can always use `cv::Mat`, `std::vector<>`, `cv::Matx<>`, `cv::Vec<>` or `cv::Scalar`. When a
function has an optional input or output array, and you do not have or do not want one, pass
cv::noArray().
@@ -289,7 +289,7 @@ the optimization algorithm did not converge), it returns a special error code (t
boolean variable).
The exceptions can be instances of the cv::Exception class or its derivatives. In its turn,
cv::Exception is a derivative of std::exception. So it can be gracefully handled in the code using
cv::Exception is a derivative of `std::exception`. So it can be gracefully handled in the code using
other standard C++ library components.
The exception is typically thrown either using the `#CV_Error(errcode, description)` macro, or its
@@ -298,7 +298,7 @@ printf-like `#CV_Error_(errcode, (printf-spec, printf-args))` variant, or using
satisfied. For performance-critical code, there is #CV_DbgAssert(condition) that is only retained in
the Debug configuration. Due to the automatic memory management, all the intermediate buffers are
automatically deallocated in case of a sudden error. You only need to add a try statement to catch
exceptions, if needed: :
exceptions, if needed:
```.cpp
try
{
+2 -2
View File
@@ -13,7 +13,7 @@ namespace cv {
CV_EXPORTS const char* depthToString(int depth);
/** Returns string of cv::Mat depth value: CV_8UC3 -> "CV_8UC3" or "<invalid type>" */
CV_EXPORTS const String typeToString(int type);
CV_EXPORTS String typeToString(int type);
//! @cond IGNORED
@@ -23,7 +23,7 @@ namespace detail {
CV_EXPORTS const char* depthToString_(int depth);
/** Returns string of cv::Mat depth value: CV_8UC3 -> "CV_8UC3" or cv::String() */
CV_EXPORTS const cv::String typeToString_(int type);
CV_EXPORTS cv::String typeToString_(int type);
enum TestOp {
TEST_CUSTOM = 0,
+2 -2
View File
@@ -155,7 +155,7 @@ public:
CV_WRAP void create(Size size, int type);
//! decreases reference counter, deallocate the data when reference counter reaches 0
void release();
CV_WRAP void release();
//! swaps with other smart pointer
CV_WRAP void swap(GpuMat& mat);
@@ -689,7 +689,7 @@ class CV_EXPORTS_W BufferPool
public:
//! Gets the BufferPool for the given stream.
explicit BufferPool(Stream& stream);
CV_WRAP explicit BufferPool(Stream& stream);
//! Allocates a new GpuMat of given size and type.
CV_WRAP GpuMat getBuffer(int rows, int cols, int type);
@@ -79,6 +79,10 @@
# endif
# define CV_FP16 1
#endif
#ifdef CV_CPU_COMPILE_NEON_DOTPROD
# include <arm_neon.h>
# define CV_NEON_DOT 1
#endif
#ifdef CV_CPU_COMPILE_AVX2
# include <immintrin.h>
# define CV_AVX2 1
@@ -420,6 +420,27 @@
#endif
#define __CV_CPU_DISPATCH_CHAIN_NEON(fn, args, mode, ...) CV_CPU_CALL_NEON(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__))
#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_NEON_DOTPROD
# define CV_TRY_NEON_DOTPROD 1
# define CV_CPU_FORCE_NEON_DOTPROD 1
# define CV_CPU_HAS_SUPPORT_NEON_DOTPROD 1
# define CV_CPU_CALL_NEON_DOTPROD(fn, args) return (cpu_baseline::fn args)
# define CV_CPU_CALL_NEON_DOTPROD_(fn, args) return (opt_NEON_DOTPROD::fn args)
#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_NEON_DOTPROD
# define CV_TRY_NEON_DOTPROD 1
# define CV_CPU_FORCE_NEON_DOTPROD 0
# define CV_CPU_HAS_SUPPORT_NEON_DOTPROD (cv::checkHardwareSupport(CV_CPU_NEON_DOTPROD))
# define CV_CPU_CALL_NEON_DOTPROD(fn, args) if (CV_CPU_HAS_SUPPORT_NEON_DOTPROD) return (opt_NEON_DOTPROD::fn args)
# define CV_CPU_CALL_NEON_DOTPROD_(fn, args) if (CV_CPU_HAS_SUPPORT_NEON_DOTPROD) return (opt_NEON_DOTPROD::fn args)
#else
# define CV_TRY_NEON_DOTPROD 0
# define CV_CPU_FORCE_NEON_DOTPROD 0
# define CV_CPU_HAS_SUPPORT_NEON_DOTPROD 0
# define CV_CPU_CALL_NEON_DOTPROD(fn, args)
# define CV_CPU_CALL_NEON_DOTPROD_(fn, args)
#endif
#define __CV_CPU_DISPATCH_CHAIN_NEON_DOTPROD(fn, args, mode, ...) CV_CPU_CALL_NEON_DOTPROD(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__))
#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_MSA
# define CV_TRY_MSA 1
# define CV_CPU_FORCE_MSA 1
@@ -268,6 +268,7 @@ namespace cv {
#define CV_CPU_AVX_5124FMAPS 27
#define CV_CPU_NEON 100
#define CV_CPU_NEON_DOTPROD 101
#define CV_CPU_MSA 150
@@ -324,6 +325,7 @@ enum CpuFeatures {
CPU_AVX_5124FMAPS = 27,
CPU_NEON = 100,
CPU_NEON_DOTPROD = 101,
CPU_MSA = 150,
+24 -42
View File
@@ -128,12 +128,8 @@
#define CV_INLINE_ISNAN_FLT(value) CV_INLINE_ISNAN_DBL(value)
#endif
#if !defined(OPENCV_USE_FASTMATH_BUILTINS) \
&& ( \
defined(__x86_64__) || defined(__i686__) \
|| defined(__arm__) \
|| defined(__PPC64__) \
)
#if !defined(OPENCV_USE_FASTMATH_BUILTINS) && \
(defined __GNUC__ || defined __clang__ || defined _MSC_VER)
/* Let builtin C math functions when available. Dedicated hardware is available to
round and convert FP values. */
#define OPENCV_USE_FASTMATH_BUILTINS 1
@@ -201,9 +197,7 @@ cvRound( double value )
{
#if defined CV_INLINE_ROUND_DBL
CV_INLINE_ROUND_DBL(value);
#elif ((defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__ \
&& defined __SSE2__ && !defined __APPLE__) || CV_SSE2) \
&& !defined(__CUDACC__)
#elif (defined _MSC_VER && defined _M_X64) && !defined(__CUDACC__)
__m128d t = _mm_set_sd( value );
return _mm_cvtsd_si32(t);
#elif defined _MSC_VER && defined _M_IX86
@@ -214,12 +208,11 @@ cvRound( double value )
fistp t;
}
return t;
#elif defined CV_ICC || defined __GNUC__
return (int)(lrint(value));
#elif defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \
defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS
return (int)__builtin_lrint(value);
#else
/* it's ok if round does not comply with IEEE754 standard;
the tests should allow +/-1 difference when the tested functions use round */
return (int)(value + (value >= 0 ? 0.5 : -0.5));
return (int)lrint(value);
#endif
}
@@ -233,11 +226,9 @@ cvRound( double value )
*/
CV_INLINE int cvFloor( double value )
{
#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \
&& ( \
defined(__PPC64__) \
)
return __builtin_floor(value);
#if defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \
defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS
return (int)__builtin_floor(value);
#else
int i = (int)value;
return i - (i > value);
@@ -253,11 +244,9 @@ CV_INLINE int cvFloor( double value )
*/
CV_INLINE int cvCeil( double value )
{
#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \
&& ( \
defined(__PPC64__) \
)
return __builtin_ceil(value);
#if defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \
defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS
return (int)__builtin_ceil(value);
#else
int i = (int)value;
return i + (i < value);
@@ -312,9 +301,7 @@ CV_INLINE int cvRound(float value)
{
#if defined CV_INLINE_ROUND_FLT
CV_INLINE_ROUND_FLT(value);
#elif ((defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__ \
&& defined __SSE2__ && !defined __APPLE__) || CV_SSE2) \
&& !defined(__CUDACC__)
#elif (defined _MSC_VER && defined _M_X64) && !defined(__CUDACC__)
__m128 t = _mm_set_ss( value );
return _mm_cvtss_si32(t);
#elif defined _MSC_VER && defined _M_IX86
@@ -325,12 +312,11 @@ CV_INLINE int cvRound(float value)
fistp t;
}
return t;
#elif defined CV_ICC || defined __GNUC__
return (int)(lrintf(value));
#elif defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \
defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS
return (int)__builtin_lrintf(value);
#else
/* it's ok if round does not comply with IEEE754 standard;
the tests should allow +/-1 difference when the tested functions use round */
return (int)(value + (value >= 0 ? 0.5f : -0.5f));
return (int)lrintf(value);
#endif
}
@@ -343,11 +329,9 @@ CV_INLINE int cvRound( int value )
/** @overload */
CV_INLINE int cvFloor( float value )
{
#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \
&& ( \
defined(__PPC64__) \
)
return __builtin_floorf(value);
#if defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \
defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS
return (int)__builtin_floorf(value);
#else
int i = (int)value;
return i - (i > value);
@@ -363,11 +347,9 @@ CV_INLINE int cvFloor( int value )
/** @overload */
CV_INLINE int cvCeil( float value )
{
#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \
&& ( \
defined(__PPC64__) \
)
return __builtin_ceilf(value);
#if defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \
defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS
return (int)__builtin_ceilf(value);
#else
int i = (int)value;
return i + (i < value);
@@ -200,7 +200,7 @@ using namespace CV_CPU_OPTIMIZATION_HAL_NAMESPACE;
# undef CV_RVV
#endif
#if (CV_SSE2 || CV_NEON || CV_VSX || CV_MSA || CV_WASM_SIMD || CV_RVV071 || CV_RVV) && !defined(CV_FORCE_SIMD128_CPP)
#if (CV_SSE2 || CV_NEON || CV_VSX || CV_MSA || CV_WASM_SIMD || CV_RVV071) && !defined(CV_FORCE_SIMD128_CPP)
#define CV__SIMD_FORWARD 128
#include "opencv2/core/hal/intrin_forward.hpp"
#endif
@@ -229,9 +229,10 @@ using namespace CV_CPU_OPTIMIZATION_HAL_NAMESPACE;
#elif CV_WASM_SIMD && !defined(CV_FORCE_SIMD128_CPP)
#include "opencv2/core/hal/intrin_wasm.hpp"
#elif CV_RVV && !defined(CV_FORCE_SIMD128_CPP)
#elif CV_RVV && !defined(CV_FORCE_SIMD128_CPP) && !defined(CV_RVV_SCALABLE)
#include "opencv2/core/hal/intrin_rvv.hpp"
#elif CV_RVV && !defined(CV_FORCE_SIMD128_CPP) && CV_RVV_SCALABLE
#include "opencv2/core/hal/intrin_rvv_scalable.hpp"
#else
#include "opencv2/core/hal/intrin_cpp.hpp"
@@ -314,6 +315,14 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN
#define CV_SIMD512_FP16 0
#endif
#ifndef CV_SIMD_SCALABLE
#define CV_SIMD_SCALABLE 0
#endif
#ifndef CV_SIMD_SCALABLE_64F
#define CV_SIMD_SCALABLE_64F 0
#endif
//==================================================================================================
template<typename _Tp> struct V_RegTraits
@@ -375,6 +384,18 @@ template<typename _Tp> struct V_RegTraits
CV_DEF_REG_TRAITS(v512, v_int64x8, int64, s64, v_uint64x8, void, void, v_int64x8, void);
CV_DEF_REG_TRAITS(v512, v_float64x8, double, f64, v_float64x8, void, void, v_int64x8, v_int32x16);
#endif
#if CV_SIMD_SCALABLE
CV_DEF_REG_TRAITS(v, v_uint8, uchar, u8, v_uint8, v_uint16, v_uint32, v_int8, void);
CV_DEF_REG_TRAITS(v, v_int8, schar, s8, v_uint8, v_int16, v_int32, v_int8, void);
CV_DEF_REG_TRAITS(v, v_uint16, ushort, u16, v_uint16, v_uint32, v_uint64, v_int16, void);
CV_DEF_REG_TRAITS(v, v_int16, short, s16, v_uint16, v_int32, v_int64, v_int16, void);
CV_DEF_REG_TRAITS(v, v_uint32, unsigned, u32, v_uint32, v_uint64, void, v_int32, void);
CV_DEF_REG_TRAITS(v, v_int32, int, s32, v_uint32, v_int64, void, v_int32, void);
CV_DEF_REG_TRAITS(v, v_float32, float, f32, v_float32, v_float64, void, v_int32, v_int32);
CV_DEF_REG_TRAITS(v, v_uint64, uint64, u64, v_uint64, void, void, v_int64, void);
CV_DEF_REG_TRAITS(v, v_int64, int64, s64, v_uint64, void, void, v_int64, void);
CV_DEF_REG_TRAITS(v, v_float64, double, f64, v_float64, void, void, v_int64, v_int32);
#endif
//! @endcond
#if CV_SIMD512 && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 512)
@@ -488,6 +509,17 @@ namespace CV__SIMD_NAMESPACE {
#define VXPREFIX(func) v##func
} // namespace
using namespace CV__SIMD_NAMESPACE;
#elif CV_SIMD_SCALABLE
#define CV__SIMD_NAMESPACE simd
namespace CV__SIMD_NAMESPACE {
#define CV_SIMD 0
#define CV_SIMD_WIDTH 128 /* 1024/8 */
#define VXPREFIX(func) v##func
} // namespace
using namespace CV__SIMD_NAMESPACE;
#endif
namespace CV__SIMD_NAMESPACE {
@@ -663,6 +695,180 @@ namespace CV__SIMD_NAMESPACE {
/** @brief SIMD processing state cleanup call */
inline void vx_cleanup() { VXPREFIX(_cleanup)(); }
#if !CV_SIMD_SCALABLE
// Compatibility layer
template<typename T> struct VTraits {
static inline int vlanes() { return T::nlanes; }
enum { nlanes = T::nlanes, max_nlanes = T::nlanes };
using lane_type = typename T::lane_type;
};
#define OPENCV_HAL_WRAP_BIN_OP_ADDSUB(_Tpvec) \
inline _Tpvec v_add(const _Tpvec& a, const _Tpvec& b) \
{ \
return a + b; \
} \
inline _Tpvec v_sub(const _Tpvec& a, const _Tpvec& b) \
{ \
return a - b; \
} \
template<typename... Args> \
inline _Tpvec v_add(const _Tpvec& f1, const _Tpvec& f2, const Args&... vf) { \
return v_add(f1 + f2, vf...); \
}
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_uint8)
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_uint16)
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_uint32)
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_uint64)
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_int8)
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_int16)
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_int32)
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_int64)
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_float32)
#if CV_SIMD_64F
OPENCV_HAL_WRAP_BIN_OP_ADDSUB(v_float64)
#endif
#define OPENCV_HAL_WRAP_BIN_OP_LOGIC(_Tpvec) \
inline _Tpvec v_and(const _Tpvec& a, const _Tpvec& b) \
{ \
return a & b; \
} \
inline _Tpvec v_or(const _Tpvec& a, const _Tpvec& b) \
{ \
return a | b; \
} \
inline _Tpvec v_xor(const _Tpvec& a, const _Tpvec& b) \
{ \
return a ^ b; \
} \
inline _Tpvec v_not(const _Tpvec& a) \
{ \
return ~a; \
}
OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_uint8)
OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_uint16)
OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_uint32)
OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_uint64)
OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_int8)
OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_int16)
OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_int32)
OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_int64)
#define OPENCV_HAL_WRAP_BIN_OP_MUL(_Tpvec) \
inline _Tpvec v_mul(const _Tpvec& a, const _Tpvec& b) \
{ \
return a * b; \
} \
template<typename... Args> \
inline _Tpvec v_mul(const _Tpvec& f1, const _Tpvec& f2, const Args&... vf) { \
return v_mul(f1 * f2, vf...); \
}
OPENCV_HAL_WRAP_BIN_OP_MUL(v_uint8)
OPENCV_HAL_WRAP_BIN_OP_MUL(v_int8)
OPENCV_HAL_WRAP_BIN_OP_MUL(v_uint16)
OPENCV_HAL_WRAP_BIN_OP_MUL(v_uint32)
OPENCV_HAL_WRAP_BIN_OP_MUL(v_int16)
OPENCV_HAL_WRAP_BIN_OP_MUL(v_int32)
OPENCV_HAL_WRAP_BIN_OP_MUL(v_float32)
#if CV_SIMD_64F
OPENCV_HAL_WRAP_BIN_OP_MUL(v_float64)
#endif
inline v_float32 v_div(const v_float32& a, const v_float32& b) \
{ \
return a / b; \
}
#if CV_SIMD_64F
inline v_float64 v_div(const v_float64& a, const v_float64& b) \
{ \
return a / b; \
}
#endif
#define OPENCV_HAL_WRAP_CMP_OP(_Tpvec, intrin, op) \
inline _Tpvec v_##intrin(const _Tpvec& a, const _Tpvec& b) \
{ \
return a op b; \
}
#define OPENCV_HAL_WRAP_CMP(_Tpvec) \
OPENCV_HAL_WRAP_CMP_OP(_Tpvec, eq, ==) \
OPENCV_HAL_WRAP_CMP_OP(_Tpvec, ne, !=) \
OPENCV_HAL_WRAP_CMP_OP(_Tpvec, lt, <) \
OPENCV_HAL_WRAP_CMP_OP(_Tpvec, gt, >) \
OPENCV_HAL_WRAP_CMP_OP(_Tpvec, le, <=) \
OPENCV_HAL_WRAP_CMP_OP(_Tpvec, ge, >=)
OPENCV_HAL_WRAP_CMP(v_uint8)
OPENCV_HAL_WRAP_CMP(v_uint16)
OPENCV_HAL_WRAP_CMP(v_uint32)
// OPENCV_HAL_WRAP_CMP(v_uint64)
OPENCV_HAL_WRAP_CMP(v_int8)
OPENCV_HAL_WRAP_CMP(v_int16)
OPENCV_HAL_WRAP_CMP(v_int32)
// OPENCV_HAL_WRAP_CMP(v_int64)
OPENCV_HAL_WRAP_CMP(v_float32)
#if CV_SIMD_64F
OPENCV_HAL_WRAP_CMP(v_float64)
#endif
//////////// get0 ////////////
#define OPENCV_HAL_WRAP_GRT0_INT(_Tpvec, _Tp) \
inline _Tp v_get0(const v_##_Tpvec& v) \
{ \
return v.get0(); \
}
OPENCV_HAL_WRAP_GRT0_INT(uint8, uchar)
OPENCV_HAL_WRAP_GRT0_INT(int8, schar)
OPENCV_HAL_WRAP_GRT0_INT(uint16, ushort)
OPENCV_HAL_WRAP_GRT0_INT(int16, short)
OPENCV_HAL_WRAP_GRT0_INT(uint32, unsigned)
OPENCV_HAL_WRAP_GRT0_INT(int32, int)
OPENCV_HAL_WRAP_GRT0_INT(uint64, uint64)
OPENCV_HAL_WRAP_GRT0_INT(int64, int64)
OPENCV_HAL_WRAP_GRT0_INT(float32, float)
#if CV_SIMD_64F
OPENCV_HAL_WRAP_GRT0_INT(float64, double)
#endif
#define OPENCV_HAL_WRAP_EXTRACT(_Tpvec, _Tp, vl) \
inline _Tp v_extract_highest(const _Tpvec& v) \
{ \
return v_extract_n<vl-1>(v); \
}
OPENCV_HAL_WRAP_EXTRACT(v_uint8, uchar, VTraits<v_uint8>::nlanes)
OPENCV_HAL_WRAP_EXTRACT(v_int8, schar, VTraits<v_int8>::nlanes)
OPENCV_HAL_WRAP_EXTRACT(v_uint16, ushort, VTraits<v_uint16>::nlanes)
OPENCV_HAL_WRAP_EXTRACT(v_int16, short, VTraits<v_int16>::nlanes)
OPENCV_HAL_WRAP_EXTRACT(v_uint32, unsigned int, VTraits<v_uint32>::nlanes)
OPENCV_HAL_WRAP_EXTRACT(v_int32, int, VTraits<v_int32>::nlanes)
OPENCV_HAL_WRAP_EXTRACT(v_uint64, uint64, VTraits<v_uint64>::nlanes)
OPENCV_HAL_WRAP_EXTRACT(v_int64, int64, VTraits<v_int64>::nlanes)
OPENCV_HAL_WRAP_EXTRACT(v_float32, float, VTraits<v_float32>::nlanes)
#if CV_SIMD_64F
OPENCV_HAL_WRAP_EXTRACT(v_float64, double, VTraits<v_float64>::nlanes)
#endif
#define OPENCV_HAL_WRAP_BROADCAST(_Tpvec) \
inline _Tpvec v_broadcast_highest(const _Tpvec& v) \
{ \
return v_broadcast_element<VTraits<_Tpvec>::nlanes-1>(v); \
}
OPENCV_HAL_WRAP_BROADCAST(v_uint32)
OPENCV_HAL_WRAP_BROADCAST(v_int32)
OPENCV_HAL_WRAP_BROADCAST(v_float32)
#endif //!CV_SIMD_SCALABLE
//! @cond IGNORED
@@ -48,6 +48,7 @@
#include <limits>
#include <cstring>
#include <algorithm>
#include "opencv2/core/utility.hpp"
#include "opencv2/core/saturate.hpp"
//! @cond IGNORED
@@ -78,8 +78,6 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN
#define CV_NEON_AARCH64 0
#endif
// TODO
#define CV_NEON_DOT 0
//////////// Utils ////////////
@@ -665,11 +663,22 @@ inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64
}
// 8 >> 32
#ifdef CV_NEON_DOT
#define OPENCV_HAL_IMPL_NEON_DOT_PRODUCT_OP(_Tpvec1, _Tpvec2, suffix) \
inline _Tpvec1 v_dotprod_expand(const _Tpvec2& a, const _Tpvec2& b) \
{ \
return _Tpvec1(vdotq_##suffix(vdupq_n_##suffix(0), a.val, b.val));\
} \
inline _Tpvec1 v_dotprod_expand(const _Tpvec2& a, const _Tpvec2& b, const _Tpvec1& c) \
{ \
return _Tpvec1(vdotq_##suffix(c.val, a.val, b.val)); \
}
OPENCV_HAL_IMPL_NEON_DOT_PRODUCT_OP(v_uint32x4, v_uint8x16, u32)
OPENCV_HAL_IMPL_NEON_DOT_PRODUCT_OP(v_int32x4, v_int8x16, s32)
#else
inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b)
{
#if CV_NEON_DOT
return v_uint32x4(vdotq_u32(vdupq_n_u32(0), a.val, b.val));
#else
const uint8x16_t zero = vreinterpretq_u8_u32(vdupq_n_u32(0));
const uint8x16_t mask = vreinterpretq_u8_u32(vdupq_n_u32(0x00FF00FF));
const uint16x8_t zero32 = vreinterpretq_u16_u32(vdupq_n_u32(0));
@@ -685,23 +694,15 @@ inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b)
uint32x4_t s1 = vaddq_u32(vshrq_n_u32(vreinterpretq_u32_u16(even), 16),
vshrq_n_u32(vreinterpretq_u32_u16(odd), 16));
return v_uint32x4(vaddq_u32(s0, s1));
#endif
}
inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b,
const v_uint32x4& c)
{
#if CV_NEON_DOT
return v_uint32x4(vdotq_u32(c.val, a.val, b.val));
#else
return v_dotprod_expand(a, b) + c;
#endif
}
inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b)
{
#if CV_NEON_DOT
return v_int32x4(vdotq_s32(vdupq_n_s32(0), a.val, b.val));
#else
int16x8_t p0 = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val));
int16x8_t p1 = vmull_s8(vget_high_s8(a.val), vget_high_s8(b.val));
int16x8_t uzp1, uzp2;
@@ -710,18 +711,13 @@ inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b)
int16x4_t uzpl1, uzpl2;
_v128_unzip(vget_low_s16(sum), vget_high_s16(sum), uzpl1, uzpl2);
return v_int32x4(vaddl_s16(uzpl1, uzpl2));
#endif
}
inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b,
const v_int32x4& c)
{
#if CV_NEON_DOT
return v_int32x4(vdotq_s32(c.val, a.val, b.val));
#else
return v_dotprod_expand(a, b) + c;
#endif
}
#endif
// 16 >> 64
inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b)
{
@@ -830,45 +826,44 @@ inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_
}
// 8 >> 32
#ifdef CV_NEON_DOT
#define OPENCV_HAL_IMPL_NEON_DOT_PRODUCT_FAST_OP(_Tpvec1, _Tpvec2, suffix) \
inline _Tpvec1 v_dotprod_expand_fast(const _Tpvec2& a, const _Tpvec2& b) \
{ \
return v_dotprod_expand(a, b); \
} \
inline _Tpvec1 v_dotprod_expand_fast(const _Tpvec2& a, const _Tpvec2& b, const _Tpvec1& c) \
{ \
return v_dotprod_expand(a, b, c); \
}
OPENCV_HAL_IMPL_NEON_DOT_PRODUCT_FAST_OP(v_uint32x4, v_uint8x16, u32)
OPENCV_HAL_IMPL_NEON_DOT_PRODUCT_FAST_OP(v_int32x4, v_int8x16, s32)
#else
inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b)
{
#if CV_NEON_DOT
return v_uint32x4(vdotq_u32(vdupq_n_u32(0), a.val, b.val));
#else
uint16x8_t p0 = vmull_u8(vget_low_u8(a.val), vget_low_u8(b.val));
uint16x8_t p1 = vmull_u8(vget_high_u8(a.val), vget_high_u8(b.val));
uint32x4_t s0 = vaddl_u16(vget_low_u16(p0), vget_low_u16(p1));
uint32x4_t s1 = vaddl_u16(vget_high_u16(p0), vget_high_u16(p1));
return v_uint32x4(vaddq_u32(s0, s1));
#endif
}
inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c)
{
#if CV_NEON_DOT
return v_uint32x4(vdotq_u32(c.val, a.val, b.val));
#else
return v_dotprod_expand_fast(a, b) + c;
#endif
}
inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b)
{
#if CV_NEON_DOT
return v_int32x4(vdotq_s32(vdupq_n_s32(0), a.val, b.val));
#else
int16x8_t prod = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val));
prod = vmlal_s8(prod, vget_high_s8(a.val), vget_high_s8(b.val));
return v_int32x4(vaddl_s16(vget_low_s16(prod), vget_high_s16(prod)));
#endif
}
inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c)
{
#if CV_NEON_DOT
return v_int32x4(vdotq_s32(c.val, a.val, b.val));
#else
return v_dotprod_expand_fast(a, b) + c;
#endif
}
#endif
// 16 >> 64
inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b)
@@ -0,0 +1,493 @@
#ifndef OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP
#define OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP
#include <initializer_list>
#include <assert.h>
#include <vector>
#ifndef CV_RVV_MAX_VLEN
#define CV_RVV_MAX_VLEN 1024
#endif
namespace cv
{
CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN
#define CV_SIMD_SCALABLE 1
#define CV_SIMD_SCALABLE_64F 1
using v_uint8 = vuint8m1_t;
using v_int8 = vint8m1_t;
using v_uint16 = vuint16m1_t;
using v_int16 = vint16m1_t;
using v_uint32 = vuint32m1_t;
using v_int32 = vint32m1_t;
using v_uint64 = vuint64m1_t;
using v_int64 = vint64m1_t;
using v_float32 = vfloat32m1_t;
#if CV_SIMD_SCALABLE_64F
using v_float64 = vfloat64m1_t;
#endif
using uchar = unsigned char;
using schar = signed char;
using ushort = unsigned short;
using uint = unsigned int;
using uint64 = unsigned long int;
using int64 = long int;
static const int __cv_rvv_e8_nlanes = vsetvlmax_e8m1();
static const int __cv_rvv_e16_nlanes = vsetvlmax_e16m1();
static const int __cv_rvv_e32_nlanes = vsetvlmax_e32m1();
static const int __cv_rvv_e64_nlanes = vsetvlmax_e64m1();
template <class T>
struct VTraits;
template <>
struct VTraits<v_uint8>
{
static inline int vlanes() { return __cv_rvv_e8_nlanes; }
using lane_type = uchar;
static const int max_nlanes = CV_RVV_MAX_VLEN/8;
};
template <>
struct VTraits<v_int8>
{
static inline int vlanes() { return __cv_rvv_e8_nlanes; }
using lane_type = schar;
static const int max_nlanes = CV_RVV_MAX_VLEN/8;
};
template <>
struct VTraits<v_uint16>
{
static inline int vlanes() { return __cv_rvv_e16_nlanes; }
using lane_type = ushort;
static const int max_nlanes = CV_RVV_MAX_VLEN/16;
};
template <>
struct VTraits<v_int16>
{
static inline int vlanes() { return __cv_rvv_e16_nlanes; }
using lane_type = short;
static const int max_nlanes = CV_RVV_MAX_VLEN/16;
};
template <>
struct VTraits<v_uint32>
{
static inline int vlanes() { return __cv_rvv_e32_nlanes; }
using lane_type = uint;
static const int max_nlanes = CV_RVV_MAX_VLEN/32;
};
template <>
struct VTraits<v_int32>
{
static inline int vlanes() { return __cv_rvv_e32_nlanes; }
using lane_type = int;
static const int max_nlanes = CV_RVV_MAX_VLEN/32;
};
template <>
struct VTraits<v_float32>
{
static inline int vlanes() { return __cv_rvv_e32_nlanes; }
using lane_type = float;
static const int max_nlanes = CV_RVV_MAX_VLEN/32;
};
template <>
struct VTraits<v_uint64>
{
static inline int vlanes() { return __cv_rvv_e64_nlanes; }
using lane_type = uint64;
static const int max_nlanes = CV_RVV_MAX_VLEN/64;
};
template <>
struct VTraits<v_int64>
{
static inline int vlanes() { return __cv_rvv_e64_nlanes; }
using lane_type = int64;
static const int max_nlanes = CV_RVV_MAX_VLEN/64;
};
#if CV_SIMD_SCALABLE_64F
template <>
struct VTraits<v_float64>
{
static inline int vlanes() { return __cv_rvv_e64_nlanes; }
using lane_type = double;
static const int max_nlanes = CV_RVV_MAX_VLEN/64;
};
#endif
//////////// get0 ////////////
#define OPENCV_HAL_IMPL_RVV_GRT0_INT(_Tpvec, _Tp) \
inline _Tp v_get0(const v_##_Tpvec& v) \
{ \
return vmv_x(v); \
}
OPENCV_HAL_IMPL_RVV_GRT0_INT(uint8, uchar)
OPENCV_HAL_IMPL_RVV_GRT0_INT(int8, schar)
OPENCV_HAL_IMPL_RVV_GRT0_INT(uint16, ushort)
OPENCV_HAL_IMPL_RVV_GRT0_INT(int16, short)
OPENCV_HAL_IMPL_RVV_GRT0_INT(uint32, unsigned)
OPENCV_HAL_IMPL_RVV_GRT0_INT(int32, int)
OPENCV_HAL_IMPL_RVV_GRT0_INT(uint64, uint64)
OPENCV_HAL_IMPL_RVV_GRT0_INT(int64, int64)
inline float v_get0(const v_float32& v) \
{ \
return vfmv_f(v); \
}
#if CV_SIMD_SCALABLE_64F
inline double v_get0(const v_float64& v) \
{ \
return vfmv_f(v); \
}
#endif
//////////// Initial ////////////
#define OPENCV_HAL_IMPL_RVV_INIT_INTEGER(_Tpvec, _Tp, suffix1, suffix2, vl) \
inline v_##_Tpvec v_setzero_##suffix1() \
{ \
return vmv_v_x_##suffix2##m1(0, vl); \
} \
inline v_##_Tpvec v_setall_##suffix1(_Tp v) \
{ \
return vmv_v_x_##suffix2##m1(v, vl); \
}
OPENCV_HAL_IMPL_RVV_INIT_INTEGER(uint8, uchar, u8, u8, VTraits<v_uint8>::vlanes())
OPENCV_HAL_IMPL_RVV_INIT_INTEGER(int8, schar, s8, i8, VTraits<v_int8>::vlanes())
OPENCV_HAL_IMPL_RVV_INIT_INTEGER(uint16, ushort, u16, u16, VTraits<v_uint16>::vlanes())
OPENCV_HAL_IMPL_RVV_INIT_INTEGER(int16, short, s16, i16, VTraits<v_int16>::vlanes())
OPENCV_HAL_IMPL_RVV_INIT_INTEGER(uint32, uint, u32, u32, VTraits<v_uint32>::vlanes())
OPENCV_HAL_IMPL_RVV_INIT_INTEGER(int32, int, s32, i32, VTraits<v_int32>::vlanes())
OPENCV_HAL_IMPL_RVV_INIT_INTEGER(uint64, uint64, u64, u64, VTraits<v_uint64>::vlanes())
OPENCV_HAL_IMPL_RVV_INIT_INTEGER(int64, int64, s64, i64, VTraits<v_int64>::vlanes())
#define OPENCV_HAL_IMPL_RVV_INIT_FP(_Tpv, _Tp, suffix, vl) \
inline v_##_Tpv v_setzero_##suffix() \
{ \
return vfmv_v_f_##suffix##m1(0, vl); \
} \
inline v_##_Tpv v_setall_##suffix(_Tp v) \
{ \
return vfmv_v_f_##suffix##m1(v, vl); \
}
OPENCV_HAL_IMPL_RVV_INIT_FP(float32, float, f32, VTraits<v_float32>::vlanes())
#if CV_SIMD_SCALABLE_64F
OPENCV_HAL_IMPL_RVV_INIT_FP(float64, double, f64, VTraits<v_float64>::vlanes())
#endif
//////////// Reinterpret ////////////
#define OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(_Tpvec1, suffix1) \
inline v_##_Tpvec1 v_reinterpret_as_##suffix1(const v_##_Tpvec1& v) \
{ \
return v;\
}
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(uint8, u8)
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(uint16, u16)
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(uint32, u32)
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(uint64, u64)
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(int8, s8)
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(int16, s16)
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(int32, s32)
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(int64, s64)
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(float32, f32)
#if CV_SIMD_SCALABLE_64F
OPENCV_HAL_IMPL_RVV_NOTHING_REINTERPRET(float64, f64)
#endif
// TODO: can be simplified by using overloaded RV intrinsic
#define OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(_Tpvec1, _Tpvec2, suffix1, suffix2, nsuffix1, nsuffix2) \
inline v_##_Tpvec1 v_reinterpret_as_##suffix1(const v_##_Tpvec2& v) \
{ \
return v_##_Tpvec1(vreinterpret_v_##nsuffix2##m1_##nsuffix1##m1(v));\
} \
inline v_##_Tpvec2 v_reinterpret_as_##suffix2(const v_##_Tpvec1& v) \
{ \
return v_##_Tpvec2(vreinterpret_v_##nsuffix1##m1_##nsuffix2##m1(v));\
}
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint8, int8, u8, s8, u8, i8)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint16, int16, u16, s16, u16, i16)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint32, int32, u32, s32, u32, i32)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint32, float32, u32, f32, u32, f32)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(int32, float32, s32, f32, i32, f32)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint64, int64, u64, s64, u64, i64)
#if CV_SIMD_SCALABLE_64F
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint64, float64, u64, f64, u64, f64)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(int64, float64, s64, f64, i64, f64)
#endif
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint8, uint16, u8, u16, u8, u16)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint8, uint32, u8, u32, u8, u32)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint8, uint64, u8, u64, u8, u64)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint16, uint32, u16, u32, u16, u32)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint16, uint64, u16, u64, u16, u64)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(uint32, uint64, u32, u64, u32, u64)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(int8, int16, s8, s16, i8, i16)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(int8, int32, s8, s32, i8, i32)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(int8, int64, s8, s64, i8, i64)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(int16, int32, s16, s32, i16, i32)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(int16, int64, s16, s64, i16, i64)
OPENCV_HAL_IMPL_RVV_NATIVE_REINTERPRET(int32, int64, s32, s64, i32, i64)
#define OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(_Tpvec1, _Tpvec2, suffix1, suffix2, nsuffix1, nsuffix2, width1, width2) \
inline v_##_Tpvec1 v_reinterpret_as_##suffix1(const v_##_Tpvec2& v) \
{ \
return vreinterpret_v_##nsuffix1##width2##m1_##nsuffix1##width1##m1(vreinterpret_v_##nsuffix2##width2##m1_##nsuffix1##width2##m1(v));\
} \
inline v_##_Tpvec2 v_reinterpret_as_##suffix2(const v_##_Tpvec1& v) \
{ \
return vreinterpret_v_##nsuffix1##width2##m1_##nsuffix2##width2##m1(vreinterpret_v_##nsuffix1##width1##m1_##nsuffix1##width2##m1(v));\
}
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint8, int16, u8, s16, u, i, 8, 16)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint8, int32, u8, s32, u, i, 8, 32)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint8, int64, u8, s64, u, i, 8, 64)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint16, int8, u16, s8, u, i, 16, 8)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint16, int32, u16, s32, u, i, 16, 32)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint16, int64, u16, s64, u, i, 16, 64)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint32, int8, u32, s8, u, i, 32, 8)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint32, int16, u32, s16, u, i, 32, 16)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint32, int64, u32, s64, u, i, 32, 64)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint64, int8, u64, s8, u, i, 64, 8)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint64, int16, u64, s16, u, i, 64, 16)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint64, int32, u64, s32, u, i, 64, 32)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint8, float32, u8, f32, u, f, 8, 32)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint16, float32, u16, f32, u, f, 16, 32)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint64, float32, u64, f32, u, f, 64, 32)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(int8, float32, s8, f32, i, f, 8, 32)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(int16, float32, s16, f32, i, f, 16, 32)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(int64, float32, s64, f32, i, f, 64, 32)
#if CV_SIMD_SCALABLE_64F
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint8, float64, u8, f64, u, f, 8, 64)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint16, float64, u16, f64, u, f, 16, 64)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(uint32, float64, u32, f64, u, f, 32, 64)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(int8, float64, s8, f64, i, f, 8, 64)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(int16, float64, s16, f64, i, f, 16, 64)
OPENCV_HAL_IMPL_RVV_TWO_TIMES_REINTERPRET(int32, float64, s32, f64, i, f, 32, 64)
// Three times reinterpret
inline v_float32 v_reinterpret_as_f32(const v_float64& v) \
{ \
return vreinterpret_v_u32m1_f32m1(vreinterpret_v_u64m1_u32m1(vreinterpret_v_f64m1_u64m1(v)));\
}
inline v_float64 v_reinterpret_as_f64(const v_float32& v) \
{ \
return vreinterpret_v_u64m1_f64m1(vreinterpret_v_u32m1_u64m1(vreinterpret_v_f32m1_u32m1(v)));\
}
#endif
////////////// Load/Store //////////////
#define OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(_Tpvec, _nTpvec, _Tp, hvl, vl, width, suffix, vmv) \
inline _Tpvec v_load(const _Tp* ptr) \
{ \
return vle##width##_v_##suffix##m1(ptr, vl); \
} \
inline _Tpvec v_load_aligned(const _Tp* ptr) \
{ \
return vle##width##_v_##suffix##m1(ptr, vl); \
} \
inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode /*mode*/) \
{ \
vse##width##_v_##suffix##m1(ptr, a, vl); \
} \
inline _Tpvec v_load_low(const _Tp* ptr) \
{ \
return vle##width##_v_##suffix##m1(ptr, hvl); \
} \
inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \
{ \
return vslideup(vle##width##_v_##suffix##m1(ptr0, hvl), vle##width##_v_##suffix##m1(ptr1, hvl), hvl, vl); \
} \
inline void v_store(_Tp* ptr, const _Tpvec& a) \
{ \
vse##width(ptr, a, vl); \
} \
inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \
{ \
vse##width(ptr, a, vl); \
} \
inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \
{ \
vse##width(ptr, a, vl); \
} \
inline void v_store_low(_Tp* ptr, const _Tpvec& a) \
{ \
vse##width(ptr, a, hvl); \
} \
inline void v_store_high(_Tp* ptr, const _Tpvec& a) \
{ \
vse##width(ptr, vslidedown_vx_##suffix##m1(vmv(0, vl), a, hvl, vl), hvl); \
} \
inline _Tpvec v_load(std::initializer_list<_Tp> nScalars) \
{ \
assert(nScalars.size() == vl); \
return vle##width##_v_##suffix##m1(nScalars.begin(), nScalars.size()); \
} \
template<typename... Targs> \
_Tpvec v_load_##suffix(Targs... nScalars) \
{ \
return v_load({nScalars...}); \
}
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_uint8, vuint8m1_t, uchar, VTraits<v_uint8>::vlanes() / 2, VTraits<v_uint8>::vlanes(), 8, u8, vmv_v_x_u8m1)
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_int8, vint8m1_t, schar, VTraits<v_int8>::vlanes() / 2, VTraits<v_int8>::vlanes(), 8, i8, vmv_v_x_i8m1)
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_uint16, vuint16m1_t, ushort, VTraits<v_uint16>::vlanes() / 2, VTraits<v_uint16>::vlanes(), 16, u16, vmv_v_x_u16m1)
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_int16, vint16m1_t, short, VTraits<v_int16>::vlanes() / 2, VTraits<v_int16>::vlanes(), 16, i16, vmv_v_x_i16m1)
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_uint32, vuint32m1_t, unsigned int, VTraits<v_uint32>::vlanes() / 2, VTraits<v_uint32>::vlanes(), 32, u32, vmv_v_x_u32m1)
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_int32, vint32m1_t, int, VTraits<v_int32>::vlanes() / 2, VTraits<v_int32>::vlanes(), 32, i32, vmv_v_x_i32m1)
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_uint64, vuint64m1_t, uint64, VTraits<v_uint64>::vlanes() / 2, VTraits<v_uint64>::vlanes(), 64, u64, vmv_v_x_u64m1)
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_int64, vint64m1_t, int64, VTraits<v_int64>::vlanes() / 2, VTraits<v_int64>::vlanes(), 64, i64, vmv_v_x_i64m1)
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_float32, vfloat32m1_t, float, VTraits<v_float32>::vlanes() /2 , VTraits<v_float32>::vlanes(), 32, f32, vfmv_v_f_f32m1)
#if CV_SIMD_SCALABLE_64F
OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_float64, vfloat64m1_t, double, VTraits<v_float64>::vlanes() / 2, VTraits<v_float64>::vlanes(), 64, f64, vfmv_v_f_f64m1)
#endif
////////////// Lookup table access ////////////////////
#define OPENCV_HAL_IMPL_RVV_LUT(_Tpvec, _Tp, suffix) \
inline _Tpvec v_lut(const _Tp* tab, const int* idx) \
{ \
vuint32##suffix##_t vidx = vmul(vreinterpret_u32##suffix(vle32_v_i32##suffix(idx, VTraits<_Tpvec>::vlanes())), sizeof(_Tp), VTraits<_Tpvec>::vlanes()); \
return vloxei32(tab, vidx, VTraits<_Tpvec>::vlanes()); \
} \
inline _Tpvec v_lut_pairs(const _Tp* tab, const int* idx) \
{ \
std::vector<uint> idx_; \
for (size_t i = 0; i < VTraits<v_int16>::vlanes(); ++i) { \
idx_.push_back(idx[i]); \
idx_.push_back(idx[i]+1); \
} \
vuint32##suffix##_t vidx = vmul(vle32_v_u32##suffix(idx_.data(), VTraits<_Tpvec>::vlanes()), sizeof(_Tp), VTraits<_Tpvec>::vlanes()); \
return vloxei32(tab, vidx, VTraits<_Tpvec>::vlanes()); \
} \
inline _Tpvec v_lut_quads(const _Tp* tab, const int* idx) \
{ \
std::vector<uint> idx_; \
for (size_t i = 0; i < VTraits<v_int32>::vlanes(); ++i) { \
idx_.push_back(idx[i]); \
idx_.push_back(idx[i]+1); \
idx_.push_back(idx[i]+2); \
idx_.push_back(idx[i]+3); \
} \
vuint32##suffix##_t vidx = vmul(vle32_v_u32##suffix(idx_.data(), VTraits<_Tpvec>::vlanes()), sizeof(_Tp), VTraits<_Tpvec>::vlanes()); \
return vloxei32(tab, vidx, VTraits<_Tpvec>::vlanes()); \
}
OPENCV_HAL_IMPL_RVV_LUT(v_int8, schar, m4)
OPENCV_HAL_IMPL_RVV_LUT(v_int16, short, m2)
OPENCV_HAL_IMPL_RVV_LUT(v_int32, int, m1)
OPENCV_HAL_IMPL_RVV_LUT(v_int64, int64_t, mf2)
OPENCV_HAL_IMPL_RVV_LUT(v_float32, float, m1)
inline v_uint8 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); }
inline v_uint8 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
inline v_uint8 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); }
inline v_uint16 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((short*)tab, idx)); }
inline v_uint16 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((short*)tab, idx)); }
inline v_uint16 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((short*)tab, idx)); }
inline v_uint32 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((int*)tab, idx)); }
inline v_uint32 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((int*)tab, idx)); }
inline v_uint32 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((int*)tab, idx)); }
inline v_uint64 v_lut(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); }
inline v_uint64 v_lut_pairs(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); }
inline v_uint64 v_lut_quads(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_quads((const int64_t*)tab, idx)); }
////////////// Min/Max //////////////
#define OPENCV_HAL_IMPL_RVV_BIN_FUNC(_Tpvec, func, intrin, vl) \
inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \
{ \
return intrin(a, b, vl); \
}
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint8, v_min, vminu, VTraits<v_uint8>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint8, v_max, vmaxu, VTraits<v_uint8>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int8, v_min, vmin, VTraits<v_int8>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int8, v_max, vmax, VTraits<v_int8>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint16, v_min, vminu, VTraits<v_uint16>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint16, v_max, vmaxu, VTraits<v_uint16>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int16, v_min, vmin, VTraits<v_int16>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int16, v_max, vmax, VTraits<v_int16>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint32, v_min, vminu, VTraits<v_uint32>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint32, v_max, vmaxu, VTraits<v_uint32>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int32, v_min, vmin, VTraits<v_int32>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int32, v_max, vmax, VTraits<v_int32>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float32, v_min, vfmin, VTraits<v_float32>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float32, v_max, vfmax, VTraits<v_float32>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint64, v_min, vminu, VTraits<v_uint64>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint64, v_max, vmaxu, VTraits<v_uint64>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int64, v_min, vmin, VTraits<v_int64>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int64, v_max, vmax, VTraits<v_int64>::vlanes())
#if CV_SIMD_SCALABLE_64F
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float64, v_min, vfmin, VTraits<v_float64>::vlanes())
OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float64, v_max, vfmax, VTraits<v_float64>::vlanes())
#endif
//////////// Value reordering ////////////
#define OPENCV_HAL_IMPL_RVV_EXPAND(_Tp, _Tpwvec, _Tpwvec_m2, _Tpvec, width, suffix, suffix2, cvt) \
inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \
{ \
_Tpwvec_m2 temp = cvt(a, vsetvlmax_e##width##m1()); \
b0 = vget_##suffix##m1(temp, 0); \
b1 = vget_##suffix##m1(temp, 1); \
} \
inline _Tpwvec v_expand_low(const _Tpvec& a) \
{ \
_Tpwvec_m2 temp = cvt(a, vsetvlmax_e##width##m1()); \
return vget_##suffix##m1(temp, 0); \
} \
inline _Tpwvec v_expand_high(const _Tpvec& a) \
{ \
_Tpwvec_m2 temp = cvt(a, vsetvlmax_e##width##m1()); \
return vget_##suffix##m1(temp, 1); \
} \
inline _Tpwvec v_load_expand(const _Tp* ptr) \
{ \
return cvt(vle##width##_v_##suffix2##mf2(ptr, vsetvlmax_e##width##m1()), vsetvlmax_e##width##m1()); \
}
OPENCV_HAL_IMPL_RVV_EXPAND(uchar, v_uint16, vuint16m2_t, v_uint8, 8, u16, u8, vwcvtu_x)
OPENCV_HAL_IMPL_RVV_EXPAND(schar, v_int16, vint16m2_t, v_int8, 8, i16, i8, vwcvt_x)
OPENCV_HAL_IMPL_RVV_EXPAND(ushort, v_uint32, vuint32m2_t, v_uint16, 16, u32, u16, vwcvtu_x)
OPENCV_HAL_IMPL_RVV_EXPAND(short, v_int32, vint32m2_t, v_int16, 16, i32, i16, vwcvt_x)
OPENCV_HAL_IMPL_RVV_EXPAND(uint, v_uint64, vuint64m2_t, v_uint32, 32, u64, u32, vwcvtu_x)
OPENCV_HAL_IMPL_RVV_EXPAND(int, v_int64, vint64m2_t, v_int32, 32, i64, i32, vwcvt_x)
inline v_uint32 v_load_expand_q(const uchar* ptr)
{
return vwcvtu_x(vwcvtu_x(vle8_v_u8mf4(ptr, VTraits<v_uint32>::vlanes()), VTraits<v_uint32>::vlanes()), VTraits<v_uint32>::vlanes());
}
inline v_int32 v_load_expand_q(const schar* ptr)
{
return vwcvt_x(vwcvt_x(vle8_v_i8mf4(ptr, VTraits<v_int32>::vlanes()), VTraits<v_int32>::vlanes()), VTraits<v_int32>::vlanes());
}
////// FP16 support ///////
inline v_float32 v_load_expand(const float16_t* ptr)
{
// TODO
return vundefined_f32m1();
}
inline void v_cleanup() {}
CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END
} //namespace cv
#endif //OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP
@@ -128,8 +128,48 @@ template<> inline Type2Vec512_Traits<double>::vec_type v512_setall<double>(const
#endif // SIMD512
#if CV_SIMD_SCALABLE
template<typename _T> struct Type2Vec_Traits;
#define CV_INTRIN_DEF_TYPE2VEC_TRAITS(type_, vec_type_) \
template<> struct Type2Vec_Traits<type_> \
{ \
typedef vec_type_ vec_type; \
}
#if CV_SIMD_WIDTH == 16
CV_INTRIN_DEF_TYPE2VEC_TRAITS(uchar, v_uint8);
CV_INTRIN_DEF_TYPE2VEC_TRAITS(schar, v_int8);
CV_INTRIN_DEF_TYPE2VEC_TRAITS(ushort, v_uint16);
CV_INTRIN_DEF_TYPE2VEC_TRAITS(short, v_int16);
CV_INTRIN_DEF_TYPE2VEC_TRAITS(unsigned, v_uint32);
CV_INTRIN_DEF_TYPE2VEC_TRAITS(int, v_int32);
CV_INTRIN_DEF_TYPE2VEC_TRAITS(float, v_float32);
CV_INTRIN_DEF_TYPE2VEC_TRAITS(uint64, v_uint64);
CV_INTRIN_DEF_TYPE2VEC_TRAITS(int64, v_int64);
#if CV_SIMD_SCALABLE_64F
CV_INTRIN_DEF_TYPE2VEC_TRAITS(double, v_float64);
#endif
template<typename _T> static inline
typename Type2Vec_Traits<_T>::vec_type v_setall(const _T& a);
template<> inline Type2Vec_Traits< uchar>::vec_type v_setall< uchar>(const uchar& a) { return v_setall_u8(a); }
template<> inline Type2Vec_Traits< schar>::vec_type v_setall< schar>(const schar& a) { return v_setall_s8(a); }
template<> inline Type2Vec_Traits<ushort>::vec_type v_setall<ushort>(const ushort& a) { return v_setall_u16(a); }
template<> inline Type2Vec_Traits< short>::vec_type v_setall< short>(const short& a) { return v_setall_s16(a); }
template<> inline Type2Vec_Traits< uint>::vec_type v_setall< uint>(const uint& a) { return v_setall_u32(a); }
template<> inline Type2Vec_Traits< int>::vec_type v_setall< int>(const int& a) { return v_setall_s32(a); }
template<> inline Type2Vec_Traits<uint64>::vec_type v_setall<uint64>(const uint64& a) { return v_setall_u64(a); }
template<> inline Type2Vec_Traits< int64>::vec_type v_setall< int64>(const int64& a) { return v_setall_s64(a); }
template<> inline Type2Vec_Traits< float>::vec_type v_setall< float>(const float& a) { return v_setall_f32(a); }
#if CV_SIMD_SCALABLE_64F
template<> inline Type2Vec_Traits<double>::vec_type v_setall<double>(const double& a) { return v_setall_f64(a); }
#endif
#endif
#if CV_SIMD_SCALABLE
template<typename _T> static inline
typename Type2Vec_Traits<_T>::vec_type vx_setall(const _T& a) { return v_setall(a); }
#elif CV_SIMD_WIDTH == 16
template<typename _T> static inline
typename Type2Vec128_Traits<_T>::vec_type vx_setall(const _T& a) { return v_setall(a); }
#elif CV_SIMD_WIDTH == 32
+2 -2
View File
@@ -1013,7 +1013,7 @@ public:
@param copyData Flag to specify whether the underlying data of the STL vector should be copied
to (true) or shared with (false) the newly constructed matrix. When the data is copied, the
allocated buffer is managed using Mat reference counting mechanism. While the data is shared,
the reference counter is NULL, and you should not deallocate the data until the matrix is not
the reference counter is NULL, and you should not deallocate the data until the matrix is
destructed.
*/
template<typename _Tp> explicit Mat(const std::vector<_Tp>& vec, bool copyData=false);
@@ -2275,7 +2275,7 @@ public:
std::reverse_iterator<const_iterator> rbegin() const;
std::reverse_iterator<const_iterator> rend() const;
//! template methods for for operation over all matrix elements.
//! template methods for operation over all matrix elements.
// the operations take care of skipping gaps in the end of rows (if any)
template<typename Functor> void forEach(const Functor& operation);
template<typename Functor> void forEach(const Functor& operation) const;
@@ -46,6 +46,7 @@
#define OPENCV_CORE_SATURATE_HPP
#include "opencv2/core/cvdef.h"
#include <climits>
#include "opencv2/core/fast_math.hpp"
namespace cv
@@ -147,7 +147,7 @@ public:
return handle != NULL;
}
void* getSymbol(const char* symbolName) const;
const std::string getName() const;
std::string getName() const;
private:
void libraryLoad(const FileSystemPath_t& filename);
void libraryRelease();
+2 -2
View File
@@ -98,7 +98,7 @@ static bool updateIdx(cv::Mat* mat, std::vector<int>& indices, size_t inc) {
- (instancetype)initWithSize:(Size2i*)size type:(int)type {
self = [super init];
if (self) {
_nativePtr = new cv::Mat(size.width, size.height, type);
_nativePtr = new cv::Mat(size.height, size.width, type);
}
return self;
}
@@ -128,7 +128,7 @@ static bool updateIdx(cv::Mat* mat, std::vector<int>& indices, size_t inc) {
self = [super init];
if (self) {
cv::Scalar scalerTemp(scalar.val[0].doubleValue, scalar.val[1].doubleValue, scalar.val[2].doubleValue, scalar.val[3].doubleValue);
_nativePtr = new cv::Mat(size.width, size.height, type, scalerTemp);
_nativePtr = new cv::Mat(size.height, size.width, type, scalerTemp);
}
return self;
}
@@ -17,6 +17,12 @@ class MatTests: OpenCVTestCase {
super.tearDown()
}
func testInitWithSize() {
let size = Size(width: 7, height: 9)
let mat = Mat(size: size, type: CvType.CV_8U)
assertSizeEquals(size, mat.size())
}
func testAdjustROI() throws {
let roi = gray0.submat(rowStart: 3, rowEnd: 5, colStart: 7, colEnd: 10)
let originalroi = roi.clone()
+1
View File
@@ -233,6 +233,7 @@ PERF_TEST_P_(OpenCLBuffer, cpu_read)
for (size_t x_bytes = 0; x_bytes < width_bytes; x_bytes++)
counter += (unsigned)(ptr[x_bytes]);
}
(void)counter; // To avoid -Wunused-but-set-variable
}
SANITY_CHECK_NOTHING();
+2 -2
View File
@@ -92,7 +92,7 @@ static bool ocl_binary_op(InputArray _src1, InputArray _src2, OutputArray _dst,
const int dstType1 = CV_MAKETYPE(dstDepth, 1);
const int scalarType = CV_MAKETYPE(srcdepth, scalarcn);
sprintf(opts, "-D %s%s -D %s%s -D dstT=%s -D DEPTH_dst=%d -D dstT_C1=%s -D workST=%s -D cn=%d -D rowsPerWI=%d",
snprintf(opts, sizeof(opts), "-D %s%s -D %s%s -D dstT=%s -D DEPTH_dst=%d -D dstT_C1=%s -D workST=%s -D cn=%d -D rowsPerWI=%d",
haveMask ? "MASK_" : "", haveScalar ? "UNARY_OP" : "BINARY_OP", oclop2str[oclop],
doubleSupport ? " -D DOUBLE_SUPPORT" : "",
bitwise ? ocl::memopTypeToStr(dstType) : ocl::typeToStr(dstType),
@@ -490,7 +490,7 @@ static bool ocl_arithm_op(InputArray _src1, InputArray _src2, OutputArray _dst,
int scalarcn = kercn == 3 ? 4 : kercn, rowsPerWI = d.isIntel() ? 4 : 1;
char cvtstr[4][32], opts[1024];
sprintf(opts, "-D %s%s -D %s -D srcT1=%s -D srcT1_C1=%s -D srcT2=%s -D srcT2_C1=%s "
snprintf(opts, sizeof(opts), "-D %s%s -D %s -D srcT1=%s -D srcT1_C1=%s -D srcT2=%s -D srcT2_C1=%s "
"-D dstT=%s -D DEPTH_dst=%d -D dstT_C1=%s -D workT=%s -D workST=%s -D scaleT=%s -D wdepth=%d -D convertToWT1=%s "
"-D convertToWT2=%s -D convertToDT=%s%s -D cn=%d -D rowsPerWI=%d -D convertFromU=%s",
(haveMask ? "MASK_" : ""), (haveScalar ? "UNARY_OP" : "BINARY_OP"),
+20
View File
@@ -266,24 +266,30 @@ struct op_absdiff
template<>
struct op_absdiff<schar, v_int8>
{
#if CV_SIMD
static inline v_int8 r(const v_int8& a, const v_int8& b)
{ return v_absdiffs(a, b); }
#endif
static inline schar r(schar a, schar b)
{ return c_absdiff(a, b); }
};
template<>
struct op_absdiff<short, v_int16>
{
#if CV_SIMD
static inline v_int16 r(const v_int16& a, const v_int16& b)
{ return v_absdiffs(a, b); }
#endif
static inline short r(short a, short b)
{ return c_absdiff(a, b); }
};
template<>
struct op_absdiff<int, v_int32>
{
#if CV_SIMD
static inline v_int32 r(const v_int32& a, const v_int32& b)
{ return v_reinterpret_as_s32(v_absdiff(a, b)); }
#endif
static inline int r(int a, int b)
{ return c_absdiff(a, b); }
};
@@ -1430,11 +1436,13 @@ struct op_mul
template<typename T1, typename T2, typename Tvec>
struct op_mul_scale
{
#if CV_SIMD
static inline v_float32 r(const v_float32& a, const v_float32& b, const T2* scalar)
{
const v_float32 v_scalar = vx_setall_f32(*scalar);
return v_scalar * a * b;
}
#endif
static inline T1 r(T1 a, T1 b, const T2* scalar)
{ return c_mul(a, b, *scalar); }
static inline Tvec pre(const Tvec&, const Tvec& res)
@@ -1569,6 +1577,7 @@ struct op_div_f
template<typename T1, typename T2, typename Tvec>
struct op_div_scale
{
#if CV_SIMD
static inline v_float32 r(const v_float32& a, const v_float32& b, const T2* scalar)
{
const v_float32 v_scalar = vx_setall_f32(*scalar);
@@ -1579,6 +1588,7 @@ struct op_div_scale
const Tvec v_zero = vx_setall<typename Tvec::lane_type>(0);
return v_select(denom == v_zero, v_zero, res);
}
#endif
static inline T1 r(T1 a, T1 denom, const T2* scalar)
{
CV_StaticAssert(std::numeric_limits<T1>::is_integer, "");
@@ -1589,11 +1599,13 @@ struct op_div_scale
template<>
struct op_div_scale<float, float, v_float32>
{
#if CV_SIMD
static inline v_float32 r(const v_float32& a, const v_float32& b, const float* scalar)
{
const v_float32 v_scalar = vx_setall_f32(*scalar);
return a * v_scalar / b;
}
#endif
static inline float r(float a, float denom, const float* scalar)
{ return c_div(a, denom, *scalar); }
};
@@ -1673,11 +1685,13 @@ DEFINE_SIMD_ALL(div, div_loop)
template<typename T1, typename T2, typename Tvec>
struct op_add_scale
{
#if CV_SIMD
static inline v_float32 r(const v_float32& a, const v_float32& b, const T2* scalar)
{
const v_float32 v_alpha = vx_setall_f32(*scalar);
return v_fma(a, v_alpha, b);
}
#endif
static inline T1 r(T1 a, T1 b, const T2* scalar)
{ return c_add(a, b, *scalar); }
static inline Tvec pre(const Tvec&, const Tvec& res)
@@ -1704,6 +1718,7 @@ struct op_add_scale<double, double, v_float64>
template<typename T1, typename T2, typename Tvec>
struct op_add_weighted
{
#if CV_SIMD
static inline v_float32 r(const v_float32& a, const v_float32& b, const T2* scalars)
{
const v_float32 v_alpha = vx_setall_f32(scalars[0]);
@@ -1711,6 +1726,7 @@ struct op_add_weighted
const v_float32 v_gamma = vx_setall_f32(scalars[2]);
return v_fma(a, v_alpha, v_fma(b, v_beta, v_gamma));
}
#endif
static inline T1 r(T1 a, T1 b, const T2* scalars)
{ return c_add(a, b, scalars[0], scalars[1], scalars[2]); }
static inline Tvec pre(const Tvec&, const Tvec& res)
@@ -1819,6 +1835,7 @@ DEFINE_SIMD_F64(addWeighted, add_weighted_loop_d)
template<typename T1, typename T2, typename Tvec>
struct op_recip
{
#if CV_SIMD
static inline v_float32 r(const v_float32& a, const T2* scalar)
{
const v_float32 v_scalar = vx_setall_f32(*scalar);
@@ -1829,6 +1846,7 @@ struct op_recip
const Tvec v_zero = vx_setall<typename Tvec::lane_type>(0);
return v_select(denom == v_zero, v_zero, res);
}
#endif
static inline T1 r(T1 denom, const T2* scalar)
{
CV_StaticAssert(std::numeric_limits<T1>::is_integer, "");
@@ -1839,11 +1857,13 @@ struct op_recip
template<>
struct op_recip<float, float, v_float32>
{
#if CV_SIMD
static inline v_float32 r(const v_float32& a, const float* scalar)
{
const v_float32 v_scalar = vx_setall_f32(*scalar);
return v_scalar / a;
}
#endif
static inline float r(float denom, const float* scalar)
{ return c_div(*scalar, denom); }
};
+1 -2
View File
@@ -29,8 +29,7 @@ public:
}
void cleanup() const
{
CV_Assert(ptr && *ptr);
*ptr = 0;
CV_DbgAssert(ptr);
if (raw_mem)
fastFree(raw_mem);
}
+2 -2
View File
@@ -14,7 +14,7 @@ const char* depthToString(int depth)
return s ? s : "<invalid depth>";
}
const cv::String typeToString(int type)
cv::String typeToString(int type)
{
cv::String s = detail::typeToString_(type);
if (s.empty())
@@ -47,7 +47,7 @@ const char* depthToString_(int depth)
return (depth <= CV_16F && depth >= 0) ? depthNames[depth] : NULL;
}
const cv::String typeToString_(int type)
cv::String typeToString_(int type)
{
int depth = CV_MAT_DEPTH(type);
int cn = CV_MAT_CN(type);
+1 -3
View File
@@ -133,8 +133,6 @@ cvCreateChildMemStorage( CvMemStorage * parent )
static void
icvDestroyMemStorage( CvMemStorage* storage )
{
int k = 0;
CvMemBlock *block;
CvMemBlock *dst_top = 0;
@@ -144,7 +142,7 @@ icvDestroyMemStorage( CvMemStorage* storage )
if( storage->parent )
dst_top = storage->parent->top;
for( block = storage->bottom; block != 0; k++ )
for( block = storage->bottom; block != 0; )
{
CvMemBlock *temp = block;
+1 -1
View File
@@ -67,7 +67,7 @@
#if defined(__clang__) && defined(__has_feature)
#if __has_feature(memory_sanitizer)
#define CV_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
__msan_unpoison(adresse, size)
__msan_unpoison(address, size)
#endif
#endif
#ifndef CV_ANNOTATE_MEMORY_IS_INITIALIZED
+1 -4
View File
@@ -253,11 +253,8 @@ static int initialize_simplex(Mat_<double>& c, Mat_<double>& b,double& v,vector<
}
static int inner_simplex(Mat_<double>& c, Mat_<double>& b,double& v,vector<int>& N,vector<int>& B,vector<unsigned int>& indexToRow){
int count=0;
for(;;){
dprintf(("iteration #%d\n",count));
count++;
for(;;){
static MatIterator_<double> pos_ptr;
int e=-1,pos_ctr=0,min_var=INT_MAX;
bool all_nonzero=true;
+1 -1
View File
@@ -231,7 +231,7 @@ static const bool CV_OPENCL_DISABLE_BUFFER_RECT_OPERATIONS = utils::getConfigura
#endif
);
static const String getBuildExtraOptions()
static String getBuildExtraOptions()
{
static String param_buildExtraOptions;
static bool initialized = false;
+10 -10
View File
@@ -70,14 +70,14 @@ namespace cv
char braces[5];
void (FormattedImpl::*valueToStr)();
void valueToStr8u() { sprintf(buf, "%3d", (int)mtx.ptr<uchar>(row, col)[cn]); }
void valueToStr8s() { sprintf(buf, "%3d", (int)mtx.ptr<schar>(row, col)[cn]); }
void valueToStr16u() { sprintf(buf, "%d", (int)mtx.ptr<ushort>(row, col)[cn]); }
void valueToStr16s() { sprintf(buf, "%d", (int)mtx.ptr<short>(row, col)[cn]); }
void valueToStr32s() { sprintf(buf, "%d", mtx.ptr<int>(row, col)[cn]); }
void valueToStr32f() { sprintf(buf, floatFormat, mtx.ptr<float>(row, col)[cn]); }
void valueToStr64f() { sprintf(buf, floatFormat, mtx.ptr<double>(row, col)[cn]); }
void valueToStr16f() { sprintf(buf, floatFormat, (float)mtx.ptr<float16_t>(row, col)[cn]); }
void valueToStr8u() { snprintf(buf, sizeof(buf), "%3d", (int)mtx.ptr<uchar>(row, col)[cn]); }
void valueToStr8s() { snprintf(buf, sizeof(buf), "%3d", (int)mtx.ptr<schar>(row, col)[cn]); }
void valueToStr16u() { snprintf(buf, sizeof(buf), "%d", (int)mtx.ptr<ushort>(row, col)[cn]); }
void valueToStr16s() { snprintf(buf, sizeof(buf), "%d", (int)mtx.ptr<short>(row, col)[cn]); }
void valueToStr32s() { snprintf(buf, sizeof(buf), "%d", mtx.ptr<int>(row, col)[cn]); }
void valueToStr32f() { snprintf(buf, sizeof(buf), floatFormat, mtx.ptr<float>(row, col)[cn]); }
void valueToStr64f() { snprintf(buf, sizeof(buf), floatFormat, mtx.ptr<double>(row, col)[cn]); }
void valueToStr16f() { snprintf(buf, sizeof(buf), floatFormat, (float)mtx.ptr<float16_t>(row, col)[cn]); }
void valueToStrOther() { buf[0] = 0; }
public:
@@ -151,10 +151,10 @@ namespace cv
}
else
row = 0;
sprintf(buf, "\n(:, :, %d) = \n", cn+1);
snprintf(buf, sizeof(buf), "\n(:, :, %d) = \n", cn+1);
return buf;
}
sprintf(buf, "(:, :, %d) = \n", cn+1);
snprintf(buf, sizeof(buf), "(:, :, %d) = \n", cn+1);
return buf;
case STATE_EPILOGUE:
state = STATE_FINISHED;
+1 -1
View File
@@ -986,7 +986,7 @@ int getNumberOfCPUs_()
#endif
#if !defined(_WIN32) && !defined(__APPLE__)
#if !defined(_WIN32) && !defined(__APPLE__) && defined(_SC_NPROCESSORS_ONLN)
static unsigned cpu_count_sysconf = (unsigned)sysconf( _SC_NPROCESSORS_ONLN );
ncpus = minNonZero(ncpus, cpu_count_sysconf);
+13 -13
View File
@@ -56,7 +56,7 @@ char* itoa( int _val, char* buffer, int /*radix*/ )
return ptr;
}
char* doubleToString( char* buf, double value, bool explicitZero )
char* doubleToString( char* buf, size_t bufSize, double value, bool explicitZero )
{
Cv64suf val;
unsigned ieee754_hi;
@@ -70,15 +70,15 @@ char* doubleToString( char* buf, double value, bool explicitZero )
if( ivalue == value )
{
if( explicitZero )
sprintf( buf, "%d.0", ivalue );
snprintf( buf, bufSize, "%d.0", ivalue );
else
sprintf( buf, "%d.", ivalue );
snprintf( buf, bufSize, "%d.", ivalue );
}
else
{
static const char* fmt = "%.16e";
char* ptr = buf;
sprintf( buf, fmt, value );
snprintf( buf, bufSize, fmt, value );
if( *ptr == '+' || *ptr == '-' )
ptr++;
for( ; cv_isdigit(*ptr); ptr++ )
@@ -99,7 +99,7 @@ char* doubleToString( char* buf, double value, bool explicitZero )
return buf;
}
char* floatToString( char* buf, float value, bool halfprecision, bool explicitZero )
char* floatToString( char* buf, size_t bufSize, float value, bool halfprecision, bool explicitZero )
{
Cv32suf val;
unsigned ieee754;
@@ -112,17 +112,17 @@ char* floatToString( char* buf, float value, bool halfprecision, bool explicitZe
if( ivalue == value )
{
if( explicitZero )
sprintf( buf, "%d.0", ivalue );
snprintf( buf, bufSize, "%d.0", ivalue );
else
sprintf( buf, "%d.", ivalue );
snprintf( buf, bufSize, "%d.", ivalue );
}
else
{
char* ptr = buf;
if (halfprecision)
sprintf(buf, "%.4e", value);
snprintf(buf, bufSize, "%.4e", value);
else
sprintf(buf, "%.8e", value);
snprintf(buf, bufSize, "%.8e", value);
if( *ptr == '+' || *ptr == '-' )
ptr++;
for( ; cv_isdigit(*ptr); ptr++ )
@@ -585,7 +585,7 @@ bool FileStorage::Impl::open(const char *filename_or_buf, int _flags, const char
CV_Assert(strlen(encoding) < 1000);
char buf[1100];
sprintf(buf, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", encoding);
snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"%s\"?>\n", encoding);
puts(buf);
} else
puts("<?xml version=\"1.0\"?>\n");
@@ -1107,15 +1107,15 @@ void FileStorage::Impl::writeRawData(const std::string &dt, const void *_data, s
data += sizeof(int);
break;
case CV_32F:
ptr = fs::floatToString(buf, *(float *) data, false, explicitZero);
ptr = fs::floatToString(buf, sizeof(buf), *(float *) data, false, explicitZero);
data += sizeof(float);
break;
case CV_64F:
ptr = fs::doubleToString(buf, *(double *) data, explicitZero);
ptr = fs::doubleToString(buf, sizeof(buf), *(double *) data, explicitZero);
data += sizeof(double);
break;
case CV_16F: /* reference */
ptr = fs::floatToString(buf, (float) *(float16_t *) data, true, explicitZero);
ptr = fs::floatToString(buf, sizeof(buf), (float) *(float16_t *) data, true, explicitZero);
data += sizeof(float16_t);
break;
default:
+2 -2
View File
@@ -86,8 +86,8 @@ namespace fs
{
int strcasecmp(const char* str1, const char* str2);
char* itoa( int _val, char* buffer, int /*radix*/ );
char* floatToString( char* buf, float value, bool halfprecision, bool explicitZero );
char* doubleToString( char* buf, double value, bool explicitZero );
char* floatToString( char* buf, size_t bufSize, float value, bool halfprecision, bool explicitZero );
char* doubleToString( char* buf, size_t bufSize, double value, bool explicitZero );
int calcStructSize( const char* dt, int initial_size );
int calcElemSize( const char* dt, int initial_size );
+1 -1
View File
@@ -84,7 +84,7 @@ public:
void write( const char* key, double value )
{
char buf[128];
writeScalar( key, fs::doubleToString( buf, value, true ));
writeScalar( key, fs::doubleToString( buf, sizeof(buf), value, true ));
}
void write(const char* key, const char* str, bool quote)
+1 -1
View File
@@ -148,7 +148,7 @@ public:
void write( const char* key, double value )
{
char buf[128];
writeScalar( key, fs::doubleToString( buf, value, false ) );
writeScalar( key, fs::doubleToString( buf, sizeof(buf), value, false ) );
}
void write(const char* key, const char* str, bool quote)
+4 -4
View File
@@ -40,7 +40,7 @@ public:
{
/* reset struct flag. in order not to print ']' */
struct_flags = FileNode::SEQ;
sprintf(buf, "!!binary |");
snprintf(buf, sizeof(buf), "!!binary |");
data = buf;
}
else if( FileNode::isFlow(struct_flags))
@@ -49,7 +49,7 @@ public:
struct_flags |= FileNode::FLOW;
if( type_name )
sprintf( buf, "!!%s %c", type_name, c );
snprintf( buf, sizeof(buf), "!!%s %c", type_name, c );
else
{
buf[0] = c;
@@ -59,7 +59,7 @@ public:
}
else if( type_name )
{
sprintf( buf, "!!%s", type_name );
snprintf( buf, sizeof(buf), "!!%s", type_name );
data = buf;
}
@@ -110,7 +110,7 @@ public:
void write( const char* key, double value )
{
char buf[128];
writeScalar( key, fs::doubleToString( buf, value, false ));
writeScalar( key, fs::doubleToString( buf, sizeof(buf), value, false ));
}
void write(const char* key, const char* str, bool quote)
+1
View File
@@ -365,6 +365,7 @@ extern CV_EXPORTS
bool __termination; // skip some cleanups, because process is terminating
// (for example, if ExitProcess() was already called)
CV_EXPORTS
cv::Mutex& getInitializationMutex();
/// @brief Returns timestamp in nanoseconds since program launch
+40 -4
View File
@@ -305,6 +305,9 @@ DECLARE_CV_CPUID_X86
#endif
#endif
#if defined CV_CXX11
#include <chrono>
#endif
namespace cv
{
@@ -414,6 +417,7 @@ struct HWFeatures
g_hwFeatureNames[CPU_AVX_5124FMAPS] = "AVX5124FMAPS";
g_hwFeatureNames[CPU_NEON] = "NEON";
g_hwFeatureNames[CPU_NEON_DOTPROD] = "NEON_DOTPROD";
g_hwFeatureNames[CPU_VSX] = "VSX";
g_hwFeatureNames[CPU_VSX3] = "VSX3";
@@ -561,6 +565,24 @@ struct HWFeatures
#ifdef __aarch64__
have[CV_CPU_NEON] = true;
have[CV_CPU_FP16] = true;
int cpufile = open("/proc/self/auxv", O_RDONLY);
if (cpufile >= 0)
{
Elf64_auxv_t auxv;
const size_t size_auxv_t = sizeof(auxv);
while ((size_t)read(cpufile, &auxv, size_auxv_t) == size_auxv_t)
{
if (auxv.a_type == AT_HWCAP)
{
have[CV_CPU_NEON_DOTPROD] = (auxv.a_un.a_val & (1 << 20)) != 0;
break;
}
}
close(cpufile);
}
#elif defined __arm__ && defined __ANDROID__
#if defined HAVE_CPUFEATURES
CV_LOG_INFO(NULL, "calling android_getCpuFeatures() ...");
@@ -604,13 +626,20 @@ struct HWFeatures
close(cpufile);
}
#endif
#elif (defined __clang__ || defined __APPLE__)
#elif (defined __APPLE__)
#if (defined __ARM_NEON__ || (defined __ARM_NEON && defined __aarch64__))
have[CV_CPU_NEON] = true;
#endif
#if (defined __ARM_FP && (((__ARM_FP & 0x2) != 0) && defined __ARM_NEON__))
have[CV_CPU_FP16] = true;
#endif
#elif (defined __clang__)
#if (defined __ARM_NEON__ || (defined __ARM_NEON && defined __aarch64__))
have[CV_CPU_NEON] = true;
#if (defined __ARM_FP && ((__ARM_FP & 0x2) != 0))
have[CV_CPU_FP16] = true;
#endif
#endif
#endif
#if defined _ARM_ && (defined(_WIN32_WCE) && _WIN32_WCE >= 0x800)
have[CV_CPU_NEON] = true;
@@ -846,7 +875,10 @@ bool useOptimized(void)
int64 getTickCount(void)
{
#if defined _WIN32 || defined WINCE
#if defined CV_CXX11
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
return (int64)now.time_since_epoch().count();
#elif defined _WIN32 || defined WINCE
LARGE_INTEGER counter;
QueryPerformanceCounter( &counter );
return (int64)counter.QuadPart;
@@ -865,7 +897,11 @@ int64 getTickCount(void)
double getTickFrequency(void)
{
#if defined _WIN32 || defined WINCE
#if defined CV_CXX11
using clock_period_t = std::chrono::steady_clock::duration::period;
double clock_freq = clock_period_t::den / clock_period_t::num;
return clock_freq;
#elif defined _WIN32 || defined WINCE
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
return (double)freq.QuadPart;
@@ -1326,7 +1362,7 @@ CV_IMPL const char* cvErrorStr( int status )
case CV_OpenGlApiCallError : return "OpenGL API call";
};
sprintf(buf, "Unknown %s code %d", status >= 0 ? "status":"error", status);
snprintf(buf, sizeof(buf), "Unknown %s code %d", status >= 0 ? "status":"error", status);
return buf;
}
+1 -2
View File
@@ -988,7 +988,6 @@ void parallelForFinalize(const Region& rootRegion)
std::vector<TraceManagerThreadLocal*> threads_ctx;
getTraceManager().tls.gather(threads_ctx);
RegionStatistics parallel_for_stat;
int threads = 0;
for (size_t i = 0; i < threads_ctx.size(); i++)
{
TraceManagerThreadLocal* child_ctx = threads_ctx[i];
@@ -996,7 +995,6 @@ void parallelForFinalize(const Region& rootRegion)
if (child_ctx && child_ctx->stackTopRegion() == &rootRegion)
{
CV_LOG_PARALLEL(NULL, "Thread=" << child_ctx->threadID << " " << child_ctx->stat);
threads++;
RegionStatistics child_stat;
child_ctx->stat.grab(child_stat);
parallel_for_stat.append(child_stat);
@@ -1012,6 +1010,7 @@ void parallelForFinalize(const Region& rootRegion)
}
}
}
float parallel_coeff = std::min(1.0f, duration / (float)(parallel_for_stat.duration));
CV_LOG_PARALLEL(NULL, "parallel_coeff=" << 1.0f / parallel_coeff);
CV_LOG_PARALLEL(NULL, parallel_for_stat);
@@ -56,7 +56,7 @@ void* DynamicLib::getSymbol(const char* symbolName) const
return res;
}
const std::string DynamicLib::getName() const
std::string DynamicLib::getName() const
{
return toPrintablePath(fname);
}
+1 -1
View File
@@ -7,7 +7,7 @@
#include "opencv2/core/hal/intrin.hpp"
#undef CV__SIMD_FORCE_WIDTH
#if CV_SIMD_WIDTH != 16
#if CV_SIMD && CV_SIMD_WIDTH != 16
#error "Invalid build configuration"
#endif
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -487,7 +487,7 @@ TEST(Core_InputOutput, FileStorage)
cv::FileStorage f(file, cv::FileStorage::WRITE);
char arr[66];
sprintf(arr, "sprintf is hell %d", 666);
snprintf(arr, sizeof(arr), "snprintf is hell %d", 666);
EXPECT_NO_THROW(f << arr);
}
@@ -1765,8 +1765,8 @@ TEST(Core_InputOutput, FileStorage_JSON_VeryLongLines)
std::string val;
for(int i = 0; i < 52500; i += 100)
{
sprintf(key, "KEY%d", i);
sprintf(val0, "VALUE%d", i);
snprintf(key, sizeof(key), "KEY%d", i);
snprintf(val0, sizeof(val0), "VALUE%d", i);
fs[key] >> val;
ASSERT_EQ(val, val0);
}
+1 -1
View File
@@ -161,7 +161,7 @@ int Core_ReduceTest::checkOp( const Mat& src, int dstType, int opType, const Mat
getMatTypeStr( dstType, dstTypeStr );
const char* dimStr = dim == 0 ? "ROWS" : "COLS";
sprintf( msg, "bad accuracy with srcType = %s, dstType = %s, opType = %s, dim = %s",
snprintf( msg, sizeof(msg), "bad accuracy with srcType = %s, dstType = %s, opType = %s, dim = %s",
srcTypeStr.c_str(), dstTypeStr.c_str(), opTypeStr, dimStr );
ts->printf( cvtest::TS::LOG, msg );
return cvtest::TS::FAIL_BAD_ACCURACY;
-7
View File
@@ -408,9 +408,6 @@ TEST_P(BufferArea, basic)
EXPECT_EQ((double)0, dbl_ptr[i]);
}
}
EXPECT_TRUE(int_ptr == NULL);
EXPECT_TRUE(uchar_ptr == NULL);
EXPECT_TRUE(dbl_ptr == NULL);
}
TEST_P(BufferArea, align)
@@ -447,10 +444,6 @@ TEST_P(BufferArea, align)
}
}
}
for (size_t i = 0; i < CNT; ++i)
{
EXPECT_TRUE(buffers[i] == NULL);
}
}
TEST_P(BufferArea, default_align)
+16 -1
View File
@@ -263,6 +263,10 @@ CV__DNN_INLINE_NS_BEGIN
public:
int input_zp, output_zp;
float input_sc, output_sc;
// quantization type flag. The perChannel default is true, that means it contains the parameters
// of per-Channel quantization. Otherwise, that means this layer contains per-Tensor quantized parameters.
bool per_channel;
static Ptr<BaseConvolutionLayer> create(const LayerParams& params);
};
@@ -330,7 +334,8 @@ CV__DNN_INLINE_NS_BEGIN
{
public:
int reduceType;
std::vector<size_t> reduceDims;
// reduceDims contains the dimensions that need to be reduced, targetDims is the target output dimension.
std::vector<size_t> reduceDims, targetDims;
static Ptr<ReduceLayer> create(const LayerParams& params);
};
@@ -368,6 +373,10 @@ CV__DNN_INLINE_NS_BEGIN
public:
int input_zp, output_zp;
float input_sc, output_sc;
// quantization type flag. The perChannel default is true, that means it contains the parameters
// of per-Channel quantization. Otherwise, that means this layer contains per-Tensor quantized parameters.
bool per_channel;
static Ptr<InnerProductLayerInt8> create(const LayerParams& params);
};
@@ -841,6 +850,12 @@ CV__DNN_INLINE_NS_BEGIN
static Ptr<EltwiseLayerInt8> create(const LayerParams &params);
};
class CV_EXPORTS NaryEltwiseLayer : public Layer
{
public:
static Ptr<NaryEltwiseLayer> create(const LayerParams &params);
};
class CV_EXPORTS BatchNormLayer : public ActivationLayer
{
public:
+3 -1
View File
@@ -621,8 +621,10 @@ CV__DNN_INLINE_NS_BEGIN
* @param calibData Calibration data to compute the quantization parameters.
* @param inputsDtype Datatype of quantized net's inputs. Can be CV_32F or CV_8S.
* @param outputsDtype Datatype of quantized net's outputs. Can be CV_32F or CV_8S.
* @param perChannel Quantization granularity of quantized Net. The default is true, that means quantize model
* in per-channel way (channel-wise). Set it false to quantize model in per-tensor way (or tensor-wise).
*/
CV_WRAP Net quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype);
CV_WRAP Net quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype, bool perChannel=true);
/** @brief Returns input scale and zeropoint for a quantized Net.
* @param scales output parameter for returning input scales.
+1 -1
View File
@@ -6,7 +6,7 @@
#define OPENCV_DNN_VERSION_HPP
/// Use with major OpenCV version only.
#define OPENCV_DNN_API_VERSION 20211230
#define OPENCV_DNN_API_VERSION 20220821
#if !defined CV_DOXYGEN && !defined CV_STATIC_ANALYSIS && !defined CV_DNN_DONT_ADD_INLINE_NS
#define CV__DNN_INLINE_NS __CV_CAT(dnn5_v, OPENCV_DNN_API_VERSION)
+150
View File
@@ -55,7 +55,156 @@ struct Layer_Slice : public TestBaseWithParam<tuple<Backend, Target> >
}
};
struct Layer_NaryEltwise : public TestBaseWithParam<tuple<Backend, Target> >
{
void test_layer(const std::vector<int>& a_shape, const std::vector<int>& b_shape, const String op, bool isRef = false)
{
int backendId = get<0>(GetParam());
int targetId = get<1>(GetParam());
Mat a(a_shape, CV_32FC1);
Mat b(b_shape, CV_32FC1);
Scalar mean = 0.f;
Scalar std = 1.f;
randn(a, mean, std);
randn(b, mean, std);
Net net;
LayerParams lp;
if (isRef)
lp.type = "Eltwise";
else
lp.type = "NaryEltwise";
lp.name = "testLayer";
lp.set("operation", op);
int id = net.addLayerToPrev(lp.name, lp.type, lp);
net.connect(0, 1, id, 1);
// warmup
{
std::vector<String> inpNames(2);
inpNames[0] = "a";
inpNames[1] = "b";
net.setInputsNames(inpNames);
net.setInput(a, inpNames[0]);
net.setInput(b, inpNames[1]);
net.setPreferableBackend(backendId);
net.setPreferableTarget(targetId);
Mat out = net.forward();
}
TEST_CYCLE()
{
Mat res = net.forward();
}
SANITY_CHECK_NOTHING();
}
int N = 8;
int C = 256;
int H = 128;
int W = 100;
};
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_add)
{
test_layer({N, C, H, W}, {N, C, H, W}, "add");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_div)
{
test_layer({N, C, H, W}, {N, C, H, W}, "div");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_ref_div)
{
test_layer({N, C, H, W}, {N, C, H, W}, "div", true);
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_equal)
{
test_layer({N, C, H, W}, {N, C, H, W}, "equal");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_greater)
{
test_layer({N, C, H, W}, {N, C, H, W}, "greater");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_less)
{
test_layer({N, C, H, W}, {N, C, H, W}, "less");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_max)
{
test_layer({N, C, H, W}, {N, C, H, W}, "max");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_ref_max)
{
test_layer({N, C, H, W}, {N, C, H, W}, "max", true);
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_mean)
{
test_layer({N, C, H, W}, {N, C, H, W}, "mean");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_min)
{
test_layer({N, C, H, W}, {N, C, H, W}, "min");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_ref_min)
{
test_layer({N, C, H, W}, {N, C, H, W}, "min", true);
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_mul)
{
test_layer({N, C, H, W}, {N, C, H, W}, "mul");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_ref_mul)
{
test_layer({N, C, H, W}, {N, C, H, W}, "prod", true);
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_pow)
{
test_layer({N, C, H, W}, {N, C, H, W}, "pow");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_sub)
{
test_layer({N, C, H, W}, {N, C, H, W}, "sub");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_sum)
{
test_layer({N, C, H, W}, {N, C, H, W}, "sum");
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_NCHW_ref_sum)
{
test_layer({N, C, H, W}, {N, C, H, W}, "sum", true);
}
PERF_TEST_P_(Layer_NaryEltwise, NCHW_C_sum)
{
test_layer({N, C, H, W}, {C, 1, 1}, "sum");
}
PERF_TEST_P_(Layer_NaryEltwise, NHWC_C)
{
test_layer({N, H, W, C}, {1, C}, "sum");
}
PERF_TEST_P_(Layer_Slice, YOLOv4_tiny_1)
{
@@ -91,5 +240,6 @@ PERF_TEST_P_(Layer_Slice, FastNeuralStyle_eccv16)
}
INSTANTIATE_TEST_CASE_P(/**/, Layer_Slice, dnnBackendsAndTargets(false, false));
INSTANTIATE_TEST_CASE_P(/**/, Layer_NaryEltwise, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU)));
} // namespace
+1 -1
View File
@@ -207,7 +207,7 @@ Net readNetFromDarknet(const String &cfgFile, const String &darknetModel /*= Str
std::ifstream cfgStream(cfgFile.c_str());
if (!cfgStream.is_open())
{
CV_Error(cv::Error::StsParseError, "Failed to parse NetParameter file: " + std::string(cfgFile));
CV_Error(cv::Error::StsParseError, "Failed to open NetParameter file: " + std::string(cfgFile));
}
if (darknetModel != String())
{
+1
View File
@@ -150,6 +150,7 @@ void initializeLayerFactory()
CV_DNN_REGISTER_LAYER_CLASS(Crop, CropLayer);
CV_DNN_REGISTER_LAYER_CLASS(Eltwise, EltwiseLayer);
CV_DNN_REGISTER_LAYER_CLASS(NaryEltwise, NaryEltwiseLayer);
CV_DNN_REGISTER_LAYER_CLASS(Permute, PermuteLayer);
CV_DNN_REGISTER_LAYER_CLASS(ShuffleChannel, ShuffleChannelLayer);
CV_DNN_REGISTER_LAYER_CLASS(PriorBox, PriorBoxLayer);
@@ -51,6 +51,7 @@ public:
input_zp = params.get<int>("input_zeropoint");
output_zp = params.get<int>("zeropoints");
output_sc = params.get<float>("scales");
per_channel = params.get<bool>("per_channel", true);
if (kernel_size.size() == 2) {
kernel = Size(kernel_size[1], kernel_size[0]);
@@ -26,6 +26,8 @@ public:
output_zp = params.get<int>("zeropoints");
output_sc = params.get<float>("scales");
axis = params.get<int>("axis", 1);
per_channel = params.get<bool>("per_channel", true);
if (blobs.size() == 3)
{
// blobs[0] - Weights
+25 -4
View File
@@ -38,6 +38,15 @@ public:
{
reduceDims[i] = tempDims.get<int>(i);
}
CV_Assert(params.has("target_dims"));
tempDims = params.get("target_dims");
n = tempDims.size();
targetDims.resize(n);
for (i = 0; i < n; i++)
{
targetDims[i] = tempDims.get<int>(i);
}
}
virtual bool supportBackend(int backendId) CV_OVERRIDE
@@ -161,18 +170,30 @@ public:
std::vector<MatShape> &internals) const CV_OVERRIDE
{
CV_Assert(inputs.size() > 0);
CV_Assert(reduceDims.size() != 0 && inputs[0].size() >= reduceDims.size());
CV_Assert( reduceDims.size() !=0 && targetDims.size() != 0 && inputs[0].size() >= reduceDims.size());
std::vector<int> outShape;
// outShapeTmp can save the right number of `total(outShapeTmp)`. And the outShape is used as the final output shape.
std::vector<int> outShapeTmp, outShape;
outShape.assign(targetDims.begin(), targetDims.end());
if (inputs[0].size() == reduceDims.size())
outShape.push_back(1);
outShapeTmp.push_back(1);
else
{
for (int i = 0; i < inputs[0].size() - reduceDims.size(); i++)
{
outShape.push_back(inputs[0][i]);
outShapeTmp.push_back(inputs[0][i]);
}
}
// Support dynamic shape of Batch size.
// Note that: when there are multiple dynamic inputs, we will give an error.
if (total(outShape) != total(outShapeTmp))
{
if (outShape[0] != outShapeTmp[0])
outShape[0] = outShapeTmp[0];
}
CV_Assert(total(outShape) == total(outShapeTmp));
outputs.assign(1, outShape);
return false;
+75 -13
View File
@@ -71,6 +71,8 @@ using namespace cv::dnn::ocl4dnn;
using namespace cv::dnn::cuda4dnn;
#endif
#include "fast_convolution/fast_convolution.hpp"
namespace cv
{
namespace dnn
@@ -253,11 +255,14 @@ class ConvolutionLayerImpl CV_FINAL : public BaseConvolutionLayerImpl
{
public:
enum { VEC_ALIGN = 8, DFT_TYPE = CV_32F };
Mat weightsMat;
Mat weightsMat; // Used to store weight params. It will be used for layer fusion and memory alignment.
std::vector<float> biasvec;
std::vector<float> reluslope;
Ptr<ActivationLayer> activ;
Mat fastWeights; // Used to store weight params. It will be used for layer fusion and without memory alignment.
Ptr<FastConv2d> fastConv2dImpl;
#ifdef HAVE_OPENCL
Ptr<OCL4DNNConvSpatial<float> > convolutionOp;
std::vector<UMat> umat_blobs;
@@ -433,6 +438,7 @@ public:
wm.copyTo(wm_aligned);
wm = wm_aligned;
}
fastWeights = blobs[0].reshape(1, numOutput);
weightsMat = wm;
}
else
@@ -628,14 +634,26 @@ public:
if (weightsMat.data == blobs[0].data)
weightsMat = weightsMat.clone();
// If fastWeights is the same as weightsMat, we don't need to allocate more space for fastWeights.
bool sameFastWeights = false;
if (fastWeights.step1() == weightsMat.step1()) // If weightsMat is realigned, it is not the same as fastWeights.
sameFastWeights = true;
if (!sameFastWeights && fastWeights.data == blobs[0].data)
fastWeights = fastWeights.clone();
Mat originWeights = blobs[0].reshape(1, outCn);
for (int i = 0; i < outCn; ++i)
{
double wi = w.at<float>(i);
weightsMultipliers[i] *= wi;
cv::multiply(originWeights.row(i), weightsMultipliers[i], weightsMat.row(i));
if (!sameFastWeights)
cv::multiply(originWeights.row(i), weightsMultipliers[i], fastWeights.row(i));
biasvec[i] *= wi;
}
if (sameFastWeights)
fastWeights = weightsMat;
}
if (!b.empty())
@@ -1948,8 +1966,13 @@ public:
int outCn = blobs.empty() ? inputs[1].size[0] : blobs[0].size[0];
// Need to align non-const blobs
bool variableWeight = false;
if (blobs.empty())
{
variableWeight = true;
if (fastWeights.data != inputs[1].data)
fastWeights = inputs[1].clone();
Mat wm = inputs[1].reshape(1, outCn);
if (wm.data != weightsMat.data)
{
@@ -2066,8 +2089,37 @@ public:
{
int nstripes = std::max(getNumThreads(), 1);
// Initialization of FastCovn2d
if ((!fastConv2dImpl || variableWeight) && inputs[0].dims == 4)
{
int K = outputs[0].size[1];
int C = inputs[0].size[1];
int Hk = kernel_size[kernel_size.size() - 2];
int Wk = kernel_size.back();
CV_Assert(outputs[0].size[1] % ngroups == 0);
int stride_h = strides[strides.size() - 2];
int stride_w = strides.back();
int dilation_h = dilations[dilations.size() - 2];
int dilation_w = dilations.back();
float* weightsPtr = fastWeights.ptr<float>();
CV_Assert(weightsPtr);
fastConv2dImpl = initFastConv2d(ngroups, K, C, Hk, Wk, stride_w, stride_h,
dilation_w, dilation_h, pads_begin, pads_end, weightsPtr, &biasvec[0]);
}
if (fastConv2dImpl)
{
runFastConv2d(inputs[0], outputs[0], fastConv2dImpl, nstripes, activ);
return;
}
// Use only for Conv1D and Conv3D.
ParallelConv::run(inputs[0], outputs[0], weightsMat, biasvec, reluslope,
kernel_size, strides, pads_begin, pads_end, dilations, activ.get(), ngroups, nstripes);
}
}
@@ -2174,26 +2226,36 @@ public:
Mat weightsQuantized(weightsMat.rows, weightsMat.cols, CV_8S);
Mat biasQuantized(1, numOutput, CV_32S);
Mat outputMultiplier(1, numOutput, CV_32F);
double realMin, realMax, weightsScale;
bool perChannel = params.get<bool>("per_channel", true);
for( int i = 0; i < numOutput; i++ )
if (perChannel) // per-Channel quantization.
{
// Quantize weights
cv::minMaxIdx(weightsMat.row(i), &realMin, &realMax);
realMin = std::min(realMin, 0.0);
realMax = std::max(realMax, 0.0);
weightsScale = (realMax == realMin) ? 1.0 : std::max(-realMin, realMax)/127;
weightsMat.row(i).convertTo(weightsQuantized.row(i), CV_8S, 1.f/weightsScale);
for (int i = 0; i < numOutput; i++)
{
double weightsScale = getWeightScale(weightsMat.row(i));
// Quantize biases
weightsMat.row(i).convertTo(weightsQuantized.row(i), CV_8S, 1.f/weightsScale);
float biasScale = inputScale * weightsScale;
biasQuantized.at<int>(i) = cvRound(biasvec[i]/biasScale) - inputZp*(cv::sum(weightsQuantized.row(i))[0]);
outputMultiplier.at<float>(i) = biasScale / outputScale;
}
}
else // per-Tensor quantization.
{
double weightsScale = getWeightScale(weightsMat);
weightsMat.convertTo(weightsQuantized, CV_8S, 1.f/weightsScale);
float biasScale = inputScale * weightsScale;
biasQuantized.at<int>(i) = (int)std::round(biasvec[i]/biasScale) - inputZp*(cv::sum(weightsQuantized.row(i))[0]);
// Store multiplier
outputMultiplier.at<float>(i) = biasScale / outputScale;
for (int i = 0; i < numOutput; i++)
{
biasQuantized.at<int>(i) = cvRound(biasvec[i]/biasScale) - inputZp*(cv::sum(weightsQuantized.row(i))[0]);
outputMultiplier.at<float>(i) = biasScale / outputScale;
}
}
params.blobs.clear();
params.set("per_channel", perChannel);
params.blobs.push_back(weightsQuantized.reshape(1, shape(blobs[0])));
params.blobs.push_back(biasQuantized);
params.blobs.push_back(outputMultiplier);
@@ -0,0 +1,385 @@
// 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.
// This file is modified from the ficus (https://github.com/vpisarev/ficus/blob/master/lib/NN/OpConv.fx).
// Here is the original license:
/*
This file is a part of ficus language project.
See ficus/LICENSE for the licensing terms
*/
#include "../../precomp.hpp"
#include "fast_convolution.hpp"
namespace cv { namespace dnn {
static void depthWiseBlock(const float *inptr, float *outptr, const float *weights, float biasval, int *ofstab, int *yxtab,
float minval, float maxval, int Hi, int Wi, int H0, int W0, int ksize, int pad_top, int pad_left,
int dilation_y, int stride_x, int stride_y, int inner_xleft, int inner_xright, int inner_ytop,
int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3)
{
#if CV_SIMD128
v_float32x4 vminval = v_setall_f32(minval), vmaxval = v_setall_f32(maxval);
v_float32x4 w0 = v_setall_f32(
0.f), w1 = w0, w2 = w0, w3 = w0, w4 = w0, w5 = w0, w6 = w0, w7 = w0, w8 = w0, vbias = w0;
if (useSIMD)
{
vbias = v_setall_f32(biasval);
if (is3x3)
{
w0 = v_setall_f32(weights[0]);
w1 = v_setall_f32(weights[1]);
w2 = v_setall_f32(weights[2]);
w3 = v_setall_f32(weights[3]);
w4 = v_setall_f32(weights[4]);
w5 = v_setall_f32(weights[5]);
w6 = v_setall_f32(weights[6]);
w7 = v_setall_f32(weights[7]);
w8 = v_setall_f32(weights[8]);
}
}
#endif
int dy0 = 1;
for (int y0 = 0; y0 < H0; y0 += dy0, outptr += W0 * dy0)
{
#if CV_SIMD128
dy0 = inner_ytop <= y0 && y0 + 3 < inner_ybottom && is3x3 && stride_y == 1 && dilation_y == 1
? 3 : 1;
#endif
int x0 = 0, x1 = y0 >= inner_ytop && y0 < inner_ybottom ? inner_xleft : W0;
int yi_ = y0 * stride_y - pad_top;
for (;;)
{
float s_0, s_1, s_2;
if (dy0 == 3)
{
for (; x0 < x1; x0++)
{
int xi_ = x0 * stride_x - pad_left;
s_0 = s_1 = s_2 = biasval;
for (int k = 0; k < ksize; k++)
{
int dy = yxtab[k * 2];
int yi = yi_ + dy;
int xi = xi_ + yxtab[k * 2 + 1];
float w = weights[k];
if ((unsigned) xi < (unsigned) Wi)
{
s_0 += inptr[yi * Wi + xi] * w;
s_1 += inptr[(yi + 1) * Wi + xi] * w;
s_2 += inptr[(yi + 2) * Wi + xi] * w;
}
}
s_0 = std::min(std::max(s_0, minval), maxval);
s_1 = std::min(std::max(s_1, minval), maxval);
s_2 = std::min(std::max(s_2, minval), maxval);
outptr[x0] = s_0;
outptr[x0 + W0] = s_1;
outptr[x0 + W0 * 2] = s_2;
}
}
else
{
for (; x0 < x1; x0++)
{
int xi_ = x0 * stride_x - pad_left;
s_0 = biasval;
for (int k = 0; k < ksize; k++) {
int dy = yxtab[k * 2];
int yi = yi_ + dy;
int xi = xi_ + yxtab[k * 2 + 1];
float w = weights[k];
if (((unsigned) yi < (unsigned) Hi) & ((unsigned) xi < (unsigned) Wi))
s_0 += inptr[yi * Wi + xi] * w;
}
s_0 = std::min(std::max(s_0, minval), maxval);
outptr[x0] = s_0;
}
}
if (x0 == W0)
break;
x1 = inner_xright;
#if CV_SIMD128
if (useSIMD)
{
if (is3x3)
{
if (dy0 == 3)
{
for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES)
{
int xi_ = x0 * stride_x - pad_left;
const float *inptr_xi = inptr + Wi * yi_ + xi_;
v_float32x4 s0, s1, s2;
v_float32x4 x00 = v_load(inptr_xi);
v_float32x4 x01 = v_load(inptr_xi + 1);
v_float32x4 x02 = v_load(inptr_xi + 2);
v_float32x4 x10 = v_load(inptr_xi + Wi);
v_float32x4 x11 = v_load(inptr_xi + Wi + 1);
v_float32x4 x12 = v_load(inptr_xi + Wi + 2);
v_float32x4 x20 = v_load(inptr_xi + Wi * 2);
v_float32x4 x21 = v_load(inptr_xi + Wi * 2 + 1);
v_float32x4 x22 = v_load(inptr_xi + Wi * 2 + 2);
v_float32x4 x30 = v_load(inptr_xi + Wi * 3);
v_float32x4 x31 = v_load(inptr_xi + Wi * 3 + 1);
v_float32x4 x32 = v_load(inptr_xi + Wi * 3 + 2);
v_float32x4 x40 = v_load(inptr_xi + Wi * 4);
v_float32x4 x41 = v_load(inptr_xi + Wi * 4 + 1);
v_float32x4 x42 = v_load(inptr_xi + Wi * 4 + 2);
s0 = v_fma(x00, w0, vbias);
s1 = v_fma(x10, w0, vbias);
s2 = v_fma(x20, w0, vbias);
s0 = v_fma(x01, w1, s0);
s1 = v_fma(x11, w1, s1);
s2 = v_fma(x21, w1, s2);
s0 = v_fma(x02, w2, s0);
s1 = v_fma(x12, w2, s1);
s2 = v_fma(x22, w2, s2);
s0 = v_fma(x10, w3, s0);
s1 = v_fma(x20, w3, s1);
s2 = v_fma(x30, w3, s2);
s0 = v_fma(x11, w4, s0);
s1 = v_fma(x21, w4, s1);
s2 = v_fma(x31, w4, s2);
s0 = v_fma(x12, w5, s0);
s1 = v_fma(x22, w5, s1);
s2 = v_fma(x32, w5, s2);
s0 = v_fma(x20, w6, s0);
s1 = v_fma(x30, w6, s1);
s2 = v_fma(x40, w6, s2);
s0 = v_fma(x21, w7, s0);
s1 = v_fma(x31, w7, s1);
s2 = v_fma(x41, w7, s2);
s0 = v_fma(x22, w8, s0);
s1 = v_fma(x32, w8, s1);
s2 = v_fma(x42, w8, s2);
if (ifMinMaxAct)
{
s0 = v_min(v_max(s0, vminval), vmaxval);
s1 = v_min(v_max(s1, vminval), vmaxval);
s2 = v_min(v_max(s2, vminval), vmaxval);
}
v_store(outptr + x0, s0);
v_store(outptr + W0 + x0, s1);
v_store(outptr + W0 * 2 + x0, s2);
}
}
else
{
for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES)
{
int xi_ = x0 * stride_x - pad_left;
const float *inptr_xi = inptr + Wi * yi_ + xi_;
v_float32x4 s0 = v_fma(v_load(inptr_xi + ofstab[0]), w0, vbias);
v_float32x4 s1 = v_load(inptr_xi + ofstab[1]) * w1;
v_float32x4 s2 = v_load(inptr_xi + ofstab[2]) * w2;
s0 = v_fma(v_load(inptr_xi + ofstab[3]), w3, s0);
s1 = v_fma(v_load(inptr_xi + ofstab[4]), w4, s1);
s2 = v_fma(v_load(inptr_xi + ofstab[5]), w5, s2);
s0 = v_fma(v_load(inptr_xi + ofstab[6]), w6, s0);
s1 = v_fma(v_load(inptr_xi + ofstab[7]), w7, s1);
s2 = v_fma(v_load(inptr_xi + ofstab[8]), w8, s2);
s0 = s0 + s1 + s2;
if (ifMinMaxAct)
s0 = v_min(v_max(s0, vminval), vmaxval);
v_store(outptr + x0, s0);
}
}
}
else
{
for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES)
{
int xi_ = x0 * stride_x - pad_left, k = 0;
const float *inptr_xi = inptr + Wi * yi_ + xi_;
v_float32x4 s0 = vbias;
for (; k <= ksize - 4; k += 4)
{
v_float32x4 v0 = v_load(inptr_xi + ofstab[k]);
v_float32x4 v1 = v_load(inptr_xi + ofstab[k + 1]);
v_float32x4 v2 = v_load(inptr_xi + ofstab[k + 2]);
v_float32x4 v3 = v_load(inptr_xi + ofstab[k + 3]);
v_float32x4 ww0 = v_setall_f32(weights[k]);
v_float32x4 ww1 = v_setall_f32(weights[k+1]);
v_float32x4 ww2 = v_setall_f32(weights[k+2]);
v_float32x4 ww3 = v_setall_f32(weights[k+3]);
s0 = v_fma(v0, ww0, s0);
s0 = v_fma(v1, ww1, s0);
s0 = v_fma(v2, ww2, s0);
s0 = v_fma(v3, ww3, s0);
}
for (; k < ksize; k++)
s0 = v_fma(v_load(inptr_xi + ofstab[k]),
v_setall_f32(weights[k]), s0);
if (ifMinMaxAct)
s0 = v_min(v_max(s0, vminval), vmaxval);
v_store(outptr + x0, s0);
}
}
}
#endif
if (dy0 == 3)
{
for (; x0 < x1; x0++)
{
int xi_ = x0 * stride_x - pad_left;
const float *inptr_xi = inptr + W0 * yi_ + xi_;
s_0 = s_1 = s_2 = biasval;
for (int k = 0; k < ksize; k++)
{
int inp_ofs = ofstab[k];
float w = weights[k];
s_0 += inptr_xi[inp_ofs] * w;
s_1 += inptr_xi[inp_ofs + Wi] * w;
s_2 += inptr_xi[inp_ofs + Wi * 2] * w;
}
if (ifMinMaxAct)
{
s_0 = std::min(std::max(s_0, minval), maxval);
s_1 = std::min(std::max(s_1, minval), maxval);
s_2 = std::min(std::max(s_2, minval), maxval);
}
outptr[x0] = s_0;
outptr[x0 + W0] = s_1;
outptr[x0 + W0 * 2] = s_2;
}
}
else
{
for (; x0 < x1; x0++)
{
int xi_ = x0 * stride_x - pad_left;
const float *inptr_xi = inptr + Wi * yi_ + xi_;
s_0 = biasval;
for (int k = 0; k < ksize; k++)
{
s_0 += inptr_xi[ofstab[k]] * weights[k];
}
if (ifMinMaxAct)
s_0 = std::min(std::max(s_0, minval), maxval);
outptr[x0] = s_0;
}
}
x1 = W0;
}
}
}
void runDepthwise(InputArray _input, OutputArray _output, const Ptr<FastConv2d>& conv, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct) {
Mat input = _input.getMat();
Mat output = _output.getMat();
MatShape inputShape = shape(input);
MatShape outputShape = shape(output);
CV_Assert(inputShape.size() == 4 && outputShape.size() == 4);
int N = inputShape[0], C = inputShape[1], Hi = inputShape[2], Wi = inputShape[3]; // [N, C, H, W]
int K = conv->K, Hk = conv->Hk, Wk = conv->Wk;
int H0 = outputShape[2], W0 = outputShape[3], ngroups = conv->ngroups;
const size_t inp_planesize = (size_t) Hi * Wi;
const size_t out_planesize = (size_t) H0 * W0;
CV_Assert(ngroups > 1 && ngroups == K && ngroups == C);
int stride_y = conv->stride_y, stride_x = conv->stride_x;
int dilation_y = conv->dilation_y, dilation_x = conv->dilation_x;
int pad_top = conv->pad_top, pad_bottom = conv->pad_bottom;
int pad_left = conv->pad_left, pad_right = conv->pad_right;
int ksize = Hk * Wk, padded_ksize = ((ksize + FAST_VEC_NLANES - 1) / FAST_VEC_NLANES) * FAST_VEC_NLANES;
const float *inp = input.ptr<float>();
float *out = output.ptr<float>();
std::vector<int> ofstab_(3 * padded_ksize, 0);
int *ofstab = ofstab_.data();
int *yxtab = ofstab + padded_ksize;
for (int k = 0; k < padded_ksize; k++)
{
int y = k < ksize ? k / Wk : 0;
int x = k < ksize ? k % Wk : 0;
int dy = y * dilation_y, dx = x * dilation_x;
yxtab[k * 2] = dy;
yxtab[k * 2 + 1] = dx;
ofstab[k] = dy * Wi + dx;
}
const float *weights0 = conv->weightsBuf.data(), *bias = conv->biasBuf.data();
int inner_ytop = (pad_bottom + stride_y - 1) / stride_y, inner_ybottom = 3;
int inner_xleft = (pad_left + stride_x - 1) / stride_x, inner_xright = 4;
CV_Assert(ksize > 1 || (pad_left == 0 && pad_right == 0 && pad_top == 0 && pad_bottom == 0));
inner_xright = (Wi - (Wk - 1) * dilation_x + pad_left) / stride_x;
inner_xright += inner_xright * stride_x - pad_left + (Wk - 1) * dilation_x < Wi;
inner_ybottom = (Hi - (Hk - 1) * dilation_y + pad_top) / stride_y;
inner_ybottom += inner_ybottom * stride_y - pad_top + (Hk - 1) * dilation_y < Hi;
if (inner_xleft >= inner_xright || inner_ytop >= inner_ybottom)
{
inner_xleft = W0;
inner_ytop = H0;
}
inner_ybottom = inner_ybottom < H0 ? inner_ybottom : H0;
bool useSIMD = stride_x == 1 && inner_xleft < W0;
bool is3x3 = Hk == 3 && Wk == 3;
parallel_for_(Range(0, N * C), [&](const Range &r0) {
for (int nc = r0.start; nc < r0.end; nc++)
{
int c = nc % C;
const float *inptr = inp + inp_planesize * nc;
float *outptr0 = out + out_planesize * nc;
float biasval = bias[c];
const float *weights = weights0 + c * padded_ksize;
#if CV_TRY_AVX2
if (conv->useAVX2)
opt_AVX2::depthWiseBlock_AVX2(inptr, outptr0, weights, biasval, ofstab, yxtab, minval, maxval, Hi, Wi, H0, W0, ksize,
pad_top, pad_left, dilation_y, stride_x, stride_y, inner_xleft, inner_xright, inner_ytop,
inner_ybottom, ifMinMaxAct, useSIMD, is3x3);
else
#endif
depthWiseBlock(inptr, outptr0, weights, biasval, ofstab, yxtab, minval, maxval, Hi, Wi, H0, W0, ksize,
pad_top, pad_left, dilation_y, stride_x, stride_y, inner_xleft, inner_xright, inner_ytop,
inner_ybottom, ifMinMaxAct, useSIMD, is3x3);
if (activ)
activ->forwardSlice(outptr0, outptr0, (int) out_planesize, out_planesize, c, c+1);
}
});
}
}} // namespace cv::dnn
@@ -0,0 +1,361 @@
// 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 "../../precomp.hpp"
#include "fast_convolution.hpp"
namespace cv {
namespace opt_AVX2
{
#if CV_TRY_AVX2
void convBlock_AVX2(int k, const float *a, const float *b,
float *c, int ldc, const float *bias,
float minval, float maxval, bool ifActiv)
{
#if FAST_CONV_MR == 4 && FAST_CONV_NR == 24
__m256 vminval = _mm256_set1_ps(minval), vmaxval = _mm256_set1_ps(maxval);
__m256 c0 = _mm256_set1_ps(bias[0]), c1 = c0, c2 = c0;
__m256 c3 = _mm256_set1_ps(bias[1]), c4 = c3, c5 = c3;
__m256 c6 = _mm256_set1_ps(bias[2]), c7 = c6, c8 = c6;
__m256 c9 = _mm256_set1_ps(bias[3]), c10 = c9, c11 = c9;
__m256 a0 = _mm256_setzero_ps(), a1 = _mm256_setzero_ps();
__m256 b0 = _mm256_setzero_ps(), b1 = _mm256_setzero_ps(), b2 = _mm256_setzero_ps();
for (int p = 0; p < k; p++, a += FAST_CONV_MR, b += FAST_CONV_NR)
{
a0 = _mm256_set1_ps(a[0]), a1 = _mm256_set1_ps(a[1]);
b0 = _mm256_load_ps(b), b1 = _mm256_load_ps(b + 8), b2 = _mm256_load_ps(b + 16);
c0 = _mm256_fmadd_ps(b0, a0, c0);
c1 = _mm256_fmadd_ps(b1, a0, c1);
c2 = _mm256_fmadd_ps(b2, a0, c2);
c3 = _mm256_fmadd_ps(b0, a1, c3);
a0 = _mm256_set1_ps(a[2]);
c4 = _mm256_fmadd_ps(b1, a1, c4);
c5 = _mm256_fmadd_ps(b2, a1, c5);
c6 = _mm256_fmadd_ps(b0, a0, c6);
a1 = _mm256_set1_ps(a[3]);
c7 = _mm256_fmadd_ps(b1, a0, c7);
c8 = _mm256_fmadd_ps(b2, a0, c8);
c9 = _mm256_fmadd_ps(b0, a1, c9);
c10 = _mm256_fmadd_ps(b1, a1, c10);
c11 = _mm256_fmadd_ps(b2, a1, c11);
}
if (ifActiv)
{
c0 = _mm256_min_ps(_mm256_max_ps(c0, vminval), vmaxval);
c1 = _mm256_min_ps(_mm256_max_ps(c1, vminval), vmaxval);
c2 = _mm256_min_ps(_mm256_max_ps(c2, vminval), vmaxval);
c3 = _mm256_min_ps(_mm256_max_ps(c3, vminval), vmaxval);
c4 = _mm256_min_ps(_mm256_max_ps(c4, vminval), vmaxval);
c5 = _mm256_min_ps(_mm256_max_ps(c5, vminval), vmaxval);
c6 = _mm256_min_ps(_mm256_max_ps(c6, vminval), vmaxval);
c7 = _mm256_min_ps(_mm256_max_ps(c7, vminval), vmaxval);
c8 = _mm256_min_ps(_mm256_max_ps(c8, vminval), vmaxval);
c9 = _mm256_min_ps(_mm256_max_ps(c9, vminval), vmaxval);
c10 = _mm256_min_ps(_mm256_max_ps(c10, vminval), vmaxval);
c11 = _mm256_min_ps(_mm256_max_ps(c11, vminval), vmaxval);
}
_mm256_storeu_ps(c, c0); _mm256_storeu_ps(c+8, c1); _mm256_storeu_ps(c+16, c2);
_mm256_storeu_ps(c + ldc, c3); _mm256_storeu_ps(c + ldc + 8, c4); _mm256_storeu_ps(c + ldc + 16, c5);
_mm256_storeu_ps(c + ldc*2, c6); _mm256_storeu_ps(c + ldc*2 + 8, c7); _mm256_storeu_ps(c + ldc*2 + 16, c8);
_mm256_storeu_ps(c + ldc*3, c9); _mm256_storeu_ps(c + ldc*3 + 8, c10); _mm256_storeu_ps(c + ldc*3 + 16, c11);
_mm256_zeroupper();
#else
#error "unsupported FAST_CONV_MR and/or FAST_CONV_NR in convBlock_AVX2."
#endif
}
void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights, float biasval, int *ofstab, int *yxtab,
float minval, float maxval, int Hi, int Wi, int H0, int W0, int ksize, int pad_top, int pad_left,
int dilation_y, int stride_x, int stride_y, int inner_xleft, int inner_xright, int inner_ytop,
int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3)
{
const int VECSZ = 8;
__m256 vminval = _mm256_set1_ps(minval);
__m256 vmaxval = _mm256_set1_ps(maxval);
__m256 w0 = _mm256_setzero_ps(),
w1 = w0, w2 = w0, w3 = w0, w4 = w0, w5 = w0, w6 = w0, w7 = w0, w8 = w0, vbias = w0;
if (useSIMD)
{
vbias = _mm256_set1_ps(biasval);
if (is3x3)
{
w0 = _mm256_set1_ps(weights[0]);
w1 = _mm256_set1_ps(weights[1]);
w2 = _mm256_set1_ps(weights[2]);
w3 = _mm256_set1_ps(weights[3]);
w4 = _mm256_set1_ps(weights[4]);
w5 = _mm256_set1_ps(weights[5]);
w6 = _mm256_set1_ps(weights[6]);
w7 = _mm256_set1_ps(weights[7]);
w8 = _mm256_set1_ps(weights[8]);
}
}
int dy0 = 1;
for (int y0 = 0; y0 < H0; y0 += dy0, outptr += W0 * dy0)
{
dy0 = inner_ytop <= y0 && y0 + 3 < inner_ybottom && is3x3 && stride_y == 1 && dilation_y == 1
? 3 : 1;
int x0 = 0, x1 = y0 >= inner_ytop && y0 < inner_ybottom ? inner_xleft : W0;
int yi_ = y0 * stride_y - pad_top;
for (;;)
{
float s_0, s_1, s_2;
if (dy0 == 3)
{
for (; x0 < x1; x0++)
{
int xi_ = x0 * stride_x - pad_left;
s_0 = s_1 = s_2 = biasval;
for (int k = 0; k < ksize; k++)
{
int dy = yxtab[k * 2];
int yi = yi_ + dy;
int xi = xi_ + yxtab[k * 2 + 1];
float w = weights[k];
if ((unsigned) xi < (unsigned) Wi)
{
s_0 += inptr[yi * Wi + xi] * w;
s_1 += inptr[(yi + 1) * Wi + xi] * w;
s_2 += inptr[(yi + 2) * Wi + xi] * w;
}
}
if (ifMinMaxAct)
{
s_0 = std::min(std::max(s_0, minval), maxval);
s_1 = std::min(std::max(s_1, minval), maxval);
s_2 = std::min(std::max(s_2, minval), maxval);
}
outptr[x0] = s_0;
outptr[x0 + W0] = s_1;
outptr[x0 + W0 * 2] = s_2;
}
}
else
{
for (; x0 < x1; x0++)
{
int xi_ = x0 * stride_x - pad_left;
s_0 = biasval;
for (int k = 0; k < ksize; k++) {
int dy = yxtab[k * 2];
int yi = yi_ + dy;
int xi = xi_ + yxtab[k * 2 + 1];
float w = weights[k];
if (((unsigned) yi < (unsigned) Hi) & ((unsigned) xi < (unsigned) Wi))
s_0 += inptr[yi * Wi + xi] * w;
}
if (ifMinMaxAct)
s_0 = std::min(std::max(s_0, minval), maxval);
outptr[x0] = s_0;
}
}
if (x0 == W0)
break;
x1 = inner_xright;
if (useSIMD)
{
if (is3x3)
{
if (dy0 == 3)
{
for (; x0 <= x1 - VECSZ; x0 += VECSZ)
{
int xi_ = x0 * stride_x - pad_left;
const float *inptr_xi = inptr + Wi * yi_ + xi_;
__m256 s0, s1, s2;
__m256 x00 = _mm256_loadu_ps(inptr_xi);
__m256 x01 = _mm256_loadu_ps(inptr_xi + 1);
__m256 x02 = _mm256_loadu_ps(inptr_xi + 2);
__m256 x10 = _mm256_loadu_ps(inptr_xi + Wi);
__m256 x11 = _mm256_loadu_ps(inptr_xi + Wi + 1);
__m256 x12 = _mm256_loadu_ps(inptr_xi + Wi + 2);
__m256 x20 = _mm256_loadu_ps(inptr_xi + Wi * 2);
__m256 x21 = _mm256_loadu_ps(inptr_xi + Wi * 2 + 1);
__m256 x22 = _mm256_loadu_ps(inptr_xi + Wi * 2 + 2);
__m256 x30 = _mm256_loadu_ps(inptr_xi + Wi * 3);
__m256 x31 = _mm256_loadu_ps(inptr_xi + Wi * 3 + 1);
__m256 x32 = _mm256_loadu_ps(inptr_xi + Wi * 3 + 2);
__m256 x40 = _mm256_loadu_ps(inptr_xi + Wi * 4);
__m256 x41 = _mm256_loadu_ps(inptr_xi + Wi * 4 + 1);
__m256 x42 = _mm256_loadu_ps(inptr_xi + Wi * 4 + 2);
s0 = _mm256_fmadd_ps(x00, w0, vbias);
s1 = _mm256_fmadd_ps(x10, w0, vbias);
s2 = _mm256_fmadd_ps(x20, w0, vbias);
s0 = _mm256_fmadd_ps(x01, w1, s0);
s1 = _mm256_fmadd_ps(x11, w1, s1);
s2 = _mm256_fmadd_ps(x21, w1, s2);
s0 = _mm256_fmadd_ps(x02, w2, s0);
s1 = _mm256_fmadd_ps(x12, w2, s1);
s2 = _mm256_fmadd_ps(x22, w2, s2);
s0 = _mm256_fmadd_ps(x10, w3, s0);
s1 = _mm256_fmadd_ps(x20, w3, s1);
s2 = _mm256_fmadd_ps(x30, w3, s2);
s0 = _mm256_fmadd_ps(x11, w4, s0);
s1 = _mm256_fmadd_ps(x21, w4, s1);
s2 = _mm256_fmadd_ps(x31, w4, s2);
s0 = _mm256_fmadd_ps(x12, w5, s0);
s1 = _mm256_fmadd_ps(x22, w5, s1);
s2 = _mm256_fmadd_ps(x32, w5, s2);
s0 = _mm256_fmadd_ps(x20, w6, s0);
s1 = _mm256_fmadd_ps(x30, w6, s1);
s2 = _mm256_fmadd_ps(x40, w6, s2);
s0 = _mm256_fmadd_ps(x21, w7, s0);
s1 = _mm256_fmadd_ps(x31, w7, s1);
s2 = _mm256_fmadd_ps(x41, w7, s2);
s0 = _mm256_fmadd_ps(x22, w8, s0);
s1 = _mm256_fmadd_ps(x32, w8, s1);
s2 = _mm256_fmadd_ps(x42, w8, s2);
if (ifMinMaxAct)
{
s0 = _mm256_min_ps(_mm256_max_ps(s0, vminval), vmaxval);
s1 = _mm256_min_ps(_mm256_max_ps(s1, vminval), vmaxval);
s2 = _mm256_min_ps(_mm256_max_ps(s2, vminval), vmaxval);
}
_mm256_storeu_ps(outptr + x0, s0);
_mm256_storeu_ps(outptr + W0 + x0, s1);
_mm256_storeu_ps(outptr + W0 * 2 + x0, s2);
}
}
else
{
for (; x0 <= x1 - VECSZ; x0 += VECSZ)
{
int xi_ = x0 * stride_x - pad_left;
const float *inptr_xi = inptr + Wi * yi_ + xi_;
__m256 s0 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[0]), w0, vbias);
__m256 s1 = _mm256_mul_ps(_mm256_loadu_ps(inptr_xi + ofstab[1]), w1);
__m256 s2 = _mm256_mul_ps(_mm256_loadu_ps(inptr_xi + ofstab[2]), w2);
s0 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[3]), w3, s0);
s1 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[4]), w4, s1);
s2 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[5]), w5, s2);
s0 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[6]), w6, s0);
s1 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[7]), w7, s1);
s2 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[8]), w8, s2);
s0 = _mm256_add_ps(_mm256_add_ps(s0, s1), s2);
if (ifMinMaxAct)
s0 = _mm256_min_ps(_mm256_max_ps(s0, vminval), vmaxval);
_mm256_storeu_ps(outptr + x0, s0);
}
}
}
else
{
for (; x0 <= x1 - VECSZ; x0 += VECSZ)
{
int xi_ = x0 * stride_x - pad_left, k = 0;
const float *inptr_xi = inptr + Wi * yi_ + xi_;
__m256 s0 = vbias;
for (; k <= ksize - 4; k += 4)
{
__m256 v0 = _mm256_loadu_ps(inptr_xi + ofstab[k]);
__m256 v1 = _mm256_loadu_ps(inptr_xi + ofstab[k + 1]);
__m256 v2 = _mm256_loadu_ps(inptr_xi + ofstab[k + 2]);
__m256 v3 = _mm256_loadu_ps(inptr_xi + ofstab[k + 3]);
__m256 ww0 = _mm256_set1_ps(weights[k]);
__m256 ww1 = _mm256_set1_ps(weights[k+1]);
__m256 ww2 = _mm256_set1_ps(weights[k+2]);
__m256 ww3 = _mm256_set1_ps(weights[k+3]);
s0 = _mm256_fmadd_ps(v0, ww0, s0);
s0 = _mm256_fmadd_ps(v1, ww1, s0);
s0 = _mm256_fmadd_ps(v2, ww2, s0);
s0 = _mm256_fmadd_ps(v3, ww3, s0);
}
for (; k < ksize; k++)
s0 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[k]),
_mm256_set1_ps(weights[k]), s0);
if (ifMinMaxAct)
s0 = _mm256_min_ps(_mm256_max_ps(s0, vminval), vmaxval);
_mm256_storeu_ps(outptr + x0, s0);
}
}
}
if (dy0 == 3)
{
for (; x0 < x1; x0++)
{
int xi_ = x0 * stride_x - pad_left;
const float *inptr_xi = inptr + W0 * yi_ + xi_;
s_0 = s_1 = s_2 = biasval;
for (int k = 0; k < ksize; k++) {
int inp_ofs = ofstab[k];
float w = weights[k];
s_0 += inptr_xi[inp_ofs] * w;
s_1 += inptr_xi[inp_ofs + Wi] * w;
s_2 += inptr_xi[inp_ofs + Wi * 2] * w;
}
if (ifMinMaxAct)
{
s_0 = std::min(std::max(s_0, minval), maxval);
s_1 = std::min(std::max(s_1, minval), maxval);
s_2 = std::min(std::max(s_2, minval), maxval);
}
outptr[x0] = s_0;
outptr[x0 + W0] = s_1;
outptr[x0 + W0 * 2] = s_2;
}
}
else
{
for (; x0 < x1; x0++)
{
int xi_ = x0 * stride_x - pad_left;
const float *inptr_xi = inptr + Wi * yi_ + xi_;
s_0 = biasval;
for (int k = 0; k < ksize; k++)
{
s_0 += inptr_xi[ofstab[k]] * weights[k];
}
if (ifMinMaxAct)
s_0 = std::min(std::max(s_0, minval), maxval);
outptr[x0] = s_0;
}
}
x1 = W0;
}
}
}
#endif
} // namespace opt_AVX2
} // namespace cv
@@ -0,0 +1,694 @@
// 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.
// This file is modified from the ficus (https://github.com/vpisarev/ficus/blob/master/lib/NN/OpConv.fx).
// Here is the original license:
/*
This file is a part of ficus language project.
See ficus/LICENSE for the licensing terms
*/
#include "../../precomp.hpp"
#include "fast_convolution.hpp"
#include "fast_convolution.simd.hpp"
namespace cv { namespace dnn {
Ptr<FastConv2d> initFastConv2d(
int ngroups,
int K, int C, int Hk, int Wk,
int stride_x, int stride_y,
int dilation_x, int dilation_y,
const std::vector<size_t>& pads_begin,
const std::vector<size_t>& pads_end,
float* srcWeights,
float* srcBias)
{
Ptr<FastConv2d> conv = makePtr<FastConv2d>();
CV_Assert(ngroups > 0 && K > 0 && C > 0 && K % ngroups == 0);
CV_Assert(Hk > 0 && Wk > 0);
CV_Assert(stride_y > 0 && stride_x > 0);
CV_Assert(dilation_y > 0 && dilation_x > 0);
conv->K = K; conv->C = C; conv->Hk = Hk; conv->Wk = Wk; // [K, iC, kH, kW]
conv->stride_y = stride_y;
conv->stride_x = stride_x;
conv->dilation_y = dilation_y;
conv->dilation_x = dilation_x;
conv->ngroups = ngroups;
conv->pad_top = pads_begin[0];
conv->pad_bottom = pads_end[0];
conv->pad_left = pads_begin[1];
conv->pad_right = pads_end[1];
// store bias; append some zero's to make sure that
// we can always read FAST_CONV_MR elements starting from any valid index
{
int k = 0, nbias = K + FAST_CONV_MR-1;
conv->biasBuf.reserve(nbias);
float* biasBufPtr = conv->biasBuf.data();
for(; k < K; k++)
biasBufPtr[k] = srcBias ? srcBias[k] : 0.f;
for(; k < nbias; k++)
biasBufPtr[k] = 0.f;
}
#if CV_NEON // For now, winograd is ARM platform only.
if (ngroups == 1 && Hk ==3 && Wk == 3 && stride_x == 1 && stride_y == 1 && dilation_x == 1 && dilation_y ==1
&& K >= 16 && C >= 16 )
conv->ifWinograd63 = true;
#else
conv->ifWinograd63 = false;
#endif
if (ngroups > 1 && ngroups == K && ngroups == C)
{
// for depth-wise convolutions on NCHW data we just preserve the weights in KCHW layout,
// but add some padding to make the weights array layout more SIMD-friendly
int ksize = Hk*Wk;
int padded_ksize = ((ksize + FAST_VEC_NLANES-1)/FAST_VEC_NLANES)*FAST_VEC_NLANES; // this code aims to let memory fit with vector size.
int nweights = C*padded_ksize;
conv->weightsBuf.reserve(nweights);
float* weightsBufPtr = conv->weightsBuf.data();
memset(weightsBufPtr, 0, nweights*sizeof(weightsBufPtr[0]));
for(int c = 0; c < C; c++)
{
for (int k = 0; k < ksize; k++)
weightsBufPtr[c*padded_ksize + k] = srcWeights[c*ksize + k];
}
}
else
{
// The weights are packed as
// ngroups x (ceil((K/ngroups)/FAST_CONV_MR)*FAST_CONV_MR) x (Cg*Hk*Wk) x FAST_CONV_MR tensor
int Kg = K/ngroups, Cg = max(C/ngroups, 1);
int Kg_aligned = ((Kg + FAST_CONV_MR - 1)/FAST_CONV_MR)*FAST_CONV_MR;
size_t nweights = ngroups*Kg_aligned*Cg*Hk*Wk;
conv->weightsBuf.reserve(nweights);
float* weightsBufPtr = conv->weightsBuf.data();
memset(weightsBufPtr, 0, nweights*sizeof(weightsBufPtr[0]));
float* packed_wptr = weightsBufPtr;
// pack the weight.
for(int g = 0; g < ngroups; g++)
{
for(int k0 = 0; k0 < Kg_aligned; k0 += FAST_CONV_MR)
{
int dk = Kg - k0 < FAST_CONV_MR ? Kg - k0 : FAST_CONV_MR;
for(int c = 0; c < Cg; c++)
{
for(int yx = 0; yx < Hk*Wk; yx++, packed_wptr += FAST_CONV_MR)
{
const float* wptr = srcWeights + ((g*Kg + k0)*Cg + c)*Hk*Wk + yx;
int k = 0;
for(; k < dk; k++, wptr += Cg*Hk*Wk)
packed_wptr[k] = *wptr;
for(; k < FAST_CONV_MR; k++)
packed_wptr[k] = 0.f;
}
}
}
}
// Prepare Weight for Winograd F(6x6, 3x3)
if (conv->ifWinograd63)
{
initWinograd63(conv, srcWeights, K, C);
}
}
return conv;
}
static void packInput(float* inpbuf, const float* inptr, int* yxtab, int ksize, int Cg, int Hi, int Wi, int W0,
int pad_top, int pad_left, int stride_x, int stride_y, int yx0, int slice_len,
bool fast_1x1, bool partial0, bool s1d1p0, bool s1d1)
{
const size_t inp_planesize = (size_t)Hi*Wi;
if (fast_1x1)
{
/*
super-fast branch for 1x1 convolutions with sy=sx=1.
in this case each feature plane can be safely treated
as 1D array and we just extract next portion
of FAST_CONV_NR elements from each feature plane and
put it together.
*/
inptr += yx0;
if (!partial0)
{
// Make special branch where memcpy() is called with a constant buffer size.
// Compilers will likely unroll this loop properly.
for (int c = 0; c < Cg; c++, inptr += inp_planesize, inpbuf += FAST_CONV_NR)
memcpy(inpbuf, inptr, FAST_CONV_NR * sizeof(inpbuf[0]));
}
else
{
for (int c = 0; c < Cg; c++, inptr += inp_planesize, inpbuf += FAST_CONV_NR)
{
memcpy(inpbuf, inptr, slice_len * sizeof(inpbuf[0]));
memset(inpbuf + slice_len, 0, (FAST_CONV_NR - slice_len) * sizeof(inpbuf[0]));
}
}
}
else if (s1d1p0)
{
/*
slower, but still fast branch for sy=sx=1, dy=dx=1 and without padding,
in this case we copy data from input tensors by chunks.
*/
for (int c = 0; c < Cg; c++)
{
float *inpbuf_c = inpbuf + c * (FAST_CONV_NR * ksize);
const float *inptr_c = inptr + c * inp_planesize;
for (int k = 0; k < ksize; k++)
{
int y0 = yx0 / W0, x0 = yx0 % W0;
int yi = y0 + yxtab[k * 2], xi = x0 + yxtab[k * 2 + 1];
float *inpbuf_k = inpbuf_c + k * FAST_CONV_NR;
int xi_0 = yxtab[k * 2 + 1];
int i = 0;
for (; i < slice_len;)
{
const float *inptr_k = inptr_c + yi * Wi + xi;
int copy_len = std::min(slice_len - i, W0 - x0);
int di_z = (slice_len == i + copy_len) ? FAST_CONV_NR - slice_len : 0;
memcpy(inpbuf_k + i,
inptr_k,
copy_len * sizeof(inpbuf_k[0]));
memset(inpbuf_k + i + copy_len,
0, di_z * sizeof(inpbuf_k[0]));
i += copy_len;
x0 = 0;
xi = xi_0;
yi++;
}
}
}
}
else if (s1d1)
{
/*
slower, but still fast branch for sy=sx=1, dy=dx=1.
in this case we copy data from input tensors by chunks and
interleave the data in inpbuf with 0's
(that correspond to the padding elements) when necessary
*/
int y0 = yx0 / W0, x0 = yx0 % W0;
for (int c = 0; c < Cg; c++)
{
float *inpbuf_c = inpbuf + c * (FAST_CONV_NR * ksize);
const float *inptr_c = inptr + c * inp_planesize;
for (int k = 0; k < ksize; k++)
{
int x0_tmp = x0;
int xi_0 = yxtab[k * 2 + 1] - pad_left;
int yi = y0 + yxtab[k * 2] - pad_top, xi = x0_tmp + xi_0;
float *inpbuf_k = inpbuf_c + k * FAST_CONV_NR;
int i = 0;
for (; i < slice_len;) {
int copyLen = std::min(slice_len - i, W0 - x0_tmp);
int di_z = (i + copyLen == slice_len) ? FAST_CONV_NR - slice_len
: 0; // The final padding.
// pad_top or pad bottom
if (yi < 0 || yi > Hi - 1)
{
memset(inpbuf_k + i,
0, (copyLen + di_z) * sizeof(inpbuf_k[0]));
i += copyLen + di_z;
}
else
{
int x_pad_left = 0, x_pad_right = 0;
// pad_left
if (xi < 0)
{
x_pad_left = std::min(-xi, copyLen);
xi = 0;
copyLen -= x_pad_left;
}
memset(inpbuf_k + i,
0, x_pad_left * sizeof(inpbuf_k[0]));
i += x_pad_left;
// pad right
if (xi + copyLen > Wi)
{
if (xi > Wi)
{
x_pad_right = copyLen;
copyLen = 0;
}
else
{
x_pad_right = std::min(xi + copyLen - Wi, copyLen);
copyLen -= x_pad_right;
}
}
CV_Assert(copyLen >= 0);
const float *inptr_k = inptr_c + yi * Wi + xi;
memcpy(inpbuf_k + i,
inptr_k,
copyLen * sizeof(inpbuf_k[0]));
i += copyLen;
// pad_right and the final padding.
memset(inpbuf_k + i,
0, (di_z + x_pad_right) * sizeof(inpbuf_k[0]));
i += x_pad_right + di_z;
}
x0_tmp = 0;
xi = xi_0;
yi++;
}
}
}
}
else
{
int y0_ = yx0 / W0, x0_ = yx0 - y0_ * W0;
for (int k = 0; k < ksize; k++)
{
int dy = yxtab[k * 2], dx = yxtab[k * 2 + 1];
int i = 0, y0 = y0_, x0 = x0_;
for (; i < FAST_CONV_NR;)
{
float *inpbuf_ki = inpbuf + k * FAST_CONV_NR + i;
int yi = y0 * stride_y + dy - pad_top;
int xi = x0 * stride_x + dx - pad_left;
if ((unsigned) yi < (unsigned) Hi &&
(unsigned) xi < (unsigned) Wi)
{
const float *inptr_ki = inptr + yi * Wi + xi;
if (i + 4 <= FAST_CONV_NR && x0 + 4 <= W0 && xi + stride_x * 4 <= Wi)
{
if (stride_x == 2) {
for (int c = 0; c < Cg; c++, inpbuf_ki += FAST_CONV_NR *
ksize, inptr_ki += inp_planesize)
{
float t0 = inptr_ki[0], t1 = inptr_ki[2];
float t2 = inptr_ki[4], t3 = inptr_ki[6];
inpbuf_ki[0] = t0;
inpbuf_ki[1] = t1;
inpbuf_ki[2] = t2;
inpbuf_ki[3] = t3;
}
}
else
{
for (int c = 0; c < Cg; c++, inpbuf_ki += FAST_CONV_NR *
ksize, inptr_ki += inp_planesize)
{
float t0 = inptr_ki[0], t1 = inptr_ki[stride_x];
float t2 = inptr_ki[stride_x * 2], t3 = inptr_ki[stride_x * 3];
inpbuf_ki[0] = t0;
inpbuf_ki[1] = t1;
inpbuf_ki[2] = t2;
inpbuf_ki[3] = t3;
}
}
i += 4;
x0 += 4;
}
else
{
for (int c = 0; c < Cg; c++, inpbuf_ki += FAST_CONV_NR *
ksize, inptr_ki += inp_planesize)
*inpbuf_ki = *inptr_ki;
i++;
x0++;
}
}
else
{
for (int c = 0; c < Cg; c++, inpbuf_ki += FAST_CONV_NR * ksize)
inpbuf_ki[0] = 0.f;
i++;
x0++;
}
int mask = x0 >= W0;
y0 += mask;
x0 &= mask - 1;
}
}
}
}
static void matMulCompute(float* outptr0, float* inpbuf_task, float* cbuf, const Ptr<FastConv2d>& conv, int HkWkCg,
int k0, int k1, int yx0, int yx1, size_t out_planesize, int g, int Kg, int Kg_aligned,
bool partial0, ActivationLayer*& activ, float minval, float maxval, bool ifMinMaxAct)
{
int outstep0 = out_planesize;
for (int k = k0; k < k1; k += FAST_CONV_MR, outptr0 += outstep0 * FAST_CONV_MR)
{
int dk = Kg - k < FAST_CONV_MR ? Kg - k : FAST_CONV_MR;
bool partial = partial0 || dk < FAST_CONV_MR;
float *outptr = outptr0;
int outstep = outstep0;
if (partial)
{
outptr = cbuf;
outstep = FAST_CONV_NR;
}
#if CV_TRY_AVX2
if (conv->useAVX2)
opt_AVX2::convBlock_AVX2( HkWkCg, conv->weightsBuf.data() + (g * Kg_aligned + k) * HkWkCg,
inpbuf_task, outptr, outstep, conv->biasBuf.data() + Kg * g + k,
minval, maxval, ifMinMaxAct);
else
#endif
#if CV_TRY_NEON
if (conv->useNEON)
opt_NEON::convBlock_NEON(HkWkCg, conv->weightsBuf.data() + (g * Kg_aligned + k) * HkWkCg,
inpbuf_task, outptr, outstep, conv->biasBuf.data() + Kg * g + k,
minval, maxval, ifMinMaxAct);
else
#endif
convBlock(HkWkCg, conv->weightsBuf.data() + (g * Kg_aligned + k) * HkWkCg,
inpbuf_task, outptr, outstep, conv->biasBuf.data() + Kg * g + k,
minval, maxval, ifMinMaxAct);
// activation
if (activ)
activ->forwardSlice(outptr, outptr, yx1 - yx0, outstep, Kg * g + k,
Kg * g + k + dk);
if (partial)
{
for (int i = 0; i < dk; i++)
memcpy(outptr0 + i * outstep0, cbuf + i * FAST_CONV_NR,
(yx1 - yx0) * sizeof(cbuf[0]));
}
}
}
void runFastConv2d(InputArray _input, OutputArray _output,
const Ptr<FastConv2d>& conv, int ntasks, const Ptr<ActivationLayer>& actLayer)
{
Mat input = _input.getMat();
Mat output = _output.getMat();
MatShape inputShape = shape(input);
MatShape outputShape = shape(output);
CV_Assert(inputShape.size() == 4 && outputShape.size() == 4);
ActivationLayer* activ = 0;
float minval = -FLT_MAX, maxval = FLT_MAX;
bool ifMinMaxAct = false;
if (actLayer)
{
Ptr<ReLULayer> activ_relu = actLayer.dynamicCast<ReLULayer>();
Ptr<ReLU6Layer> activ_relu6 = actLayer.dynamicCast<ReLU6Layer>();
if (!activ_relu.empty())
{
if (activ_relu->negativeSlope == 0.0f)
{
minval = 0.0f;
ifMinMaxAct = true;
activ = nullptr;
}
else // Leaky ReLU
{
activ = actLayer.get();
}
}
else if (!activ_relu6.empty())
{
minval = activ_relu6->minValue;
maxval = activ_relu6->maxValue;
ifMinMaxAct = true;
activ = nullptr;
}
else
activ = actLayer.get();
}
else
activ = nullptr;
if (conv->ngroups > 1 && conv->ngroups == conv->K && conv->ngroups == conv->C)
{
return runDepthwise(input, output, conv, minval, maxval, activ, ifMinMaxAct);
}
#if CV_NEON
if ( conv->ifWinograd63
&& inputShape[2] > 12 && inputShape[3] > 12
&& inputShape[2] < 120 && inputShape[3] < 120 )
{
// In general, for winograd branch, more cores will give better performance.
int maxNumThread = std::max(getNumThreads(), 1);
if (runWinograd63(input, output, conv, maxNumThread, minval, maxval, activ, ifMinMaxAct))
return;
}
#endif
float* inp = input.ptr<float>();
float* out = output.ptr<float>();
int N = inputShape[0], C = inputShape[1], Hi = inputShape[2], Wi = inputShape[3]; // [N, C, H, W]
int K = conv->K, Hk = conv->Hk, Wk = conv->Wk;
int H0 = outputShape[2], W0 = outputShape[3], ngroups = conv->ngroups; // ngroups
int Cg = C/ngroups, Kg = K/ngroups;
int Kg_nblocks = (Kg + FAST_CONV_MR-1)/FAST_CONV_MR, Kg_aligned = Kg_nblocks*FAST_CONV_MR; // align to MR
const size_t inp_planesize = (size_t)Hi*Wi;
const size_t out_planesize = (size_t)H0*W0;
int pad_top = conv->pad_top, pad_bottom = conv->pad_bottom;
int pad_left = conv->pad_left;
int pad_right = conv->pad_right;
int stride_y = conv->stride_y, stride_x = conv->stride_x;
int dilation_y = conv->dilation_y, dilation_x = conv->dilation_x;
int ksize = Hk * Wk;
bool s1d1 = stride_x == 1 && stride_y == 1 && dilation_x == 1 && dilation_y == 1;
bool s1d1p0 = s1d1 && pad_top == 0 && pad_left ==0 && pad_bottom == 0 && pad_right == 0;
bool fast_1x1 = stride_x == 1 && stride_y == 1 && ksize == 1;
int HkWkCg = Hk*Wk*Cg;
enum { VEC_ALIGN = 8, DFT_TYPE = CV_32F };
size_t taskbufsize = FAST_CONV_NR*HkWkCg; // input buffer
size_t taskbufsizeOutput = FAST_CONV_NR * FAST_CONV_MR;
size_t inputbufsize = 0;
size_t outbufsize = ntasks * taskbufsizeOutput;
int stripes_per_sample = (out_planesize + FAST_CONV_NR - 1)/FAST_CONV_NR; // align to NR
size_t hw_task = stripes_per_sample;
size_t hw_aligned = stripes_per_sample * FAST_CONV_NR;
bool separatedLoop = false;
if (stripes_per_sample < 4 * ntasks)
{
// If stripes_per_sample is small, we parallelize on K (output channel).
stripes_per_sample = 1;
// Separated Parallelloop could save much time in packing input data. But it may cost more memory, we use it when batch size is 1.
if (N == 1)
{
separatedLoop = true;
inputbufsize = ngroups * hw_aligned * HkWkCg;
}
if (!separatedLoop)
{
inputbufsize = taskbufsize * ntasks;
}
}
else
{
// If stripes_per_sample is big, we parallelize on H0*W0.
Kg_nblocks = 1;
inputbufsize = taskbufsize * ntasks;
}
int Kstripes = Kg_nblocks*stripes_per_sample;
int nsubtasks = N*ngroups*Kstripes;
AutoBuffer<float> inpbuf_all_, outputbuf_;
inputbufsize = alignSize(inputbufsize, VEC_ALIGN);
inpbuf_all_.allocate(inputbufsize + VEC_ALIGN);
float* inpbuf_all = alignPtr(inpbuf_all_.data(), (int)(VEC_ALIGN*sizeof(float)));
outbufsize = alignSize(outbufsize, VEC_ALIGN);
outputbuf_.allocate(outbufsize + VEC_ALIGN);
float* output_buf = alignPtr(outputbuf_.data(), (int)(VEC_ALIGN*sizeof(float)));
std::vector<int> ofstab_(Hk*Wk*3, 0);
int* ofstab = ofstab_.data();
int* yxtab = ofstab + Hk*Wk;
for (int y = 0; y < Hk; y++)
for( int x = 0; x < Wk; x++)
{
int k = y*Wk + x;
int dy = y*dilation_y, dx = x*dilation_x;
yxtab[k*2] = dy;
yxtab[k*2+1] = dx;
ofstab[k] = dy*Wi + dx;
}
if (ksize == 1)
{
CV_Assert(pad_left == 0 && pad_right == 0 && pad_top == 0 && pad_bottom == 0);
CV_Assert(stride_x != 1 || stride_y != 1 || (H0 == Hi && W0 == Wi));
}
if (separatedLoop)
{
// For now this branch only handles batch size = 1. Maybe we could support batch size < 10 in the future.
// Pack Input data
parallel_for_(Range(0, ngroups * hw_task), [&](const Range& r0)
{
for (int nhwi = r0.start; nhwi < r0.end; nhwi++)
{
int g = nhwi/hw_task;
int hw_i = nhwi % hw_task;
int hw0 = hw_i * FAST_CONV_NR;
float* inpbuf = inpbuf_all + g * hw_aligned * HkWkCg + hw0 * HkWkCg;
const float* inptr = inp + g * Cg * inp_planesize;
bool partial0 = hw0 + FAST_CONV_NR > out_planesize? true: false;
int slice_len = FAST_CONV_NR;
if (partial0)
slice_len = out_planesize - hw0;
packInput(inpbuf, inptr, yxtab, ksize, Cg, Hi, Wi, W0, pad_top, pad_left, stride_x, stride_y,
hw0, slice_len, fast_1x1, partial0, s1d1p0, s1d1);
}
});
// Compute
parallel_for_(Range(0, ntasks), [&](const Range& r0)
{
for (int task_id = r0.start; task_id < r0.end; task_id++)
{
float *cbuf = output_buf + task_id * taskbufsizeOutput;
int ngs0 = (int) ((size_t) nsubtasks * task_id / ntasks);
int ngs1 = (int) ((size_t) nsubtasks * (task_id + 1) / ntasks);
for (int subtask = ngs0; subtask < ngs1;)
{
int ng = subtask / Kstripes;
int kyx0 = subtask - ng * Kstripes;
int kyx1 = kyx0 + (ngs1 - subtask);
int n = ng / ngroups, g = ng - n * ngroups;
CV_Assert(n <= 1);
kyx1 = kyx1 <= Kstripes ? kyx1 : Kstripes; // Guarantee that maximum kyx1 is Kstripes.
subtask += kyx1 - kyx0;
int k0 = kyx0 * FAST_CONV_MR;
int k1 = kyx1 * FAST_CONV_MR;
k1 = k1 <= Kg ? k1 : Kg;
for (int yx0 = 0; yx0 < out_planesize; yx0 += FAST_CONV_NR)
{
float* inpbuf_task = inpbuf_all + g * hw_aligned * HkWkCg + yx0 * HkWkCg;
int yx1 = yx0 + FAST_CONV_NR;
yx1 = yx1 <= out_planesize ? yx1 : out_planesize;
int slice_len = yx1 - yx0;
bool partial0 = slice_len < FAST_CONV_NR;
int outstep0 = out_planesize;
size_t outofs = ((n * ngroups + g) * Kg + k0) * outstep0 + yx0;
float *outptr0 = out + outofs;
matMulCompute(outptr0, inpbuf_task, cbuf, conv, HkWkCg, k0, k1, yx0, yx1, out_planesize, g,
Kg, Kg_aligned, partial0, activ, minval, maxval, ifMinMaxAct);
}
}
}
});
}
else
{
parallel_for_(Range(0, ntasks), [&](const Range &r0) {
for (int task_id = r0.start; task_id < r0.end; task_id++) {
float *inpbuf_task = &inpbuf_all[taskbufsize * task_id];
float *cbuf = output_buf + task_id * taskbufsizeOutput;
int ngs0 = (int) ((size_t) nsubtasks * task_id / ntasks);
int ngs1 = (int) ((size_t) nsubtasks * (task_id + 1) / ntasks);
for (int subtask = ngs0; subtask < ngs1;)
{
int ng = subtask / Kstripes;
int kyx0 = subtask - ng * Kstripes;
int kyx1 = kyx0 + (ngs1 - subtask);
int n = ng / ngroups, g = ng - n * ngroups;
size_t inp_plane_ofs = (size_t) (n * ngroups + g) * Cg * inp_planesize;
kyx1 = kyx1 <= Kstripes ? kyx1 : Kstripes; // Guarantee that maximum kyx1 is Kstripes.
subtask += kyx1 - kyx0;
int k0, k1;
int yx0, yx_limit;
if (stripes_per_sample == 1)
{
k0 = kyx0 * FAST_CONV_MR;
k1 = kyx1 * FAST_CONV_MR;
k1 = k1 <= Kg ? k1 : Kg;
yx0 = 0;
yx_limit = out_planesize;
}
else
{
k0 = 0;
k1 = Kg;
yx0 = kyx0 * FAST_CONV_NR;
yx_limit = kyx1 * FAST_CONV_NR;
yx_limit = yx_limit < out_planesize ? yx_limit : out_planesize;
}
for (; yx0 < yx_limit; yx0 += FAST_CONV_NR)
{
float *inpbuf = inpbuf_task;
const float *inptr = inp + inp_plane_ofs;
int yx1 = yx0 + FAST_CONV_NR;
yx1 = yx1 <= yx_limit ? yx1 : yx_limit;
int slice_len = yx1 - yx0;
bool partial0 = slice_len < FAST_CONV_NR;
packInput(inpbuf, inptr, yxtab, ksize, Cg, Hi, Wi, W0, pad_top, pad_left, stride_x, stride_y,
yx0, slice_len, fast_1x1, partial0, s1d1p0, s1d1);
// 2. do convolution, compute Kg x (yx1 - yx0) part of the output tensor
int outstep0 = out_planesize;
size_t outofs = ((n * ngroups + g) * Kg + k0) * outstep0 + yx0;
float *outptr0 = out + outofs;
matMulCompute(outptr0, inpbuf_task, cbuf, conv, HkWkCg, k0, k1, yx0, yx1, out_planesize, g,
Kg, Kg_aligned, partial0, activ, minval, maxval, ifMinMaxAct);
}
}
}
});
}
}
}} // namespace cv::dnn
@@ -0,0 +1,89 @@
// 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_FAST_CONVOLUTION_HPP
#define OPENCV_FAST_CONVOLUTION_HPP
#include "opencv2/core/hal/intrin.hpp"
#ifndef FAST_CONV_PRAM
#define FAST_CONV_PRAM
#if CV_NEON && CV_NEON_AARCH64 // 32 registers.
#define FAST_CONV_MR 4
#define FAST_CONV_NR 28
enum { FAST_VEC_NLANES=4 };
#elif CV_NEON // 16 registers.
#define FAST_CONV_MR 4
#define FAST_CONV_NR 12
enum { FAST_VEC_NLANES=4 };
#else // SIMD 128, AVX or AVX2
#define FAST_CONV_MR 4
#define FAST_CONV_NR 24
enum { FAST_VEC_NLANES=4 };
#endif
#endif
namespace cv {
namespace dnn {
struct FastConv2d
{
int ngroups;
int K, C, Hk, Wk;
int stride_y, stride_x;
int dilation_y, dilation_x;
int pad_top, pad_bottom, pad_left, pad_right;
std::vector<float> weightsBuf; // For generic Conv 2D
std::vector<float> weightsWino63Buf; // For Winograd F(6x6, 3x3).
std::vector<float> biasBuf;
bool ifWinograd63 = false;
bool useAVX2 = checkHardwareSupport(CPU_AVX2);
bool useNEON = checkHardwareSupport(CPU_NEON);
};
// return a FastConv2d instance.
Ptr<FastConv2d> initFastConv2d(
int ngroups,
int K, int C, int Hk, int Wk,
int stride_x, int stride_y,
int dilation_x, int dilation_y,
const std::vector<size_t>& pads_begin,
const std::vector<size_t>& pads_end,
float* srcWeights,
float* srcBias);
// It contains different computing branches, like winograd, 1x1 conv.
void runFastConv2d(InputArray _input, OutputArray _output,
const Ptr<FastConv2d>& conv, int ntasks, const Ptr<ActivationLayer>& actLayer);
void runDepthwise(InputArray _input, OutputArray _output, const Ptr<FastConv2d>& conv, float minval, float maxval,
ActivationLayer* activ, bool ifMinMaxAct);
// winograd init
void initWinograd63(Ptr<FastConv2d>& conv, float* src_weight, int K, int C);
int runWinograd63(InputArray _input, OutputArray _output, const Ptr<FastConv2d>& conv, int ntasks,
float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct);
} // namespace dnn
namespace opt_AVX2
{
#if CV_TRY_AVX2
void convBlock_AVX2(int k, const float *a, const float *b,
float *c, int ldc, const float *bias,
float minval, float maxval, bool ifActiv);
void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights, float biasval, int *ofstab, int *yxtab,
float minval, float maxval, int Hi, int Wi, int H0, int W0, int ksize, int pad_top, int pad_left,
int dilation_y, int stride_x, int stride_y, int inner_xleft, int inner_xright, int inner_ytop,
int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3);
#endif
} // namespace opt_AVX2
} // namespace cv
#endif //OPENCV_FAST_CONVOLUTION_HPP
@@ -0,0 +1,343 @@
// 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_FAST_CONVOLUTION_SIMD_HPP
#define OPENCV_FAST_CONVOLUTION_SIMD_HPP
#include "opencv2/core/hal/intrin.hpp"
#include <opencv2/core/utils/logger.hpp>
namespace cv {
namespace dnn {
void convBlock(int k, const float *a, const float *b,
float *c, int ldc, const float *bias,
float minval, float maxval, bool ifActiv)
{
#if CV_SIMD128
#if FAST_CONV_MR == 4 && FAST_CONV_NR == 24
{
v_float32x4 c0 = v_setall_f32(bias[0]), c1 = c0, c2 = c0, c3 = c0, c4 = c0, c5 = c0;
v_float32x4 c6 = v_setall_f32(bias[1]), c7 = c6, c8 = c6, c9 = c6, c10 = c6, c11 = c6;
v_float32x4 c12 = v_setall_f32(bias[2]), c13 = c12, c14 = c12, c15 = c12, c16 = c12, c17 = c12;
v_float32x4 c18 = v_setall_f32(bias[3]), c19 = c18, c20 = c18, c21 = c18, c22 = c18, c23 = c18;
for (int p = 0; p < k; p++, a += FAST_CONV_MR, b += FAST_CONV_NR)
{
v_float32x4 a0 = v_setall_f32(a[0]);
v_float32x4 b0 = v_load(b), b1 = v_load(b + 4), b2 = v_load(b + 8);
v_float32x4 b3 = v_load(b + 12), b4 = v_load(b + 16), b5 = v_load(b + 20);
c0 = v_fma(b0, a0, c0);
c1 = v_fma(b1, a0, c1);
c2 = v_fma(b2, a0, c2);
c3 = v_fma(b3, a0, c3);
c4 = v_fma(b4, a0, c4);
c5 = v_fma(b5, a0, c5);
a0 = v_setall_f32(a[1]);
c6 = v_fma(b0, a0, c6);
c7 = v_fma(b1, a0, c7);
c8 = v_fma(b2, a0, c8);
c9 = v_fma(b3, a0, c9);
c10 = v_fma(b4, a0, c10);
c11 = v_fma(b5, a0, c11);
a0 = v_setall_f32(a[2]);
c12 = v_fma(b0, a0, c12);
c13 = v_fma(b1, a0, c13);
c14 = v_fma(b2, a0, c14);
c15 = v_fma(b3, a0, c15);
c16 = v_fma(b4, a0, c16);
c17 = v_fma(b5, a0, c17);
a0 = v_setall_f32(a[3]);
c18 = v_fma(b0, a0, c18);
c19 = v_fma(b1, a0, c19);
c20 = v_fma(b2, a0, c20);
c21 = v_fma(b3, a0, c21);
c22 = v_fma(b4, a0, c22);
c23 = v_fma(b5, a0, c23);
}
if (ifActiv) {
v_float32x4 vmin = v_setall_f32(minval), vmax = v_setall_f32(maxval);
c0 = v_min(v_max(c0, vmin), vmax);
c1 = v_min(v_max(c1, vmin), vmax);
c2 = v_min(v_max(c2, vmin), vmax);
c3 = v_min(v_max(c3, vmin), vmax);
c4 = v_min(v_max(c4, vmin), vmax);
c5 = v_min(v_max(c5, vmin), vmax);
c6 = v_min(v_max(c6, vmin), vmax);
c7 = v_min(v_max(c7, vmin), vmax);
c8 = v_min(v_max(c8, vmin), vmax);
c9 = v_min(v_max(c9, vmin), vmax);
c10 = v_min(v_max(c10, vmin), vmax);
c11 = v_min(v_max(c11, vmin), vmax);
c12 = v_min(v_max(c12, vmin), vmax);
c13 = v_min(v_max(c13, vmin), vmax);
c14 = v_min(v_max(c14, vmin), vmax);
c15 = v_min(v_max(c15, vmin), vmax);
c16 = v_min(v_max(c16, vmin), vmax);
c17 = v_min(v_max(c17, vmin), vmax);
c18 = v_min(v_max(c18, vmin), vmax);
c19 = v_min(v_max(c19, vmin), vmax);
c20 = v_min(v_max(c20, vmin), vmax);
c21 = v_min(v_max(c21, vmin), vmax);
c22 = v_min(v_max(c22, vmin), vmax);
c23 = v_min(v_max(c23, vmin), vmax);
}
v_store(c, c0);
v_store(c + 4, c1);
v_store(c + 8, c2);
v_store(c + 12, c3);
v_store(c + 16, c4);
v_store(c + 20, c5);
v_store(c + ldc, c6);
v_store(c + ldc + 4, c7);
v_store(c + ldc + 8, c8);
v_store(c + ldc + 12, c9);
v_store(c + ldc + 16, c10);
v_store(c + ldc + 20, c11);
v_store(c + ldc * 2, c12);
v_store(c + ldc * 2 + 4, c13);
v_store(c + ldc * 2 + 8, c14);
v_store(c + ldc * 2 + 12, c15);
v_store(c + ldc * 2 + 16, c16);
v_store(c + ldc * 2 + 20, c17);
v_store(c + ldc * 3, c18);
v_store(c + ldc * 3 + 4, c19);
v_store(c + ldc * 3 + 8, c20);
v_store(c + ldc * 3 + 12, c21);
v_store(c + ldc * 3 + 16, c22);
v_store(c + ldc * 3 + 20, c23);
}
#endif
#else
for (int i = 0; i < FAST_CONV_MR; i++)
{
float beta = bias[i];
for (int j = 0; j < FAST_CONV_NR; j++)
c[i*ldc + j] = beta;
}
for (int p = 0; p < k; p++)
{
for (int i = 0; i < FAST_CONV_MR; i++)
{
float alpha = a[FAST_CONV_MR*p + i];
for (int j = 0; j < FAST_CONV_NR; j++)
{
c[i*ldc+j] += b[FAST_CONV_NR*p + j]*alpha;
}
}
}
if (ifActiv)
{
for (int i = 0; i < FAST_CONV_MR; i++)
{
for (int j = 0; j < FAST_CONV_NR; j++)
{
float v = c[i*ldc + j];
v = std::min(std::max(v, minval), maxval);
c[i*ldc + j] = v;
}
}
}
#endif
}
} // namespace dnn
namespace opt_NEON
{
#if CV_TRY_NEON
void convBlock_NEON(int k, const float *a, const float *b,
float *c, int ldc, const float *bias,
float minval, float maxval, bool ifActiv)
{
#if CV_NEON_AARCH64 && FAST_CONV_MR == 4 && FAST_CONV_NR == 28 // AARCH64
{
float32x4_t c0 = vdupq_n_f32(bias[0]), c1 = c0, c2 = c0, c3 = c0, c4 = c0, c5 = c0, c24 = c0;
float32x4_t c6 = vdupq_n_f32(bias[1]), c7 = c6, c8 = c6, c9 = c6, c10 = c6, c11 = c6, c25 = c6;
float32x4_t c12 = vdupq_n_f32(bias[2]), c13 = c12, c14 = c12, c15 = c12, c16 = c12, c17 = c12, c26 = c12;
float32x4_t c18 = vdupq_n_f32(bias[3]), c19 = c18, c20 = c18, c21 = c18, c22 = c18, c23 = c18, c27 = c18;
float32x4_t a0 = vdupq_n_f32(0.0f);
float32x4_t b0 = vdupq_n_f32(0.0f), b1 = vdupq_n_f32(0.0f), b2 = vdupq_n_f32(0.0f);
for (int p = 0; p < k; p++, a += FAST_CONV_MR)
{
a0 = vld1q_f32(a);
b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4), b2 = vld1q_f32(b + 8);
b += 12;
c0 = vfmaq_laneq_f32(c0, b0, a0, 0);
c1 = vfmaq_laneq_f32(c1, b1, a0, 0);
c2 = vfmaq_laneq_f32(c2, b2, a0, 0);
c6 = vfmaq_laneq_f32(c6, b0, a0, 1);
c7 = vfmaq_laneq_f32(c7, b1, a0, 1);
c8 = vfmaq_laneq_f32(c8, b2, a0, 1);
c12 = vfmaq_laneq_f32(c12, b0, a0, 2);
c13 = vfmaq_laneq_f32(c13, b1, a0, 2);
c14 = vfmaq_laneq_f32(c14, b2, a0, 2);
c18 = vfmaq_laneq_f32(c18, b0, a0, 3);
c19 = vfmaq_laneq_f32(c19, b1, a0, 3);
c20 = vfmaq_laneq_f32(c20, b2, a0, 3);
b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4), b2 = vld1q_f32(b + 8);
b += 12;
c3 = vfmaq_laneq_f32(c3, b0, a0, 0);
c4 = vfmaq_laneq_f32(c4, b1, a0, 0);
c5 = vfmaq_laneq_f32(c5, b2, a0, 0);
c9 = vfmaq_laneq_f32(c9, b0, a0, 1);
c10 = vfmaq_laneq_f32(c10, b1, a0, 1);
c11 = vfmaq_laneq_f32(c11, b2, a0, 1);
c15 = vfmaq_laneq_f32(c15, b0, a0, 2);
c16 = vfmaq_laneq_f32(c16, b1, a0, 2);
c17 = vfmaq_laneq_f32(c17, b2, a0, 2);
c21 = vfmaq_laneq_f32(c21, b0, a0, 3);
b0 = vld1q_f32(b);
b += 4;
c22 = vfmaq_laneq_f32(c22, b1, a0, 3);
c23 = vfmaq_laneq_f32(c23, b2, a0, 3);
c24 = vfmaq_laneq_f32(c24, b0, a0, 0);
c25 = vfmaq_laneq_f32(c25, b0, a0, 1);
c26 = vfmaq_laneq_f32(c26, b0, a0, 2);
c27 = vfmaq_laneq_f32(c27, b0, a0, 3);
}
if (ifActiv) {
b0 = vdupq_n_f32(minval), b1 = vdupq_n_f32(maxval);
c0 = vminq_f32(vmaxq_f32(c0, b0), b1);
c1 = vminq_f32(vmaxq_f32(c1, b0), b1);
c2 = vminq_f32(vmaxq_f32(c2, b0), b1);
c3 = vminq_f32(vmaxq_f32(c3, b0), b1);
c4 = vminq_f32(vmaxq_f32(c4, b0), b1);
c5 = vminq_f32(vmaxq_f32(c5, b0), b1);
c6 = vminq_f32(vmaxq_f32(c6, b0), b1);
c7 = vminq_f32(vmaxq_f32(c7, b0), b1);
c8 = vminq_f32(vmaxq_f32(c8, b0), b1);
c9 = vminq_f32(vmaxq_f32(c9, b0), b1);
c10 = vminq_f32(vmaxq_f32(c10, b0), b1);
c11 = vminq_f32(vmaxq_f32(c11, b0), b1);
c12 = vminq_f32(vmaxq_f32(c12, b0), b1);
c13 = vminq_f32(vmaxq_f32(c13, b0), b1);
c14 = vminq_f32(vmaxq_f32(c14, b0), b1);
c15 = vminq_f32(vmaxq_f32(c15, b0), b1);
c16 = vminq_f32(vmaxq_f32(c16, b0), b1);
c17 = vminq_f32(vmaxq_f32(c17, b0), b1);
c18 = vminq_f32(vmaxq_f32(c18, b0), b1);
c19 = vminq_f32(vmaxq_f32(c19, b0), b1);
c20 = vminq_f32(vmaxq_f32(c20, b0), b1);
c21 = vminq_f32(vmaxq_f32(c21, b0), b1);
c22 = vminq_f32(vmaxq_f32(c22, b0), b1);
c23 = vminq_f32(vmaxq_f32(c23, b0), b1);
c24 = vminq_f32(vmaxq_f32(c24, b0), b1);
c25 = vminq_f32(vmaxq_f32(c25, b0), b1);
c26 = vminq_f32(vmaxq_f32(c26, b0), b1);
c27 = vminq_f32(vmaxq_f32(c27, b0), b1);
}
vst1q_f32(c, c0);
vst1q_f32(c + 4, c1);
vst1q_f32(c + 8, c2);
vst1q_f32(c + 12, c3);
vst1q_f32(c + 16, c4);
vst1q_f32(c + 20, c5);
vst1q_f32(c + 24, c24);
vst1q_f32(c + ldc, c6);
vst1q_f32(c + ldc + 4, c7);
vst1q_f32(c + ldc + 8, c8);
vst1q_f32(c + ldc + 12, c9);
vst1q_f32(c + ldc + 16, c10);
vst1q_f32(c + ldc + 20, c11);
vst1q_f32(c + ldc + 24, c25);
vst1q_f32(c + ldc * 2, c12);
vst1q_f32(c + ldc * 2 + 4, c13);
vst1q_f32(c + ldc * 2 + 8, c14);
vst1q_f32(c + ldc * 2 + 12, c15);
vst1q_f32(c + ldc * 2 + 16, c16);
vst1q_f32(c + ldc * 2 + 20, c17);
vst1q_f32(c + ldc * 2 + 24, c26);
vst1q_f32(c + ldc * 3, c18);
vst1q_f32(c + ldc * 3 + 4, c19);
vst1q_f32(c + ldc * 3 + 8, c20);
vst1q_f32(c + ldc * 3 + 12, c21);
vst1q_f32(c + ldc * 3 + 16, c22);
vst1q_f32(c + ldc * 3 + 20, c23);
vst1q_f32(c + ldc * 3 + 24, c27);
}
#elif (!defined(CV_NEON_AARCH64) || !CV_NEON_AARCH64) && FAST_CONV_MR == 4 && FAST_CONV_NR == 12 // ARMv7
{
float32x4_t c0 = vdupq_n_f32(bias[0]), c1 = c0, c2 = c0;
float32x4_t c3 = vdupq_n_f32(bias[1]), c4 = c3, c5 = c3;
float32x4_t c6 = vdupq_n_f32(bias[2]), c7 = c6, c8 = c6;
float32x4_t c9 = vdupq_n_f32(bias[3]), c10 = c9, c11 = c9;
float32x2_t a0 = vdup_n_f32(0.0f), a1 = a0;
float32x4_t b0 = vdupq_n_f32(0.0f), b1 = vdupq_n_f32(0.0f), b2 = vdupq_n_f32(0.0f);
for (int p = 0; p < k; p++, a += FAST_CONV_MR, b += FAST_CONV_NR)
{
a0 = vld1_f32(a), a1 = vld1_f32(a+2);
b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4), b2 = vld1q_f32(b + 8);
c0 = vmlaq_lane_f32(c0, b0, a0, 0);
c1 = vmlaq_lane_f32(c1, b1, a0, 0);
c2 = vmlaq_lane_f32(c2, b2, a0, 0);
c3 = vmlaq_lane_f32(c3, b0, a0, 1);
c4 = vmlaq_lane_f32(c4, b1, a0, 1);
c5 = vmlaq_lane_f32(c5, b2, a0, 1);
c6 = vmlaq_lane_f32(c6, b0, a1, 0);
c7 = vmlaq_lane_f32(c7, b1, a1, 0);
c8 = vmlaq_lane_f32(c8, b2, a1, 0);
c9 = vmlaq_lane_f32(c9 , b0, a1, 1);
c10 = vmlaq_lane_f32(c10, b1, a1, 1);
c11 = vmlaq_lane_f32(c11, b2, a1, 1);
}
if (ifActiv)
{
b0 = vdupq_n_f32(minval), b1 = vdupq_n_f32(maxval);
c0 = vminq_f32(vmaxq_f32(c0, b0), b1);
c1 = vminq_f32(vmaxq_f32(c1, b0), b1);
c2 = vminq_f32(vmaxq_f32(c2, b0), b1);
c3 = vminq_f32(vmaxq_f32(c3, b0), b1);
c4 = vminq_f32(vmaxq_f32(c4, b0), b1);
c5 = vminq_f32(vmaxq_f32(c5, b0), b1);
c6 = vminq_f32(vmaxq_f32(c6, b0), b1);
c7 = vminq_f32(vmaxq_f32(c7, b0), b1);
c8 = vminq_f32(vmaxq_f32(c8, b0), b1);
c9 = vminq_f32(vmaxq_f32(c9, b0), b1);
c10 = vminq_f32(vmaxq_f32(c10, b0), b1);
c11 = vminq_f32(vmaxq_f32(c11, b0), b1);
}
vst1q_f32(c, c0); vst1q_f32(c+4, c1); vst1q_f32(c+8, c2);
vst1q_f32(c + ldc, c3); vst1q_f32(c + ldc + 4, c4); vst1q_f32(c + ldc + 8, c5);
vst1q_f32(c + ldc*2, c6); vst1q_f32(c + ldc*2 + 4, c7); vst1q_f32(c + ldc*2 + 8, c8);
vst1q_f32(c + ldc*3, c9); vst1q_f32(c + ldc*3 + 4, c10); vst1q_f32(c + ldc*3 + 8, c11);
}
#else
#error "unsupported FAST_CONV_MR and/or FAST_CONV_NR in convBlock_NEON."
#endif
}
#endif
} // namespace opt_NEON
} // namespace cv
#endif //OPENCV_FAST_CONVOLUTION_SIMD_HPP
File diff suppressed because it is too large Load Diff
@@ -619,26 +619,36 @@ public:
Mat weightsQuantized(weightsMat.rows, weightsMat.cols, CV_8S);
Mat biasQuantized(1, numOutput, CV_32S);
Mat outputMultiplier(1, numOutput, CV_32F);
bool perChannel = params.get<bool>("per_channel", true);
double realMin, realMax, weightsScale;
for( int i = 0; i < numOutput; i++ )
if (perChannel) // per-Channel quantization.
{
// Quantize weights
cv::minMaxIdx(weightsMat.row(i), &realMin, &realMax);
realMin = std::min(realMin, 0.0);
realMax = std::max(realMax, 0.0);
weightsScale = (realMax == realMin) ? 1.0 : std::max(-realMin, realMax)/127;
weightsMat.row(i).convertTo(weightsQuantized.row(i), CV_8S, 1.f/weightsScale);
for (int i = 0; i < numOutput; i++)
{
double weightsScale = getWeightScale(weightsMat.row(i));
// Quantize biases
weightsMat.row(i).convertTo(weightsQuantized.row(i), CV_8S, 1.f/weightsScale);
float biasScale = inputScale * weightsScale;
biasQuantized.at<int>(i) = cvRound(biasMat.at<float>(i)/biasScale) - inputZp*(cv::sum(weightsQuantized.row(i))[0]);
outputMultiplier.at<float>(i) = biasScale / outputScale;
}
}
else // per-Tensor quantization.
{
double weightsScale = getWeightScale(weightsMat);
weightsMat.convertTo(weightsQuantized, CV_8S, 1.f/weightsScale);
float biasScale = inputScale * weightsScale;
biasQuantized.at<int>(i) = (int)std::round(biasMat.at<float>(i)/biasScale) - inputZp*(cv::sum(weightsQuantized.row(i))[0]);
// Store multiplier
outputMultiplier.at<float>(i) = biasScale / outputScale;
for (int i = 0; i < numOutput; i++)
{
biasQuantized.at<int>(i) = cvRound(biasMat.at<float>(i)/biasScale) - inputZp*(cv::sum(weightsQuantized.row(i))[0]);
outputMultiplier.at<float>(i) = biasScale / outputScale;
}
}
params.blobs.clear();
params.set("per_channel", perChannel);
params.blobs.push_back(weightsQuantized.reshape(1, shape(blobs[0])));
params.blobs.push_back(biasQuantized);
params.blobs.push_back(outputMultiplier);
+11
View File
@@ -250,5 +250,16 @@ void getConvPoolPaddings(const std::vector<int>& inp, const std::vector<size_t>&
}
}
double getWeightScale(const Mat& weightsMat)
{
double realMin, realMax;
cv::minMaxIdx(weightsMat, &realMin, &realMax);
realMin = std::min(realMin, 0.0);
realMax = std::max(realMax, 0.0);
return (realMax == realMin) ? 1.0 : std::max(-realMin, realMax)/127;
}
}
}
+4 -1
View File
@@ -70,9 +70,12 @@ void getConvPoolOutParams(const std::vector<int>& inp, const std::vector<size_t>
const std::vector<size_t>& stride, const String &padMode,
const std::vector<size_t>& dilation, std::vector<int>& out);
void getConvPoolPaddings(const std::vector<int>& inp, const std::vector<size_t>& kernel,
void getConvPoolPaddings(const std::vector<int>& inp, const std::vector<size_t>& kernel,
const std::vector<size_t>& strides, const String &padMode,
std::vector<size_t>& pads_begin, std::vector<size_t>& pads_end);
// Used in quantized model. It will return the (Max_element - Min_element)/127.
double getWeightScale(const Mat& weightsMat);
}
}
@@ -0,0 +1,664 @@
// 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 "../precomp.hpp"
#include "layers_common.hpp"
#include <opencv2/dnn/shape_utils.hpp>
#include <algorithm>
#include <iterator>
#include <numeric>
namespace cv
{
namespace dnn
{
class NaryEltwiseLayerImpl CV_FINAL : public NaryEltwiseLayer
{
public:
enum class OPERATION
{
AND = 0,
EQUAL,
GREATER,
GREATER_EQUAL,
LESS,
LESS_EQUAL,
OR,
POW,
XOR,
BITSHIFT,
MAX,
MEAN,
MIN,
MOD,
PROD,
SUB,
SUM,
ADD,
DIV,
} op;
NaryEltwiseLayerImpl(const LayerParams& params)
{
setParamsFrom(params);
String operation = toLowerCase(params.get<String>("operation", "sum"));
if (operation == "equal")
op = OPERATION::EQUAL;
else if (operation == "greater")
op = OPERATION::GREATER;
else if (operation == "greater_equal")
op = OPERATION::GREATER_EQUAL;
else if (operation == "less")
op = OPERATION::LESS;
else if (operation == "less_equal")
op = OPERATION::LESS_EQUAL;
else if (operation == "pow")
op = OPERATION::POW;
else if (operation == "bitshift")
op = OPERATION::BITSHIFT;
else if (operation == "max")
op = OPERATION::MAX;
else if (operation == "mean")
op = OPERATION::MEAN;
else if (operation == "min")
op = OPERATION::MIN;
else if (operation == "mod")
op = OPERATION::MOD;
else if (operation == "mul")
op = OPERATION::PROD;
else if (operation == "sub")
op = OPERATION::SUB;
else if (operation == "sum")
op = OPERATION::SUM;
else if (operation == "add")
op = OPERATION::ADD;
else if (operation == "div")
op = OPERATION::DIV;
else if (operation == "and")
op = OPERATION::AND;
else if (operation == "or")
op = OPERATION::OR;
else if (operation == "xor")
op = OPERATION::XOR;
else
CV_Error(cv::Error::StsBadArg, "Unknown operation type \"" + operation + "\"");
}
virtual bool supportBackend(int backendId) CV_OVERRIDE
{
return backendId == DNN_BACKEND_OPENCV;
}
static MatShape findCommonShape(std::vector<MatShape> shapes)
{
CV_Assert(!shapes.empty());
const size_t dim = std::max_element(shapes.begin(), shapes.end(),
[](const MatShape& a, const MatShape& b)
{ return a.size() < b.size(); })->size();
for (auto& shape : shapes)
{
shape.insert(shape.begin(), dim - shape.size(), 1);
}
MatShape outShape(dim, 1);
for (size_t i = 0; i < dim; ++i)
{
for (const auto& shape : shapes)
{
if (shape[i] != outShape[i])
{
CV_Assert(shape[i] == 1 || outShape[i] == 1);
outShape[i] = std::max(outShape[i], shape[i]);
}
}
}
return outShape;
}
static bool prepare_for_broadcast_op(
int narrays, int max_ndims, const size_t* elemsize,
const int* ndims, const int** shape_, const size_t** step_,
int** shape, size_t** step)
{
int i, j, k;
// step 1.
// * make all inputs and the output max_ndims-dimensional.
// ** prepend dimension 1 to the mat of less dims
// * compute proper step's
for (i = max_ndims-1; i >= 0; i-- ) {
for (k = 0; k < narrays; k++) {
j = ndims[k] - (max_ndims - i);
int sz_i = j >= 0 ? shape_[k][j] : 1;
size_t st_i = j >= 0 && step_ && step_[k] && step_[k][j] > 0 ? step_[k][j] :
i == max_ndims-1 ? elemsize[k] : step[k][i+1]*shape[k][i+1];
assert(st_i % elemsize[k] == 0);
shape[k][i] = sz_i;
step[k][i] = st_i;
if (shape[k][i] == 0)
return false;
}
}
// step 3. Let's do the flattening first,
// since we'd need proper values of steps to check continuity.
// this loop is probably the most tricky part
// in the whole implementation of broadcasting.
j = max_ndims-1;
for (i = j - 1; i >= 0; i--) {
bool all_contiguous = true, all_scalars = true, all_consistent = true;
for(k = 0; k < narrays; k++) {
size_t st = step[k][j]*shape[k][j];
bool prev_scalar = shape[k][j] == 1;
bool scalar = shape[k][i] == 1;
all_contiguous = all_contiguous && (st == step[k][i]);
all_scalars = all_scalars && scalar;
all_consistent = all_consistent && (scalar == prev_scalar);
}
if (all_contiguous && (all_consistent || all_scalars)) {
for(k = 0; k < narrays; k++)
shape[k][j] *= shape[k][i];
} else {
j--;
if (i < j) {
for(k = 0; k < narrays; k++) {
shape[k][j] = shape[k][i];
step[k][j] = step[k][i];
}
}
}
}
// step 2. Set some step's to 0's.
for (i = max_ndims-1; i >= j; i--) {
for (k = 0; k < narrays; k++)
step[k][i] = shape[k][i] == 1 ? 0 : step[k][i];
}
for (; i >= 0; i--) {
for (k = 0; k < narrays; k++) {
step[k][i] = 0;
shape[k][i] = 1;
}
}
return true;
}
bool getMemoryShapes(const std::vector<MatShape> &inputs,
const int requiredOutputs,
std::vector<MatShape> &outputs,
std::vector<MatShape> &internals) const CV_OVERRIDE
{
MatShape outShape = findCommonShape(inputs);
outputs.assign(1, outShape);
return false;
}
template <typename T, typename Functor>
void binary_forward_impl(
int ndims, const int* shape,
const char* data1, const size_t* step1,
const char* data2, const size_t* step2,
char* data, const size_t* step,
const Functor& op)
{
assert(ndims >= 2);
size_t dp1 = step1[ndims-1]/sizeof(T);
size_t dp2 = step2[ndims-1]/sizeof(T);
size_t dp = step[ndims-1]/sizeof(T);
int k, n1 = shape[ndims-1], n2 = shape[ndims-2];
size_t plane_idx, nplanes = 1;
for (k = 0; k < ndims-2; k++) nplanes *= shape[k];
for (plane_idx = 0; plane_idx < nplanes; plane_idx++) {
const char* ptr1_ = data1;
const char* ptr2_ = data2;
char* ptr_ = data;
size_t idx = plane_idx;
for (k = ndims-3; k >= 0; k--) {
size_t next_idx = idx/shape[k];
int i_k = (int)(idx - next_idx*shape[k]);
ptr1_ += i_k*step1[k];
ptr2_ += i_k*step2[k];
ptr_ += i_k*step[k];
idx = next_idx;
}
for (int i2 = 0; i2 < n2; i2++, ptr1_ += step1[ndims-2],
ptr2_ += step2[ndims-2],
ptr_ += step[ndims-2])
{
const T* ptr1 = (const T*)ptr1_;
const T* ptr2 = (const T*)ptr2_;
T* ptr = (T*)ptr_;
if (dp1 == 1 && dp2 == 1 && dp == 1) {
for(int i1 = 0; i1 < n1; i1++)
ptr[i1] = op(ptr1[i1], ptr2[i1]);
} else if (dp1 == 1 && dp2 == 0 && dp == 1){
T x2 = *ptr2;
for(int i1 = 0; i1 < n1; i1++)
ptr[i1] = op(ptr1[i1], x2);
} else if (dp1 == 0 && dp2 == 1 && dp == 1){
T x1 = *ptr1;
for(int i1 = 0; i1 < n1; i1++)
ptr[i1] = op(x1, ptr2[i1]);
} else {
for(int i1 = 0; i1 < n1; i1++, ptr1 += dp1, ptr2 += dp2, ptr += dp)
*ptr = op(*ptr1, *ptr2);
}
}
}
}
template <typename T, typename Functor>
void binary_forward(const Functor& f, const std::vector<Mat>& inputs, std::vector<Mat>& outputs)
{
const Mat& a = inputs[0];
const Mat& b = inputs[1];
Mat& out = outputs[0];
// collect info of inputs and output
const int* in_shape[] = {a.size.p, b.size.p};
const size_t* in_step[] = {a.step.p, b.step.p};
const int* out_shape = out.size.p;
const size_t* out_step = out.step.p;
const int in_ndims[] = {a.dims, b.dims};
int out_ndims = out.dims;
int max_ndims = std::max(a.dims, std::max(b.dims, out.dims));
// buf holds the folllowing for a, b & output:
// * orig_shapes, shapes (result_shape), orig_steps, steps (result_step), 3*4 elements in total
// * shape_buf & step_buf, 3*2*max_ndims elements in total
// * all_ndims, 3*1 elements in total
// * all_type_sizes, 3*1 elements in total
AutoBuffer<size_t> buf(3 * (2 * max_ndims + 6));
int** orig_shapes = (int**)(buf.data());
int** shapes = orig_shapes + 3;
size_t** orig_steps = (size_t**)(shapes + 3);
size_t** steps = orig_steps + 3;
int* shape_buf = (int*)(steps + 3);
size_t* step_buf = (size_t*)(shape_buf + 3 * max_ndims);
int* all_ndims = (int*)(step_buf + 3 * max_ndims);
size_t* all_type_sizes = (size_t*)(all_ndims + 3);
// assign orig_shapes, shapes, orig_steps, steps, all_ndims, all_type_sizes
for (int i = 0; i < 3; i++)
{
orig_shapes[i] = (int*)(i == 0 ? out_shape : in_shape[i-1]);
orig_steps[i] = (size_t*)(i == 0 ? out_step : in_step[i-1]);
shapes[i] = shape_buf + i * max_ndims;
steps[i] = step_buf + i * max_ndims;
all_ndims[i] = i == 0 ? out_ndims : in_ndims[i-1];
all_type_sizes[i] = sizeof(T);
}
if (!prepare_for_broadcast_op(3, max_ndims, all_type_sizes,
all_ndims, (const int**)orig_shapes,
(const size_t**)orig_steps,
shapes, steps))
return;
binary_forward_impl<T, Functor>(
max_ndims, shapes[0], a.ptr<char>(), steps[1],
b.ptr<char>(), steps[2], out.ptr<char>(), steps[0],
f);
}
template<typename T, typename Functor>
void nary_forward_impl(
const Functor& f, const T scale, int ninputs, int ndims, const int* shape,
const char** inp, char* out,
const size_t** steps, char** ptrs)
{
CV_Assert(ndims >= 2);
size_t dp = steps[0][ndims-1]/sizeof(T);
size_t dp1 = steps[1][ndims-1]/sizeof(T);
size_t dp2 = steps[2][ndims-1]/sizeof(T);
CV_Assert(dp == 1);
enum { BLOCK_SIZE = 1024 };
T blck[BLOCK_SIZE];
int k, i, di1=0, n1 = shape[ndims-1], n2 = shape[ndims-2];
int second = ninputs == 1 ? 1 : 2;
size_t plane_idx, nplanes = 1;
for (k = 0; k < ndims-2; k++) nplanes *= shape[k];
for (plane_idx = 0; plane_idx < nplanes; plane_idx++) {
ptrs[0] = out;
for (i = 0; i < ninputs; i++) ptrs[i+1] = (char*)inp[i];
size_t idx = plane_idx;
for (k = ndims-3; k >= 0; k--) {
size_t next_idx = idx/shape[k];
int i_k = (int)(idx - next_idx*shape[k]);
for (i = 0; i < ninputs; i++)
ptrs[i] += i_k*steps[i][k];
idx = next_idx;
}
for (int i2 = 0; i2 < n2; i2++)
{
const T* ptr1 = (const T*)(ptrs[1] + steps[1][ndims-2]*i2);
const T* ptr2 = (const T*)(ptrs[second] + steps[second][ndims-2]*i2);
T* ptr = (T*)(ptrs[0] + steps[0][ndims-2]*i2);
if (ninputs <= 2) {
if (dp1 == 1 && dp2 == 1) {
for (int i1 = 0; i1 < n1; i1++)
ptr[i1] = saturate_cast<T>(f(ptr1[i1], ptr2[i1])*scale);
} else {
for(int i1 = 0; i1 < n1; i1++, ptr1 += dp1, ptr2 += dp2, ptr += dp)
*ptr = saturate_cast<T>(f(*ptr1, *ptr2)*scale);
}
} else {
for (int i1 = 0; i1 < n1; i1 += di1, ptr += di1) {
di1 = BLOCK_SIZE < n1-i1 ? BLOCK_SIZE : n1-i1;
if (dp1 == 1 && dp2 == 1) {
for (int j = 0; j < di1; j++)
blck[j] = f(ptr1[j], ptr2[j]);
ptr1 += di1;
ptr2 += di1;
} else {
for(int j = 0; j < di1; j++, ptr1 += dp1, ptr2 += dp2)
blck[j] = f(*ptr1, *ptr2);
}
for(i = 2; i < ninputs; i++) {
int dp_i = steps[i+1][ndims-1]/sizeof(T);
const T* ptr_i = (const T*)(ptrs[i+1] +
steps[i+1][ndims-2]*i2) + i1*dp_i;
if (dp_i == 1) {
if (i < ninputs-1) {
for (int j = 0; j < di1; j++)
blck[j] = f(blck[j], ptr_i[j]);
} else {
for (int j = 0; j < di1; j++)
ptr[j] = saturate_cast<T>(f(blck[j], ptr_i[j]) * scale);
}
} else {
if (i < ninputs-1) {
for (int j = 0; j < di1; j++, ptr_i += dp_i)
blck[j] = f(blck[j], *ptr_i);
} else {
for (int j = 0; j < di1; j++, ptr_i += dp_i)
ptr[j] = saturate_cast<T>(f(blck[j], *ptr_i) * scale);
}
}
}
}
}
}
}
}
template <typename T, typename Functor>
void nary_forward(
const Functor& f, T scale,
const std::vector<Mat>& inputs, std::vector<Mat>& outputs
)
{
int ninputs = inputs.size();
// collect all input
std::vector<const char*> v_inp;
std::transform(inputs.begin(), inputs.end(), std::back_inserter(v_inp), [] (const Mat& m) { return m.template ptr<const char>(); });
const char** inp = v_inp.data();
// collect ndims of all input
std::vector<int> v_inp_dims;
std::transform(inputs.begin(), inputs.end(), std::back_inserter(v_inp_dims), [] (const Mat& m) { return m.dims; });
const int* inp_ndims = v_inp_dims.data();
// collect shapes of all input
std::vector<const int*> v_inp_shape;
std::transform(inputs.begin(), inputs.end(), std::back_inserter(v_inp_shape), [] (const Mat& m) { return m.size.p; });
const int** inp_shape = v_inp_shape.data();
// collect steps of all input
std::vector<const size_t*> v_inp_step;
std::transform(inputs.begin(), inputs.end(), std::back_inserter(v_inp_step), [] (const Mat& m) { return m.step.p; });
const size_t** inp_step = v_inp_step.data();
// collect info of output (ndims, shape, step)
char* out = outputs[0].ptr<char>();
int out_ndims = outputs[0].dims;
const int* out_shape = outputs[0].size.p;
const size_t* out_step = outputs[0].step.p;
// find max ndims for broadcasting
int i, max_ndims = out_ndims > 2 ? out_ndims : 2;
for(i = 0; i < ninputs; i++)
max_ndims = max_ndims > inp_ndims[i] ? max_ndims : inp_ndims[i];
// buf holds the following buffers for inputs & output:
// * orig_shapes, shapes (result_shape), orig_steps, steps (result_step), (ninputs+1)*4 elements in total
// * ptrs, (ninputs+1)*1 elements in total
// * shape_buf & step_buf, (ninputs+1)*2*max_ndims elements in total
// * all_ndims, (ninputs+1)*1 elements in total
// * all_type_sizes, (ninputs+1)*1 elements in total
AutoBuffer<size_t> buf((ninputs + 1) * (2 * max_ndims + 7));
int** orig_shapes = (int**)buf.data();
int** shapes = orig_shapes + ninputs + 1;
size_t** orig_steps = (size_t**)(shapes + ninputs + 1);
size_t** steps = orig_steps + ninputs + 1;
char** ptrs = (char**)(steps + ninputs + 1);
size_t* step_buf = (size_t*)(ptrs + ninputs + 1);
int* shape_buf = (int*)(step_buf + (ninputs + 1)*max_ndims);
int* all_ndims = shape_buf + (ninputs + 1)*max_ndims;
size_t* all_type_sizes = (size_t*)(all_ndims + ninputs + 1);
for(i = 0; i <= ninputs; i++) {
all_ndims[i] = i == 0 ? out_ndims : inp_ndims[i-1];
all_type_sizes[i] = sizeof(T);
orig_shapes[i] = (int*)(i == 0 ? out_shape : inp_shape ? inp_shape[i-1] : 0);
orig_steps[i] = (size_t*)(i == 0 ? out_step : inp_step ? inp_step[i-1] : 0);
shapes[i] = shape_buf + max_ndims*i;
steps[i] = step_buf + max_ndims*i;
}
if (!prepare_for_broadcast_op(ninputs + 1, max_ndims, all_type_sizes,
all_ndims, (const int**)orig_shapes,
(const size_t**)orig_steps,
shapes, steps))
return;
nary_forward_impl<T>(
f, scale, ninputs, max_ndims, shapes[0], inp, out, (const size_t **) steps, ptrs);
}
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
{
CV_TRACE_FUNCTION();
CV_TRACE_ARG_VALUE(name, "name", name.c_str());
if (inputs_arr.depth() == CV_16S)
{
forward_fallback(inputs_arr, outputs_arr, internals_arr);
return;
}
std::vector<Mat> inputs, outputs;
inputs_arr.getMatVector(inputs);
outputs_arr.getMatVector(outputs);
// TODO: assert types
typeDispatch(outputs[0].type(), inputs.size(), inputs, outputs);
}
template<typename T, typename... Args>
inline void opDispatch(size_t ninputs, Args&&... args)
{
switch (op)
{
case OPERATION::EQUAL:
{
auto equal = [](const T &a, const T &b) { return a == b; };
binary_forward<T>(equal, std::forward<Args>(args)...);
break;
}
case OPERATION::GREATER:
{
auto greater = [](const T &a, const T &b) { return a > b; };
binary_forward<T>(greater, std::forward<Args>(args)...);
break;
}
case OPERATION::GREATER_EQUAL:
{
auto greater_equal = [](const T &a, const T &b) { return a >= b; };
binary_forward<T>(greater_equal, std::forward<Args>(args)...);
break;
}
case OPERATION::LESS:
{
auto less = [](const T &a, const T &b) { return a < b; };
binary_forward<T>(less, std::forward<Args>(args)...);
break;
}
case OPERATION::LESS_EQUAL:
{
auto less_equal = [](const T &a, const T &b) { return a <= b; };
binary_forward<T>(less_equal, std::forward<Args>(args)...);
break;
}
case OPERATION::POW:
{
auto pow = [] (const T& a, const T& b) { return std::pow(a, b); };
binary_forward<T>(pow, std::forward<Args>(args)...);
break;
}
case OPERATION::BITSHIFT:
{
auto bitshift = [] (const uint8_t &a, const uint8_t &b) { return a << b; };
binary_forward<T>(bitshift, std::forward<Args>(args)...);
break;
}
case OPERATION::MAX:
{
auto max = [](const T &a, const T &b) { return std::max(a, b); };
nary_forward<T>(max, T{1}, std::forward<Args>(args)...);
break;
}
case OPERATION::MEAN:
{
auto mean = [](const T &a, const T &b) { return (a + b) / T{2}; };
nary_forward<T>(mean, T{1} / ninputs, std::forward<Args>(args)...);
break;
}
case OPERATION::MIN:
{
auto min = [](const T &a, const T &b) { return std::min(a, b); };
nary_forward<T>(min, T{1}, std::forward<Args>(args)...);
break;
}
case OPERATION::MOD:
{
auto mod = [](const uint8_t &a, const uint8_t &b) { return a % b; };
binary_forward<T>(mod, std::forward<Args>(args)...);
break;
}
case OPERATION::PROD:
{
auto prod = [](const T &a, const T &b) { return a * b; };
binary_forward<T>(prod, std::forward<Args>(args)...);
break;
}
case OPERATION::SUB:
{
auto sub = [](const T &a, const T &b) { return a - b; };
binary_forward<T>(sub, std::forward<Args>(args)...);
break;
}
case OPERATION::SUM:
{
auto sum = [](const T &a, const T &b) { return a + b; };
nary_forward<T>(sum, T{1}, std::forward<Args>(args)...);
break;
}
case OPERATION::ADD:
{
auto add = [](const T &a, const T &b) { return a + b; };
binary_forward<T>(add, std::forward<Args>(args)...);
break;
}
case OPERATION::DIV:
{
auto div = [](const T &a, const T &b) { return a / b; };
binary_forward<T>(div, std::forward<Args>(args)...);
break;
}
case OPERATION::AND:
{
auto op_and = [](const uint8_t &a, const uint8_t &b) { return a & b; };
binary_forward<T>(op_and, std::forward<Args>(args)...);
break;
}
case OPERATION::OR:
{
auto op_or = [](const uint8_t &a, const uint8_t &b) { return a | b; };
binary_forward<T>(op_or, std::forward<Args>(args)...);
break;
}
case OPERATION::XOR:
{
auto op_xor = [](const uint8_t &a, const uint8_t &b) { return a ^ b; };
binary_forward<T>(op_xor, std::forward<Args>(args)...);
break;
}
default:
CV_Error(Error::StsBadArg, "Unsupported operation.");
};
}
template<typename... Args>
inline void typeDispatch(const int type, Args&&... args)
{
switch (type)
{
case CV_8U:
opDispatch<uint8_t>(std::forward<Args>(args)...);
break;
case CV_32S:
opDispatch<int32_t>(std::forward<Args>(args)...);
break;
case CV_32F:
CV_Assert(op != OPERATION::BITSHIFT && op != OPERATION::MOD &&
op != OPERATION::AND && op != OPERATION::OR &&
op != OPERATION::XOR);
opDispatch<float>(std::forward<Args>(args)...);
break;
default:
CV_Error(cv::Error::BadDepth, "Unsupported type.");
};
}
virtual bool tryQuantize(const std::vector<std::vector<float> > &scales,
const std::vector<std::vector<int> > &zeropoints, LayerParams& params) CV_OVERRIDE
{
return false;
}
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
const std::vector<MatShape> &outputs) const CV_OVERRIDE
{
CV_Assert(inputs.size());
return inputs.size() * total(outputs[0]);
}
};
Ptr<NaryEltwiseLayer> NaryEltwiseLayer::create(const LayerParams& params)
{
return Ptr<NaryEltwiseLayer>(new NaryEltwiseLayerImpl(params));
}
}
}
+24 -4
View File
@@ -61,6 +61,15 @@ public:
{
reduceDims[i] = tempDims.get<int>(i);
}
CV_Assert(params.has("target_dims"));
tempDims = params.get("target_dims");
n = tempDims.size();
targetDims.resize(n);
for (i = 0; i < n; i++)
{
targetDims[i] = tempDims.get<int>(i);
}
}
virtual bool supportBackend(int backendId) CV_OVERRIDE
@@ -325,18 +334,29 @@ public:
std::vector<MatShape> &internals) const CV_OVERRIDE
{
CV_Assert(inputs.size() > 0);
CV_Assert(reduceDims.size() != 0 && inputs[0].size() >= reduceDims.size());
CV_Assert( reduceDims.size() !=0 && targetDims.size() != 0 && inputs[0].size() >= reduceDims.size());
std::vector<int> outShape;
// outShapeTmp can save the right number of `total(outShapeTmp)`. And the outShape is used as the final output shape.
std::vector<int> outShapeTmp, outShape;
outShape.assign(targetDims.begin(), targetDims.end());
if (inputs[0].size() == reduceDims.size())
outShape.push_back(1);
outShapeTmp.push_back(1);
else
{
for (int i = 0; i < inputs[0].size() - reduceDims.size(); i++)
{
outShape.push_back(inputs[0][i]);
outShapeTmp.push_back(inputs[0][i]);
}
}
// Support dynamic shape of Batch size.
// Note that: when there are multiple dynamic inputs, we will give an error.
if (total(outShape) != total(outShapeTmp) && outShape[0] != outShapeTmp[0])
{
outShape[0] = outShapeTmp[0];
}
CV_Assert(total(outShape) == total(outShapeTmp));
outputs.assign(1, outShape);
return false;
+10
View File
@@ -132,6 +132,16 @@ public:
if (hasWeights && hasBias)
CV_CheckEQ(weights.total(), bias.total(), "Incompatible weights/bias blobs");
if (weights.total() == 1)
{
// The total() of bias should be same as weights.
if (hasBias)
inpBlob.convertTo(outBlob, CV_32F, weights.at<float>(0), bias.at<float>(0));
else
inpBlob.convertTo(outBlob, CV_32F, weights.at<float>(0));
return;
}
int endAxis;
for (endAxis = axis + 1; endAxis <= inpBlob.dims; ++endAxis)
{
+2 -2
View File
@@ -115,12 +115,12 @@ void Net::forward(std::vector<std::vector<Mat>>& outputBlobs,
}
// FIXIT drop from inference API
Net Net::quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype)
Net Net::quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype, bool perChannel)
{
CV_TRACE_FUNCTION();
CV_Assert(impl);
CV_Assert(!empty());
return impl->quantize(calibData, inputsDtype, outputsDtype);
return impl->quantize(calibData, inputsDtype, outputsDtype, perChannel);
}
// FIXIT drop from inference API
+1 -1
View File
@@ -258,7 +258,7 @@ struct Net::Impl : public detail::NetImplBase
void dumpNetworkToFile() const;
// FIXIT drop from inference API
Net quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype) /*const*/;
Net quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype, bool perChannel) /*const*/;
void getInputDetails(std::vector<float>& scales, std::vector<int>& zeropoints) /*const*/;
void getOutputDetails(std::vector<float>& scales, std::vector<int>& zeropoints) /*const*/;
+5 -1
View File
@@ -33,7 +33,7 @@ void getQuantizationParams(const Mat& src, std::vector<float>& scales, std::vect
}
// FIXIT drop from inference API
Net Net::Impl::quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype)
Net Net::Impl::quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype, bool perChannel)
{
// Net can be quantized only once.
if (netWasQuantized)
@@ -192,6 +192,10 @@ Net Net::Impl::quantize(InputArrayOfArrays calibData, int inputsDtype, int outpu
inp_out_sc[1] = scales[ld.id];
inp_out_zp[1] = zeropoints[ld.id];
// Set the quantization type, per-tensor quantize or per-channel quantize.
// Especially for Convolution layer and Fully connection layer.
ld.params.set("per_channel", perChannel);
// Quantize layer
Ptr<Layer> layer = ld.layerInstance;
if (layer->tryQuantize(inp_out_sc, inp_out_zp, ld.params))
@@ -531,6 +531,35 @@ public:
}
};
// softplus(x) = log(exp(x) + 1)
class SoftplusSubgraph: public Subgraph
{
public:
SoftplusSubgraph()
{
int input = addNodeToMatch("");
int exp = addNodeToMatch("Exp", input);
int addVal = addNodeToMatch("");
int add = addNodeToMatch("Add", addVal, exp);
addNodeToMatch("Log", add);
setFusedNode("Softplus", input);
}
};
class SoftplusSubgraph2: public Subgraph
{
public:
SoftplusSubgraph2()
{
int input = addNodeToMatch("");
int exp = addNodeToMatch("Exp", input);
int addVal = addNodeToMatch("");
int add = addNodeToMatch("Add", exp, addVal);
addNodeToMatch("Log", add);
setFusedNode("Softplus", input);
}
};
class MulCastSubgraph : public Subgraph
{
public:
@@ -734,6 +763,8 @@ void simplifySubgraphs(opencv_onnx::GraphProto& net)
subgraphs.push_back(makePtr<BatchNormalizationSubgraph1>());
subgraphs.push_back(makePtr<BatchNormalizationSubgraph2>());
subgraphs.push_back(makePtr<ExpandSubgraph>());
subgraphs.push_back(makePtr<SoftplusSubgraph>());
subgraphs.push_back(makePtr<SoftplusSubgraph2>());
subgraphs.push_back(makePtr<MishSubgraph>());
subgraphs.push_back(makePtr<NormalizeSubgraph4>());
subgraphs.push_back(makePtr<NormalizeSubgraph5>());
+335 -379
View File
@@ -63,10 +63,17 @@ class ONNXImporter
LayerInfo(int _layerId = 0, int _outputId = 0) : layerId(_layerId), outputId(_outputId) {}
};
struct TensorInfo {
int real_ndims;
TensorInfo(int _real_ndims = 0) : real_ndims(_real_ndims) {}
};
std::map<std::string, Mat> getGraphTensors(
const opencv_onnx::GraphProto& graph_proto);
Mat getBlob(const opencv_onnx::NodeProto& node_proto, int index);
Mat getBlob(const std::string& input_name);
TensorInfo getBlobExtraInfo(const opencv_onnx::NodeProto& node_proto, int index);
TensorInfo getBlobExtraInfo(const std::string& input_name);
LayerParams getLayerParams(const opencv_onnx::NodeProto& node_proto);
@@ -101,6 +108,7 @@ protected:
std::string framework_name;
std::map<std::string, Mat> constBlobs;
std::map<std::string, TensorInfo> constBlobsExtraInfo;
std::map<std::string, MatShape> outShapes; // List of internal blobs shapes.
bool hasDynamicShapes; // Whether the model has inputs with dynamic shapes
@@ -134,9 +142,6 @@ private:
void parseReduce (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseSlice (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseSplit (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseBias (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parsePow (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseMinMax (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseNeg (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseConstant (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseLSTM (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -148,14 +153,12 @@ private:
void parseElu (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseTanh (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseAbs (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseCompare (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parsePRelu (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseLRN (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseInstanceNormalization(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseBatchNormalization (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseGemm (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseMatMul (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseMul (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseConv (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseConvTranspose (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseTranspose (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -175,6 +178,8 @@ private:
void parseSoftMax (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseDetectionOutput (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseCumSum (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseElementWise (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseDepthToSpace (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseSimpleLayers (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
// Domain: com.microsoft
@@ -196,8 +201,18 @@ private:
void parseOperatorSet();
const std::string str_domain_ai_onnx = "ai.onnx";
bool useLegacyNames;
bool getParamUseLegacyNames()
{
bool param = utils::getConfigurationParameterBool("OPENCV_DNN_ONNX_USE_LEGACY_NAMES", false);
return param;
}
std::string extractNodeName(const opencv_onnx::NodeProto& node_proto);
};
class ONNXLayerHandler : public detail::LayerHandler
{
public:
@@ -232,6 +247,7 @@ ONNXImporter::ONNXImporter(Net& net, const char *onnxFile)
: layerHandler(DNN_DIAGNOSTICS_RUN ? new ONNXLayerHandler(this) : nullptr)
, dstNet(net)
, onnx_opset(0)
, useLegacyNames(getParamUseLegacyNames())
{
hasDynamicShapes = false;
CV_Assert(onnxFile);
@@ -255,6 +271,7 @@ ONNXImporter::ONNXImporter(Net& net, const char* buffer, size_t sizeBuffer)
: layerHandler(DNN_DIAGNOSTICS_RUN ? new ONNXLayerHandler(this) : nullptr)
, dstNet(net)
, onnx_opset(0)
, useLegacyNames(getParamUseLegacyNames())
{
hasDynamicShapes = false;
CV_LOG_DEBUG(NULL, "DNN/ONNX: processing in-memory ONNX model (" << sizeBuffer << " bytes)");
@@ -277,6 +294,7 @@ ONNXImporter::ONNXImporter(Net& net, const char* buffer, size_t sizeBuffer)
populateNet();
}
inline void replaceLayerParam(LayerParams& layerParams, const String& oldKey, const String& newKey)
{
if (layerParams.has(oldKey)) {
@@ -385,6 +403,7 @@ std::map<std::string, Mat> ONNXImporter::getGraphTensors(
continue;
layers_weights.insert(std::make_pair(tensor_proto.name(), mat));
constBlobsExtraInfo.insert(std::make_pair(tensor_proto.name(), TensorInfo(tensor_proto.dims_size())));
}
return layers_weights;
}
@@ -492,6 +511,7 @@ LayerParams ONNXImporter::getLayerParams(const opencv_onnx::NodeProto& node_prot
opencv_onnx::TensorProto tensor = attribute_proto.t();
Mat blob = getMatFromTensor(tensor);
lp.blobs.push_back(blob);
lp.set("original_dims_of_mat", tensor.dims_size());
}
else if (attribute_proto.has_g())
{
@@ -559,6 +579,23 @@ Mat ONNXImporter::getBlob(const std::string& input_name)
return constBlob->second;
}
ONNXImporter::TensorInfo ONNXImporter::getBlobExtraInfo(const opencv_onnx::NodeProto &node_proto, int index)
{
CV_Assert(index < node_proto.input_size());
const std::string& input_name = node_proto.input(index);
return getBlobExtraInfo(input_name);
}
ONNXImporter::TensorInfo ONNXImporter::getBlobExtraInfo(const std::string& input_name)
{
std::map<std::string, TensorInfo>::const_iterator constBlobExtraInfo = constBlobsExtraInfo.find(input_name);
if (constBlobExtraInfo == constBlobsExtraInfo.end())
{
CV_Error(Error::StsBadArg, std::string("Blob ") + input_name + " not found in const blobs of extra info");
}
return constBlobExtraInfo->second;
}
void ONNXImporter::addLayer(LayerParams& layerParams,
const opencv_onnx::NodeProto& node_proto)
{
@@ -908,11 +945,14 @@ const ONNXImporter::DispatchMap& ONNXImporter::getDispatchMap(const opencv_onnx:
return it->second;
}
const std::string& extractNodeName(const opencv_onnx::NodeProto& node_proto)
std::string ONNXImporter::extractNodeName(const opencv_onnx::NodeProto& node_proto)
{
// We need to rework DNN outputs API, this is a workaround for #21698
if (node_proto.has_name() && !node_proto.name().empty())
{
return node_proto.name();
if (useLegacyNames)
return node_proto.name();
return cv::format("onnx_node!%s", node_proto.name().c_str());
}
for (int i = 0; i < node_proto.output_size(); ++i)
{
@@ -922,7 +962,9 @@ const std::string& extractNodeName(const opencv_onnx::NodeProto& node_proto)
// the second method is to use an empty string in place of an input or output name.
if (!name.empty())
{
return name;
if (useLegacyNames)
return name.c_str();
return cv::format("onnx_node_output_%d!%s", i, name.c_str());
}
}
CV_Error(Error::StsAssert, "Couldn't deduce Node name.");
@@ -1161,36 +1203,47 @@ void ONNXImporter::parseReduce(LayerParams& layerParams, const opencv_onnx::Node
layerParams.set("reduce", reduceType);
bool keepdims = layerParams.get<int>("keepdims", 1) == 1;
if (layer_type == "ReduceSum" && node_proto.input_size() == 2)
{
// TODO support the opset 13 of ReduceSum.
// in opset 13, the ReduceSum has two input, it takes axes as input instead of attribute
// details:https://github.com/onnx/onnx/issues/3420#issuecomment-844295687
CV_Error(Error::StsNotImplemented, "Unsupported " + layer_type + " operation of opset 13, please try to "
"re-export the onnx model with opset 11.");
}
MatShape inpShape = outShapes[node_proto.input(0)];
std::vector<bool> shouldDelete(inpShape.size(), false);
if (layerParams.has("axes"))
if (layer_type == "ReduceSum" && node_proto.input_size() == 2)
{
DictValue axes = layerParams.get("axes");
for (int i = 0; i < axes.size(); i++)
if (constBlobs.find(node_proto.input(1)) != constBlobs.end())
{
int axis = normalize_axis(axes.get<int>(i), inpShape.size());
shouldDelete[axis] = true;
Mat axesMat = getBlob(node_proto, 1);
int axesNum = axesMat.total();
for (int i = 0; i < axesNum; i++)
{
int axis = normalize_axis(axesMat.at<int>(i), inpShape.size());
shouldDelete[axis] = true;
}
}
else
// in opset 13, the ReduceSum has two input, it takes axes as input instead of attribute
// details:https://github.com/onnx/onnx/issues/3420#issuecomment-844295687
CV_Error(Error::StsNotImplemented, "Non-constant axis values in ReduceSum are not supported.");
}
else
{
for (int i = 0; i < inpShape.size(); i++)
if (layerParams.has("axes"))
{
shouldDelete[i] = true;
DictValue axes = layerParams.get("axes");
for (int i = 0; i < axes.size(); i++)
{
int axis = normalize_axis(axes.get<int>(i), inpShape.size());
shouldDelete[axis] = true;
}
}
else
{
for (int i = 0; i < inpShape.size(); i++)
{
shouldDelete[i] = true;
}
}
}
MatShape targetShape;
std::vector<int> targetShape;
for (int i = 0; i < inpShape.size(); ++i)
{
if (!shouldDelete[i])
@@ -1260,19 +1313,10 @@ void ONNXImporter::parseReduce(LayerParams& layerParams, const opencv_onnx::Node
}
}
LayerParams reduceLp = layerParams;
reduceLp.name = layerParams.name + "/reduce";
CV_Assert(layer_id.find(reduceLp.name) == layer_id.end());
reduceLp.set("deleted_dims", DictValue::arrayInt(&deletedDims[0], deletedDims.size()));
layerParams.set("deleted_dims", DictValue::arrayInt(&deletedDims[0], deletedDims.size()));
layerParams.set("target_dims", DictValue::arrayInt(&targetShape[0], targetShape.size()));
node_proto.set_input(0, inputString);
node_proto.set_output(0, reduceLp.name);
addLayer(reduceLp, node_proto);
layerParams.type = (depth == CV_8S) ? "ReshapeInt8" : "Reshape";
layerParams.set("dim", DictValue::arrayInt(&targetShape[0], targetShape.size()));
node_proto.set_input(0, node_proto.output(0));
node_proto.set_output(0, output_name);
addLayer(layerParams, node_proto);
@@ -1410,145 +1454,6 @@ void ONNXImporter::parseSplit(LayerParams& layerParams, const opencv_onnx::NodeP
addLayer(layerParams, node_proto);
}
void ONNXImporter::parseBias(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_)
{
opencv_onnx::NodeProto node_proto = node_proto_;
const std::string& layer_type = node_proto.op_type();
bool isSub = layer_type == "Sub";
if (layer_type == "Sum" && node_proto.input_size() == 1)
{
layerParams.type = "Identity";
addLayer(layerParams, node_proto);
return;
}
CV_Assert((node_proto.input_size() == 2) || (layer_type == "Sum" && node_proto.input_size() > 2));
if (layer_type == "Sum" && node_proto.input_size() > 2)
{
for (int i = 0; i < node_proto.input_size(); ++i)
{
if (layer_id.find(node_proto.input(i)) == layer_id.end())
{
CV_Error(Error::StsNotImplemented, "Sum of constants is not implemented for inputs > 2");
}
}
}
bool is_const_0 = layer_id.find(node_proto.input(0)) == layer_id.end();
bool is_const_1 = layer_id.find(node_proto.input(1)) == layer_id.end();
if (is_const_0 && is_const_1)
{
Mat blob_0 = getBlob(node_proto, 0);
Mat blob_1 = getBlob(node_proto, 1);
CV_Assert(blob_0.size == blob_1.size);
Mat output = isSub ? (blob_0 - blob_1) : (blob_0 + blob_1);
addConstant(node_proto.output(0), output);
return;
}
else if (is_const_0 || is_const_1)
{
int const_blob_id = is_const_0 ? 0 : 1;
int input_id = 1 - const_blob_id;
Mat blob = getBlob(node_proto, const_blob_id);
int blob_total = blob.total();
const float inputScale = isSub && is_const_0 ? -1.f : 1.f;
const float constScale = isSub && is_const_1 ? -1.f : 1.f;
if (blob_total == 1) {
layerParams.type = "Power";
layerParams.set("scale", inputScale);
layerParams.set("shift", constScale * blob.ptr<float>()[0]);
}
else {
MatShape inpShape = outShapes[node_proto.input(input_id)];
if (shape(blob) == inpShape)
{
LayerParams constParams;
constParams.name = layerParams.name + "/const";
constParams.type = "Const";
constParams.blobs.push_back(blob);
int id = dstNet.addLayer(constParams.name, constParams.type, constParams);
layer_id.insert(std::make_pair(constParams.name, LayerInfo(id, 0)));
outShapes[constParams.name] = shape(blob);
layerParams.type = "Eltwise";
float coeffs[] = {1., isSub ? -1.f : 1.f};
layerParams.set("coeff", DictValue::arrayReal<float*>(coeffs, 2));
node_proto.set_input(const_blob_id, constParams.name);
}
else
{
if (inputScale < 0.f)
{
addNegation(layerParams, node_proto, input_id);
}
layerParams.type = "Scale";
layerParams.set("bias_term", true);
int axis = 1;
for (int i = 0; i < graph_proto.initializer_size(); i++)
{
opencv_onnx::TensorProto tensor_proto = graph_proto.initializer(i);
if (tensor_proto.name() == node_proto.input(const_blob_id))
{
axis = inpShape.size() - tensor_proto.dims_size();
break;
}
}
layerParams.set("axis", axis);
blob = blob.reshape(1, 1);
layerParams.blobs.push_back(constScale * blob);
}
}
}
else if (outShapes[node_proto.input(0)] == outShapes[node_proto.input(1)])
{
layerParams.type = "Eltwise";
if (isSub)
{
static float subCoeffs[] = {1.f, -1.f};
layerParams.set("coeff", DictValue::arrayReal<float*>(subCoeffs, 2));
}
}
else
{
if (isSub)
{
addNegation(layerParams, node_proto, 1);
}
layerParams.type = "Scale";
layerParams.set("bias_term", true);
}
addLayer(layerParams, node_proto);
}
void ONNXImporter::parsePow(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
if (layer_id.find(node_proto.input(1)) != layer_id.end())
CV_Error(Error::StsNotImplemented, "Unsupported Pow op with variable power");
Mat blob = getBlob(node_proto, 1);
if (blob.total() != 1)
CV_Error(Error::StsNotImplemented, "Pow op supports only scalar power");
blob.convertTo(blob, CV_32F);
layerParams.type = "Power";
layerParams.set("power", blob.ptr<float>()[0]);
addLayer(layerParams, node_proto);
}
// "Min" "Max"
void ONNXImporter::parseMinMax(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
const std::string& layer_type = node_proto.op_type();
layerParams.type = "Eltwise";
layerParams.set("operation", layer_type == "Max" ? "max" : "min");
addLayer(layerParams, node_proto);
}
void ONNXImporter::parseNeg(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
layerParams.type = "Power";
@@ -1561,6 +1466,12 @@ void ONNXImporter::parseConstant(LayerParams& layerParams, const opencv_onnx::No
CV_Assert(node_proto.input_size() == 0);
CV_Assert(layerParams.blobs.size() == 1);
addConstant(node_proto.output(0), layerParams.blobs[0]);
// add constant for constBlobsExtraInfo
if (layerParams.has("original_dims_of_mat"))
{
int original_dims_of_mat = layerParams.get<int>("original_dims_of_mat");
constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), TensorInfo(original_dims_of_mat)));
}
}
void transformBlobs(std::vector<Mat>& blobs)
@@ -1912,10 +1823,29 @@ void ONNXImporter::parseImageScaler(LayerParams& layerParams, const opencv_onnx:
void ONNXImporter::parseClip(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
CV_CheckEQ(node_proto.input_size(), 1, "");
layerParams.type = "ReLU6";
layerParams.set("min_value", layerParams.get<float>("min", -FLT_MAX));
layerParams.set("max_value", layerParams.get<float>("max", FLT_MAX));
float min_value = -FLT_MAX, max_value = FLT_MAX;
int input_size = node_proto.input_size();
CV_Check(input_size, 1 <= input_size && input_size <= 3, "");
if (input_size >= 2 && !node_proto.input(1).empty())
{
if (constBlobs.find(node_proto.input(1)) != constBlobs.end())
min_value = getBlob(node_proto, 1).at<float>(0);
else
CV_Error(Error::StsNotImplemented, "Non-constant min values in Clip are not supported");
}
if (input_size == 3 && !node_proto.input(2).empty())
{
if (constBlobs.find(node_proto.input(2)) != constBlobs.end())
max_value = getBlob(node_proto, 2).at<float>(0);
else
CV_Error(Error::StsNotImplemented, "Non-constant max values in Clip are not supported");
}
layerParams.set("min_value", layerParams.get<float>("min", min_value));
layerParams.set("max_value", layerParams.get<float>("max", max_value));
addLayer(layerParams, node_proto);
}
@@ -1950,32 +1880,6 @@ void ONNXImporter::parseAbs(LayerParams& layerParams, const opencv_onnx::NodePro
addLayer(layerParams, node_proto);
}
void ONNXImporter::parseCompare(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
CV_Assert(node_proto.input_size() == 2);
const std::string& layer_type = node_proto.op_type();
bool is_const_0 = layer_id.find(node_proto.input(0)) == layer_id.end();
bool is_const_1 = layer_id.find(node_proto.input(1)) == layer_id.end();
if (is_const_0 || is_const_1)
{
Mat blob = getBlob(node_proto, static_cast<int>(is_const_1));
blob = blob.reshape(1, 1);
layerParams.blobs.push_back(blob);
}
layerParams.type = "Compare";
if (layer_type == "Equal")
layerParams.set("mode", "equal");
else if (layer_type == "Greater")
layerParams.set("mode", "greater");
else
layerParams.set("mode", "less");
addLayer(layerParams, node_proto);
}
void ONNXImporter::parsePRelu(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
layerParams.type = "PReLU";
@@ -2061,15 +1965,17 @@ void ONNXImporter::parseBatchNormalization(LayerParams& layerParams, const openc
addLayer(layerParams, node_proto);
}
// A * B + C = Y, we require that the dimension of A is [m, k], and the dimension of B is [n, k].
// And the dim of output Y is [m, n]
void ONNXImporter::parseGemm(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
CV_Assert(node_proto.input_size() >= 2);
layerParams.type = "InnerProduct";
Mat weights = getBlob(node_proto, 1);
int ind_num_out = 0;
if (layerParams.has("transB") && !layerParams.get<int>("transB")) {
if (!layerParams.get<int>("transB", 0))
{
transpose(weights, weights);
ind_num_out = 1;
}
layerParams.blobs.push_back(weights);
@@ -2091,7 +1997,7 @@ void ONNXImporter::parseGemm(LayerParams& layerParams, const opencv_onnx::NodePr
addLayer(constParams, proto);
}
layerParams.set("num_output", layerParams.blobs[0].size[ind_num_out]);
layerParams.set("num_output", layerParams.blobs[0].size[0]);
layerParams.set("bias_term", node_proto.input_size() == 3);
addLayer(layerParams, node_proto);
}
@@ -2120,6 +2026,8 @@ void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::Node
void findBroadAxis(const MatShape& broadShape, const MatShape& outShape, size_t& axis, int& broadAxis)
{
// Currently, this function can only complete 1-dimensional expansion of broadShape.
// If there are two dimensions in broadShape that need to be expended, it will fail.
const size_t diff = outShape.size() - broadShape.size();
// find the first non-one element of the broadcasting shape
@@ -2149,169 +2057,6 @@ void findBroadAxis(const MatShape& broadShape, const MatShape& outShape, size_t&
axis += diff;
}
// "Mul" "Div"
void ONNXImporter::parseMul(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_)
{
opencv_onnx::NodeProto node_proto = node_proto_;
const std::string& layer_type = node_proto.op_type();
const std::string output_name = node_proto.output(0);
CV_Assert(node_proto.input_size() == 2);
bool isDiv = layer_type == "Div";
int constId = -1;
bool haveVariables = false;
for (int i = 0; i < 2; ++i)
{
if (constBlobs.find(node_proto.input(i)) != constBlobs.end())
constId = i;
else
haveVariables = true;
}
if (constId != -1 && haveVariables)
{
Mat blob = getBlob(node_proto, constId);
blob = blob.reshape(1, 1);
if (blob.total() == 1) {
float blob_value = blob.ptr<float>()[0];
float coeff = blob_value;
if (isDiv)
{
coeff = 1.f / blob_value;
if (constId == 0)
{
// Power layer calculates (x*scale + shift)^power, so const/x -> (x * (1/const) + 0)^(-1)
layerParams.set("power", -1.f);
}
}
layerParams.set("scale", coeff);
layerParams.type = "Power";
}
else {
if (isDiv)
divide(1.0, blob, blob);
layerParams.blobs.push_back(blob);
layerParams.type = "Scale";
}
}
else if (!haveVariables)
{
Mat inp0 = getBlob(node_proto, 0);
Mat inp1 = getBlob(node_proto, 1);
if (inp0.size != inp1.size && (inp0.total() != 1 || inp1.total() != 1))
CV_Error_(Error::StsNotImplemented, ("Different shapes case is not supported with constant inputs: %s", layer_type.c_str()));
if (inp0.total() == 1 && inp1.total() == 1 && inp0.dims != inp1.dims)
{
if (inp0.dims < inp1.dims)
{
inp0 = inp0.reshape(1, inp1.dims, inp1.size);
inp0.dims = inp1.dims;
}
else
{
inp1 = inp1.reshape(1, inp0.dims, inp0.size);
inp1.dims = inp0.dims;
}
}
Mat out;
if (inp0.total() != inp1.total())
{
if (inp0.total() == 1)
{
float inp0_value = inp0.ptr<float>()[0];
float coeff = isDiv ? 1.0 / inp0_value : inp0_value;
multiply(inp1, coeff, out);
}
else
{
float inp1_value = inp1.ptr<float>()[0];
float coeff = isDiv ? 1.0 / inp1_value : inp1_value;
multiply(inp0, coeff, out);
}
}
else
{
out = isDiv ? inp0 / inp1 : inp0.mul(inp1);
}
if (inp0.dims == 1 && inp1.dims == 1)
out.dims = 1; // to workaround dims == 1
addConstant(output_name, out);
return;
}
else if (outShapes[node_proto.input(0)] == outShapes[node_proto.input(1)])
{
layerParams.type = "Eltwise";
layerParams.set("operation", isDiv ? "div" : "prod");
}
else
{
// Scale layer allocate output with the first input shape
if (total(outShapes[node_proto.input(0)]) < total(outShapes[node_proto.input(1)]))
{
opencv_onnx::NodeProto proto;
proto.add_input(node_proto.input(1));
proto.add_input(node_proto.input(0));
proto.add_output(output_name);
node_proto = proto;
}
if (isDiv)
{
LayerParams powerParams;
powerParams.name = layerParams.name + "/inv";
powerParams.type = "Power";
powerParams.set("power", -1);
//Create Power layer
int id = dstNet.addLayer(powerParams.name, powerParams.type, powerParams);
//Connect to input
IterLayerId_t layerId = layer_id.find(node_proto.input(1));
CV_Assert(layerId != layer_id.end());
dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
//Add shape
layer_id.insert(std::make_pair(powerParams.name, LayerInfo(id, 0)));
outShapes[powerParams.name] = outShapes[node_proto.input(1)];
//Replace input to Power
node_proto.set_input(1, powerParams.name);
}
const MatShape& broadShape = outShapes[node_proto.input(1)];
const MatShape& outShape = outShapes[node_proto.input(0)];
size_t axis = 0;
int broadAxis = -1;
findBroadAxis(broadShape, outShape, axis, broadAxis);
// if there is a one dimension in the middle that should be broadcasted, broadcast it
if (broadAxis != -1)
{
opencv_onnx::NodeProto concat_node_proto = node_proto;
const std::string& input1 = concat_node_proto.input(1);
expandMid(layerParams.name, concat_node_proto, input1, outShape[broadAxis]);
LayerParams concatLP;
concatLP.name = layerParams.name + "/concat";
concatLP.set("axis", broadAxis);
concatLP.type = "Concat";
concat_node_proto.set_output(0, concatLP.name);
addLayer(concatLP, concat_node_proto);
node_proto.set_input(1, concatLP.name);
}
CV_Assert(axis != outShape.size());
layerParams.set("axis", static_cast<int>(axis));
layerParams.type = "Scale";
}
addLayer(layerParams, node_proto);
}
void ONNXImporter::parseConv(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_)
{
opencv_onnx::NodeProto node_proto = node_proto_;
@@ -2502,6 +2247,10 @@ void ONNXImporter::parseFlatten(LayerParams& layerParams, const opencv_onnx::Nod
if (constBlobs.find(node_proto.input(0)) != constBlobs.end())
{
Mat input = getBlob(node_proto, 0);
if (constBlobsExtraInfo.find(node_proto.input(0)) != constBlobsExtraInfo.end())
{
constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), getBlobExtraInfo(node_proto, 0)));
}
int axis = normalize_axis(axis_, input.dims);
int out_size[2] = {1, 1};
@@ -2574,12 +2323,16 @@ void ONNXImporter::parseUnsqueeze(LayerParams& layerParams, const opencv_onnx::N
{
// Constant input.
Mat input = getBlob(node_proto, 0);
int input_dims = input.dims;
if (constBlobsExtraInfo.find(node_proto.input(0)) != constBlobsExtraInfo.end())
if (getBlobExtraInfo(node_proto, 0).real_ndims == 1)
input_dims = 1;
std::vector<int> dims;
for (int j = 0; j < input.dims; j++) {
for (int j = 0; j < input_dims; j++) {
dims.push_back(input.size[j]);
}
CV_Assert(axes.getIntValue(axes.size()-1) <= dims.size());
// CV_Assert(axes.getIntValue(axes.size()-1) <= dims.size());
for (int j = 0; j < axes.size(); j++) {
const int idx = axes.getIntValue(j);
CV_Assert(idx <= dims.size());
@@ -2834,6 +2587,10 @@ void ONNXImporter::parseCast(LayerParams& layerParams, const opencv_onnx::NodePr
if (constBlobs.find(node_proto.input(0)) != constBlobs.end())
{
Mat blob = getBlob(node_proto, 0);
if (constBlobsExtraInfo.find(node_proto.input(0)) != constBlobsExtraInfo.end())
{
constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), getBlobExtraInfo(node_proto, 0)));
}
int type;
switch (layerParams.get<int>("to"))
{
@@ -2971,6 +2728,10 @@ void ONNXImporter::parseConcat(LayerParams& layerParams, const opencv_onnx::Node
break;
}
}
if (constBlobsExtraInfo.find(node_proto.input(0)) != constBlobsExtraInfo.end())
{
constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), getBlobExtraInfo(node_proto, 0)));
}
if (!hasVariableInps)
{
@@ -3183,6 +2944,157 @@ void ONNXImporter::parseCumSum(LayerParams& layerParams, const opencv_onnx::Node
addLayer(layerParams, node_proto);
}
// "Equal" "Greater" "Less" "Pow" "Add" "Sub" "Mul" "Div" "Sum" "Min" "Max"
void ONNXImporter::parseElementWise(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_)
{
opencv_onnx::NodeProto node_proto = node_proto_;
String op_type = toLowerCase(node_proto.op_type());
layerParams.type = "NaryEltwise";
layerParams.set("operation", toLowerCase(node_proto.op_type()));
// element-wise layers that can have >=1 inputs but actually have one input
if (node_proto.input_size() == 1 && (op_type == "max" || op_type == "min" || op_type == "mean" || op_type == "sum"))
{
layerParams.type = "Identity";
addLayer(layerParams, node_proto);
return;
}
auto pre_broadcast_transform = [](Mat& t, int t_real_ndims) {
if (t.dims == 2 && t_real_ndims == 1 && t.size[1] == 1)
transpose(t, t);
};
size_t consts = 0;
for (size_t i = 0; i < node_proto.input_size(); ++i)
{
if (layer_id.find(node_proto.input(i)) == layer_id.end())
{
++consts;
}
}
if (consts == node_proto.input_size())
{
std::vector<Mat> inputs, output;
for (size_t i = 0; i < node_proto.input_size(); ++i)
{
inputs.push_back(getBlob(node_proto, i));
}
runLayer(layerParams, inputs, output);
CV_Assert(output.size() == 1);
addConstant(node_proto.output(0), output[0]);
return;
}
else if (consts > 0)
{
for (size_t i = 0; i < node_proto.input_size(); ++i)
{
if (layer_id.find(node_proto.input(i)) == layer_id.end())
{
Mat inp = getBlob(node_proto, i);
// for cases like a tensor of shape (2,), it will be loaded as shape (2, 1) in OpenCV Mat,
// but for correct broadcast, we need to make it of shape (1, 2)
if (constBlobsExtraInfo.find(node_proto.input(i)) != constBlobsExtraInfo.end())
pre_broadcast_transform(inp, getBlobExtraInfo(node_proto, i).real_ndims);
// carry the constant by adding a Const node
LayerParams constParams;
constParams.name = node_proto.input(i);
constParams.type = "Const";
constParams.blobs.push_back(inp);
opencv_onnx::NodeProto proto;
proto.add_output(constParams.name);
addLayer(constParams, proto);
}
}
}
// add element-wise layer
addLayer(layerParams, node_proto);
}
void ONNXImporter::parseDepthToSpace(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_)
{
// We parse "DepthToSpace" and "SpaceToDepth" in this function.
opencv_onnx::NodeProto node_proto = node_proto_;
const std::string& layer_type = node_proto.op_type();
CV_Assert(layer_type == "DepthToSpace" || layer_type == "SpaceToDepth");
// Get blocksize
CV_Assert(layerParams.has("blocksize"));
int blocksize = layerParams.get<int>("blocksize");
CV_Assert(blocksize > 0);
// Get mode, only for "DepthToSpace"
std::string modeType = layerParams.get<std::string>("mode", "DCR");
MatShape inpShape = outShapes[node_proto.input(0)];
CV_Assert(inpShape.size() == 4);
int N = inpShape[0], C = inpShape[1], H = inpShape[2], W = inpShape[3];
// Implement DepthToSpace and SpaceToDepth by the Reshape and Permute layer.
std::array<int, 6> shape0, perm;
std::array<int, 4> shape1;
if (layer_type == "DepthToSpace")
{
if (modeType == "DCR")
{
shape0 = {N, blocksize, blocksize, C/(blocksize * blocksize), H, W};
perm = {0, 3, 4, 1, 5, 2};
shape1 = {N, C/(blocksize * blocksize), H * blocksize, W * blocksize};
}
else if (modeType == "CRD")
{
shape0 = {N, C/(blocksize * blocksize), blocksize, blocksize, H, W};
perm = {0, 1, 4, 2, 5, 3};
shape1 = {N, C/(blocksize * blocksize), H * blocksize, W * blocksize};
}
else
CV_Error(Error::StsNotImplemented, "The mode of " + modeType + " in " + layer_type + " Layer is not supported");
}
else // SpaceToDepth
{
shape0 = {N, C, H/blocksize, blocksize, W/blocksize, blocksize};
perm = {0, 3, 5, 1, 2, 4};
shape1 = {N, C * blocksize * blocksize, H/blocksize, W/blocksize};
}
// Step1: Reshape
LayerParams reshapeLp;
reshapeLp.name = layerParams.name + "/reshape";
reshapeLp.type = "Reshape";
CV_Assert(layer_id.find(reshapeLp.name) == layer_id.end());
reshapeLp.set("dim", DictValue::arrayInt(shape0.data(), shape0.size()));
opencv_onnx::NodeProto protoReshape;
protoReshape.add_input(node_proto.input(0));
protoReshape.add_output(reshapeLp.name);
addLayer(reshapeLp, protoReshape);
// Step2: Transpose
LayerParams permuteLp;
permuteLp.name = layerParams.name + "/permute";
permuteLp.type = "Permute";
CV_Assert(layer_id.find(permuteLp.name) == layer_id.end());
permuteLp.set("order", DictValue::arrayInt(perm.data(), perm.size()));
opencv_onnx::NodeProto protoPermute;
protoPermute.add_input(reshapeLp.name);
protoPermute.add_output(permuteLp.name);
addLayer(permuteLp, protoPermute);
// Step3: Reshape
layerParams.type = "Reshape";
layerParams.set("dim", DictValue::arrayInt(shape1.data(), shape1.size()));
node_proto.set_input(0, permuteLp.name);
addLayer(layerParams, node_proto);
}
void ONNXImporter::parseSimpleLayers(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
for (int j = 0; j < node_proto.input_size(); j++) {
@@ -3230,18 +3142,59 @@ void ONNXImporter::parseQuantDequant(LayerParams& layerParams, const opencv_onnx
addLayer(layerParams, node_proto);
}
void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_)
{
opencv_onnx::NodeProto node_proto = node_proto_;
int ninputs = node_proto.input_size();
CV_Assert(ninputs == 8 || ninputs == 9);
Mat inp_sc = getBlob(node_proto, 1);
Mat inp_zp = getBlob(node_proto, 2);
if (layerParams.has("pad"))
{
bool asymmetricPadding = false;
DictValue pads = layerParams.get("pad");
const int dims = pads.size() / 2;
for (int i = 0; i < dims; ++i)
{
if (pads.get<int>(i) != pads.get<int>(i + dims))
{
asymmetricPadding = true;
break;
}
}
if (asymmetricPadding && pads.size() == 4)
{
layerParams.erase("pad");
std::vector<int> paddings(4, 0);
for (int i = 0; i < dims; ++i)
{
paddings.push_back(pads.get<int>(i));
paddings.push_back(pads.get<int>(dims + i));
}
LayerParams padLp;
padLp.name = layerParams.name + "/pad";
padLp.type = "PaddingInt8";
padLp.set("paddings", DictValue::arrayInt(&paddings[0], paddings.size()));
padLp.set("depth", CV_8S);
padLp.set("value", inp_zp.at<int8_t>(0));
opencv_onnx::NodeProto proto;
proto.add_input(node_proto.input(0));
proto.add_output(padLp.name);
addLayer(padLp, proto);
node_proto.set_input(0, padLp.name);
}
}
Mat weights = getBlob(node_proto, 3);
int outCn = weights.size[0];
Mat w_scale = getBlob(node_proto, 4);
CV_Assert(w_scale.total() == 1 || w_scale.total() == outCn);
bool per_channel = w_scale.total() == outCn ? true : false;
Mat wt_sc = (w_scale.total() == outCn) ? w_scale : Mat(1, outCn, CV_32F, Scalar(w_scale.at<float>(0)));
Mat out_sc = getBlob(node_proto, 6);
@@ -3260,6 +3213,7 @@ void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeP
layerParams.set("num_output", outCn);
layerParams.set("input_zeropoint", inp_zp.at<int8_t>(0));
layerParams.set("input_scale",inp_sc.at<float>(0));
layerParams.set("per_channel", per_channel);
layerParams.blobs.push_back(weights);
layerParams.blobs.push_back(biasFused);
layerParams.blobs.push_back(outputMultiplier);
@@ -3285,6 +3239,7 @@ void ONNXImporter::parseQMatMul(LayerParams& layerParams, const opencv_onnx::Nod
Mat w_scale = getBlob(node_proto, 4);
CV_Assert(w_scale.total() == 1 || w_scale.total() == outCn);
bool per_channel = w_scale.total() == outCn ? true : false;
Mat wt_sc = (w_scale.total() == outCn) ? w_scale : Mat(1, outCn, CV_32F, Scalar(w_scale.at<float>(0)));
Mat out_sc = getBlob(node_proto, 6);
@@ -3301,6 +3256,7 @@ void ONNXImporter::parseQMatMul(LayerParams& layerParams, const opencv_onnx::Nod
layerParams.set("axis", firstInpDims - secondInpDims + 1);
layerParams.set("input_scale", inp_sc.at<float>(0));
layerParams.set("input_zeropoint", inp_zp.at<int8_t>(0));
layerParams.set("per_channel", per_channel);
layerParams.blobs.push_back(weights);
layerParams.blobs.push_back(bias);
@@ -3631,9 +3587,6 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version)
dispatch["ReduceL2"] = dispatch["ReduceLogSum"] = dispatch["ReduceLogSumExp"] = &ONNXImporter::parseReduce;
dispatch["Slice"] = &ONNXImporter::parseSlice;
dispatch["Split"] = &ONNXImporter::parseSplit;
dispatch["Add"] = dispatch["Sum"] = dispatch["Sub"] = &ONNXImporter::parseBias;
dispatch["Pow"] = &ONNXImporter::parsePow;
dispatch["Min"] = dispatch["Max"] = &ONNXImporter::parseMinMax;
dispatch["Neg"] = &ONNXImporter::parseNeg;
dispatch["Constant"] = &ONNXImporter::parseConstant;
dispatch["LSTM"] = &ONNXImporter::parseLSTM;
@@ -3645,14 +3598,12 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version)
dispatch["Elu"] = &ONNXImporter::parseElu;
dispatch["Tanh"] = &ONNXImporter::parseTanh;
dispatch["Abs"] = &ONNXImporter::parseAbs;
dispatch["Equal"] = dispatch["Greater"] = dispatch["Less"] = &ONNXImporter::parseCompare;
dispatch["PRelu"] = &ONNXImporter::parsePRelu;
dispatch["LRN"] = &ONNXImporter::parseLRN;
dispatch["InstanceNormalization"] = &ONNXImporter::parseInstanceNormalization;
dispatch["BatchNormalization"] = &ONNXImporter::parseBatchNormalization;
dispatch["Gemm"] = &ONNXImporter::parseGemm;
dispatch["MatMul"] = &ONNXImporter::parseMatMul;
dispatch["Mul"] = dispatch["Div"] = &ONNXImporter::parseMul;
dispatch["Conv"] = &ONNXImporter::parseConv;
dispatch["ConvTranspose"] = &ONNXImporter::parseConvTranspose;
dispatch["Transpose"] = &ONNXImporter::parseTranspose;
@@ -3672,6 +3623,11 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version)
dispatch["SoftMax"] = dispatch["LogSoftmax"] = &ONNXImporter::parseSoftMax;
dispatch["DetectionOutput"] = &ONNXImporter::parseDetectionOutput;
dispatch["CumSum"] = &ONNXImporter::parseCumSum;
dispatch["SpaceToDepth"] = dispatch["DepthToSpace"] = &ONNXImporter::parseDepthToSpace;
dispatch["Equal"] = dispatch["Greater"] = dispatch["Less"] = dispatch["Pow"] = dispatch["Add"] =
dispatch["Sub"] = dispatch["Mul"] = dispatch["Div"] = &ONNXImporter::parseElementWise;
dispatch["Sum"] = dispatch["Min"] = dispatch["Max"] = &ONNXImporter::parseElementWise;
std::vector<std::string> simpleLayers{"Acos", "Acosh", "Asin", "Asinh", "Atan", "Atanh", "Ceil", "Celu", "Cos",
"Cosh", "Dropout", "Erf", "Exp", "Floor", "HardSigmoid", "HardSwish",
+1 -1
View File
@@ -61,7 +61,7 @@ enum Version {
// The version field is always serialized and we will use it to store the
// version that the graph is generated from. This helps us set up version
// control.
// For the IR, we are using simple numbers starting with with 0x00000001,
// For the IR, we are using simple numbers starting with 0x00000001,
// which was the version we published on Oct 10, 2017.
IR_VERSION_2017_10_10 = 0x0000000000000001;
@@ -990,6 +990,7 @@ void sortByExecutionOrder(tensorflow::GraphDef& net)
nodesMap.insert(std::make_pair(node.name(), i));
}
CV_CheckEQ(nodesMap.size(), (size_t)net.node_size(), "Node names must be unique");
// Indices of nodes which use specific node as input.
std::vector<std::vector<int> > edges(nodesMap.size());
std::vector<int> numRefsToAdd(nodesMap.size(), 0);
@@ -1007,7 +1008,7 @@ void sortByExecutionOrder(tensorflow::GraphDef& net)
nodesMapIt = nodesMap.find(inpName);
if (nodesMapIt != nodesMap.end())
{
edges[nodesMapIt->second].push_back(i);
edges.at(nodesMapIt->second).push_back(i);
numInputsInGraph += 1;
}
}
@@ -1019,11 +1020,11 @@ void sortByExecutionOrder(tensorflow::GraphDef& net)
{
int numControlEdges = 0;
for (int j = 0; j < numInputsInGraph; ++j)
numControlEdges += node.input(j)[0] == '^';
numRefsToAdd[i] = numControlEdges + 1;
numControlEdges += node.input(j).at(0) == '^';
numRefsToAdd.at(i) = numControlEdges + 1;
}
else
numRefsToAdd[i] = numInputsInGraph;
numRefsToAdd.at(i) = numInputsInGraph;
}
}
@@ -1035,17 +1036,16 @@ void sortByExecutionOrder(tensorflow::GraphDef& net)
nodesToAdd.pop_back();
permIds.push_back(nodeToAdd);
CV_Assert(nodeToAdd < edges.size());
for (int i = 0; i < edges[nodeToAdd].size(); ++i)
for (int i = 0; i < edges.at(nodeToAdd).size(); ++i)
{
int consumerId = edges[nodeToAdd][i];
if (numRefsToAdd[consumerId] > 0)
int consumerId = edges.at(nodeToAdd).at(i);
if (numRefsToAdd.at(consumerId) > 0)
{
if (numRefsToAdd[consumerId] == 1)
if (numRefsToAdd.at(consumerId) == 1)
nodesToAdd.push_back(consumerId);
else
CV_Assert(numRefsToAdd[consumerId] >= 0);
numRefsToAdd[consumerId] -= 1;
CV_Assert(numRefsToAdd.at(consumerId) >= 0);
numRefsToAdd.at(consumerId) -= 1;
}
}
}
+4 -2
View File
@@ -568,7 +568,7 @@ private:
typedef std::map<std::string, TFImporterNodeParser> DispatchMap;
const DispatchMap dispatch;
static const DispatchMap buildDispatchMap();
static DispatchMap buildDispatchMap();
void parseConvolution (tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams);
void parseBias (tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams);
@@ -645,7 +645,7 @@ protected:
TFImporter* importer;
};
const TFImporter::DispatchMap TFImporter::buildDispatchMap()
TFImporter::DispatchMap TFImporter::buildDispatchMap()
{
static DispatchMap dispatch;
dispatch["Conv2D"] = dispatch["SpaceToBatchND"] = dispatch["DepthwiseConv2dNative"] =
@@ -2541,6 +2541,8 @@ void TFImporter::parsePack(tensorflow::GraphDef& net, const tensorflow::NodeDef&
if (dim != 0)
CV_Error(Error::StsNotImplemented, "Unsupported mode of pack operation.");
data_layouts[name] = DATA_LAYOUT_UNKNOWN;
CV_Assert(hasLayerAttr(layer, "N"));
int num = (int)getLayerAttr(layer, "N").i();
CV_CheckEQ(num_inputs, num, "");
+1 -1
View File
@@ -545,7 +545,7 @@ TEST_P(DNNTestNetwork, FastNeuralStyle_eccv16)
Mat img = imread(findDataFile("dnn/googlenet_1.png"));
Mat inp = blobFromImage(img, 1.0, Size(320, 240), Scalar(103.939, 116.779, 123.68), false, false);
// Output image has values in range [-143.526, 148.539].
float l1 = 4e-5, lInf = 2e-3;
float l1 = 1e-4, lInf = 2e-3;
if (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD)
{
l1 = 0.4;
+2 -2
View File
@@ -653,7 +653,7 @@ TEST_P(opencv_face_detector, Accuracy)
0, 1, 0.98977017, 0.23901358, 0.09084064, 0.29902688, 0.1769477,
0, 1, 0.97203469, 0.67965847, 0.06876482, 0.73999709, 0.1513494,
0, 1, 0.95097077, 0.51901293, 0.45863652, 0.5777427, 0.5347801);
normAssertDetections(ref, out, "", 0.5, 1e-5, 2e-4);
normAssertDetections(ref, out, "", 0.5, 1e-4, 2e-4);
}
// False positives bug for large faces: https://github.com/opencv/opencv/issues/15106
@@ -695,7 +695,7 @@ TEST_P(Test_Caffe_nets, FasterRCNN_vgg16)
#if defined(OPENCV_32BIT_CONFIGURATION) && defined(HAVE_OPENCL)
CV_TEST_TAG_MEMORY_2GB, // utilizes ~1Gb, but huge blobs may not be allocated on 32-bit systems due memory fragmentation
#else
(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB),
CV_TEST_TAG_MEMORY_2GB,
#endif
CV_TEST_TAG_LONG,
CV_TEST_TAG_DEBUG_VERYLONG
+19 -4
View File
@@ -67,7 +67,10 @@ TEST(Test_Darknet, read_yolo_voc)
TEST(Test_Darknet, read_yolo_voc_stream)
{
applyTestTag(CV_TEST_TAG_MEMORY_1GB);
applyTestTag(
CV_TEST_TAG_MEMORY_1GB,
CV_TEST_TAG_DEBUG_VERYLONG
);
Mat ref;
Mat sample = imread(_tf("dog416.png"));
Mat inp = blobFromImage(sample, 1.0/255, Size(416, 416), Scalar(), true, false);
@@ -594,7 +597,11 @@ INSTANTIATE_TEST_CASE_P(/**/, Test_Darknet_nets_async, Combine(
TEST_P(Test_Darknet_nets, YOLOv3)
{
applyTestTag(CV_TEST_TAG_LONG, (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB));
applyTestTag(
CV_TEST_TAG_LONG,
(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB),
CV_TEST_TAG_DEBUG_VERYLONG
);
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2020040000) // nGraph compilation failure
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL)
@@ -673,7 +680,11 @@ TEST_P(Test_Darknet_nets, YOLOv3)
TEST_P(Test_Darknet_nets, YOLOv4)
{
applyTestTag(CV_TEST_TAG_LONG, (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB));
applyTestTag(
CV_TEST_TAG_LONG,
(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB),
CV_TEST_TAG_DEBUG_VERYLONG
);
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2020040000) // nGraph compilation failure
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL)
@@ -864,7 +875,11 @@ TEST_P(Test_Darknet_nets, YOLOv4_tiny)
TEST_P(Test_Darknet_nets, YOLOv4x_mish)
{
applyTestTag(CV_TEST_TAG_LONG, (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB));
applyTestTag(
CV_TEST_TAG_LONG,
(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB),
CV_TEST_TAG_DEBUG_VERYLONG
);
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021040000)
// IE exception: Ngraph operation Transpose with name permute_168 has dynamic output shape on 0 port, but CPU plug-in supports only static shape
+116 -25
View File
@@ -29,7 +29,7 @@ class Test_Int8_layers : public DNNTestLayer
public:
void testLayer(const String& basename, const String& importer, double l1, double lInf,
int numInps = 1, int numOuts = 1, bool useCaffeModel = false,
bool useCommonInputBlob = true, bool hasText = false)
bool useCommonInputBlob = true, bool hasText = false, bool perChannel = true)
{
CV_Assert_N(numInps >= 1, numInps <= 10, numOuts >= 1, numOuts <= 10);
std::vector<Mat> inps(numInps), inps_int8(numInps);
@@ -75,7 +75,7 @@ public:
for (int i = 0; i < numOuts; i++)
refs[i] = blobFromNPY(outPath + ((numOuts > 1) ? cv::format("_%d.npy", i) : ".npy"));
qnet = net.quantize(inps, CV_8S, CV_8S);
qnet = net.quantize(inps, CV_8S, CV_8S, perChannel);
qnet.getInputDetails(inputScale, inputZp);
qnet.getOutputDetails(outputScale, outputZp);
@@ -103,6 +103,12 @@ TEST_P(Test_Int8_layers, Convolution1D)
{
testLayer("conv1d", "ONNX", 0.00302, 0.00909);
testLayer("conv1d_bias", "ONNX", 0.00306, 0.00948);
{
SCOPED_TRACE("Per-tensor quantize");
testLayer("conv1d", "ONNX", 0.00302, 0.00909, 1, 1, false, true, false, false);
testLayer("conv1d_bias", "ONNX", 0.00319, 0.00948, 1, 1, false, true, false, false);
}
}
TEST_P(Test_Int8_layers, Convolution2D)
@@ -130,6 +136,18 @@ TEST_P(Test_Int8_layers, Convolution2D)
applyTestTag(CV_TEST_TAG_DNN_SKIP_TIMVX);
testLayer("layer_convolution", "Caffe", 0.0174, 0.0758, 1, 1, true);
testLayer("depthwise_conv2d", "TensorFlow", 0.0388, 0.169);
{
SCOPED_TRACE("Per-tensor quantize");
testLayer("single_conv", "TensorFlow", 0.00413, 0.02301, 1, 1, false, true, false, false);
testLayer("atrous_conv2d_valid", "TensorFlow", 0.027967, 0.07808, 1, 1, false, true, false, false);
testLayer("atrous_conv2d_same", "TensorFlow", 0.01945, 0.1322, 1, 1, false, true, false, false);
testLayer("keras_atrous_conv2d_same", "TensorFlow", 0.005677, 0.03327, 1, 1, false, true, false, false);
testLayer("convolution", "ONNX", 0.00538, 0.01517, 1, 1, false, true, false, false);
testLayer("two_convolution", "ONNX", 0.00295, 0.00926, 1, 1, false, true, false, false);
testLayer("layer_convolution", "Caffe", 0.0175, 0.0759, 1, 1, true, true, false, false);
testLayer("depthwise_conv2d", "TensorFlow", 0.041847, 0.18744, 1, 1, false, true, false, false);
}
}
TEST_P(Test_Int8_layers, Convolution3D)
@@ -144,6 +162,13 @@ TEST_P(Test_Int8_layers, Flatten)
testLayer("flatten", "TensorFlow", 0.0036, 0.0069, 1, 1, false, true, true);
testLayer("unfused_flatten", "TensorFlow", 0.0014, 0.0028);
testLayer("unfused_flatten_unknown_batch", "TensorFlow", 0.0043, 0.0051);
{
SCOPED_TRACE("Per-tensor quantize");
testLayer("conv3d", "TensorFlow", 0.00734, 0.02434, 1, 1, false, true, false, false);
testLayer("conv3d", "ONNX", 0.00377, 0.01362, 1, 1, false, true, false, false);
testLayer("conv3d_bias", "ONNX", 0.00201, 0.0039, 1, 1, false, true, false, false);
}
}
TEST_P(Test_Int8_layers, Padding)
@@ -265,14 +290,32 @@ TEST_P(Test_Int8_layers, Mish)
testLayer("mish", "ONNX", 0.0015, 0.0025);
}
TEST_P(Test_Int8_layers, Softmax)
TEST_P(Test_Int8_layers, Softmax_Caffe)
{
testLayer("layer_softmax", "Caffe", 0.0011, 0.0036);
}
TEST_P(Test_Int8_layers, Softmax_keras_TF)
{
testLayer("keras_softmax", "TensorFlow", 0.00093, 0.0027);
}
TEST_P(Test_Int8_layers, Softmax_slim_TF)
{
testLayer("slim_softmax", "TensorFlow", 0.0016, 0.0034);
}
TEST_P(Test_Int8_layers, Softmax_slim_v2_TF)
{
testLayer("slim_softmax_v2", "TensorFlow", 0.0029, 0.017);
}
TEST_P(Test_Int8_layers, Softmax_ONNX)
{
testLayer("softmax", "ONNX", 0.0016, 0.0028);
}
TEST_P(Test_Int8_layers, Softmax_log_ONNX)
{
testLayer("log_softmax", "ONNX", 0.014, 0.025);
}
TEST_P(Test_Int8_layers, DISABLED_Softmax_unfused_ONNX) // FIXIT Support 'Identity' layer for outputs (#22022)
{
testLayer("softmax_unfused", "ONNX", 0.0009, 0.0021);
}
@@ -331,6 +374,20 @@ TEST_P(Test_Int8_layers, InnerProduct)
testLayer("constant", "ONNX", 0.00021, 0.0006);
testLayer("lin_with_constant", "ONNX", 0.0011, 0.0016);
{
SCOPED_TRACE("Per-tensor quantize");
testLayer("layer_inner_product", "Caffe", 0.0055, 0.02, 1, 1, true, true, false, false);
testLayer("matmul", "TensorFlow", 0.0075, 0.019, 1, 1, false, true, false, false);
testLayer("nhwc_transpose_reshape_matmul", "TensorFlow", 0.0009, 0.0091, 1, 1, false, true, false, false);
testLayer("nhwc_reshape_matmul", "TensorFlow", 0.037, 0.071, 1, 1, false, true, false, false);
testLayer("matmul_layout", "TensorFlow", 0.035, 0.095, 1, 1, false, true, false, false);
testLayer("tf2_dense", "TensorFlow", 0, 0, 1, 1, false, true, false, false);
testLayer("matmul_add", "ONNX", 0.041, 0.082, 1, 1, false, true, false, false);
testLayer("linear", "ONNX", 0.0022, 0.004, 1, 1, false, true, false, false);
testLayer("constant", "ONNX", 0.00038, 0.0012, 1, 1, false, true, false, false);
testLayer("lin_with_constant", "ONNX", 0.0011, 0.0016, 1, 1, false, true, false, false);
}
}
TEST_P(Test_Int8_layers, Reshape)
@@ -389,7 +446,7 @@ TEST_P(Test_Int8_layers, Slice_strided_tf)
testLayer("strided_slice", "TensorFlow", 0.008, 0.0142);
}
TEST_P(Test_Int8_layers, Slice_onnx)
TEST_P(Test_Int8_layers, DISABLED_Slice_onnx) // FIXIT Support 'Identity' layer for outputs (#22022)
{
testLayer("slice", "ONNX", 0.0046, 0.0077);
}
@@ -447,9 +504,9 @@ INSTANTIATE_TEST_CASE_P(/**/, Test_Int8_layers, dnnBackendsAndTargetsInt8());
class Test_Int8_nets : public DNNTestLayer
{
public:
void testClassificationNet(Net baseNet, const Mat& blob, const Mat& ref, double l1, double lInf)
void testClassificationNet(Net baseNet, const Mat& blob, const Mat& ref, double l1, double lInf, bool perChannel = true)
{
Net qnet = baseNet.quantize(blob, CV_32F, CV_32F);
Net qnet = baseNet.quantize(blob, CV_32F, CV_32F, perChannel);
qnet.setPreferableBackend(backend);
qnet.setPreferableTarget(target);
@@ -459,9 +516,9 @@ public:
}
void testDetectionNet(Net baseNet, const Mat& blob, const Mat& ref,
double confThreshold, double scoreDiff, double iouDiff)
double confThreshold, double scoreDiff, double iouDiff, bool perChannel = true)
{
Net qnet = baseNet.quantize(blob, CV_32F, CV_32F);
Net qnet = baseNet.quantize(blob, CV_32F, CV_32F, perChannel);
qnet.setPreferableBackend(backend);
qnet.setPreferableTarget(target);
@@ -470,14 +527,14 @@ public:
normAssertDetections(ref, out, "", confThreshold, scoreDiff, iouDiff);
}
void testFaster(Net baseNet, const Mat& ref, double confThreshold, double scoreDiff, double iouDiff)
void testFaster(Net baseNet, const Mat& ref, double confThreshold, double scoreDiff, double iouDiff, bool perChannel = true)
{
Mat inp = imread(_tf("dog416.png"));
resize(inp, inp, Size(800, 600));
Mat blob = blobFromImage(inp, 1.0, Size(), Scalar(102.9801, 115.9465, 122.7717), false, false);
Mat imInfo = (Mat_<float>(1, 3) << inp.rows, inp.cols, 1.6f);
Net qnet = baseNet.quantize(std::vector<Mat>{blob, imInfo}, CV_32F, CV_32F);
Net qnet = baseNet.quantize(std::vector<Mat>{blob, imInfo}, CV_32F, CV_32F, perChannel);
qnet.setPreferableBackend(backend);
qnet.setPreferableTarget(target);
@@ -487,7 +544,7 @@ public:
normAssertDetections(ref, out, "", confThreshold, scoreDiff, iouDiff);
}
void testONNXNet(const String& basename, double l1, double lInf, bool useSoftmax = false)
void testONNXNet(const String& basename, double l1, double lInf, bool useSoftmax = false, bool perChannel = true)
{
String onnxmodel = findDataFile("dnn/onnx/models/" + basename + ".onnx", false);
@@ -497,7 +554,7 @@ public:
baseNet.setPreferableBackend(backend);
baseNet.setPreferableTarget(target);
Net qnet = baseNet.quantize(blob, CV_32F, CV_32F);
Net qnet = baseNet.quantize(blob, CV_32F, CV_32F, perChannel);
qnet.setInput(blob);
Mat out = qnet.forward();
@@ -520,7 +577,7 @@ public:
void testDarknetModel(const std::string& cfg, const std::string& weights,
const cv::Mat& ref, double scoreDiff, double iouDiff,
float confThreshold = 0.24, float nmsThreshold = 0.4)
float confThreshold = 0.24, float nmsThreshold = 0.4, bool perChannel = true)
{
CV_Assert(ref.cols == 7);
std::vector<std::vector<int> > refClassIds;
@@ -560,7 +617,7 @@ public:
Mat inp = blobFromImages(samples, 1.0/255, Size(416, 416), Scalar(), true, false);
Net baseNet = readNetFromDarknet(findDataFile("dnn/" + cfg), findDataFile("dnn/" + weights, false));
Net qnet = baseNet.quantize(inp, CV_32F, CV_32F);
Net qnet = baseNet.quantize(inp, CV_32F, CV_32F, perChannel);
qnet.setPreferableBackend(backend);
qnet.setPreferableTarget(target);
qnet.setInput(inp);
@@ -678,7 +735,7 @@ TEST_P(Test_Int8_nets, GoogLeNet)
Mat blob = blobFromImages(inpMats, 1.0, Size(224, 224), Scalar(), false);
Mat ref = blobFromNPY(_tf("googlenet_prob.npy"));
float l1 = 2e-4, lInf = 0.06;
float l1 = 2e-4, lInf = 0.07;
testClassificationNet(net, blob, ref, l1, lInf);
}
@@ -700,8 +757,13 @@ TEST_P(Test_Int8_nets, ResNet50)
Mat blob = blobFromImage(inp, 1.0, Size(224, 224), Scalar(), false);
Mat ref = blobFromNPY(_tf("resnet50_prob.npy"));
float l1 = 3e-4, lInf = 0.04;
float l1 = 3e-4, lInf = 0.05;
testClassificationNet(net, blob, ref, l1, lInf);
{
SCOPED_TRACE("Per-tensor quantize");
testClassificationNet(net, blob, ref, l1, lInf, false);
}
}
TEST_P(Test_Int8_nets, DenseNet121)
@@ -912,6 +974,7 @@ TEST_P(Test_Int8_nets, opencv_face_detector)
TEST_P(Test_Int8_nets, EfficientDet)
{
applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG);
if (target == DNN_TARGET_OPENCL_FP16 && !ocl::Device::getDefault().isIntel())
applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16);
if (target == DNN_TARGET_OPENCL && !ocl::Device::getDefault().isIntel())
@@ -934,8 +997,13 @@ TEST_P(Test_Int8_nets, EfficientDet)
0, 17, 0.8245924, 0.16657517850399017, 0.3996818959712982, 0.4111558794975281, 0.9306337833404541,
0, 7, 0.8039304, 0.6118435263633728, 0.13175517320632935, 0.9065558314323425, 0.2943994700908661);
float confThreshold = 0.65, scoreDiff = 0.17, iouDiff = 0.18;
float confThreshold = 0.65, scoreDiff = 0.3, iouDiff = 0.18;
testDetectionNet(net, blob, ref, confThreshold, scoreDiff, iouDiff);
{
SCOPED_TRACE("Per-tensor quantize");
testDetectionNet(net, blob, ref, 0.85, scoreDiff, iouDiff, false);
}
}
TEST_P(Test_Int8_nets, FasterRCNN_resnet50)
@@ -998,7 +1066,7 @@ TEST_P(Test_Int8_nets, FasterRCNN_vgg16)
#if defined(OPENCV_32BIT_CONFIGURATION) && defined(HAVE_OPENCL)
CV_TEST_TAG_MEMORY_2GB,
#else
(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB),
CV_TEST_TAG_MEMORY_2GB,
#endif
CV_TEST_TAG_LONG,
CV_TEST_TAG_DEBUG_VERYLONG
@@ -1016,7 +1084,7 @@ TEST_P(Test_Int8_nets, FasterRCNN_vgg16)
0, 7, 0.997022, 481.841, 92.3218, 722.685, 175.953,
0, 12, 0.993028, 133.221, 189.377, 350.994, 563.166);
float confThreshold = 0.8, scoreDiff = 0.024, iouDiff = 0.35;
float confThreshold = 0.8, scoreDiff = 0.048, iouDiff = 0.35;
testFaster(net, ref, confThreshold, scoreDiff, iouDiff);
}
@@ -1028,7 +1096,7 @@ TEST_P(Test_Int8_nets, FasterRCNN_zf)
#else
(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB),
#endif
CV_TEST_TAG_DEBUG_LONG
CV_TEST_TAG_DEBUG_VERYLONG
);
if (target == DNN_TARGET_OPENCL_FP16 && !ocl::Device::getDefault().isIntel())
@@ -1066,7 +1134,7 @@ TEST_P(Test_Int8_nets, RFCN)
Mat ref = (Mat_<float>(2, 7) << 0, 7, 0.991359, 491.822, 81.1668, 702.573, 178.234,
0, 12, 0.94786, 132.093, 223.903, 338.077, 566.16);
float confThreshold = 0.8, scoreDiff = 0.017, iouDiff = 0.11;
float confThreshold = 0.8, scoreDiff = 0.15, iouDiff = 0.11;
testFaster(net, ref, confThreshold, scoreDiff, iouDiff);
}
@@ -1078,7 +1146,8 @@ TEST_P(Test_Int8_nets, YoloVoc)
#else
CV_TEST_TAG_MEMORY_1GB,
#endif
CV_TEST_TAG_LONG
CV_TEST_TAG_LONG,
CV_TEST_TAG_DEBUG_VERYLONG
);
if (target == DNN_TARGET_OPENCL_FP16 && !ocl::Device::getDefault().isIntel())
@@ -1096,7 +1165,7 @@ TEST_P(Test_Int8_nets, YoloVoc)
std::string config_file = "yolo-voc.cfg";
std::string weights_file = "yolo-voc.weights";
double scoreDiff = 0.1, iouDiff = 0.3;
double scoreDiff = 0.12, iouDiff = 0.3;
{
SCOPED_TRACE("batch size 1");
testDarknetModel(config_file, weights_file, ref.rowRange(0, 3), scoreDiff, iouDiff);
@@ -1129,17 +1198,30 @@ TEST_P(Test_Int8_nets, TinyYoloVoc)
{
SCOPED_TRACE("batch size 1");
testDarknetModel(config_file, weights_file, ref.rowRange(0, 2), scoreDiff, iouDiff);
{
SCOPED_TRACE("Per-tensor quantize");
testDarknetModel(config_file, weights_file, ref.rowRange(0, 2), 0.1, 0.2, 0.24, 0.6, false);
}
}
{
SCOPED_TRACE("batch size 2");
testDarknetModel(config_file, weights_file, ref, scoreDiff, iouDiff);
{
SCOPED_TRACE("Per-tensor quantize");
testDarknetModel(config_file, weights_file, ref, 0.1, 0.2, 0.24, 0.6, false);
}
}
}
TEST_P(Test_Int8_nets, YOLOv3)
{
applyTestTag(CV_TEST_TAG_LONG, (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB));
applyTestTag(
CV_TEST_TAG_LONG,
(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB),
CV_TEST_TAG_DEBUG_VERYLONG
);
if (target == DNN_TARGET_OPENCL_FP16 && !ocl::Device::getDefault().isIntel())
applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16);
@@ -1179,7 +1261,11 @@ TEST_P(Test_Int8_nets, YOLOv3)
TEST_P(Test_Int8_nets, YOLOv4)
{
applyTestTag(CV_TEST_TAG_LONG, (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB));
applyTestTag(
CV_TEST_TAG_LONG,
(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB),
CV_TEST_TAG_DEBUG_VERYLONG
);
if (target == DNN_TARGET_OPENCL_FP16 && !ocl::Device::getDefault().isIntel())
applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16);
@@ -1251,6 +1337,11 @@ TEST_P(Test_Int8_nets, YOLOv4_tiny)
{
SCOPED_TRACE("batch size 1");
testDarknetModel(config_file, weights_file, ref.rowRange(0, N0), scoreDiff, iouDiff, confThreshold);
{
SCOPED_TRACE("Per-tensor quantize");
testDarknetModel(config_file, weights_file, ref.rowRange(0, N0), scoreDiff, 0.16, 0.7, 0.4, false);
}
}
throw SkipTestException("batch2: bad accuracy on second image");
@@ -55,3 +55,5 @@
"test_sub_bcast",
"test_sub_uint8", // output type mismatch
"test_upsample_nearest",
"test_div_bcast", // remove when 1D Mat is supported
"test_mul_bcast", // remove when 1D Mat is supported
@@ -96,10 +96,6 @@
"test_cumsum_2d_axis_0",
"test_cumsum_2d_axis_1",
"test_cumsum_2d_negative_axis",
"test_depthtospace_crd_mode",
"test_depthtospace_crd_mode_example",
"test_depthtospace_dcr_mode",
"test_depthtospace_example",
"test_dequantizelinear",
"test_dequantizelinear_axis",
"test_det_2d",
@@ -490,8 +486,6 @@
"test_slice_neg_steps",
"test_slice_negative_axes",
"test_slice_start_out_of_bounds",
"test_spacetodepth",
"test_spacetodepth_example",
"test_split_variable_parts_1d",
"test_split_variable_parts_2d",
"test_split_variable_parts_default_axis",
+24
View File
@@ -389,6 +389,13 @@ TEST_P(Test_ONNX_layers, Clip)
testONNXModels("clip", npy);
}
TEST_P(Test_ONNX_layers, Clip_init)
{
testONNXModels("clip_init_min_max");
testONNXModels("clip_init_min");
testONNXModels("clip_init_max");
}
TEST_P(Test_ONNX_layers, Shape)
{
testONNXModels("shape_of_constant");
@@ -404,6 +411,7 @@ TEST_P(Test_ONNX_layers, ReduceMean)
TEST_P(Test_ONNX_layers, ReduceSum)
{
testONNXModels("reduce_sum");
testONNXModels("reduce_sum_axis_dynamic_batch");
}
TEST_P(Test_ONNX_layers, ReduceMax)
@@ -1052,6 +1060,8 @@ TEST_P(Test_ONNX_layers, Div)
normAssert(ref, out, "", default_l1, default_lInf);
expectNoFallbacksFromIE(net);
expectNoFallbacksFromCUDA(net);
testONNXModels("div_test_1x1",npy, 0, 0, false, true, 2);
}
TEST_P(Test_ONNX_layers, DynamicReshape)
@@ -1318,6 +1328,7 @@ TEST_P(Test_ONNX_layers, ResizeOpset11_Torch1_6)
TEST_P(Test_ONNX_layers, Mish)
{
testONNXModels("mish");
testONNXModels("mish_no_softplus");
}
TEST_P(Test_ONNX_layers, CalculatePads)
@@ -1746,12 +1757,19 @@ TEST_P(Test_ONNX_layers, DivConst)
testONNXModels("div_const");
}
TEST_P(Test_ONNX_layers, Gemm)
{
testONNXModels("gemm_no_transB");
testONNXModels("gemm_transB_0");
}
TEST_P(Test_ONNX_layers, Quantized_Convolution)
{
testONNXModels("quantized_conv_uint8_weights", npy, 0.004, 0.02);
testONNXModels("quantized_conv_int8_weights", npy, 0.03, 0.5);
testONNXModels("quantized_conv_per_channel_weights", npy, 0.06, 0.4);
testONNXModels("quantized_conv_asymmetric_pads_int8_weights");
}
TEST_P(Test_ONNX_layers, Quantized_MatMul)
@@ -1853,6 +1871,11 @@ TEST_P(Test_ONNX_layers, Quantized_Constant)
testONNXModels("quantized_constant", npy, 0.002, 0.008);
}
TEST_P(Test_ONNX_layers, OutputRegistration)
{
testONNXModels("output_registration", npy, 0, 0, false, true, 2);
}
INSTANTIATE_TEST_CASE_P(/*nothing*/, Test_ONNX_layers, dnnBackendsAndTargets());
class Test_ONNX_nets : public Test_ONNX_layers
@@ -2195,6 +2218,7 @@ TEST_P(Test_ONNX_nets, Shufflenet)
TEST_P(Test_ONNX_nets, Resnet34_kinetics)
{
applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG);
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000)
// IE exception: Failed to allocate graph: MYRIAD device is not opened
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD)
+22 -8
View File
@@ -1336,7 +1336,7 @@ TEST_P(Test_TensorFlow_nets, EAST_text_detection)
}
else
{
l1_geometry = 1e-4, lInf_geometry = 3e-3;
l1_geometry = 1e-4, lInf_geometry = 4.3e-3;
}
normAssert(scores, blobFromNPY(refScoresPath), "scores", l1_scores, lInf_scores);
normAssert(geometry, blobFromNPY(refGeometryPath), "geometry", l1_geometry, lInf_geometry);
@@ -1719,13 +1719,6 @@ TEST_P(Test_TensorFlow_layers, tf2_permute_nhwc_ncwh)
runTensorFlowNet("tf2_permute_nhwc_ncwh");
}
// issue #21852
TEST_P(Test_TensorFlow_layers, tf_graph_simplifier_buffer_overflow)
{
// This just shouldn't segfault, otherwise it's fine
EXPECT_ANY_THROW(readNetFromTensorflow(path("tf_graph_simplifier_buffer_overflow_net.pb")));
}
TEST_P(Test_TensorFlow_layers, squeeze)
{
#if defined(INF_ENGINE_RELEASE)
@@ -1899,4 +1892,25 @@ TEST_P(Test_TensorFlow_nets, EfficientDet)
expectNoFallbacksFromIE(net);
}
TEST(Test_TensorFlow_Importer, tf_graph_simplifier_buffer_overflow_21852)
{
uint8_t payload[] = {0x08, 0x08, 0x0a, 0x00, 0x0a, 0x00};
EXPECT_ANY_THROW(readNetFromTensorflow(reinterpret_cast<const char*>(payload), sizeof(payload) / sizeof(payload[0])));
}
// can be triggered with -fsanitize=address
TEST(Test_TensorFlow_Importer, tf_graph_simplifier_buffer_overflow_21947)
{
uint8_t payload[] = {0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00,
0xba, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00,
0x0a, 0xbd, 0x00, 0x1a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xba,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xba, 0x0a, 0x00,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xba,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x2a, 0x00, 0xba, 0x0a, 0x00,
0x0a, 0x00, 0x5d, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x40};
EXPECT_ANY_THROW(readNetFromTensorflow(reinterpret_cast<const char*>(payload), sizeof(payload) / sizeof(payload[0])));
}
}
@@ -1132,8 +1132,8 @@ protected:
virtual void clear();
const Mat& getDescriptors() const;
const Mat getDescriptor( int imgIdx, int localDescIdx ) const;
const Mat getDescriptor( int globalDescIdx ) const;
Mat getDescriptor( int imgIdx, int localDescIdx ) const;
Mat getDescriptor( int globalDescIdx ) const;
void getLocalIdx( int globalDescIdx, int& imgIdx, int& localDescIdx ) const;
int size() const;
+2
View File
@@ -315,6 +315,8 @@ struct SIdx
const SIdx& used;
bool operator()(const SIdx& v) const { return (v.i1 == used.i1 || v.i2 == used.i2); }
UsedFinder& operator=(const UsedFinder&) = delete;
// To avoid -Wdeprecated-copy warning, copy constructor is needed.
UsedFinder(const UsedFinder&) = default;
};
};
+1 -2
View File
@@ -312,7 +312,7 @@ void KAZEFeatures::Determinant_Hessian(std::vector<KeyPoint>& kpts)
{
int level = 0;
float smax = 3.0;
int npoints = 0, id_repeated = 0;
int id_repeated = 0;
int left_x = 0, right_x = 0, up_y = 0, down_y = 0;
bool is_extremum = false, is_repeated = false, is_out = false;
@@ -383,7 +383,6 @@ void KAZEFeatures::Determinant_Hessian(std::vector<KeyPoint>& kpts)
if (is_out == false) {
if (is_repeated == false) {
kpts.push_back(kpts_par_ij);
npoints++;
}
else {
kpts[id_repeated] = kpts_par_ij;
+3 -1
View File
@@ -148,10 +148,12 @@ public:
{
return mask.at<uchar>( (int)(key_pt.pt.y + 0.5f), (int)(key_pt.pt.x + 0.5f) ) == 0;
}
MaskPredicate& operator=(const MaskPredicate&) = delete;
// To avoid -Wdeprecated-copy warning, copy constructor is needed.
MaskPredicate(const MaskPredicate&) = default;
private:
const Mat mask;
MaskPredicate& operator=(const MaskPredicate&) = delete;
};
void KeyPointsFilter::runByPixelsMask( std::vector<KeyPoint>& keypoints, const Mat& mask )
+2 -2
View File
@@ -475,7 +475,7 @@ void DescriptorMatcher::DescriptorCollection::clear()
mergedDescriptors.release();
}
const Mat DescriptorMatcher::DescriptorCollection::getDescriptor( int imgIdx, int localDescIdx ) const
Mat DescriptorMatcher::DescriptorCollection::getDescriptor( int imgIdx, int localDescIdx ) const
{
CV_Assert( imgIdx < (int)startIdxs.size() );
int globalIdx = startIdxs[imgIdx] + localDescIdx;
@@ -489,7 +489,7 @@ const Mat& DescriptorMatcher::DescriptorCollection::getDescriptors() const
return mergedDescriptors;
}
const Mat DescriptorMatcher::DescriptorCollection::getDescriptor( int globalDescIdx ) const
Mat DescriptorMatcher::DescriptorCollection::getDescriptor( int globalDescIdx ) const
{
CV_Assert( globalDescIdx < size() );
return mergedDescriptors.row( globalDescIdx );
+9
View File
@@ -981,11 +981,20 @@ else // CV_8U
__pack01 = v_pack_u(v_round(__dst0 * __nrm2), v_round(__dst1 * __nrm2));
v_pack_store(dst + k, __pack01);
}
#endif
#if defined(__GNUC__) && __GNUC__ >= 9
// avoid warning "iteration 7 invokes undefined behavior" on Linux ARM64
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Waggressive-loop-optimizations"
#endif
for( ; k < len; k++ )
{
dst[k] = saturate_cast<uchar>(rawDst[k]*nrm2);
}
#if defined(__GNUC__) && __GNUC__ >= 9
#pragma GCC diagnostic pop
#endif
}
#else
float* dst = dstMat.ptr<float>(row);

Some files were not shown because too many files have changed in this diff Show More