1093 lines
35 KiB
C++
1093 lines
35 KiB
C++
#include <iostream>
|
|
#include <HalconCpp.h>
|
|
#include <qthread.h>
|
|
#include <qqueue.h>
|
|
#include <qmutex.h>
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
#include <filesystem>
|
|
#include <opencv.hpp>
|
|
#include <string>
|
|
#include <handleai.h>
|
|
#include <sqliteManager/sqlManager.h>
|
|
#include <QtConcurrent/qtconcurrent>.
|
|
#include <threadPoolManager.h>
|
|
#include <omp.h>
|
|
#include <algorithm>
|
|
#include <triggerManager.h>
|
|
using namespace std;
|
|
using namespace HalconCpp;
|
|
enum ngTypeEnum
|
|
{
|
|
OK,
|
|
huaHeng,
|
|
aoKeng,
|
|
zangWu
|
|
};
|
|
//简单的图像相关算法
|
|
class simpleAlgo
|
|
{
|
|
public:
|
|
//融合两张图片白色区域
|
|
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相加
|
|
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(std::vector<cv::Point>contour, cv::Mat imgInput, cv::Size targetSize = cv::Size(128, 128))
|
|
{
|
|
if (contour.empty() || imgInput.empty()) {
|
|
std::cerr << "Empty contour or input image." << std::endl;
|
|
return cv::Mat();
|
|
}
|
|
cv::Rect boundingBox = cv::boundingRect(contour);
|
|
cv::Point mid(boundingBox.x + boundingBox.width / 2, boundingBox.y + boundingBox.height / 2);
|
|
int sideLength = max(max(boundingBox.width, boundingBox.height), 128);
|
|
cv::Mat squareImage(sideLength, sideLength, imgInput.type(), cv::Scalar(0, 0, 0));
|
|
cv::Rect expectRect(mid.x - sideLength / 2, mid.y - sideLength / 2, sideLength, sideLength);
|
|
int xOffset = 0;
|
|
int yOffset = 0;// (sideLength - boundingBox.height) / 2;
|
|
//越界判断
|
|
{
|
|
if (expectRect.x < 0)
|
|
{
|
|
expectRect.width += expectRect.x;
|
|
xOffset -= expectRect.x;
|
|
expectRect.x = 0;
|
|
}
|
|
if (expectRect.y < 0)
|
|
{
|
|
expectRect.height += expectRect.y;
|
|
yOffset -= expectRect.y;
|
|
expectRect.y = 0;
|
|
}
|
|
if (expectRect.x + expectRect.width > imgInput.cols)
|
|
expectRect.width = imgInput.cols - expectRect.x;
|
|
if (expectRect.y + expectRect.height > imgInput.rows)
|
|
expectRect.height = imgInput.rows - expectRect.y;
|
|
}
|
|
imgInput(expectRect).copyTo(squareImage(cv::Rect(xOffset, yOffset, expectRect.width, expectRect.height)));
|
|
resize(squareImage, squareImage, targetSize);
|
|
return squareImage;
|
|
}
|
|
static cv::Mat cutImg2Cls(cv::Rect boundingBox, cv::Mat imgInput, cv::Size targetSize = cv::Size(128, 128))
|
|
{
|
|
if (boundingBox.empty() || imgInput.empty()) {
|
|
std::cerr << "Empty contour or input image." << std::endl;
|
|
return cv::Mat();
|
|
}
|
|
cv::Point mid(boundingBox.x + boundingBox.width / 2, boundingBox.y + boundingBox.height / 2);
|
|
int sideLength = max(max(boundingBox.width, boundingBox.height), 128);
|
|
cv::Mat squareImage(sideLength, sideLength, imgInput.type(), cv::Scalar(0, 0, 0));
|
|
cv::Rect expectRect(mid.x - sideLength / 2, mid.y - sideLength / 2, sideLength, sideLength);
|
|
int xOffset = 0;
|
|
int yOffset = 0;// (sideLength - boundingBox.height) / 2;
|
|
//越界判断
|
|
{
|
|
if (expectRect.x < 0)
|
|
{
|
|
expectRect.width += expectRect.x;
|
|
xOffset -= expectRect.x;
|
|
expectRect.x = 0;
|
|
}
|
|
if (expectRect.y < 0)
|
|
{
|
|
expectRect.height += expectRect.y;
|
|
yOffset -= expectRect.y;
|
|
expectRect.y = 0;
|
|
}
|
|
if (expectRect.x + expectRect.width > imgInput.cols)
|
|
expectRect.width = imgInput.cols - expectRect.x;
|
|
if (expectRect.y + expectRect.height > imgInput.rows)
|
|
expectRect.height = imgInput.rows - expectRect.y;
|
|
}
|
|
imgInput(expectRect).copyTo(squareImage(cv::Rect(xOffset, yOffset, expectRect.width, expectRect.height)));
|
|
resize(squareImage, squareImage, targetSize);
|
|
return squareImage;
|
|
}
|
|
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;
|
|
}
|
|
//计算角度,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;
|
|
}
|
|
//通过角度计算圆上点的位置
|
|
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(max(rect1.x + rect1.width, rect2.x + rect2.width), max(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 = max(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;
|
|
}
|
|
};
|
|
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;
|
|
}
|
|
};
|
|
|
|
//计算主线程
|
|
class algoThread : public QThread
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
|
|
bool m_runFlag = true;
|
|
QString threadName;
|
|
~algoThread()
|
|
{
|
|
this->terminate();
|
|
}
|
|
//handleAiModel* modelPtr;
|
|
sqlManager* sql;
|
|
threadManager* tManager;
|
|
sncodeCls* snDatas;
|
|
triggerManager* _triggerManager;
|
|
|
|
QMutex detQueueMutex;
|
|
QQueue<std::shared_ptr <detData>> detQueue;
|
|
QMutex currentMutex, nextMutex, globalMutex;
|
|
|
|
std::string snErrorsnError = std::string("snErrNoSn");
|
|
QMutex* snAndHashMutex;
|
|
|
|
int Index = 1;
|
|
int areaIndex = 0;
|
|
void run();
|
|
bool waitForImg(std::shared_ptr <detData> data,int);
|
|
void getAllFiles(std::string path, std::vector<std::string>& files, std::string format);
|
|
void Init(threadManager* threadManager) {
|
|
tManager = threadManager;
|
|
sql = threadManager->sql;
|
|
snDatas = &threadManager->snDatas;
|
|
}
|
|
bool addDetTask(std::shared_ptr <detData>);//添加任务
|
|
|
|
//算法
|
|
void ALGO_Correction(std::shared_ptr <detData>);
|
|
void ALGO_Inspection(std::shared_ptr <detData>);
|
|
void Camera1_Algo(std::shared_ptr <detData>);
|
|
signals:
|
|
void subThreadStart();
|
|
};
|
|
|
|
//用于记录写入csv的缺陷数据
|
|
class ngNoteCls
|
|
{
|
|
public:
|
|
struct defectStruct
|
|
{
|
|
QString Name;
|
|
std::vector<double>Value;
|
|
defectStruct(QString nameInput)
|
|
{
|
|
Name = nameInput;
|
|
}
|
|
};
|
|
std::unordered_map<QString, defectStruct>ngDefectMap;
|
|
std::unordered_map<QString, defectStruct>okDefectMap;
|
|
std::unordered_map<QString, defectStruct>::iterator it;
|
|
/// <summary>
|
|
/// 数据存在返回true,不存在返回false
|
|
/// </summary>
|
|
/// <param name="name">数据名</param>
|
|
/// <returns></returns>
|
|
bool checkExisted(QString name, std::unordered_map<QString, defectStruct>* map)
|
|
{
|
|
it = map->find(name);
|
|
if (it == map->end())
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
void insertNgData(QString name)
|
|
{
|
|
if (checkExisted(name, &ngDefectMap) == false)
|
|
{
|
|
defectStruct defect(name);
|
|
ngDefectMap.insert({ name,defect });
|
|
}
|
|
}
|
|
void insertNgData(QString name, double width, double height)
|
|
{
|
|
if (checkExisted(name, &ngDefectMap) == false)
|
|
{
|
|
defectStruct defect(name);
|
|
defect.Value.push_back(width);
|
|
defect.Value.push_back(height);
|
|
ngDefectMap.insert({ name,defect });
|
|
}
|
|
else//负数缺陷大小设0
|
|
{
|
|
defectStruct* defect = &it->second;
|
|
for (auto& data : defect->Value)
|
|
data = 0;
|
|
}
|
|
}
|
|
void insertNgData(QString name, double width, double height, double depth)
|
|
{
|
|
if (checkExisted(name, &ngDefectMap) == false)
|
|
{
|
|
defectStruct defect(name);
|
|
defect.Value.push_back(width);
|
|
defect.Value.push_back(height);
|
|
defect.Value.push_back(depth);
|
|
ngDefectMap.insert({ name,defect });
|
|
}
|
|
else//负数缺陷大小设0
|
|
{
|
|
defectStruct* defect = &it->second;
|
|
for (auto& data : defect->Value)
|
|
data = 0;
|
|
}
|
|
}
|
|
QString getNgSummaryStr()
|
|
{
|
|
QString str;
|
|
for (auto it : ngDefectMap)
|
|
{
|
|
str += "&MAX-" + it.second.Name + "_(";
|
|
if (it.second.Value.size() > 0)
|
|
{
|
|
for (auto data : it.second.Value)
|
|
str += QString::number(data) + "*";
|
|
str.left(str.size() - 1);
|
|
}
|
|
str += ")";
|
|
}
|
|
return str;
|
|
}
|
|
|
|
void insertOkData(QString name)
|
|
{
|
|
if (checkExisted(name, &okDefectMap) == false)
|
|
{
|
|
defectStruct defect(name);
|
|
okDefectMap.insert({ name,defect });
|
|
}
|
|
}
|
|
void insertOkData(QString name, double width, double height)
|
|
{
|
|
if (checkExisted(name, &okDefectMap) == false)
|
|
{
|
|
defectStruct defect(name);
|
|
defect.Value.push_back(width);
|
|
defect.Value.push_back(height);
|
|
okDefectMap.insert({ name,defect });
|
|
}
|
|
else//负数缺陷大小设0
|
|
{
|
|
defectStruct* defect = &it->second;
|
|
for (auto& data : defect->Value)
|
|
data = 0;
|
|
}
|
|
}
|
|
void insertOkData(QString name, double width, double height, double depth)
|
|
{
|
|
if (checkExisted(name, &okDefectMap) == false)
|
|
{
|
|
defectStruct defect(name);
|
|
defect.Value.push_back(width);
|
|
defect.Value.push_back(height);
|
|
defect.Value.push_back(depth);
|
|
okDefectMap.insert({ name,defect });
|
|
}
|
|
else//负数缺陷大小设0
|
|
{
|
|
defectStruct* defect = &it->second;
|
|
for (auto& data : defect->Value)
|
|
data = 0;
|
|
}
|
|
}
|
|
QString getOkSummaryStr()
|
|
{
|
|
QString str;
|
|
for (auto it : okDefectMap)
|
|
{
|
|
str += "&OK-" + it.second.Name + "_(";
|
|
if (it.second.Value.size() > 0)
|
|
{
|
|
for (auto data : it.second.Value)
|
|
str += QString::number(data) + "*";
|
|
str.left(str.size() - 1);
|
|
}
|
|
str += ")";
|
|
}
|
|
return str;
|
|
}
|
|
private:
|
|
|
|
};
|
|
|
|
//用于储存以及计算需要在图片上屏蔽的区域
|
|
class avoidArea
|
|
{
|
|
public:
|
|
std::vector<std::vector<cv::Point>>avoidContours;
|
|
bool addArea(cv::Mat& detImg, handdleAI::pSegmentation* segPtr)//只取最大的
|
|
{
|
|
cv::Mat resMat = ~segPtr->Evaluate(detImg);
|
|
int maxIndex = 0, maxArea = 0;
|
|
vector<vector<cv::Point>>contours;
|
|
cv::findContours(resMat, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
|
|
if (contours.size() == 0)
|
|
return false;
|
|
for (int i = 0; i < contours.size(); i++)
|
|
{
|
|
int Area = contourArea(contours[i]);
|
|
if (Area > maxArea)
|
|
{
|
|
maxArea = Area;
|
|
maxIndex = i;
|
|
}
|
|
}
|
|
if (contours.size() != 0)
|
|
{
|
|
avoidContours.push_back(contours[maxIndex]);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
bool checkOverLap(cv::Point input)//检测与中心点是否重叠
|
|
{
|
|
for (auto& contour : avoidContours)
|
|
{
|
|
if (cv::pointPolygonTest(contour, input, true) >= 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
bool checkOverLap(std::vector<cv::Point> contourInput)
|
|
{
|
|
for (auto& contour : avoidContours)
|
|
{
|
|
if (simpleAlgo::isContoursOverlap(contour, contourInput)==true)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
//用法:先将contour用insertContours插入,然后调用fusionContourToMat生成Mat
|
|
class contour2Mat
|
|
{
|
|
public:
|
|
enum type
|
|
{
|
|
jiZhuPoSun,
|
|
aoKeng,
|
|
huaHeng,
|
|
max = huaHeng
|
|
};
|
|
struct _strcut
|
|
{
|
|
std::vector<cv::Point> contours;
|
|
cv::Point offset;
|
|
};
|
|
contour2Mat(cv::Size sizeInput)
|
|
{
|
|
contoursVec.resize(max+1);
|
|
offsets.resize(max + 1);
|
|
imgSize = sizeInput;
|
|
}
|
|
contour2Mat()
|
|
{
|
|
contoursVec.resize(max + 1);
|
|
offsets.resize(max + 1);
|
|
}
|
|
void insertContours(vector<cv::Point> contours, type t,cv::Point offset=cv::Point(0,0))
|
|
{
|
|
if (contours.size() == 0)
|
|
return;
|
|
contoursVec[t].push_back({ contours,offset });
|
|
}
|
|
cv::Mat fusionContourToMat(type t)
|
|
{
|
|
cv::Mat res;
|
|
res = cv::Mat(
|
|
cv::Size( imgSize.width,imgSize.height ),
|
|
CV_8UC1, cv::Scalar(0));
|
|
|
|
if (contoursVec[t].size() == 0)
|
|
{
|
|
cv::copyMakeBorder(res, res, offsets[t].x, 0, offsets[t].y, 0, 1, cv::Scalar(0));
|
|
return res;
|
|
}
|
|
for (auto& data : contoursVec[t])
|
|
{
|
|
cv::Rect rect = cv::boundingRect(data.contours);
|
|
if (res.cols < rect.x + rect.width)
|
|
cv::copyMakeBorder(res, res, 0, 0, 0, rect.x + rect.width - res.cols, cv::BORDER_CONSTANT, cv::Scalar(0));
|
|
if (res.rows < rect.y + rect.height)
|
|
cv::copyMakeBorder(res, res, 0, rect.y + rect.height - res.rows, 0, 0, cv::BORDER_CONSTANT, cv::Scalar(0));
|
|
}
|
|
for (auto& data : contoursVec[t])
|
|
{
|
|
cv::drawContours(res(cv::Rect(data.offset.x,data.offset.y,imgSize.width-data.offset.x,imgSize.height-data.offset.y)),
|
|
vector <vector<cv::Point>>(1,data.contours), -1, cv::Scalar(255), -1);
|
|
}
|
|
return res;
|
|
}
|
|
void setImgSize(cv::Size sizeInput)
|
|
{
|
|
imgSize = sizeInput;
|
|
}
|
|
void setOffSet(cv::Point offsetInput, type t)
|
|
{
|
|
offsets[t] = offsetInput;
|
|
}
|
|
private:
|
|
cv::Size imgSize;
|
|
std::vector<cv::Point>offsets;
|
|
std::vector<vector<_strcut>>contoursVec;
|
|
};
|
|
|
|
//用于储存检测结果,以及防止重复画图 //尚不启用
|
|
class rectAndDataCls
|
|
{
|
|
public:
|
|
vector<rectAndData>rectAndDatas;
|
|
void insert(vector<rectAndData>& input)
|
|
{
|
|
if (rectAndDatas.size() == 0)//第一次插入
|
|
{
|
|
rectAndDatas = input;
|
|
return;
|
|
}
|
|
else//第2+次插入 需要进行重合判定
|
|
{
|
|
for (auto& data : input)
|
|
{
|
|
bool flag = false;
|
|
for (auto& data2 : rectAndDatas)//检查是否重合
|
|
{
|
|
if (simpleAlgo::isContoursOverlap(data.contour, data2.contour, 50) == true)//重合部分超过30%
|
|
{
|
|
flag = true;
|
|
data2.contour = simpleAlgo::contourPlusContour(data.contour, data2.contour);//融合
|
|
data2.ngType = max(data.ngType, data2.ngType);
|
|
}
|
|
}
|
|
if (flag == false)// 否
|
|
rectAndDatas.push_back(data);
|
|
}
|
|
}
|
|
}
|
|
void insert(vector<rectAndData> input)
|
|
{
|
|
if (rectAndDatas.size() == 0)//第一次插入
|
|
{
|
|
rectAndDatas = input;
|
|
return;
|
|
}
|
|
else//第2+次插入 需要进行重合判定
|
|
{
|
|
for (auto& data : input)
|
|
{
|
|
bool flag = false;
|
|
for (auto& data2 : rectAndDatas)//检查是否重合
|
|
{
|
|
if (simpleAlgo::isContoursOverlap(data.contour, data2.contour, 50) == true)//重合部分超过30%
|
|
{
|
|
flag = true;
|
|
data2.contour = simpleAlgo::contourPlusContour(data.contour, data2.contour);//融合
|
|
data2.ngType = max(data.ngType, data2.ngType);
|
|
}
|
|
}
|
|
if (flag == false)// 否
|
|
rectAndDatas.push_back(data);
|
|
}
|
|
}
|
|
}
|
|
void clear()
|
|
{
|
|
rectAndDatas.clear();
|
|
}
|
|
private:
|
|
|
|
};
|
|
|
|
|
|
#pragma once
|