修复oneTriggerCaptrueTimes不生效的问题。 修复logwidget可能导致的死锁问题。 优化日志显示。 优化mission错误处理。 修改重试功能。 优化histogramWidget显示。
834 lines
27 KiB
C++
834 lines
27 KiB
C++
#include <HalconCpp.h>
|
||
#include <opencv.hpp>
|
||
#include <ios>
|
||
#include <algorithm>
|
||
#include <handleAI.h>
|
||
#include <QString>
|
||
#include <QByteArray>
|
||
#include <QTextCodec>
|
||
#pragma once
|
||
using namespace HalconCpp;
|
||
struct MatchResult {
|
||
cv::Rect boundingRect;
|
||
double confidence;
|
||
};
|
||
// 简单的图像相关算法
|
||
|
||
//融合两张图片白色区域
|
||
static cv::Mat mergeBinaryMats(const cv::Mat& mat1, const cv::Mat& mat2)
|
||
{
|
||
// 确保两个输入图像具有相同的尺寸
|
||
if (mat1.size() != mat2.size()) {
|
||
std::cerr << "Error: Input images must have the same size." << std::endl;
|
||
return cv::Mat();
|
||
}
|
||
|
||
// 创建一个输出图像,初始化为全黑
|
||
cv::Mat merged = cv::Mat::zeros(mat1.size(), CV_8U);
|
||
|
||
// 将两个输入图像中的白色部分合并到输出图像
|
||
cv::bitwise_or(mat1, mat2, merged);
|
||
|
||
return merged;
|
||
}
|
||
//Rect相加
|
||
static cv::Rect rectPlusRect(cv::Rect world, cv::Rect rect)
|
||
{
|
||
return cv::Rect(rect.x + world.x, rect.y + world.y, rect.width, rect.height);
|
||
}
|
||
static cv::Rect findMaxAreaContour(cv::Mat src)
|
||
{
|
||
int maxIndex = 0, maxArea = 0;
|
||
std::vector<std::vector<cv::Point>>contours;
|
||
cv::findContours(src, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
|
||
if (contours.size() == 0)
|
||
return cv::Rect(0, 0, 0, 0);
|
||
for (int i = 0; i < contours.size(); i++)
|
||
{
|
||
int Area = cv::contourArea(contours[i]);
|
||
if (Area > maxArea)
|
||
{
|
||
maxArea = Area;
|
||
maxIndex = i;
|
||
}
|
||
}
|
||
if (contours.size() != 0)
|
||
return cv::boundingRect(contours[maxIndex]);
|
||
return cv::Rect(0, 0, 0, 0);
|
||
}
|
||
static cv::Rect findMaxAreaContour(cv::Mat src, int& indexReturn)
|
||
{
|
||
int maxIndex = 0, maxArea = 0;
|
||
std::vector<std::vector<cv::Point>>contours;
|
||
cv::findContours(src, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
|
||
if (contours.size() == 0)
|
||
return cv::Rect(0, 0, 0, 0);
|
||
for (int i = 0; i < contours.size(); i++)
|
||
{
|
||
int Area = cv::contourArea(contours[i]);
|
||
if (Area > maxArea)
|
||
{
|
||
maxArea = Area;
|
||
maxIndex = i;
|
||
}
|
||
}
|
||
if (contours.size() != 0)
|
||
{
|
||
indexReturn = maxIndex;
|
||
return cv::boundingRect(contours[maxIndex]);
|
||
}
|
||
}
|
||
|
||
static cv::Mat cutImg2Cls(cv::Rect boundingBox, cv::Mat imgInput, cv::Size targetSize = cv::Size(128, 128))
|
||
{
|
||
if (boundingBox.width <= 0 || boundingBox.height <= 0 || imgInput.empty()) {
|
||
std::cerr << "Invalid bounding box or empty input image." << std::endl;
|
||
return cv::Mat();
|
||
}
|
||
|
||
// 以目标框中心为正方形中心,边长为 max(max(w,h),128)
|
||
const cv::Point mid(boundingBox.x + boundingBox.width / 2, boundingBox.y + boundingBox.height / 2);
|
||
const int sideLength = getMax(getMax(boundingBox.width, boundingBox.height), 128);
|
||
|
||
// 期望在原图上的 ROI(可能越界)
|
||
cv::Rect expectRect(mid.x - sideLength / 2, mid.y - sideLength / 2, sideLength, sideLength);
|
||
|
||
// 计算需要的边界填充(反射填充)
|
||
const int left = getMax(0, -expectRect.x);
|
||
const int top = getMax(0, -expectRect.y);
|
||
const int right = getMax(0, expectRect.x + expectRect.width - imgInput.cols);
|
||
const int bottom = getMax(0, expectRect.y + expectRect.height - imgInput.rows);
|
||
|
||
cv::Mat padded;
|
||
if (left || top || right || bottom) {
|
||
cv::copyMakeBorder(imgInput, padded, top, bottom, left, right, cv::BORDER_REFLECT_101);
|
||
} else {
|
||
padded = imgInput;
|
||
}
|
||
|
||
// 在填充后图像上的 ROI(保证不越界)
|
||
cv::Rect shifted(expectRect.x + left, expectRect.y + top, expectRect.width, expectRect.height);
|
||
|
||
// 最终裁剪并缩放
|
||
cv::Mat squareImage = padded(shifted).clone();
|
||
cv::resize(squareImage, squareImage, targetSize);
|
||
return squareImage;
|
||
}
|
||
|
||
static std::vector<cv::Mat> cutImg2ClsVec(cv::Rect boundingBox, cv::Mat imgInput, cv::Size targetSize = cv::Size(128, 128))
|
||
{
|
||
std::vector<cv::Mat> result;
|
||
|
||
if (boundingBox.width <= 0 || boundingBox.height <= 0 || imgInput.empty()) {
|
||
std::cerr << "Invalid bounding box or empty input image." << std::endl;
|
||
return result;
|
||
}
|
||
|
||
const int w = boundingBox.width;
|
||
const int h = boundingBox.height;
|
||
const int minSide = getMin(w, h);
|
||
if (minSide <= 0) {
|
||
std::cerr << "Bounding box has zero width or height." << std::endl;
|
||
return result;
|
||
}
|
||
|
||
// 沿长边切分为若干个 size=minSide 的方块,覆盖到末端且避免重复最后一块
|
||
const bool splitHoriz = (w > h); // 横向长 -> 沿X方向切
|
||
const int longSide = splitHoriz ? w : h;
|
||
const int step = minSide;
|
||
const int beginCoord = splitHoriz ? boundingBox.x : boundingBox.y;
|
||
const int endCoord = beginCoord + longSide;
|
||
|
||
// 计算切块起点列表
|
||
std::vector<int> starts;
|
||
starts.reserve((longSide + step - 1) / step);
|
||
for (int s = beginCoord; s + step <= endCoord; s += step) {
|
||
starts.emplace_back(s);
|
||
}
|
||
// 确保包含末端(避免遗漏),同时避免与最后一个重复
|
||
const int lastStart = endCoord - step;
|
||
if (starts.empty() || starts.back() != lastStart) {
|
||
starts.emplace_back(lastStart);
|
||
}
|
||
|
||
result.reserve(starts.size());
|
||
if (splitHoriz) {
|
||
for (int x : starts) {
|
||
result.emplace_back(cutImg2Cls(cv::Rect(x, boundingBox.y, minSide, minSide), imgInput, targetSize));
|
||
}
|
||
} else {
|
||
for (int y : starts) {
|
||
result.emplace_back(cutImg2Cls(cv::Rect(boundingBox.x, y, minSide, minSide), imgInput, targetSize));
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static cv::Mat HObject2Mat(HObject& Hobj) //HObject类型转Mat类型
|
||
{
|
||
HTuple htCh;
|
||
HString cType;
|
||
cv::Mat Image;
|
||
ConvertImageType(Hobj, &Hobj, "byte");
|
||
CountChannels(Hobj, &htCh);
|
||
Hlong wid = 0;
|
||
Hlong hgt = 0;
|
||
if (htCh[0].I() == 1)
|
||
{
|
||
HImage hImg(Hobj);
|
||
void* ptr = hImg.GetImagePointer1(&cType, &wid, &hgt);//GetImagePointer1(Hobj, &ptr, &cType, &wid, &hgt);
|
||
int W = wid;
|
||
int H = hgt;
|
||
Image.create(H, W, CV_8UC1);
|
||
unsigned char* pdata = static_cast<unsigned char*>(ptr);
|
||
memcpy(Image.data, pdata, W * H);
|
||
}
|
||
else if (htCh[0].I() == 3)
|
||
{
|
||
void* Rptr;
|
||
void* Gptr;
|
||
void* Bptr;
|
||
HImage hImg(Hobj);
|
||
hImg.GetImagePointer3(&Rptr, &Gptr, &Bptr, &cType, &wid, &hgt);
|
||
int W = wid;
|
||
int H = hgt;
|
||
Image.create(H, W, CV_8UC3);
|
||
std::vector<cv::Mat> VecM(3);
|
||
VecM[0].create(H, W, CV_8UC1);
|
||
VecM[1].create(H, W, CV_8UC1);
|
||
VecM[2].create(H, W, CV_8UC1);
|
||
unsigned char* R2 = (unsigned char*)Rptr;
|
||
unsigned char* G = (unsigned char*)Gptr;
|
||
unsigned char* B = (unsigned char*)Bptr;
|
||
memcpy(VecM[2].data, R2, W * H);
|
||
memcpy(VecM[1].data, G, W * H);
|
||
memcpy(VecM[0].data, B, W * H);
|
||
cv::merge(VecM, Image);
|
||
}
|
||
return Image;
|
||
}
|
||
enum MatsToHObjectEnum
|
||
{
|
||
horizontal,
|
||
vertical
|
||
};
|
||
static HalconCpp::HObject MatsToHObject(cv::Mat* img1, cv::Mat* img2, MatsToHObjectEnum type)
|
||
{
|
||
auto start3 = std::chrono::high_resolution_clock::now();
|
||
HalconCpp::HObject Hobj;
|
||
int height = 0;
|
||
int width = 0;
|
||
switch (type)
|
||
{
|
||
case horizontal:
|
||
width = img1->cols;
|
||
height = img1->rows + img2->rows;
|
||
break;
|
||
case vertical:
|
||
width = img1->cols + img2->cols;
|
||
height = img1->rows;
|
||
break;
|
||
}
|
||
if (img1->type() == CV_8UC1)
|
||
{
|
||
unsigned char* imgData = new unsigned char[width * height];
|
||
memcpy(imgData, img1->data, img1->rows * img1->cols);
|
||
memcpy(img1->rows * img1->cols + imgData, img2->data, img1->rows * img1->cols);
|
||
GenImage1(&Hobj, "byte", width, height, (Hlong)imgData);
|
||
delete[]imgData;
|
||
imgData = nullptr;
|
||
}
|
||
auto end3 = std::chrono::high_resolution_clock::now();
|
||
auto duration3 = std::chrono::duration_cast<std::chrono::milliseconds>(end3 - start3);
|
||
std::cout << "Time taken by functionxxx: " << duration3.count() << " milliseconds" << std::endl;
|
||
return Hobj;
|
||
}
|
||
static HalconCpp::HObject MatsToHObject(cv::Mat& img1, cv::Mat& img2, MatsToHObjectEnum type)
|
||
{
|
||
auto start3 = std::chrono::high_resolution_clock::now();
|
||
HalconCpp::HObject Hobj;
|
||
int height = 0;
|
||
int width = 0;
|
||
switch (type)
|
||
{
|
||
case horizontal:
|
||
width = img1.cols;
|
||
height = img1.rows + img2.rows;
|
||
break;
|
||
case vertical:
|
||
width = img1.cols + img2.cols;
|
||
height = img1.rows;
|
||
break;
|
||
}
|
||
if (img1.type() == CV_8UC1)
|
||
{
|
||
unsigned char* imgData = new unsigned char[width * height];
|
||
//#pragma omp parallel for
|
||
//for (int i = 0; i < 2; i++)
|
||
//{
|
||
// switch (i)
|
||
// {
|
||
// case 0:
|
||
// memcpy(imgData, img1.data, img1.rows * img1.cols);
|
||
// break;
|
||
// case 1:
|
||
// memcpy(img1.rows * img1.cols + imgData, img2.data, img1.rows * img1.cols);
|
||
// break;
|
||
// }
|
||
//}
|
||
memcpy(imgData, img1.data, img1.rows * img1.cols);
|
||
memcpy(img1.rows * img1.cols + imgData, img2.data, img1.rows * img1.cols);
|
||
GenImage1(&Hobj, "byte", width, height, (Hlong)imgData);
|
||
delete[]imgData;
|
||
imgData = nullptr;
|
||
}
|
||
auto end3 = std::chrono::high_resolution_clock::now();
|
||
auto duration3 = std::chrono::duration_cast<std::chrono::milliseconds>(end3 - start3);
|
||
std::cout << "Time taken by functionxxx: " << duration3.count() << " milliseconds" << std::endl;
|
||
return Hobj;
|
||
}
|
||
static void MatToHObjectV(cv::Mat& image, HalconCpp::HObject* Hobj)
|
||
{
|
||
int hgt = image.rows;
|
||
int wid = image.cols;
|
||
int i;
|
||
if (image.type() == CV_8UC3)
|
||
{
|
||
HalconCpp::HImage himage = HalconCpp::HImage();
|
||
|
||
std::vector<cv::Mat> imgchannel;
|
||
split(image, imgchannel);
|
||
cv::Mat imgB = imgchannel[0];
|
||
cv::Mat imgG = imgchannel[1];
|
||
cv::Mat imgR = imgchannel[2];
|
||
uchar* dataR = new uchar[hgt * wid];
|
||
uchar* dataG = new uchar[hgt * wid];
|
||
uchar* dataB = new uchar[hgt * wid];
|
||
for (i = 0; i < hgt; i++)
|
||
{
|
||
memcpy(dataR + wid * i, imgR.data + imgR.step * i, wid);
|
||
memcpy(dataG + wid * i, imgG.data + imgG.step * i, wid);
|
||
memcpy(dataB + wid * i, imgB.data + imgB.step * i, wid);
|
||
}
|
||
himage.GenImage3("byte", wid, hgt, dataR, dataG, dataB);
|
||
HalconCpp::HString tmpType;
|
||
Hlong tmpW, tmpH;
|
||
void* hImgPtR, * hImgPtG, * hImgPtB;
|
||
himage.GetImagePointer3(&hImgPtR, &hImgPtG, &hImgPtB, &tmpType, &tmpW, &tmpH);
|
||
|
||
GenImage3(Hobj, "byte", wid, hgt, (Hlong)hImgPtR, (Hlong)hImgPtG, (Hlong)hImgPtB);
|
||
delete[]dataR;
|
||
delete[]dataG;
|
||
delete[]dataB;
|
||
dataR = NULL;
|
||
dataG = NULL;
|
||
dataB = NULL;
|
||
}
|
||
else if (image.type() == CV_8UC1)
|
||
{
|
||
//HalconCpp::HImage himage = HalconCpp::HImage();
|
||
|
||
////uchar* data = new uchar[hgt * wid];
|
||
////for (i = 0; i < hgt; i++)
|
||
////{
|
||
//// memcpy(data + wid * i, image.data + image.step * i, wid);
|
||
////}
|
||
////gen_image_const
|
||
//himage.GenImage1("byte", wid, hgt, image.data);
|
||
|
||
//HalconCpp::HString tmpType;
|
||
//Hlong tmpW, tmpH;
|
||
//void* hImgPt;
|
||
//hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH);
|
||
//GenImage1(&Hobj, "byte", wid, hgt, (Hlong)hImgPt);
|
||
GenImage1(Hobj, "byte", wid, hgt, (Hlong)image.data);
|
||
|
||
/* delete[] data;
|
||
data = NULL;*/
|
||
|
||
}
|
||
else if (image.type() == CV_16UC1)
|
||
{
|
||
HalconCpp::HImage himage = HalconCpp::HImage();
|
||
|
||
ushort* data = new ushort[hgt * wid * 2];
|
||
|
||
memcpy(data, image.data, hgt * wid * 2);
|
||
|
||
|
||
himage.GenImage1("uint2", wid, hgt, data);
|
||
|
||
HalconCpp::HString tmpType;
|
||
Hlong tmpW, tmpH;
|
||
void* hImgPt;
|
||
hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH);
|
||
GenImage1(Hobj, "uint2", wid, hgt, (Hlong)hImgPt);
|
||
|
||
delete[] data;
|
||
data = NULL;
|
||
|
||
}
|
||
else if (image.type() == CV_16SC1)
|
||
{
|
||
HalconCpp::HImage himage = HalconCpp::HImage();
|
||
|
||
short* data = new short[hgt * wid * 2];
|
||
|
||
memcpy(data, image.data, hgt * wid * 2);
|
||
|
||
|
||
himage.GenImage1("int2", wid, hgt, data);
|
||
|
||
HalconCpp::HString tmpType;
|
||
Hlong tmpW, tmpH;
|
||
void* hImgPt;
|
||
hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH);
|
||
GenImage1(Hobj, "int2", wid, hgt, (Hlong)hImgPt);
|
||
|
||
delete[] data;
|
||
data = NULL;
|
||
|
||
}
|
||
|
||
}
|
||
static HalconCpp::HObject MatToHObject(cv::Mat& image)
|
||
{
|
||
HalconCpp::HObject Hobj;
|
||
int hgt = image.rows;
|
||
int wid = image.cols;
|
||
int i;
|
||
if (image.type() == CV_8UC3)
|
||
{
|
||
HalconCpp::HImage himage = HalconCpp::HImage();
|
||
|
||
std::vector<cv::Mat> imgchannel;
|
||
split(image, imgchannel);
|
||
cv::Mat imgB = imgchannel[0];
|
||
cv::Mat imgG = imgchannel[1];
|
||
cv::Mat imgR = imgchannel[2];
|
||
uchar* dataR = new uchar[hgt * wid];
|
||
uchar* dataG = new uchar[hgt * wid];
|
||
uchar* dataB = new uchar[hgt * wid];
|
||
for (i = 0; i < hgt; i++)
|
||
{
|
||
memcpy(dataR + wid * i, imgR.data + imgR.step * i, wid);
|
||
memcpy(dataG + wid * i, imgG.data + imgG.step * i, wid);
|
||
memcpy(dataB + wid * i, imgB.data + imgB.step * i, wid);
|
||
}
|
||
himage.GenImage3("byte", wid, hgt, dataR, dataG, dataB);
|
||
HalconCpp::HString tmpType;
|
||
Hlong tmpW, tmpH;
|
||
void* hImgPtR, * hImgPtG, * hImgPtB;
|
||
himage.GetImagePointer3(&hImgPtR, &hImgPtG, &hImgPtB, &tmpType, &tmpW, &tmpH);
|
||
|
||
GenImage3(&Hobj, "byte", wid, hgt, (Hlong)hImgPtR, (Hlong)hImgPtG, (Hlong)hImgPtB);
|
||
delete[]dataR;
|
||
delete[]dataG;
|
||
delete[]dataB;
|
||
dataR = NULL;
|
||
dataG = NULL;
|
||
dataB = NULL;
|
||
}
|
||
else if (image.type() == CV_8UC1)
|
||
{
|
||
//HalconCpp::HImage himage = HalconCpp::HImage();
|
||
|
||
////uchar* data = new uchar[hgt * wid];
|
||
////for (i = 0; i < hgt; i++)
|
||
////{
|
||
//// memcpy(data + wid * i, image.data + image.step * i, wid);
|
||
////}
|
||
////gen_image_const
|
||
//himage.GenImage1("byte", wid, hgt, image.data);
|
||
|
||
//HalconCpp::HString tmpType;
|
||
//Hlong tmpW, tmpH;
|
||
//void* hImgPt;
|
||
//hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH);
|
||
//GenImage1(&Hobj, "byte", wid, hgt, (Hlong)hImgPt);
|
||
GenImage1(&Hobj, "byte", wid, hgt, (Hlong)image.data);
|
||
|
||
/* delete[] data;
|
||
data = NULL;*/
|
||
|
||
}
|
||
else if (image.type() == CV_16UC1)
|
||
{
|
||
HalconCpp::HImage himage = HalconCpp::HImage();
|
||
|
||
ushort* data = new ushort[hgt * wid * 2];
|
||
|
||
memcpy(data, image.data, hgt * wid * 2);
|
||
|
||
|
||
himage.GenImage1("uint2", wid, hgt, data);
|
||
|
||
HalconCpp::HString tmpType;
|
||
Hlong tmpW, tmpH;
|
||
void* hImgPt;
|
||
hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH);
|
||
GenImage1(&Hobj, "uint2", wid, hgt, (Hlong)hImgPt);
|
||
|
||
delete[] data;
|
||
data = NULL;
|
||
|
||
}
|
||
else if (image.type() == CV_16SC1)
|
||
{
|
||
HalconCpp::HImage himage = HalconCpp::HImage();
|
||
|
||
short* data = new short[hgt * wid * 2];
|
||
|
||
memcpy(data, image.data, hgt * wid * 2);
|
||
|
||
|
||
himage.GenImage1("int2", wid, hgt, data);
|
||
|
||
HalconCpp::HString tmpType;
|
||
Hlong tmpW, tmpH;
|
||
void* hImgPt;
|
||
hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH);
|
||
GenImage1(&Hobj, "int2", wid, hgt, (Hlong)hImgPt);
|
||
|
||
delete[] data;
|
||
data = NULL;
|
||
|
||
}
|
||
|
||
return Hobj;
|
||
}
|
||
//获取两点之间的直线距离
|
||
static double getDistance(cv::Point pointO, cv::Point pointA)
|
||
{
|
||
double distance;
|
||
distance = powf((pointO.x - pointA.x), 2) + powf((pointO.y - pointA.y), 2);
|
||
distance = sqrtf(distance);
|
||
return distance;
|
||
}
|
||
//获取点到线段之间的直线距离
|
||
static double pointToLineDistance(const cv::Point& point, const cv::Point& lineStart, const cv::Point& lineEnd) {
|
||
double numerator = std::abs((lineEnd.y - lineStart.y) * point.x - (lineEnd.x - lineStart.x) * point.y + lineEnd.x * lineStart.y - lineEnd.y * lineStart.x);
|
||
double denominator = std::sqrt(std::pow(lineEnd.y - lineStart.y, 2) + std::pow(lineEnd.x - lineStart.x, 2));
|
||
return numerator / denominator;
|
||
}
|
||
//计算角度,pt2为顶点
|
||
static double calculateAngle(cv::Point pt1, cv::Point pt2, cv::Point pt3)
|
||
{
|
||
// 计算两个向量
|
||
cv::Point vector1 = pt1 - pt2;
|
||
cv::Point vector2 = pt3 - pt2;
|
||
|
||
// 计算两个向量的长度
|
||
double length1 = norm(vector1);
|
||
double length2 = norm(vector2);
|
||
|
||
// 计算两个向量的点积
|
||
double dotProduct = vector1.dot(vector2);
|
||
|
||
// 计算夹角的余弦值
|
||
double cosAngle = dotProduct / (length1 * length2);
|
||
|
||
// 计算夹角的弧度
|
||
double angleRadians = acos(cosAngle);
|
||
|
||
// 将弧度转换为度数
|
||
double angleDegrees = angleRadians * 180 / CV_PI;
|
||
|
||
return angleDegrees;
|
||
}
|
||
//计算角度偏移量,pt1,pt2为第一条线段,pt3,pt4为第二条线段
|
||
static double calculateAngleOffset(cv::Point pt11, cv::Point pt12, cv::Point pt21, cv::Point pt22)
|
||
{
|
||
// 计算第一条线段的向量
|
||
cv::Point vec1(pt12.x - pt11.x, pt12.y - pt11.y);
|
||
|
||
// 计算第二条线段的向量
|
||
cv::Point vec2(pt22.x - pt21.x, pt22.y - pt21.y);
|
||
|
||
// 计算向量的叉乘
|
||
double crossProduct = vec1.x * vec2.y - vec1.y * vec2.x;
|
||
|
||
// 计算角度偏移量
|
||
double angleOffset = atan2(crossProduct, vec1.dot(vec2));
|
||
|
||
// 将弧度转换为度数
|
||
angleOffset = angleOffset * 180 / CV_PI;
|
||
|
||
return angleOffset;
|
||
}
|
||
//通过角度计算圆上点的位置
|
||
static cv::Point calculatePointOnCircle(cv::Point center, double radius, double angleDegrees)
|
||
{
|
||
// 将角度转换为弧度
|
||
double angleRadians = angleDegrees * CV_PI / 180.0;
|
||
|
||
// 计算点的坐标
|
||
int x = static_cast<int>(center.x + radius * cos(angleRadians));
|
||
int y = static_cast<int>(center.y + radius * sin(angleRadians));
|
||
|
||
return cv::Point(x, y);
|
||
}
|
||
/// <summary>
|
||
/// 模板匹配,以MatchResult返回
|
||
/// </summary>
|
||
/// <param name="originalImage">图像</param>
|
||
/// <param name="templateImage">模板图像</param>
|
||
/// <returns></returns>
|
||
static MatchResult templateMatching(const cv::Mat& originalImage, const cv::Mat& templateImage) {
|
||
MatchResult result;
|
||
|
||
// 创建结果图像
|
||
int resultCols = originalImage.cols - templateImage.cols + 1;
|
||
int resultRows = originalImage.rows - templateImage.rows + 1;
|
||
cv::Mat resultImg(resultRows, resultCols, CV_32FC1);
|
||
|
||
// 进行模板匹配
|
||
cv::matchTemplate(originalImage, templateImage, resultImg, cv::TM_CCOEFF_NORMED);
|
||
|
||
// 归一化结果图像
|
||
cv::normalize(resultImg, resultImg, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
|
||
|
||
// 寻找最佳匹配位置
|
||
double minVal, maxVal;
|
||
cv::Point minLoc, maxLoc;
|
||
cv::minMaxLoc(resultImg, &minVal, &maxVal, &minLoc, &maxLoc);
|
||
|
||
// 设置矩形框和可信度
|
||
result.boundingRect = cv::Rect(maxLoc.x, maxLoc.y, templateImage.cols, templateImage.rows);
|
||
result.confidence = maxVal;
|
||
|
||
return result;
|
||
}
|
||
/// <summary>
|
||
/// 模板匹配,返回Rect以及分数
|
||
/// </summary>
|
||
/// <param name="templateImg">模板图像</param>
|
||
/// <param name="srcImg">匹配图像</param>
|
||
/// <param name="sorce">分数</param>
|
||
/// <returns></returns>
|
||
static cv::Rect doMatchTemplate(const cv::Mat& templateImg, const cv::Mat& srcImg, double* sorce)
|
||
{
|
||
cv::Mat resultImage;
|
||
matchTemplate(srcImg, templateImg, resultImage, cv::TM_CCOEFF_NORMED);
|
||
double minVal = 0, maxVal = 0;
|
||
cv::Point minLoc(0, 0), maxLoc(0, 0);
|
||
minMaxLoc(resultImage, &minVal, &maxVal, &minLoc, &maxLoc);
|
||
*sorce = maxVal;
|
||
cv::Rect rect(maxLoc.x, maxLoc.y, templateImg.cols, templateImg.rows);
|
||
return rect;
|
||
}
|
||
//计算contour是否重叠
|
||
static bool isContoursOverlap(const std::vector<cv::Point>& contour1, const std::vector<cv::Point>& contour2, double thresholdPercent = 0) {
|
||
cv::Rect rect1 = cv::boundingRect(contour1);
|
||
cv::Rect rect2 = cv::boundingRect(contour2);
|
||
cv::Size tempImgSize(getMax(rect1.x + rect1.width, rect2.x + rect2.width), getMax(rect1.y + rect1.height, rect2.y + rect2.height));
|
||
cv::Mat binaryImage = cv::Mat::zeros(tempImgSize, CV_8U);
|
||
|
||
// 画出目标轮廓
|
||
drawContours(binaryImage, std::vector<std::vector<cv::Point>>{contour2}, 0, cv::Scalar(255), -1);
|
||
|
||
double totalOverlap = 0.0;
|
||
|
||
|
||
// 创建一个临时的二值图像
|
||
cv::Mat tempBinaryImage = cv::Mat::zeros(tempImgSize, CV_8U);
|
||
// 画出当前轮廓
|
||
drawContours(tempBinaryImage, std::vector<std::vector<cv::Point>>{contour1}, 0, cv::Scalar(255), -1);
|
||
|
||
// 计算重叠的像素数
|
||
cv::Mat intersection = binaryImage & tempBinaryImage;
|
||
int overlapPixelCount = countNonZero(intersection);
|
||
|
||
// 计算当前轮廓的重叠百分比
|
||
double overlapPercentage = static_cast<double>(overlapPixelCount) / countNonZero(tempBinaryImage);
|
||
|
||
return overlapPercentage * 100.0 > thresholdPercent; // 将结果转换为百分比
|
||
}
|
||
//计算rect是否重叠
|
||
static bool isRectOverlap(const cv::Rect& rect1, const cv::Rect& rect2, double thresholdPercent = 0) {
|
||
// 计算两个矩形的重叠区域
|
||
cv::Rect intersection = rect1 & rect2;
|
||
if (thresholdPercent != 0)//如果有阈值
|
||
{
|
||
int maxCnotourArea = getMax(rect1.area(), rect2.area());
|
||
if (intersection.area() / maxCnotourArea < thresholdPercent)
|
||
return false;
|
||
}
|
||
// 如果重叠区域的面积大于0,则说明两个矩形重叠
|
||
return intersection.area() > 0;
|
||
}
|
||
//将两个contour的区域合并
|
||
static std::vector<cv::Point> contourPlusContour(const std::vector<cv::Point>& contour1, const std::vector<cv::Point>& contour2)
|
||
{
|
||
std::vector<cv::Point>res;
|
||
res.insert(res.end(), contour1.begin(), contour1.end());
|
||
res.insert(res.end(), contour2.begin(), contour2.end());
|
||
return res;
|
||
}
|
||
|
||
static cv::Rect BboxToRect(handdleAI::Bbox input)
|
||
{
|
||
return cv::Rect(input.x - input.w / 2, input.y - input.h / 2, input.w, input.h);
|
||
}
|
||
enum ngTypeEnum
|
||
{
|
||
OK,
|
||
huaHeng,
|
||
aoKeng,
|
||
zangWu
|
||
};
|
||
|
||
struct rectAndData
|
||
{
|
||
cv::Rect rect;
|
||
double defectHeight;
|
||
double defectWidth;
|
||
int badType = 0;
|
||
bool result;
|
||
ngTypeEnum ngType;
|
||
std::vector<cv::Point> contour;
|
||
rectAndData(cv::Rect rectInput, double defectHeightInput, double defectWidthInput, bool resultInput)
|
||
{
|
||
rect = rectInput;
|
||
defectHeight = defectHeightInput;
|
||
defectWidth = defectWidthInput;
|
||
result = resultInput;
|
||
}
|
||
rectAndData(cv::Rect rectInput, double defectHeightInput, double defectWidthInput, int n, bool resultInput)
|
||
{
|
||
rect = rectInput;
|
||
defectHeight = defectHeightInput;
|
||
defectWidth = defectWidthInput;
|
||
badType = n;
|
||
result = resultInput;
|
||
}
|
||
rectAndData(cv::Rect rectInput, double defectHeightInput, double defectWidthInput, int n, bool resultInput, std::vector<cv::Point>& contourInput)
|
||
{
|
||
rect = rectInput;
|
||
defectHeight = defectHeightInput;
|
||
defectWidth = defectWidthInput;
|
||
badType = n;
|
||
result = resultInput;
|
||
contour = contourInput;
|
||
}
|
||
rectAndData(cv::Rect rectInput, double defectHeightInput, double defectWidthInput, int n, bool resultInput, std::vector<cv::Point>& contourInput, ngTypeEnum inputNgType)
|
||
{
|
||
rect = rectInput;
|
||
defectHeight = defectHeightInput;
|
||
defectWidth = defectWidthInput;
|
||
badType = n;
|
||
result = resultInput;
|
||
contour = contourInput;
|
||
ngType = inputNgType;
|
||
}
|
||
};
|
||
;
|
||
//平铺图像
|
||
static cv::Mat tiledImages(std::vector<cv::Mat>& imgs)
|
||
{
|
||
if (imgs.size() == 0)
|
||
return cv::imread("./noimg.png",-1);
|
||
if (imgs.size() == 1)
|
||
return imgs[0];
|
||
for (auto& img : imgs)
|
||
{
|
||
if (img.channels() == 1)
|
||
cv::cvtColor(img, img, cv::COLOR_GRAY2BGR);
|
||
}
|
||
//总图像大小 平铺显示
|
||
int matWidth = 0, matHeight = 0;
|
||
for (auto& mat : imgs)
|
||
{
|
||
matHeight = getMax(mat.rows, matHeight);
|
||
matWidth += mat.cols;
|
||
}
|
||
cv::Mat showMat(matHeight, matWidth, CV_8UC3);
|
||
//平铺图像
|
||
int withCnt = 0;
|
||
for (auto& resultMat : imgs)
|
||
{
|
||
resultMat.copyTo(showMat(cv::Rect(withCnt, 0, resultMat.cols, resultMat.rows)));
|
||
withCnt += resultMat.cols;
|
||
}
|
||
return showMat;
|
||
};
|
||
|
||
/**
|
||
* 快速去噪函数
|
||
*
|
||
* 说明:
|
||
* - 优先使用均值滤波(box filter)实现快速平滑;
|
||
* - 对明显的椒盐噪声可开启中值滤波(按通道处理,适用于8U/16U);
|
||
* - 指定较大核或需要可控平滑强度时,使用高斯滤波作为兜底。
|
||
*
|
||
* 参数:
|
||
* - inputMat 输入图像(支持单通道/多通道);
|
||
* - outputMat 输出图像;
|
||
* - kernelSize 卷积核大小,必须为奇数且>=3(传偶数会自动+1);
|
||
* - sigma 高斯滤波标准差,<=0 表示走OpenCV自动推导;
|
||
* - useMedian 是否启用中值滤波(更抗椒盐噪声,速度较均值略慢)。
|
||
*/
|
||
static void denoiseImage(const cv::Mat& inputMat, cv::Mat& outputMat, int kernelSize = 3, double sigma = 0.0, bool useMedian = false)
|
||
{
|
||
if (inputMat.empty()) {
|
||
outputMat.release();
|
||
return;
|
||
}
|
||
|
||
int k = kernelSize;
|
||
if (k < 3) k = 3; // 最小有效核大小
|
||
|
||
if ((k & 1) == 0) {
|
||
++k; // 转换为奇数核
|
||
}
|
||
|
||
// 快速路径(椒盐噪声):启用中值滤波;多通道按通道分别处理
|
||
if (useMedian && (inputMat.depth() == CV_8U || inputMat.depth() == CV_16U)) {
|
||
if (inputMat.channels() == 1) {
|
||
cv::medianBlur(inputMat, outputMat, k);
|
||
return;
|
||
} else {
|
||
std::vector<cv::Mat> ch;
|
||
cv::split(inputMat, ch);
|
||
for (auto& c : ch) {
|
||
cv::medianBlur(c, c, k);
|
||
}
|
||
cv::merge(ch, outputMat);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 默认快速路径:小核且未指定sigma时使用均值滤波(分离实现,速度快)
|
||
if (k <= 5 && sigma <= 0.0) {
|
||
cv::blur(inputMat, outputMat, cv::Size(k, k), cv::Point(-1, -1), cv::BORDER_DEFAULT);
|
||
return;
|
||
}
|
||
|
||
// 兜底:使用可分离高斯滤波,适合较大核或需要指定平滑强度的场景
|
||
const double sigmaChecked = getMax(0.0, sigma);
|
||
cv::GaussianBlur(inputMat, outputMat, cv::Size(k, k), sigmaChecked, sigmaChecked, cv::BORDER_DEFAULT);
|
||
}
|
||
|
||
// 字符编码转换:GBK 编码的 std::string 转为 UTF-8 编码的 std::string(基于 Qt 5.14.x)
|
||
// 入参:gbkStr - GBK/GB2312/GB18030 字节序列(std::string 仅作为原始字节容器)
|
||
// 返回:UTF-8 编码的字节序列(std::string)
|
||
static inline std::string gbkToUtf8(const std::string& gbkStr)
|
||
{
|
||
// 使用 Qt 的 QTextCodec 进行编码转换
|
||
// 在 Windows 简体中文环境下,本地编码通常是 GBK/GB2312;此处显式指定 "GBK"
|
||
QTextCodec* codec = QTextCodec::codecForName("GBK");
|
||
if (!codec) {
|
||
// 兜底:如果没有 GBK codec(极少见),退化为直接当作本地 8-bit 解码
|
||
QString tmp = QString::fromLocal8Bit(gbkStr.data(), static_cast<int>(gbkStr.size()));
|
||
QByteArray utf8 = tmp.toUtf8();
|
||
return std::string(utf8.constData(), static_cast<size_t>(utf8.size()));
|
||
}
|
||
|
||
// 将 GBK 字节解码为 Unicode(QString)
|
||
QString unicode = codec->toUnicode(gbkStr.data(), static_cast<int>(gbkStr.size()));
|
||
// 再编码为 UTF-8
|
||
QByteArray utf8 = unicode.toUtf8();
|
||
return std::string(utf8.constData(), static_cast<size_t>(utf8.size()));
|
||
}
|