* -------------------------------------------------------------------------
* This file is part of the Vision SDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* Vision SDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* Description: Compute affine transformation matrices.
* Author: MindX SDK
* Create: 2020
* History: NA
*/
#include "MxBase/CV/WarpAffine/SimilarityTransform.h"
#include <cmath>
#include "MxBase/Log/Log.h"
namespace {
const int TRANS_HEIGHT = 2;
const int TRANS_WIDTH = 3;
const int MAX_POINT_PARAMETER = 8192;
const size_t MAX_POINT_COUNT = 10000;
const double EPSILON_DBL = 1e-15;
}
namespace MxBase {
SimilarityTransform::SimilarityTransform() {}
SimilarityTransform::~SimilarityTransform() {}
* @description: Calculate the mean value
* @param: srcPoint: vector of storing two-dimensional point information
* @return: Point2f
*/
cv::Point2f SimilarityTransform::GetMean(const std::vector<cv::Point2f> &srcPoint) const
{
float sumX = 0;
float sumY = 0;
for (auto point : srcPoint) {
sumX += point.x;
sumY += point.y;
}
return cv::Point2f(sumX / srcPoint.size(), sumY / srcPoint.size());
}
* @description: Calculate the variance
* @param: array: array of storing two-dimensional point information
* @return: variance
*/
double SimilarityTransform::GetSumVars(const cv::Mat &array) const
{
int rows = array.rows;
if (rows == 0) {
return 0;
}
double meanX = 0;
double meanY = 0;
for (int i = 0; i < rows; i++) {
meanX += array.at<double>(i, 0);
meanY += array.at<double>(i, 1);
}
meanX /= rows;
meanY /= rows;
double sumX = 0;
double sumY = 0;
for (int i = 0; i < rows; i++) {
sumX += (array.at<double>(i, 0) - meanX) * (array.at<double>(i, 0) - meanX);
sumY += (array.at<double>(i, 1) - meanY) * (array.at<double>(i, 1) - meanY);
}
return sumX / rows + sumY / rows;
}
static bool CheckPointValidity(const std::vector<cv::Point2f> &point)
{
for (auto p : point) {
if ((int)p.x > MAX_POINT_PARAMETER || (int)p.x < 0 ||
(int)p.y > MAX_POINT_PARAMETER || (int)p.y < 0) {
return false;
}
}
return true;
}
* @description: Calculate the affine transformation matrix
* @param: srcPoint: Point information before transformation
* dstPoint: Point information after transformation
* @return: Mat
*/
cv::Mat SimilarityTransform::Transform(const std::vector<cv::Point2f> &srcPoint,
const std::vector<cv::Point2f> &dstPoint) const
{
if (srcPoint.size() != dstPoint.size() || srcPoint.size() == 0 || srcPoint.size() > MAX_POINT_COUNT ||
!CheckPointValidity(srcPoint) || !CheckPointValidity(dstPoint)) {
return {};
}
int dim = 2;
int numPoint = static_cast<int>(srcPoint.size());
cv::Point2f srcPointMean = GetMean(srcPoint);
cv::Point2f dstPointMean = GetMean(dstPoint);
cv::Mat srcDemean(numPoint, dim, CV_64F);
cv::Mat dstDemean(numPoint, dim, CV_64F);
for (int i = 0; i < numPoint; i++) {
srcDemean.at<double>(i, 0) = srcPoint[i].x - srcPointMean.x;
srcDemean.at<double>(i, 1) = srcPoint[i].y - srcPointMean.y;
dstDemean.at<double>(i, 0) = dstPoint[i].x - dstPointMean.x;
dstDemean.at<double>(i, 1) = dstPoint[i].y - dstPointMean.y;
}
cv::Mat a = (dstDemean.t() * srcDemean) / numPoint;
cv::Mat d = cv::Mat::ones(dim, 1, CV_64F);
if (cv::determinant(a) < 0) {
d.at<double>(1, 0) = -1;
}
cv::Mat s;
cv::Mat u;
cv::Mat v;
cv::SVDecomp(a, s, u, v);
cv::Mat t = cv::Mat::eye(dim + 1, dim + 1, CV_64F);
cv::Mat tmpT = u * (cv::Mat::diag(d) * v);
cv::Mat subT = t(cv::Rect(0, 0, dim, dim));
tmpT.copyTo(subT);
double var = GetSumVars(srcDemean);
double varInv = 0.;
if (fabs(var) > EPSILON_DBL) {
varInv = 1.0 / var;
}
cv::Mat tmptd = s.t() * d;
cv::Mat scale = varInv * tmptd;
cv::Mat srcMeanMat(dim, 1, CV_64F);
srcMeanMat.at<double>(0, 0) = srcPointMean.x;
srcMeanMat.at<double>(1, 0) = srcPointMean.y;
cv::Mat dstMeanMat(dim, 1, CV_64F);
dstMeanMat.at<double>(0, 0) = dstPointMean.x;
dstMeanMat.at<double>(1, 0) = dstPointMean.y;
cv::Mat offset = dstMeanMat - scale.at<double>(0, 0) * (subT * srcMeanMat);
cv::Mat subOffset = t(cv::Rect(dim, 0, 1, dim));
offset.copyTo(subOffset);
cv::Mat subOffsetT = t(cv::Rect(0, 0, dim, dim));
subOffsetT = subOffsetT * scale.at<double>(0, 0);
return t(cv::Rect(0, 0, TRANS_WIDTH, TRANS_HEIGHT));
}
}