|
|
/*
|
|
|
* @Author: xiewenji 527774126@qq.com
|
|
|
* @Date: 2025-09-11 15:32:52
|
|
|
* @LastEditors: xiewenji 527774126@qq.com
|
|
|
* @LastEditTime: 2025-09-22 16:56:41
|
|
|
* @FilePath: /BOE_ZB_PLATE_Detect/AlgorithmModule/src/AI_Edge_Algin.cpp
|
|
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
|
*/
|
|
|
|
|
|
#include "AI_Edge_Algin.h"
|
|
|
#include "CheckErrorCodeDefine.hpp"
|
|
|
|
|
|
#define EDGE_GPU 0
|
|
|
|
|
|
#include <opencv2/opencv.hpp> // OpenCV核心功能
|
|
|
#include <opencv2/imgproc.hpp> // 图像处理模块
|
|
|
#include <vector> // 向量容器
|
|
|
#include <iostream> // 输入输出流
|
|
|
|
|
|
/**
|
|
|
* @class SimilarityTransform
|
|
|
* @brief 用于计算和存储相似变换(旋转、缩放、平移)
|
|
|
*
|
|
|
* 通过两个点对计算模板图像到检测图像的相似变换
|
|
|
*/
|
|
|
class SimilarityTransform
|
|
|
{
|
|
|
public:
|
|
|
/**
|
|
|
* @brief 构造函数,使用两个点对初始化变换矩阵
|
|
|
*
|
|
|
* @param template_p1 模板点1
|
|
|
* @param template_p2 模板点2
|
|
|
* @param detected_d1 检测点1
|
|
|
* @param detected_d2 检测点2
|
|
|
*/
|
|
|
SimilarityTransform(const cv::Point2f &template_p1,
|
|
|
const cv::Point2f &template_p2,
|
|
|
const cv::Point2f &detected_d1,
|
|
|
const cv::Point2f &detected_d2)
|
|
|
{
|
|
|
std::vector<cv::Point2f> template_pts = {template_p1, template_p2};
|
|
|
std::vector<cv::Point2f> detected_pts = {detected_d1, detected_d2};
|
|
|
|
|
|
// 计算相似变换矩阵
|
|
|
transform_matrix_ = cv::estimateAffinePartial2D(template_pts, detected_pts);
|
|
|
|
|
|
if (transform_matrix_.empty())
|
|
|
{
|
|
|
valid_ = false;
|
|
|
std::cerr << "Error: Failed to calculate transformation matrix." << std::endl;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
valid_ = true;
|
|
|
extractParameters();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief 检查变换矩阵是否有效
|
|
|
* @return bool 变换是否有效
|
|
|
*/
|
|
|
bool isValid() const { return valid_; }
|
|
|
|
|
|
/**
|
|
|
* @brief 获取旋转角度(度)
|
|
|
* @return double 旋转角度(度)
|
|
|
*/
|
|
|
double getRotationAngle() const { return rotation_angle_; }
|
|
|
|
|
|
/**
|
|
|
* @brief 获取缩放比例
|
|
|
* @return double 缩放比例
|
|
|
*/
|
|
|
double getScaleFactor() const { return scale_factor_; }
|
|
|
|
|
|
/**
|
|
|
* @brief 获取平移分量
|
|
|
* @return cv::Point2f 平移向量(tx, ty)
|
|
|
*/
|
|
|
cv::Point2f getTranslation() const
|
|
|
{
|
|
|
return cv::Point2f(transform_matrix_.at<double>(0, 2),
|
|
|
transform_matrix_.at<double>(1, 2));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief 转换点坐标
|
|
|
*
|
|
|
* @param point 输入点(模板坐标系)
|
|
|
* @return cv::Point2f 转换后的点(检测图像坐标系)
|
|
|
*/
|
|
|
cv::Point2f transformPoint(const cv::Point2f &point) const
|
|
|
{
|
|
|
if (!valid_)
|
|
|
{
|
|
|
std::cerr << "Warning: Using invalid transform! Returning original point." << std::endl;
|
|
|
return point;
|
|
|
}
|
|
|
|
|
|
// 使用矩阵乘法进行点变换
|
|
|
cv::Mat point_mat = (cv::Mat_<double>(3, 1) << point.x, point.y, 1);
|
|
|
cv::Mat result_mat = transform_matrix_ * point_mat;
|
|
|
|
|
|
return cv::Point2f(result_mat.at<double>(0), result_mat.at<double>(1));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief 批量转换点坐标
|
|
|
*
|
|
|
* @param points 输入点集(模板坐标系)
|
|
|
* @return std::vector<cv::Point2f> 转换后的点集(检测图像坐标系)
|
|
|
*/
|
|
|
std::vector<cv::Point2f> transformPoints(const std::vector<cv::Point2f> &points) const
|
|
|
{
|
|
|
if (!valid_)
|
|
|
{
|
|
|
std::cerr << "Warning: Using invalid transform! Returning original points." << std::endl;
|
|
|
return points;
|
|
|
}
|
|
|
|
|
|
std::vector<cv::Point2f> result;
|
|
|
cv::transform(points, result, transform_matrix_);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief 获取变换矩阵
|
|
|
* @return cv::Mat 2x3变换矩阵
|
|
|
*/
|
|
|
cv::Mat getTransformMatrix() const { return transform_matrix_; }
|
|
|
|
|
|
private:
|
|
|
/**
|
|
|
* @brief 从变换矩阵中提取旋转角度和缩放比例
|
|
|
*/
|
|
|
void extractParameters()
|
|
|
{
|
|
|
double a = transform_matrix_.at<double>(0, 0);
|
|
|
double b = transform_matrix_.at<double>(0, 1);
|
|
|
|
|
|
// 计算旋转角度(弧度转角度)
|
|
|
rotation_angle_ = std::atan2(b, a) * 180.0 / CV_PI;
|
|
|
|
|
|
// 计算缩放比例
|
|
|
scale_factor_ = std::sqrt(a * a + b * b);
|
|
|
}
|
|
|
|
|
|
cv::Mat transform_matrix_; // 2x3 变换矩阵
|
|
|
bool valid_ = false; // 变换是否有效
|
|
|
double rotation_angle_ = 0; // 旋转角度(度)
|
|
|
double scale_factor_ = 1; // 缩放比例
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* @brief 独立转换函数(使用变换矩阵)
|
|
|
*
|
|
|
* @param point 输入点(模板坐标系)
|
|
|
* @param transform_matrix 2x3变换矩阵
|
|
|
* @return cv::Point2f 转换后的点(检测图像坐标系)
|
|
|
*/
|
|
|
cv::Point2f transformPoint(const cv::Point2f &point, const cv::Mat &transform_matrix)
|
|
|
{
|
|
|
// 验证变换矩阵有效性
|
|
|
if (transform_matrix.empty() || transform_matrix.rows != 2 || transform_matrix.cols != 3)
|
|
|
{
|
|
|
std::cerr << "Error: Invalid transformation matrix! Returning original point." << std::endl;
|
|
|
return point;
|
|
|
}
|
|
|
|
|
|
// 使用矩阵乘法进行点变换
|
|
|
cv::Mat point_mat = (cv::Mat_<double>(3, 1) << point.x, point.y, 1);
|
|
|
cv::Mat result_mat = transform_matrix * point_mat;
|
|
|
|
|
|
return cv::Point2f(result_mat.at<double>(0), result_mat.at<double>(1));
|
|
|
}
|
|
|
|
|
|
AI_Edge_Algin::AI_Edge_Algin(std::shared_ptr<DetLog> &log_ref)
|
|
|
: m_pdetlog(log_ref)
|
|
|
{
|
|
|
m_bInitialized = false;
|
|
|
m_bModelSucc = false;
|
|
|
m_bModel_Mark_Succ = false;
|
|
|
m_bshowimg = false;
|
|
|
m_strRootPath_Big = "/home/aidlux/BOE/Algin/";
|
|
|
m_strRootPath_Mark = "/home/aidlux/BOE/MarkLine/";
|
|
|
creatsavedir();
|
|
|
std::string m_strSavePath;
|
|
|
|
|
|
m_pImageStorage = ImageStorage::getInstance();
|
|
|
}
|
|
|
|
|
|
AI_Edge_Algin::~AI_Edge_Algin()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
int AI_Edge_Algin::Init(OtherDet_Config *pOtherDet_Config)
|
|
|
{
|
|
|
m_pOtherDet_Config = pOtherDet_Config;
|
|
|
|
|
|
AI_Factory = AIFactory::GetInstance();
|
|
|
|
|
|
m_bInitialized = true;
|
|
|
return 0;
|
|
|
}
|
|
|
int AI_Edge_Algin::Detect(const cv::Mat &img, DetConfig *pDetConfig, std::shared_ptr<Edge_AI_Result> &pCheckResult_Aling)
|
|
|
{
|
|
|
m_pCheckResult_Aling = std::make_shared<Edge_AI_Result>();
|
|
|
pCheckResult_Aling = m_pCheckResult_Aling;
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "start");
|
|
|
|
|
|
m_pDetConfig = pDetConfig;
|
|
|
static int erridx = 0;
|
|
|
std::string str_error = "";
|
|
|
|
|
|
// 保存过程图片
|
|
|
if (m_pDetConfig->IsSaveProcessImg())
|
|
|
{
|
|
|
erridx++;
|
|
|
if (erridx > 9999999)
|
|
|
{
|
|
|
erridx = 0;
|
|
|
}
|
|
|
str_error = "/home/aidlux/BOE/Edge/Error/" + std::to_string(erridx) + "_src.png";
|
|
|
}
|
|
|
|
|
|
if (img.empty())
|
|
|
{
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
// 1、初步定位 找到产品大致区域
|
|
|
int re = 0;
|
|
|
|
|
|
cv::RotatedRect Big_roi;
|
|
|
cv::RotatedRect Small_roi;
|
|
|
|
|
|
re = Get_Edge(0, img, pDetConfig, m_pDetConfig->strChannel, Big_roi);
|
|
|
if (re != 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "AICheck_Edge_Big----error %d ", re);
|
|
|
if (m_pDetConfig->IsSaveProcessImg())
|
|
|
{
|
|
|
cv::imwrite(str_error, img);
|
|
|
}
|
|
|
return re;
|
|
|
}
|
|
|
re = Get_Edge(1, img, pDetConfig, m_pDetConfig->strChannel, Small_roi);
|
|
|
if (re != 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "AICheck_Edge_Small----error %d ", re);
|
|
|
if (m_pDetConfig->IsSaveProcessImg())
|
|
|
{
|
|
|
cv::imwrite(str_error, img);
|
|
|
}
|
|
|
return re;
|
|
|
}
|
|
|
|
|
|
m_pCheckResult_Aling->bigroi = Big_roi;
|
|
|
m_pCheckResult_Aling->smallroi = Small_roi;
|
|
|
|
|
|
pCheckResult_Aling = m_pCheckResult_Aling;
|
|
|
// return 1;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int AI_Edge_Algin::SaveSmallImg(const cv::Mat &img, const cv::Mat &mask, cv::Rect roi)
|
|
|
{
|
|
|
// 是否要保存中间过程的小图
|
|
|
if (m_pDetConfig->IsSaveProcessImg())
|
|
|
{
|
|
|
static int svsmallidx = 0;
|
|
|
svsmallidx++;
|
|
|
if (svsmallidx > 9999999)
|
|
|
{
|
|
|
svsmallidx = 0;
|
|
|
/* code */
|
|
|
}
|
|
|
bool bssss = false;
|
|
|
|
|
|
if (m_pDetConfig->saveProcessImg == Save_Filter)
|
|
|
{
|
|
|
// 4. 查找轮廓
|
|
|
vector<vector<Point>> contours;
|
|
|
vector<Vec4i> hierarchy;
|
|
|
findContours(mask.clone(), contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
|
|
|
if (contours.size() > 1)
|
|
|
{
|
|
|
bssss = true;
|
|
|
}
|
|
|
else if (contours.size() == 1)
|
|
|
{
|
|
|
int pointNum = 0;
|
|
|
for (int i = 0; i < contours.size(); i++)
|
|
|
{
|
|
|
pointNum += contours[i].size();
|
|
|
}
|
|
|
// printf("pointNum============== %d\n", pointNum);
|
|
|
if (pointNum > 30)
|
|
|
{
|
|
|
bssss = true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else if (m_pDetConfig->saveProcessImg == Save_ALL)
|
|
|
{
|
|
|
bssss = true;
|
|
|
}
|
|
|
|
|
|
if (bssss)
|
|
|
{
|
|
|
std::string str1 = "/home/aidlux/BOE/Edge/Smasll/" + std::to_string(svsmallidx) + "_in.png";
|
|
|
std::string str2 = "/home/aidlux/BOE/Edge/Smasll/" + std::to_string(svsmallidx) + "_in_mask.png";
|
|
|
// std::string st3 = "/home/aidlux/BOE/Edge/Smasll/" + std::to_string(svsmallidx) + "_in_show.png";
|
|
|
cv::imwrite(str1, img);
|
|
|
cv::imwrite(str2, mask);
|
|
|
// cv::Mat showsss = temDet + smask * 0.4;
|
|
|
// cv::imwrite(st3, showsss);
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int AI_Edge_Algin::InitModel_ALL()
|
|
|
{
|
|
|
|
|
|
m_bModelSucc = true;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int AI_Edge_Algin::Get_Edge(int AIModel_type, const cv::Mat &img, DetConfig *pDetConfig, std::string strChannel, cv::RotatedRect &Roi)
|
|
|
{
|
|
|
std::shared_ptr<AIModel_Base> pBackPlate_Align;
|
|
|
switch (AIModel_type)
|
|
|
{
|
|
|
case 0:
|
|
|
pBackPlate_Align = AI_Factory->Align_Outer;
|
|
|
break;
|
|
|
case 1:
|
|
|
pBackPlate_Align = AI_Factory->Align_Inner;
|
|
|
break;
|
|
|
default:
|
|
|
pBackPlate_Align = AI_Factory->Align_Outer;
|
|
|
break;
|
|
|
}
|
|
|
cv::Size sz;
|
|
|
sz.width = pBackPlate_Align->input_0.width;
|
|
|
sz.height = pBackPlate_Align->input_0.height;
|
|
|
cv::Mat detImg;
|
|
|
cout<< pDetConfig->strChannel << ": " << "---Get_Edge-resize-" << to_string(AIModel_type) <<"-- ";
|
|
|
cout << "imgSize: " << img.size() << ", " << "detImgSize: " << detImg.size() << ", " << "szSize: " << sz << endl;
|
|
|
|
|
|
cv::resize(img, detImg, sz);
|
|
|
int re = 0;
|
|
|
cv::Mat mask;
|
|
|
if (detImg.channels() != 1)
|
|
|
{
|
|
|
cv::cvtColor(detImg, detImg, cv::COLOR_RGB2GRAY);
|
|
|
}
|
|
|
|
|
|
re = pBackPlate_Align->AIDet(detImg, mask);
|
|
|
if (re != 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "AICheck_Edge_%d----error %d ", AIModel_type, re);
|
|
|
int re123 = 100 + re;
|
|
|
return re123;
|
|
|
}
|
|
|
|
|
|
if (pDetConfig->pBaseCheckFunction->saveImg.bSaveAlginImg)
|
|
|
{
|
|
|
creatsavedir();
|
|
|
static int sdk = 0;
|
|
|
std::string str = m_strSavePath_Big + std::to_string(sdk) + "_" + pDetConfig->strChannel + "_Img.png";
|
|
|
int sr = m_pImageStorage->addImage(str, detImg);
|
|
|
if (sr == 0)
|
|
|
{
|
|
|
str = m_strSavePath_Big + std::to_string(sdk) + "_" + pDetConfig->strChannel + "_Img_mask.png";
|
|
|
int sr = m_pImageStorage->addImage(str, mask, true);
|
|
|
sdk++;
|
|
|
if (sdk > 99999)
|
|
|
{
|
|
|
sdk = 0;
|
|
|
/* code */
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// if (m_pDetConfig->bSaveResultImg)
|
|
|
{
|
|
|
cv::imwrite(strChannel +"_edge_"+ to_string(AIModel_type) +"_in.png", detImg);
|
|
|
cv::imwrite(strChannel +"_edge_"+ to_string(AIModel_type) +"_out_mask.png", mask);
|
|
|
}
|
|
|
|
|
|
// 找到最大轮廓
|
|
|
bool found;
|
|
|
std::vector<cv::Point> boundingBox = CheckUtil::getLargestContourROI(mask, found);
|
|
|
if (!found)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "No contours found!----error ");
|
|
|
return 4;
|
|
|
}
|
|
|
|
|
|
// 边缘轮廓点集还原为原图坐标,并计算得到RotateROI
|
|
|
if (sz.width <= 0 || sz.height <= 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "invalid model input size!----error ");
|
|
|
return 5;
|
|
|
}
|
|
|
|
|
|
// 检测小图 到 原图 的缩放比例
|
|
|
const float scale_x = static_cast<float>(img.cols) / sz.width;
|
|
|
const float scale_y = static_cast<float>(img.rows) / sz.height;
|
|
|
|
|
|
// 轮廓点还原到原图坐标系
|
|
|
std::vector<cv::Point2f> srcContour;
|
|
|
srcContour.reserve(boundingBox.size());
|
|
|
for (const auto &pt : boundingBox)
|
|
|
{
|
|
|
srcContour.emplace_back(pt.x * scale_x, pt.y * scale_y);
|
|
|
}
|
|
|
|
|
|
if (srcContour.size() < 3)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "contour points too few!----error ");
|
|
|
return 6;
|
|
|
}
|
|
|
|
|
|
Roi = cv::minAreaRect(srcContour);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
int AI_Edge_Algin::creatsavedir()
|
|
|
{
|
|
|
std::string curDate = CheckUtil::getCurrentDate();
|
|
|
if (curDate == m_strLastDate)
|
|
|
{
|
|
|
return 0;
|
|
|
}
|
|
|
m_strLastDate = curDate;
|
|
|
m_strSavePath_Big = m_strRootPath_Big + curDate + "/";
|
|
|
m_strSavePath_Mark = m_strRootPath_Mark + curDate + "/";
|
|
|
|
|
|
CheckUtil::CreateDir(m_strSavePath_Big);
|
|
|
CheckUtil::CreateDir(m_strSavePath_Mark);
|
|
|
|
|
|
return 0;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
Image_Feature_Algin::Image_Feature_Algin()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
Image_Feature_Algin::~Image_Feature_Algin()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
int Image_Feature_Algin::Detect(DetConfig *pDetConfig, Align_Result *pResult, std::vector<std::string> &LogList)
|
|
|
{
|
|
|
// 检测目标:找到 参数模版图到 检测图的 映射关系。包括 缩放和移动。
|
|
|
|
|
|
// 先缩放 在 移动
|
|
|
std::string strlog = "";
|
|
|
if (!pDetConfig)
|
|
|
{
|
|
|
return 1;
|
|
|
}
|
|
|
if (pDetConfig->TemplateImg.empty())
|
|
|
{
|
|
|
strlog = m_PrintLog.printstr(Print_Level_Error, "Image_Align", "TemplateImg is empty ");
|
|
|
LogList.push_back(strlog);
|
|
|
return 1;
|
|
|
}
|
|
|
// 裁切位置;
|
|
|
pResult->Crop_Roi_DetImg = pDetConfig->DetImg_CropROi;
|
|
|
// 1、缩放尺度
|
|
|
float fx = 1;
|
|
|
float fy = 1;
|
|
|
// 裁切尺寸 存在 并合理
|
|
|
if (pDetConfig->param_CropRoi.width > 0 && pDetConfig->param_CropRoi.height > 0 &&
|
|
|
pDetConfig->DetImg_CropROi.width > 0 && pDetConfig->DetImg_CropROi.height > 0)
|
|
|
{
|
|
|
fx = pDetConfig->DetImg_CropROi.width * 1.0f / pDetConfig->param_CropRoi.width;
|
|
|
fy = pDetConfig->DetImg_CropROi.height * 1.0f / pDetConfig->param_CropRoi.height;
|
|
|
if (fx > 0.5 && fx < 2 && fy > 0.5 && fy < 2)
|
|
|
{
|
|
|
pResult->fCropROI_Scale_ParmToDet_X = fx;
|
|
|
pResult->fCropROI_Scale_ParmToDet_Y = fy;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
strlog = m_PrintLog.printstr(Print_Level_Error, "Image_Align", "Scale out 0.5--2");
|
|
|
LogList.push_back(strlog);
|
|
|
return 1;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
strlog = m_PrintLog.printstr(Print_Level_Error, "Image_Align", "crop ROI Error");
|
|
|
LogList.push_back(strlog);
|
|
|
return 1;
|
|
|
}
|
|
|
// strlog = m_PrintLog.printstr(Print_Level_Info, "Image_Align", "Scale x %f y %f\n", fx, fy);
|
|
|
// LogList.push_back(strlog);
|
|
|
|
|
|
// 2、定位
|
|
|
// 1)、模版特征图片的 缩放。
|
|
|
cv::Mat TemplateFeature;
|
|
|
cv::Size sz;
|
|
|
// fx = 1;
|
|
|
// fy = 1;
|
|
|
sz.width = int(pDetConfig->TemplateImg.cols * fx);
|
|
|
sz.height = int(pDetConfig->TemplateImg.rows * fy);
|
|
|
cv::resize(pDetConfig->TemplateImg, TemplateFeature, sz);
|
|
|
|
|
|
if (!CheckUtil::RoiInImg(pDetConfig->Search_Roi, pDetConfig->DetImg))
|
|
|
{
|
|
|
strlog = m_PrintLog.printstr(Print_Level_Error, "Image_Align", "Search_Roi ROI Error Not In img");
|
|
|
LogList.push_back(strlog);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
cv::Mat DetFeature = pDetConfig->DetImg(pDetConfig->Search_Roi).clone();
|
|
|
|
|
|
double confidence = 0;
|
|
|
|
|
|
int kernel_size = 128;
|
|
|
int search_size = 1024;
|
|
|
int det_search_min_size = DetFeature.cols;
|
|
|
if (DetFeature.rows < det_search_min_size)
|
|
|
{
|
|
|
det_search_min_size = DetFeature.rows;
|
|
|
}
|
|
|
int template_kernel_min_size = TemplateFeature.cols;
|
|
|
if (TemplateFeature.rows < template_kernel_min_size)
|
|
|
{
|
|
|
template_kernel_min_size = TemplateFeature.rows;
|
|
|
}
|
|
|
float f_search = search_size * 1.0f / det_search_min_size;
|
|
|
float f_Kernel = kernel_size * 1.0f / template_kernel_min_size;
|
|
|
float falign = f_search;
|
|
|
if (f_Kernel > falign)
|
|
|
{
|
|
|
falign = f_Kernel;
|
|
|
}
|
|
|
cv::Size Search_sz;
|
|
|
Search_sz.width = int(DetFeature.cols * falign);
|
|
|
Search_sz.height = int(DetFeature.rows * falign);
|
|
|
cv::Mat Search_img;
|
|
|
cv::resize(DetFeature, Search_img, Search_sz);
|
|
|
cv::Size Kernel_sz;
|
|
|
Kernel_sz.width = int(TemplateFeature.cols * falign);
|
|
|
Kernel_sz.height = int(TemplateFeature.rows * falign);
|
|
|
cv::Mat Kernel_img;
|
|
|
cv::resize(TemplateFeature, Kernel_img, Kernel_sz);
|
|
|
|
|
|
auto bestMatch = findBestTemplateMatch(Search_img, Kernel_img, confidence);
|
|
|
|
|
|
bestMatch.x /= falign;
|
|
|
bestMatch.y /= falign;
|
|
|
|
|
|
pResult->bestMatch = bestMatch;
|
|
|
if (pDetConfig->bSaveImg)
|
|
|
{
|
|
|
cv::imwrite("Align_template.png", TemplateFeature);
|
|
|
cv::imwrite("Align_Det.png", DetFeature);
|
|
|
}
|
|
|
|
|
|
if (confidence != -1)
|
|
|
{
|
|
|
std::cout << "最佳匹配位置: (" << bestMatch.x << ", " << bestMatch.y
|
|
|
<< "), 得分: " << confidence << std::endl;
|
|
|
if (confidence > pDetConfig->fscore)
|
|
|
{
|
|
|
|
|
|
/* code */
|
|
|
|
|
|
int m_x = pDetConfig->feature_Roi.x * fx - pDetConfig->Search_Roi.x;
|
|
|
int m_y = pDetConfig->feature_Roi.y * fy - pDetConfig->Search_Roi.y;
|
|
|
pResult->offt_x = bestMatch.x - m_x;
|
|
|
pResult->offt_y = bestMatch.y - m_y;
|
|
|
// printf("m_x %d bestMatch.x %d offt_x %d\n", m_x, bestMatch.x, pResult->offt_x);
|
|
|
// printf("m_y %d bestMatch.y %d offt_y %d\n", m_y, bestMatch.y, pResult->offt_y);
|
|
|
|
|
|
pResult->bDet = true;
|
|
|
|
|
|
pResult->Crop_Roi_ParmImg = pResult->Det_srcToParm_src_Rect(pResult->Crop_Roi_DetImg);
|
|
|
|
|
|
strlog = m_PrintLog.printstr(Print_Level_Info, "Image_Align", " -- Succ :Align score %f > %f offt x %d y %d Scale x %f y %f",
|
|
|
confidence, pDetConfig->fscore, pResult->offt_x, pResult->offt_y, pResult->fCropROI_Scale_ParmToDet_X, pResult->fCropROI_Scale_ParmToDet_Y);
|
|
|
|
|
|
LogList.push_back(strlog);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
|
|
|
strlog = m_PrintLog.printstr(Print_Level_Error, "Image_Align", " error :Align score %f< 0.9", confidence);
|
|
|
pResult->fCropROI_Scale_ParmToDet_X = 1;
|
|
|
pResult->fCropROI_Scale_ParmToDet_Y = 1;
|
|
|
LogList.push_back(strlog);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
pResult->fCropROI_Scale_ParmToDet_X = 1;
|
|
|
pResult->fCropROI_Scale_ParmToDet_Y = 1;
|
|
|
std::cout << "未找到有效匹配" << std::endl;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
cv::Point Image_Feature_Algin::findBestTemplateMatch(
|
|
|
const cv::Mat &detectionImage,
|
|
|
const cv::Mat &templateImage,
|
|
|
double &bestScore,
|
|
|
int method)
|
|
|
{
|
|
|
// 输入验证
|
|
|
if (detectionImage.empty() || templateImage.empty())
|
|
|
{
|
|
|
throw std::invalid_argument("输入图像不能为空");
|
|
|
}
|
|
|
if (detectionImage.channels() != 1 || templateImage.channels() != 1)
|
|
|
{
|
|
|
throw std::invalid_argument("必须输入灰度图像");
|
|
|
}
|
|
|
if (templateImage.rows > detectionImage.rows ||
|
|
|
templateImage.cols > detectionImage.cols)
|
|
|
{
|
|
|
throw std::invalid_argument("模板尺寸不能大于被检测图像");
|
|
|
}
|
|
|
|
|
|
// cv::imwrite("detectionImage.png", detectionImage);
|
|
|
// cv::imwrite("templateImage.png", templateImage);
|
|
|
// 执行模板匹配
|
|
|
cv::Mat resultMatrix;
|
|
|
cv::matchTemplate(detectionImage, templateImage, resultMatrix, method);
|
|
|
|
|
|
// 确定极值搜索方式
|
|
|
const bool findMinima = (method == cv::TM_SQDIFF || method == cv::TM_SQDIFF_NORMED);
|
|
|
|
|
|
// 查找极值位置
|
|
|
cv::Point extremaLoc = cv::Point(0, 0);
|
|
|
double extremaVal;
|
|
|
cv::minMaxLoc(resultMatrix,
|
|
|
findMinima ? &extremaVal : nullptr,
|
|
|
findMinima ? nullptr : &extremaVal,
|
|
|
findMinima ? &extremaLoc : nullptr,
|
|
|
findMinima ? nullptr : &extremaLoc);
|
|
|
|
|
|
// 设置有效性检查阈值(可根据方法动态调整)
|
|
|
double threshold = 0.0;
|
|
|
switch (method)
|
|
|
{
|
|
|
case cv::TM_CCOEFF_NORMED:
|
|
|
threshold = 0.6;
|
|
|
break; // [-1, 1]
|
|
|
case cv::TM_CCORR_NORMED:
|
|
|
threshold = 0.7;
|
|
|
break; // [0, 1]
|
|
|
case cv::TM_SQDIFF_NORMED:
|
|
|
threshold = 0.2;
|
|
|
break; // [0, 1]
|
|
|
default:
|
|
|
threshold = 0.0;
|
|
|
}
|
|
|
|
|
|
// 验证匹配有效性
|
|
|
const bool isValid = findMinima ? (extremaVal <= threshold) : (extremaVal >= threshold);
|
|
|
|
|
|
if (isValid)
|
|
|
{
|
|
|
bestScore = extremaVal;
|
|
|
return extremaLoc;
|
|
|
}
|
|
|
bestScore = -1; // 无效时的默认值
|
|
|
return extremaLoc;
|
|
|
}
|