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:
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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, ¢ers[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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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); }
|
||||
};
|
||||
|
||||
@@ -29,8 +29,7 @@ public:
|
||||
}
|
||||
void cleanup() const
|
||||
{
|
||||
CV_Assert(ptr && *ptr);
|
||||
*ptr = 0;
|
||||
CV_DbgAssert(ptr);
|
||||
if (raw_mem)
|
||||
fastFree(raw_mem);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ¶ms);
|
||||
};
|
||||
|
||||
class CV_EXPORTS NaryEltwiseLayer : public Layer
|
||||
{
|
||||
public:
|
||||
static Ptr<NaryEltwiseLayer> create(const LayerParams ¶ms);
|
||||
};
|
||||
|
||||
class CV_EXPORTS BatchNormLayer : public ActivationLayer
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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*/;
|
||||
|
||||
|
||||
@@ -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>());
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, "");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user