1526 lines
57 KiB
C++
1526 lines
57 KiB
C++
#include "Hd_AI_2DHanHou_module.h"
|
||
#include <QDateTime>
|
||
#include <QtConcurrent/QtConcurrent>
|
||
#include <qfuture.h>
|
||
#include <handleAI.h>
|
||
#include <Halcon.h>
|
||
#include <QTextCodec>
|
||
#include <future>
|
||
using namespace cv;
|
||
//using namespace handdleAI;
|
||
using namespace std;
|
||
using namespace HalconCpp;
|
||
using namespace handdleAI;
|
||
//原图保存文件夹
|
||
QString oriImgSaveDir = "F:/ORI/";
|
||
//原图class图保存文件夹
|
||
QString classImgSaveDir = "F:/ORIclass/";
|
||
//结果保存文件夹
|
||
QString resImgSaveDir = "F:/res/";
|
||
|
||
//切图的class的width,根据项目实际设置!
|
||
int sizeW = 150;
|
||
//切图的class的height,根据项目实际设置!
|
||
int sizeH = 150;
|
||
//切图的class的width,根据项目实际设置!0为黑,255为白;
|
||
int fillColour = 0;
|
||
|
||
//检测线程锁
|
||
QMutex* detect_mutex;
|
||
//保存线程锁
|
||
QMutex* saveStruct_mutex;
|
||
//检测线程运行标志
|
||
bool ifdetectThread = false;
|
||
//检测标志
|
||
QString qstring_data;
|
||
QString cameraKey_index = "";
|
||
QString sData;
|
||
QStringList splitDataList;
|
||
//初始化参数列表
|
||
QByteArrayList initValueList;
|
||
QMap<QString, int>m_maplist;
|
||
QMap<QString, int>m_mapnumlist;
|
||
pDetection* det_hanhou;
|
||
pSegmentation* seg_hanhou;
|
||
pClassification* cls_hanhou;
|
||
bool halconResultIsNG = false; //NG为true
|
||
bool handleAiResultNG = true; //NG为false
|
||
std::string modelPath_det_hanhou = "../bin/model/det_hanhou.BHD";
|
||
std::string modelPath_seg_hanhou = "../bin/model/seg_hanhou.BHD";
|
||
std::string modelPath_cls_hanhou = "../bin/model/cls_hanhou.BHD";
|
||
QString byteArrayToUnicode(const QByteArray array) {
|
||
|
||
// state用于保存转换状态,它的成员invalidChars,可用来判断是否转换成功
|
||
// 如果转换成功,则值为0,如果值大于0,则说明转换失败
|
||
QTextCodec::ConverterState state;
|
||
// 先尝试使用utf-8的方式把QByteArray转换成QString
|
||
QString text = QTextCodec::codecForName("UTF-8")->toUnicode(array.constData(), array.size(), &state);
|
||
// 如果转换时无效字符数量大于0,说明编码格式不对
|
||
if (state.invalidChars > 0)
|
||
{
|
||
// 再尝试使用GBK的方式进行转换,一般就能转换正确(当然也可能是其它格式,但比较少见了)
|
||
text = QTextCodec::codecForName("GBK")->toUnicode(array);
|
||
}
|
||
return text;
|
||
}
|
||
cv::Mat LocalCutImg_DET(pDetection* pDet, cv::Mat src, float zoom, int checkNameId, cv::Rect& cutRoi)
|
||
{
|
||
cv::Mat dst;
|
||
cv::resize(src, dst, cv::Size(src.cols / zoom, src.rows / zoom));
|
||
vector<Bbox> resultBox = pDet->Evaluate(dst); //结果为中心点+宽高
|
||
|
||
cv::Mat contourImg;
|
||
std::vector<std::vector<cv::Point>> contours;
|
||
//cv::threshold(dstImg, contourImg, pSeg->m_classes, 255, cv::THRESH_BINARY_INV);
|
||
//cv::findContours(contourImg, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
|
||
|
||
cv::Mat roiImg;
|
||
|
||
if (resultBox.size() <= 0)
|
||
{
|
||
cv::Mat rstMat = cv::Mat(100, 100, CV_8UC1, cv::Scalar(0, 0, 0));
|
||
cutRoi = cv::Rect(0, 0, 100, 100);
|
||
return rstMat;
|
||
}
|
||
|
||
double maxArea = 0;
|
||
int index = -1;
|
||
for (int j = 0; j < resultBox.size(); j++)
|
||
{
|
||
double area = resultBox[j].w * resultBox[j].h;
|
||
if (area >= maxArea)
|
||
{
|
||
maxArea = area;
|
||
index = j;
|
||
}
|
||
}
|
||
|
||
/*cv::Rect rect = cv::boundingRect(contours[index]);
|
||
cv::rectangle(roiImg, cv::Rect(resultBox[j].x - resultBox[j].w / 2, resultBox[j].y - resultBox[j].h / 2, resultBox[j].w, resultBox[j].h), cv::Scalar(0, 0, 255), 5);*/
|
||
int addpixel = 15;
|
||
|
||
cutRoi.x = ((resultBox[index].x - resultBox[index].w / 2) * zoom);
|
||
cutRoi.y = ((resultBox[index].y - resultBox[index].h / 2) * zoom);
|
||
cutRoi.width = (resultBox[index].w * zoom);
|
||
cutRoi.height = (resultBox[index].h * zoom);
|
||
//cv::rectangle(src, cutRoi, cv::Scalar(0, 0, 255), 2, 8);
|
||
/* cutRoi.x = rect.x * zoom;
|
||
cutRoi.y = rect.y * zoom;
|
||
cutRoi.width = rect.width * zoom;
|
||
cutRoi.height = rect.height * zoom;*/
|
||
|
||
src(cutRoi).copyTo(roiImg);
|
||
return roiImg;
|
||
}
|
||
|
||
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 std::vector<cv::Mat>dingGaiHanClsCut(cv::Rect boundingBox, cv::Mat imgInput, cv::Size targetSize = cv::Size(128, 128))
|
||
{
|
||
std::vector<cv::Mat> result;
|
||
int splitCount = std::max(boundingBox.width, boundingBox.height) / std::min(boundingBox.width, boundingBox.height);
|
||
//std::cout << "splitCount" << splitCount;
|
||
if (splitCount > 2) {//大于2分段裁
|
||
if (boundingBox.width > boundingBox.height) {//宽大于高,横着裁
|
||
for (int i = 0; i < splitCount; i++) {
|
||
result.push_back(cutImg2Cls(cv::Rect(boundingBox.x + boundingBox.height * i, boundingBox.y, boundingBox.height, boundingBox.height), imgInput));
|
||
}
|
||
result.push_back(cutImg2Cls(cv::Rect(boundingBox.x + boundingBox.width - boundingBox.height, boundingBox.y, boundingBox.height, boundingBox.height), imgInput));
|
||
}
|
||
else {
|
||
for (int i = 0; i < splitCount; i++) {
|
||
result.push_back(cutImg2Cls(cv::Rect(boundingBox.x, boundingBox.y + boundingBox.width * i, boundingBox.width, boundingBox.width), imgInput));
|
||
}
|
||
result.push_back(cutImg2Cls(cv::Rect(boundingBox.x, boundingBox.y + boundingBox.height - boundingBox.width, boundingBox.width, boundingBox.width), imgInput));
|
||
}
|
||
return result;
|
||
}
|
||
else
|
||
return std::vector<cv::Mat>(1, cutImg2Cls(boundingBox, imgInput, targetSize));
|
||
}
|
||
|
||
// 缺陷信息结构体
|
||
typedef struct {
|
||
QString defectName = "";//缺陷名称
|
||
cv::Rect defectRect = { 0,0,0,0 };//缺陷位置
|
||
cv::Scalar defectScalar = cv::Scalar(0, 0, 0);//画框颜色
|
||
double defectArea = 0;//缺陷真实面积
|
||
double defectDepth = 0;//缺陷深度/高度,3D信息
|
||
}defectInfo;
|
||
|
||
|
||
|
||
//判断文件是否存在,不存在则创建
|
||
bool isFileExist(QString fullFileName)
|
||
{
|
||
QFileInfo fileInfo(fullFileName);
|
||
if (fileInfo.isFile())
|
||
{
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void GetStringSize(HDC hdc, const char* str, int* w, int* h)
|
||
{
|
||
SIZE size;
|
||
GetTextExtentPoint32A(hdc, str, strlen(str), &size);
|
||
if (w != 0) *w = size.cx;
|
||
if (h != 0) *h = size.cy;
|
||
}
|
||
//在图像写中文,英文也可以写
|
||
void putTextZH(cv::Mat& dst, const char* str, cv::Point org, cv::Scalar color, int fontsize,
|
||
const char* fn = "Arial", bool italic = false, bool underline = false)
|
||
{
|
||
CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));
|
||
|
||
|
||
int x, y, r, b;
|
||
//坐标点大于图像宽和高直接返回
|
||
if (org.x > dst.cols || org.y > dst.rows) return;
|
||
x = org.x < 0 ? -org.x : 0;
|
||
y = org.y < 0 ? -org.y : 0;
|
||
|
||
|
||
LOGFONTA lf;
|
||
lf.lfHeight = -fontsize; //字体高度
|
||
lf.lfWidth = 0; //平均宽度
|
||
lf.lfEscapement = 0; //字符排列角度
|
||
lf.lfOrientation = 0; //字符本身旋转的角度
|
||
lf.lfWeight = 5; //设置字体线条的宽度
|
||
lf.lfItalic = italic; //斜体
|
||
lf.lfUnderline = underline; //下划线
|
||
lf.lfStrikeOut = 0; //是否字符中央加横线
|
||
lf.lfCharSet = DEFAULT_CHARSET; //字符集
|
||
lf.lfOutPrecision = 0; //字体的精确度
|
||
lf.lfQuality = PROOF_QUALITY; //字体质量
|
||
lf.lfPitchAndFamily = 0; //选择字体的间距和字体家族
|
||
strcpy_s(lf.lfFaceName, fn); //字体的名称
|
||
|
||
|
||
HFONT hf = CreateFontIndirectA(&lf);
|
||
HDC hdc = CreateCompatibleDC(0);
|
||
HFONT holdfont = (HFONT)SelectObject(hdc, hf);
|
||
|
||
|
||
int strBaseW = 0, strBaseH = 0;
|
||
int singleRow = 0;
|
||
char buf[1 << 12];
|
||
strcpy_s(buf, str);
|
||
char* bufT[1 << 12]; //这个用于分隔字符串后剩余的字符,可能会超出。
|
||
|
||
|
||
//处理多行
|
||
{
|
||
int nnh = 0;
|
||
int cw, ch;
|
||
|
||
|
||
const char* ln = strtok_s(buf, "\n", bufT);
|
||
while (ln != 0)
|
||
{
|
||
GetStringSize(hdc, ln, &cw, &ch);
|
||
strBaseW = max(strBaseW, cw);
|
||
strBaseH = max(strBaseH, ch);
|
||
|
||
|
||
ln = strtok_s(0, "\n", bufT);
|
||
nnh++;
|
||
}
|
||
singleRow = strBaseH;
|
||
strBaseH *= nnh;
|
||
}
|
||
|
||
|
||
if (org.x + strBaseW < 0 || org.y + strBaseH < 0)
|
||
{
|
||
SelectObject(hdc, holdfont);
|
||
DeleteObject(hf);
|
||
DeleteObject(hdc);
|
||
return;
|
||
}
|
||
|
||
|
||
r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;
|
||
b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;
|
||
org.x = org.x < 0 ? 0 : org.x;
|
||
org.y = org.y < 0 ? 0 : org.y;
|
||
|
||
|
||
BITMAPINFO bmp = { 0 };
|
||
BITMAPINFOHEADER& bih = bmp.bmiHeader;
|
||
int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));
|
||
|
||
|
||
bih.biSize = sizeof(BITMAPINFOHEADER);
|
||
bih.biWidth = strBaseW;
|
||
bih.biHeight = strBaseH;
|
||
bih.biPlanes = 1;
|
||
bih.biBitCount = 24;
|
||
bih.biCompression = BI_RGB;
|
||
bih.biSizeImage = strBaseH * strDrawLineStep;
|
||
bih.biClrUsed = 0;
|
||
bih.biClrImportant = 0;
|
||
|
||
|
||
void* pDibData = 0;
|
||
HBITMAP hBmp = CreateDIBSection(hdc, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);
|
||
|
||
|
||
CV_Assert(pDibData != 0);
|
||
HBITMAP hOldBmp = (HBITMAP)SelectObject(hdc, hBmp);
|
||
|
||
|
||
//color.val[2], color.val[1], color.val[0]
|
||
SetTextColor(hdc, RGB(255, 255, 255));
|
||
SetBkColor(hdc, 0);
|
||
//SetStretchBltMode(hDC, COLORONCOLOR);
|
||
|
||
|
||
strcpy_s(buf, str);
|
||
const char* ln = strtok_s(buf, "\n", bufT);
|
||
int outTextY = 0;
|
||
while (ln != 0)
|
||
{
|
||
TextOutA(hdc, 0, outTextY, ln, strlen(ln));
|
||
outTextY += singleRow;
|
||
ln = strtok_s(0, "\n", bufT);
|
||
}
|
||
uchar* dstData = (uchar*)dst.data;
|
||
int dstStep = dst.step / sizeof(dstData[0]);
|
||
unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;
|
||
unsigned char* pStr = (unsigned char*)pDibData + x * 3;
|
||
for (int tty = y; tty <= b; ++tty)
|
||
{
|
||
unsigned char* subImg = pImg + (tty - y) * dstStep;
|
||
unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;
|
||
for (int ttx = x; ttx <= r; ++ttx)
|
||
{
|
||
for (int n = 0; n < dst.channels(); ++n) {
|
||
double vtxt = subStr[n] / 255.0;
|
||
int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];
|
||
subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);
|
||
}
|
||
|
||
|
||
subStr += 3;
|
||
subImg += dst.channels();
|
||
}
|
||
}
|
||
|
||
|
||
SelectObject(hdc, hOldBmp);
|
||
SelectObject(hdc, holdfont);
|
||
DeleteObject(hf);
|
||
DeleteObject(hBmp);
|
||
DeleteDC(hdc);
|
||
}
|
||
|
||
void drawDefects(cv::Mat& sourceMat, QQueue<defectInfo> defectInfoList)
|
||
{
|
||
for (int i = 0; i < defectInfoList.size(); i++)
|
||
{
|
||
defectInfo mdefect = defectInfoList[i];
|
||
|
||
cv::rectangle(sourceMat, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y),
|
||
cv::Point(mdefect.defectRect.x + mdefect.defectRect.width, mdefect.defectRect.y + mdefect.defectRect.height), mdefect.defectScalar, 3, 8);
|
||
|
||
QByteArray path_arr = mdefect.defectName.toLocal8Bit();
|
||
const char* path = path_arr.data();
|
||
putTextZH(sourceMat, path, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y - 50), mdefect.defectScalar, 50, "微软雅黑");
|
||
}
|
||
}
|
||
|
||
cv::Mat RotateImage180(const cv::Mat& image)
|
||
{
|
||
cv::Mat rotatedImage;
|
||
cv::flip(image, rotatedImage, -1);
|
||
return rotatedImage;
|
||
}
|
||
|
||
HObject Mat2HObject(const cv::Mat& image)
|
||
{
|
||
HObject Hobj = HObject();
|
||
int hgt = image.rows;
|
||
int wid = image.cols;
|
||
int i;
|
||
// CV_8UC3
|
||
if (image.type() == CV_8UC3)
|
||
{
|
||
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);
|
||
}
|
||
GenImage3(&Hobj, "byte", wid, hgt, (Hlong)dataR, (Hlong)dataG, (Hlong)dataB);
|
||
delete[]dataR;
|
||
delete[]dataG;
|
||
delete[]dataB;
|
||
dataR = NULL;
|
||
dataG = NULL;
|
||
dataB = NULL;
|
||
}
|
||
// CV_8UCU1
|
||
else if (image.type() == CV_8UC1)
|
||
{
|
||
uchar* data = new uchar[hgt * wid];
|
||
for (i = 0; i < hgt; i++)
|
||
memcpy(data + wid * i, image.data + image.step * i, wid);
|
||
GenImage1(&Hobj, "byte", wid, hgt, (Hlong)data);
|
||
delete[] data;
|
||
data = NULL;
|
||
}
|
||
return Hobj;
|
||
}
|
||
|
||
cv::Mat HObject2Mat(HalconCpp::HObject& Hobj) //HObject类型转Mat类型
|
||
{
|
||
HalconCpp::HTuple htCh;
|
||
HalconCpp::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;
|
||
}
|
||
//判断文件夹是否存在,不存在则创建
|
||
void Hd_AI_2DHanHou_module::isDirExist(QString fullDirName)
|
||
{
|
||
QDir* photo = new QDir;
|
||
bool exist = photo->exists(fullDirName);
|
||
if (exist)
|
||
{
|
||
//qDebug() << fullDirName << "::已存在";
|
||
}
|
||
else
|
||
{
|
||
//创建photo文件夹
|
||
//qDebug() << fullDirName << "::不存在,创建";
|
||
photo->mkdir(fullDirName);
|
||
}
|
||
delete photo;
|
||
photo = nullptr;
|
||
}
|
||
|
||
Hd_AI_2DHanHou_module* create()
|
||
{
|
||
qDebug() << "[INFO] " << " Hd_AI_2DHanHou_module create success!";
|
||
return new Hd_AI_2DHanHou_module;
|
||
}
|
||
|
||
void destory(Hd_AI_2DHanHou_module* ptr)
|
||
{
|
||
qDebug() << "[INFO] " << "Hd_AI_2DHanHou_module destory success!";
|
||
if (ptr)
|
||
{
|
||
delete ptr;
|
||
ptr = nullptr;
|
||
}
|
||
}
|
||
|
||
Hd_AI_2DHanHou_module::Hd_AI_2DHanHou_module()
|
||
{
|
||
detect_mutex = new QMutex;
|
||
saveStruct_mutex = new QMutex;
|
||
ifdetectThread = false;
|
||
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
QString DirPath = "E:/testImg/source" + QString::number(i);
|
||
QStringList imgFilters;
|
||
imgFilters << "*.jpg" << "*.png" << "*.PNG" << "*.gif" << "*.tif" << "*.jpeg" << "*.bmp";
|
||
QDir qdir(DirPath);
|
||
//返回文件名列表
|
||
QStringList localSourceImgsName = qdir.entryList(imgFilters, QDir::Files | QDir::Readable, QDir::Name);
|
||
for (int j = 0; j < localSourceImgsName.size(); j++)
|
||
{
|
||
QString imgPath = DirPath + "/" + localSourceImgsName[j];
|
||
QFile myf(imgPath);
|
||
if (myf.exists())
|
||
myf.remove();
|
||
//Sleep(1);
|
||
}
|
||
}
|
||
}
|
||
|
||
Hd_AI_2DHanHou_module::~Hd_AI_2DHanHou_module()
|
||
{
|
||
ifdetectThread = false;
|
||
Sleep(10);
|
||
if (detect_mutex)
|
||
{
|
||
delete detect_mutex;
|
||
detect_mutex = nullptr;
|
||
}
|
||
if (saveStruct_mutex)
|
||
{
|
||
delete saveStruct_mutex;
|
||
saveStruct_mutex = nullptr;
|
||
}
|
||
/*DestroySegObj(pSeg);
|
||
DestroyClsObj(pCls);*/
|
||
}
|
||
//从类中读数据到实例对象
|
||
bool Hd_AI_2DHanHou_module::readData(std::vector<cv::Mat>& vec_mat, QByteArray& data)
|
||
{
|
||
detect_mutex->lock();
|
||
//qDebug() << __FUNCTION__ << " line:" <<__LINE__ << " data len:" << data.length();
|
||
if (resMat.size() > 0)
|
||
{
|
||
vec_mat.push_back(resMat[0]);
|
||
resMat.clear();
|
||
}
|
||
else
|
||
{
|
||
if (HikMat.size() > 0)
|
||
vec_mat.push_back(HikMat[0]);
|
||
//qDebug() << __FUNCTION__ << " line:" << __LINE__ << " resMat.data err:";
|
||
}
|
||
//if (allNum++ % 2 == 0)
|
||
// data = "2";// resByte.dequeue();
|
||
//else
|
||
//data = "1*1.1,2.2";//1OK,2NG
|
||
|
||
////之后根据结果类修改
|
||
//if(resIfNg == true)
|
||
// data = "NG";
|
||
//else data = "OK";
|
||
cv::resize(vec_mat[0], vec_mat[0],cv::Size(),0.5,0.5);
|
||
if (!savePath_result.isEmpty())
|
||
{
|
||
//std::cout << "try savePath::" << savePath.toStdString() << std::endl;
|
||
//测试相机的,
|
||
//while (HikMat[0].cols > 2000)cv::resize(HikMat[0], HikMat[0], cv::Size(), 0.08, 0.08);'
|
||
for (int i = 0; i < initValueList.size(); i++)
|
||
{
|
||
QString minit = byteArrayToUnicode(initValueList[i]);
|
||
if (initValueList[i].contains(moduleData.split(',')[2].split('_')[0]))
|
||
{
|
||
if (initValueList[i].contains(QString("结果图_保存").toLocal8Bit()))
|
||
{
|
||
if (resultOKNG == 1)
|
||
{
|
||
isDirExist(savePath_result + "/OK");
|
||
|
||
addImgToSaveStruct(vec_mat[0], "0", savePath_result + "/OK/" + SN + "_" + splitDataList[2] + "_" + sdataTime_name + ".jpg");//保存结果图
|
||
}
|
||
else
|
||
{
|
||
isDirExist(savePath_result + "/NG");
|
||
addImgToSaveStruct(vec_mat[0], "0", savePath_result + "/NG/" + SN + "_" + splitDataList[2] + "_" + sdataTime_name + ".jpg");//保存结果图
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
//resultOKNG = 1; //强制OK
|
||
if (!m_maplist.contains(SN)) {
|
||
m_maplist.insert(SN, resultOKNG);
|
||
m_mapnumlist.insert(SN, 1);
|
||
}
|
||
else
|
||
{
|
||
m_maplist.insert(SN, m_maplist.value(SN) + resultOKNG - 1);
|
||
m_mapnumlist.insert(SN, m_mapnumlist.value(SN) + 1);
|
||
}
|
||
if (m_mapnumlist.value(SN) == 2) {
|
||
if (m_maplist.value(SN) == 1) {
|
||
data = QByteArray::number(1) + "*" + QByteArray::number(resultData[0]) + "," + QByteArray::number(resultData[1]);
|
||
}
|
||
else {
|
||
data = QByteArray::number(2) + "*" + QByteArray::number(resultData[0]) + "," + QByteArray::number(resultData[1]);
|
||
}
|
||
m_mapnumlist.remove(SN);
|
||
m_maplist.remove(SN);
|
||
QString sdataTime_msz = QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz");
|
||
QString outResultText = sdataTime_msz + "," + SN + "," + splitDataList[1] + "," + QByteArray::number(resultOKNG);
|
||
//判断 文件夹/文件 是否存在
|
||
QFileInfo csv_fileInfo(savePath_csv + ".csv");
|
||
if (!csv_fileInfo.exists())
|
||
{
|
||
//创建文件1
|
||
QFile file(savePath_csv + ".csv");
|
||
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||
{
|
||
QTextStream out(&file);
|
||
out.setCodec(QTextCodec::codecForName("utf-8"));
|
||
out << QString::fromStdString("日期,产品条码,相机工位,相机结果 \n");
|
||
//out << QString::fromStdString("时间码,二维码,存盘路径,总结果,工位1_2D_TF,工位1_2D_TR,工位1_3D_TF,工位1_3D_TR,工位1_DE2,工位1_DE3 \n");
|
||
out << outResultText << "\n";
|
||
file.close();
|
||
}
|
||
else
|
||
{
|
||
qDebug() << "____________________________" << __LINE__ << " csv !open ";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//创建文件1
|
||
QFile file(savePath_csv + ".csv");
|
||
if (file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
|
||
{
|
||
QTextStream out(&file);
|
||
out.setCodec(QTextCodec::codecForName("utf-8"));
|
||
out << outResultText << "\n";
|
||
file.close();
|
||
}
|
||
else
|
||
{
|
||
qDebug() << "____________________________" << __LINE__ << " csv !open ";
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
data = "";
|
||
}
|
||
|
||
resByte.clear();
|
||
detect_mutex->unlock();
|
||
/*if(cameraKey_index.contains('_'))
|
||
qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName:"
|
||
<< cameraKey_index.split("_")[0] << " imgIndex:" << cameraKey_index.split("_")[1];
|
||
else
|
||
qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName imgIndex:" << cameraKey_index;*/
|
||
return true;
|
||
}
|
||
//从类中读数据到实例对象
|
||
bool Hd_AI_2DHanHou_module::encodeResultValue(QVector<std::string> append_value, QByteArray& resultByte)
|
||
{
|
||
try
|
||
{
|
||
std::string sendMsg;
|
||
for (int i = 0; i < append_value.size(); i++)
|
||
{
|
||
std::string value = append_value.at(1);
|
||
//type
|
||
int type = 3;
|
||
std::string m_type;
|
||
m_type.insert(0, 4, '1');
|
||
memcpy((void*)m_type.c_str(), (void*)&type, 4);
|
||
sendMsg.append(m_type);
|
||
|
||
//len
|
||
int len = value.length();
|
||
std::string m_len;
|
||
m_len.insert(0, 4, '1');
|
||
memcpy((void*)m_len.c_str(), (void*)&len, 4);
|
||
sendMsg.append(m_len);
|
||
|
||
//data x
|
||
sendMsg.append(value);
|
||
}
|
||
resultByte = QByteArray::fromStdString(sendMsg);
|
||
}
|
||
catch (QString ev)
|
||
{
|
||
qCritical() << "[Error] " << " ev:" << ev;
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
int myallNum[4] = { 0 };
|
||
QMap<int, cv::Mat> lastImgMap;
|
||
//接收数据
|
||
bool Hd_AI_2DHanHou_module::writeData(std::vector<cv::Mat>& vec_mat, QByteArray& data)
|
||
{
|
||
QString sdataTime = QDateTime::currentDateTime().toString("yyyy-MM-dd");
|
||
//图片保存文件夹
|
||
QByteArray allDir = initValueList.last().split('#')[1];
|
||
isDirExist(allDir + "/" + sdataTime);
|
||
|
||
QString name = "原图";
|
||
saveDir_sorce = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_sorce); //不存在,创建
|
||
name = "seg图";
|
||
saveDir_seg = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_seg); //不存在,创建
|
||
name = "cls图";
|
||
saveDir_cls = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_cls); //不存在,创建
|
||
name = "结果图";
|
||
saveDir_result = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_result); //不存在,创建
|
||
|
||
name = "csv数据表";
|
||
saveDir_csv = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_csv); //不存在,创建
|
||
savePath_csv = saveDir_csv + "/" + sdataTime;
|
||
|
||
qDebug() << __FUNCTION__ << "line:" << __LINE__ << " data:" << data;
|
||
if (vec_mat.empty())
|
||
{
|
||
qCritical() << "[Error] " << " vec_mat is null !";
|
||
return false;
|
||
}
|
||
detect_mutex->lock();
|
||
HikMat.clear();
|
||
for (int i = 0; i < vec_mat.size(); i++)
|
||
HikMat.enqueue(vec_mat[i]);
|
||
|
||
//QByteArray source_savePath;
|
||
sData = byteArrayToUnicode(data);
|
||
splitDataList = sData.split(',');
|
||
SN = splitDataList[3];
|
||
if (splitDataList.size() >= 4)
|
||
{
|
||
//if (splitDataList[2].contains('_'))
|
||
// cameraKey_index = splitDataList[2];
|
||
//QByteArray saveDir = splitDataList[0] + "/" + splitDataList[1] + "/";
|
||
//isDirExist(byteArrayToUnicode(saveDir)); //不存在,创建
|
||
QString sdataTime_msz = QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz");
|
||
sdataTime_name = sdataTime_msz;
|
||
//QString sdataTime = QDateTime::currentDateTime().toString("yyyy-MM-dd");
|
||
QString sdataTime_h = QDateTime::currentDateTime().toString("hh");
|
||
savePath_sorce = saveDir_sorce + "/" + splitDataList[1];
|
||
savePath_seg = saveDir_seg + "/" + splitDataList[1];
|
||
savePath_cls = saveDir_cls + "/" + splitDataList[1];
|
||
savePath_result = saveDir_result + "/" + splitDataList[1];
|
||
|
||
|
||
|
||
/*if (!checkedNameList.contains(splitDataList[1]))
|
||
{
|
||
isDirExist(saveDir_sorce + "/" + splitDataList[1]);
|
||
isDirExist(saveDir_seg + "/" + splitDataList[1]);
|
||
isDirExist(saveDir_cls + "/" + splitDataList[1]);
|
||
isDirExist(saveDir_result + "/" + splitDataList[1]);
|
||
|
||
|
||
checkedNameList.append(splitDataList[1]);
|
||
|
||
}*/
|
||
isDirExist(savePath_sorce);
|
||
isDirExist(savePath_seg);
|
||
isDirExist(savePath_cls);
|
||
isDirExist(savePath_result);
|
||
savePath_sorce = saveDir_sorce + "/" + splitDataList[1] + "/" + sdataTime_h + "H";
|
||
savePath_seg = saveDir_seg + "/" + splitDataList[1] + "/" + sdataTime_h + "H";
|
||
savePath_cls = saveDir_cls + "/" + splitDataList[1] + "/" + sdataTime_h + "H";
|
||
savePath_result = saveDir_result + "/" + splitDataList[1] + "/" + sdataTime_h + "H";
|
||
|
||
isDirExist(savePath_sorce);
|
||
isDirExist(savePath_seg);
|
||
isDirExist(savePath_cls);
|
||
isDirExist(savePath_result);
|
||
|
||
savePath_sorce = saveDir_sorce + "/" + splitDataList[1] + "/" + sdataTime_h + "H" + "/" + SN;
|
||
isDirExist(savePath_sorce);
|
||
}
|
||
|
||
resultOKNG = 1;
|
||
resultData[0] = 0;
|
||
resultData[1] = 0;
|
||
|
||
if (!savePath_sorce.isEmpty())
|
||
{
|
||
//std::cout << "try savePath::" << savePath.toStdString() << std::endl;
|
||
//测试相机的,
|
||
//while (HikMat[0].cols > 2000)cv::resize(HikMat[0], HikMat[0], cv::Size(), 0.08, 0.08);'
|
||
for (int i = 0; i < initValueList.size(); i++)
|
||
{
|
||
QString minit = byteArrayToUnicode(initValueList[i]);
|
||
if (initValueList[i].contains(data.split(',')[2].split('_')[0]))
|
||
if (initValueList[i].contains(QString("原图_保存").toLocal8Bit()))
|
||
{
|
||
addImgToSaveStruct(HikMat[0], "0", savePath_sorce + "/" + SN + "_ori_" + sdataTime_name + ".bmp");//保存原图
|
||
}
|
||
}
|
||
}
|
||
|
||
moduleData = data;
|
||
qstring_data = byteArrayToUnicode(data);
|
||
detect_mutex->unlock();
|
||
|
||
//cv::Mat resultDstImg = vec_mat[0].clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。
|
||
//resultDstImg = pSeg->Evaluate(resultDstImg);
|
||
//resultDstImg.release();
|
||
|
||
return true;
|
||
}
|
||
|
||
//传入initParas函数,格式为:相机key+参数名1#参数值1#参数值2+参数名2#参数值1...
|
||
//初始化
|
||
bool Hd_AI_2DHanHou_module::initParas(QByteArray& byte)
|
||
{
|
||
myEngine.SetProcedurePath("./halconFilesAlgo/AlgoLibrary_628_1.hdpl"); //设置hdvp路径
|
||
initValueList = byte.split('+');
|
||
QStringList valueiList;
|
||
|
||
if (byte.contains("onlySetParameter"))
|
||
{
|
||
qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,valueList[0]:" << initValueList[0];
|
||
if (initValueList.size() > 1)
|
||
for (int i = 1; i < initValueList.size(); i++)
|
||
{
|
||
QString valuei = byteArrayToUnicode(initValueList[i]);
|
||
qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,lastName:" << initValueList[0] << " ,valuei:" << valuei;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
/*if (initValueList.size() > 1)
|
||
for (int i = 1; i < initValueList.size(); i++)
|
||
{
|
||
QString valuei = byteArrayToUnicode(initValueList[i]);
|
||
valueiList.append(valuei);
|
||
}*/
|
||
QString sdataTime = QDateTime::currentDateTime().toString("yyyy-MM-dd");
|
||
//图片保存文件夹
|
||
QByteArray allDir = initValueList.last().split('#')[1];
|
||
isDirExist(allDir + "/" + sdataTime);
|
||
|
||
QString name = "原图";
|
||
saveDir_sorce = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_sorce); //不存在,创建
|
||
name = "seg图";
|
||
saveDir_seg = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_seg); //不存在,创建
|
||
name = "cls图";
|
||
saveDir_cls = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_cls); //不存在,创建
|
||
name = "结果图";
|
||
saveDir_result = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_result); //不存在,创建
|
||
|
||
name = "csv数据表";
|
||
saveDir_csv = allDir + "/" + sdataTime + "/" + name;
|
||
isDirExist(saveDir_csv); //不存在,创建
|
||
savePath_csv = saveDir_csv + "/" + sdataTime;
|
||
//StationNo = vec_ret.at(0).toInt();
|
||
//InitModels();
|
||
//InitThreads(StationNo, img2D);
|
||
//InitModels(StationNo, img2D);
|
||
|
||
//运行检测线程
|
||
ifdetectThread = true;
|
||
|
||
//线程检测:结果类完成时,使用
|
||
//ifThreadRunDetect = true;
|
||
//QFuture<void> future1 = QtConcurrent::run(this, &Hd_AI_2DHanHou_module::detectThread);
|
||
//运行存图线程
|
||
QFuture<void> future2 = QtConcurrent::run(this, &Hd_AI_2DHanHou_module::saveImgThread);
|
||
//detectThread();
|
||
|
||
//原图总文件夹
|
||
isDirExist(oriImgSaveDir);
|
||
//原图class总文件夹
|
||
isDirExist(classImgSaveDir);
|
||
//结果图总文件夹
|
||
isDirExist(resImgSaveDir);
|
||
|
||
InitModels();
|
||
//qDebug() << "[INFO] " << " StationNo:" << StationNo << " img2D:" << img2D ;
|
||
return true;
|
||
}
|
||
|
||
bool Hd_AI_2DHanHou_module::InitModels() {
|
||
|
||
int flag = 0;
|
||
det_hanhou = CreateDetObjS((char*)modelPath_det_hanhou.c_str(), 1, 0); if (det_hanhou->getStatus() != DetStatus::Det_Success)flag++;
|
||
seg_hanhou = CreateSegObjS((char*)modelPath_seg_hanhou.c_str(), 1, 0); if (seg_hanhou->getStatus() != SegStatus::Seg_Success)flag++;
|
||
cls_hanhou = CreateClsObjV3((char*)modelPath_cls_hanhou.c_str(), 0, 1); if (cls_hanhou->getStatus() != ClsStatus::Cls_Success)flag++;
|
||
return (flag == 0);
|
||
}
|
||
// 解码
|
||
bool Hd_AI_2DHanHou_module::decodeAiValue(QByteArray byte, QVector<QByteArray>& vec_ret)
|
||
{
|
||
if (byte.isEmpty())
|
||
{
|
||
qCritical() << "[Error] " << " byte is null!";
|
||
return false;
|
||
}
|
||
|
||
/*******************************************/
|
||
byte.remove(0, 8);
|
||
QByteArray mm_data = byte.mid(0, 4);
|
||
memcpy((void*)¶mNum, (void*)mm_data.data(), 4);
|
||
qDebug() << "[INFO] " << " paramNum:" << paramNum;
|
||
byte.remove(0, 4);
|
||
/*******************************************/
|
||
|
||
while (!byte.isEmpty())
|
||
{
|
||
int type = 0;
|
||
QByteArray m_type = byte.mid(0, 4);
|
||
memcpy((void*)&type, (void*)m_type.data(), 4);
|
||
byte.remove(0, 4);
|
||
|
||
int len = 0;
|
||
QByteArray m_len = byte.mid(0, 4);
|
||
memcpy((void*)&len, (void*)m_len.data(), 4);
|
||
byte.remove(0, 4);
|
||
|
||
QByteArray mm_data = byte.mid(0, len);
|
||
vec_ret.append(mm_data);
|
||
byte.remove(0, len);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//---------检测流程,根据实际项目修改---------
|
||
bool Hd_AI_2DHanHou_module::AIdetect(cv::Mat img2D, QString mysaveKey, QString myDir2D, QString mySN, QString myDataTime)
|
||
{
|
||
try
|
||
{
|
||
//存图数据加保存队列,加锁,避免数据访问冲突
|
||
saveStruct_mutex->lock();
|
||
saveImgStruct mySave;
|
||
Mat sourceImg;
|
||
sourceImg = img2D.clone();
|
||
//img2D.copyTo(sourceImg);
|
||
//img2D.release();
|
||
QString mysaveKey1 = mysaveKey; //"offlineStation1_off_1"
|
||
saveStruct_mutex->unlock();
|
||
|
||
QJsonDocument document;
|
||
QJsonObject obj;
|
||
obj.insert("result_key", "mysaveKey");
|
||
obj.insert("SN", "mySN");
|
||
obj.insert("DataTime", "myDataTime");
|
||
|
||
|
||
//QFile csvName("test.csv");
|
||
//QStringList lines;
|
||
//lines << "class,name\n" << "3,4,5,6,7\n" << "a,b,c,d\n";
|
||
//if (csvName.open(QIODevice::WriteOnly)) //若文件不存在,会自动创建
|
||
//{
|
||
// for (int i = 0; i < lines.size(); i++)
|
||
// csvName.write(lines[i].toStdString().c_str());
|
||
// csvName.close();
|
||
//}
|
||
|
||
QQueue<defectInfo> defectInfoList;
|
||
|
||
//seg后class检测逻辑
|
||
/*if (0) {
|
||
Mat sourceRectangles;// 在原图缺陷位置,边界矩形框出
|
||
sourceRectangles = sourceImg.clone();
|
||
//sourceImg.copyTo(sourceRectangles);
|
||
vector<vector<Point>> contours;
|
||
|
||
//seg检测
|
||
Mat resultDstImg;// = sourceImg.clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。
|
||
resultDstImg = pSeg->Evaluate(sourceImg);
|
||
//int resultClass = pCls->Evaluate(sourceImg); //类别id
|
||
|
||
//opencv对二值图进行处理
|
||
//GaussianBlur(resultDstImg, resultDstImg, Size(3, 3), 1); //缺陷过滤值较大时,可以减少检出数;缺陷较小时,漏检较多
|
||
threshold(resultDstImg, resultDstImg, pSeg->m_classes, 255, THRESH_BINARY_INV);//大于部分设为0,小于部分设为255
|
||
//findContours(resultDstImg, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); //不识别内部轮廓,有些情况抓到的图像外框?
|
||
findContours(resultDstImg, contours, cv::noArray(), cv::RETR_TREE, cv::CHAIN_APPROX_NONE); //需要内部轮廓的写法
|
||
|
||
if (contours.size() == 0)
|
||
{
|
||
//printf("Notlabeled\n");
|
||
}
|
||
else
|
||
{
|
||
for (int j = 0; j < contours.size(); j++)
|
||
{
|
||
Rect rect1 = boundingRect(Mat(contours[j]));
|
||
|
||
//最小缺陷过滤参数,根据项目实际设置!
|
||
int filterNum = 10;
|
||
float w = rect1.width * 1.0;
|
||
float h = rect1.height * 1.0;
|
||
if ((w / sourceImg.cols > 0.98) || (h / sourceImg.rows > 0.98))
|
||
{
|
||
//抓到图像外框了
|
||
//qDebug() << "NG is too big...";
|
||
continue;
|
||
}
|
||
//且逻辑,有的项目需要或逻辑!!
|
||
if ((w > filterNum && h > filterNum))//||rect1.area> filterNum*filterNum*1.5)
|
||
{
|
||
//class检测mat
|
||
Mat imgCls;
|
||
|
||
//此段为切图逻辑为:
|
||
//缺陷外扩5像素;
|
||
//判断最大边,大于class建网大小的,按照最大边尺寸比例切大图,然后resize为建网大小;最大边小于等于建网大小的,按照建网大小切图;
|
||
//越界填充
|
||
{
|
||
int startX = rect1.x - 5;
|
||
int startY = rect1.y - 5;
|
||
int endX = rect1.x + rect1.width + 5;
|
||
int endY = rect1.y + rect1.height + 5;
|
||
|
||
//尺寸比例
|
||
double maxscaler, scalerX, scalerY;
|
||
maxscaler = 1.0;
|
||
scalerX = (endX - startX) * 1.0 / sizeW;//*1.0转为float,避免被取整了
|
||
scalerY = (endY - startY) * 1.0 / sizeH;
|
||
|
||
maxscaler = max(maxscaler, max(scalerX, scalerY));
|
||
|
||
int avgX = (rect1.x + rect1.x + rect1.width) / 2; //中点X位置
|
||
int avgY = (rect1.y + rect1.y + rect1.height) / 2;//中点Y位置
|
||
|
||
startX = avgX - int(maxscaler * sizeW / 2);//强转为int
|
||
endX = startX + int(maxscaler * sizeW);
|
||
startY = avgY - int(maxscaler * sizeH / 2);
|
||
endY = startY + int(maxscaler * sizeH);
|
||
|
||
//判断是否越界,越界要填充的像素个数
|
||
int copyTop = max(0, 0 - startY);
|
||
int copyButton = max(0, endY - sourceImg.rows);
|
||
int copyLeft = max(0, 0 - startX);
|
||
int copyRight = max(0, endX - sourceImg.cols);
|
||
|
||
//切图不要越界,会崩溃
|
||
Mat imgCut = sourceImg(Range(max(0, startY - copyTop), min(sourceImg.rows, endY - copyButton)),
|
||
Range(max(0, startX - copyLeft), min(sourceImg.cols, endX - copyRight))); //起始行,终止行;起始列,终止列
|
||
|
||
//图像扩充
|
||
if (sourceImg.channels() == 1)
|
||
{
|
||
copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, fillColour);
|
||
}
|
||
else
|
||
{
|
||
copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, Scalar(fillColour, fillColour, fillColour));
|
||
}
|
||
//resize为建网大小
|
||
cv::resize(imgCut, imgCls, cv::Size(sizeW, sizeH));
|
||
imgCut.release();
|
||
}
|
||
|
||
|
||
int resultClass = 0;
|
||
float prob = 1.0;
|
||
|
||
resultClass = pCls->Evaluate(imgCls); //类别id
|
||
prob = pCls->getProbability(); //可能性
|
||
|
||
//切图保存,按需
|
||
{
|
||
isDirExist(classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass));
|
||
//存图数据入队,按需
|
||
addImgToSaveStruct(imgCls, mysaveKey1, classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass) + "/" + mySN + myDataTime + QString::number(j) + ".bmp");
|
||
}
|
||
|
||
//此处应添加缺陷计数逻辑
|
||
|
||
if (resultClass == 0)//OK
|
||
{
|
||
;
|
||
}
|
||
else if (resultClass == 1)//划伤Scratch
|
||
{
|
||
resIfNg = true;
|
||
//rectangle(sourceRectangles, rect1.tl(), rect1.br(), Scalar(0, 0, 255), 3, 8);
|
||
////可行,必须转一下格式
|
||
//{
|
||
// QString pathQstr = "划伤";
|
||
// QByteArray path_arr = pathQstr.toLocal8Bit();
|
||
// const char* path = path_arr.data();
|
||
// putTextZH(sourceRectangles, path, cv::Point(rect1.tl().x, rect1.tl().y - 5), Scalar(255, 0, 0), 50, "微软雅黑");
|
||
//}
|
||
|
||
defectInfo mdefectInfo;
|
||
mdefectInfo.defectName = "划伤";
|
||
mdefectInfo.defectRect = rect1;
|
||
mdefectInfo.defectArea = contourArea(contours[j]);
|
||
mdefectInfo.defectScalar = cv::Scalar(0, 0, 255);
|
||
defectInfoList.enqueue(mdefectInfo);
|
||
}
|
||
else if (resultClass == 2)//碰伤
|
||
{
|
||
resIfNg = true;
|
||
defectInfo mdefectInfo;
|
||
mdefectInfo.defectName = "碰伤";
|
||
mdefectInfo.defectRect = rect1;
|
||
mdefectInfo.defectArea = contourArea(contours[j]);
|
||
mdefectInfo.defectScalar = cv::Scalar(0, 255, 255);
|
||
defectInfoList.enqueue(mdefectInfo);
|
||
}
|
||
imgCls.release();
|
||
}
|
||
else {
|
||
//cout << "ng is too small<" << filterNum << endl;
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
//cv::Mat resMat = img2D.clone();
|
||
sourceRectangles.release();
|
||
contours.resize(0);
|
||
resultDstImg.release();
|
||
}
|
||
*/
|
||
|
||
detect_mutex->lock();
|
||
//resMat = sourceImg.clone();
|
||
//sourceImg.copyTo(resMat);
|
||
resMat.push_back(sourceImg);
|
||
//结果图画框和缺陷,在resMat上绘制
|
||
if (resMat.size() > 0)
|
||
drawDefects(resMat[0], defectInfoList);
|
||
|
||
//QByteArray,根据实际项目进行解析
|
||
//参数,例:返回结果图;相机关键字(SN或userID)+保存路径+缺陷信息列表(缺陷名称,缺陷位置int(x,y,w,h),画框颜色Scalar(a,b,c),缺陷面积double)
|
||
|
||
QJsonArray detaillists;
|
||
//QByteArrayList resQbyte;
|
||
for (int i = 0; i < defectInfoList.size(); i++)
|
||
{
|
||
defectInfo mdefect = defectInfoList[i];
|
||
//resQbyte.push_back(mdefect.defectName.toLocal8Bit()); //中文也可以QString::number(1.1).toUtf8()
|
||
//resQbyte.push_back(QString::number(mdefect.defectRect.x).toUtf8());
|
||
//resQbyte.push_back(QString::number(mdefect.defectRect.y).toUtf8());
|
||
//resQbyte.push_back(QString::number(mdefect.defectRect.width).toUtf8());
|
||
//resQbyte.push_back(QString::number(mdefect.defectRect.height).toUtf8());
|
||
//resQbyte.push_back(QString::number(mdefect.defectScalar[0]).toUtf8());
|
||
//resQbyte.push_back(QString::number(mdefect.defectScalar[1]).toUtf8());
|
||
//resQbyte.push_back(QString::number(mdefect.defectScalar[2]).toUtf8());
|
||
//resQbyte.push_back(QString::number(mdefect.defectArea).toUtf8());
|
||
QJsonObject detailObj;
|
||
detailObj.insert("defectName", "mdefect.defectName");
|
||
detailObj.insert("defectRect.x", QString::number(mdefect.defectRect.x));
|
||
detailObj.insert("defectRect.y", QString::number(mdefect.defectRect.y));
|
||
detailObj.insert("defectRect.width", QString::number(mdefect.defectRect.width));
|
||
detailObj.insert("defectRect.height", QString::number(mdefect.defectRect.height));
|
||
detailObj.insert("defectScalar0", QString::number(mdefect.defectScalar[0]));
|
||
detailObj.insert("defectScalar1", QString::number(mdefect.defectScalar[1]));
|
||
detailObj.insert("defectScalar2", QString::number(mdefect.defectScalar[2]));
|
||
detailObj.insert("defectArea", QString::number(mdefect.defectArea));
|
||
detailObj.insert("defectLength", QString::number(sqrt(mdefect.defectRect.width * mdefect.defectRect.width + mdefect.defectRect.height * mdefect.defectRect.height)));
|
||
|
||
detaillists.append(detailObj);
|
||
}
|
||
|
||
|
||
//使用完删除
|
||
defectInfoList.clear();
|
||
|
||
//往主界面发
|
||
//emit sendResultData(resMat, resQbyte);
|
||
//resQbyte.clear();
|
||
|
||
isDirExist(resImgSaveDir + mysaveKey1 + "/");
|
||
QString qimgsavePath = resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png";
|
||
//存图数据入队,按需
|
||
if (resMat.size() > 0)
|
||
addImgToSaveStruct(resMat[0], mysaveKey1, resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png");
|
||
detect_mutex->unlock();
|
||
|
||
obj.insert("detaillists", detaillists);
|
||
obj.insert("qimgsavePath", qimgsavePath);
|
||
document.setObject(obj);
|
||
//resByte.clear();
|
||
QByteArray qa = document.toJson();
|
||
//resByte .enqueue(qa);
|
||
|
||
|
||
//resMat.release();
|
||
sourceImg.release();
|
||
|
||
mySave.out_Mat.resize(0);
|
||
mysaveKey1.clear();
|
||
qApp->processEvents();
|
||
|
||
|
||
////原图和结果都往主界面发?
|
||
//std::vector<cv::Mat> resMat;
|
||
//resMat.push_back(img2D);
|
||
//resMat.push_back(sourceRectangles);
|
||
//QByteArray resByte = mysaveKey.toLocal8Bit();
|
||
//resByte.append(myDir2D.toLocal8Bit());
|
||
//resByte.append(mySN.toLocal8Bit());
|
||
//resByte.append(myDataTime.toLocal8Bit());
|
||
|
||
////往主界面发送图片和数据
|
||
////emit writeData(resMat, resByte);
|
||
//resMat.resize(0);
|
||
//resByte.resize(0);
|
||
}
|
||
catch (...)
|
||
{
|
||
qDebug() << "[Error] " << QString::fromUtf8("检测失败!");
|
||
return false;
|
||
}
|
||
return true;
|
||
|
||
}
|
||
|
||
//存图加入队列
|
||
void Hd_AI_2DHanHou_module::addImgToSaveStruct(cv::Mat img2D, QString mysaveKey, QString savePath)
|
||
{
|
||
//存图数据加保存队列,加锁,避免数据访问冲突
|
||
saveStruct_mutex->lock();
|
||
saveImgStruct mySave;
|
||
mySave.out_Mat = img2D.clone();
|
||
//img2D.copyTo(mySave.out_Mat);
|
||
mySave.saveKey = mysaveKey.toLocal8Bit().toStdString();
|
||
mySave.savePath = savePath.toLocal8Bit().toStdString();
|
||
saveStruct.enqueue(mySave);
|
||
saveStruct_mutex->unlock();
|
||
}
|
||
void Hd_AI_2DHanHou_module::saveImgThread()
|
||
{
|
||
while (1)
|
||
{
|
||
if (!ifdetectThread)return;
|
||
_sleep(5);
|
||
qApp->processEvents();
|
||
if (saveStruct.size() < 1)continue;
|
||
for (int i = 0; i < saveStruct.size(); i++)
|
||
{
|
||
clock_t start, stop;
|
||
start = clock();
|
||
saveStruct_mutex->lock();
|
||
saveImgStruct mysaveStruct = saveStruct.dequeue();
|
||
saveStruct_mutex->unlock();
|
||
stop = clock();
|
||
if (stop - start > 1000)
|
||
//std::cout << "lock stop-start >1000::************************" << stop - start << std::endl;
|
||
start = clock();
|
||
if (mysaveStruct.out_Mat.data)
|
||
imwrite(mysaveStruct.savePath, mysaveStruct.out_Mat);
|
||
//std::cout << "savePath::" << mysaveStruct.savePath << std::endl;
|
||
stop = clock();
|
||
if (stop - start > 1000)
|
||
//std::cout << "imwrite stop-start >1000::--------------------------" << stop - start << std::endl;
|
||
_sleep(1);
|
||
}
|
||
}
|
||
}
|
||
//检测线程,应当只运行一次
|
||
void Hd_AI_2DHanHou_module::detectThread()
|
||
{
|
||
do
|
||
{
|
||
if (!ifdetectThread)return;
|
||
|
||
//此两行代码,避免程序卡死;_sleep的时间根据实际项目调整
|
||
_sleep(2);
|
||
qApp->processEvents();
|
||
//取数据加锁,避免数据访问冲突
|
||
detect_mutex->lock();
|
||
//if (HikMat.size() < 1 || saveKey.size() < 1 || Dir2D.size() < 1 || SN.size() < 1 )//|| DateTime1.size() < 1)
|
||
//{
|
||
// detect_mutex->unlock();
|
||
// continue;
|
||
//}
|
||
|
||
cv::Mat sourceImg1 = HikMat[0];// .dequeue();
|
||
QString mysaveKey = "saveKey";// .dequeue();
|
||
QString myDir2D = "Dir2D";// .dequeue();
|
||
QString mySN = "SN";// .dequeue();
|
||
QString myDataTime = "DateTime1";
|
||
detect_mutex->unlock();
|
||
|
||
//判断存图文件夹是否存在,不存在则创建
|
||
{
|
||
//原图总文件夹
|
||
isDirExist(oriImgSaveDir + mysaveKey);
|
||
//原图class总文件夹
|
||
isDirExist(classImgSaveDir + mysaveKey);
|
||
//结果图总文件夹
|
||
isDirExist(resImgSaveDir + mysaveKey);
|
||
|
||
}
|
||
|
||
//测试相机的,
|
||
// while (sourceImg1.cols > 2000)cv::resize(sourceImg1, sourceImg1, cv::Size(), 0.5, 0.5);
|
||
|
||
//存图数据入队
|
||
//addImgToSaveStruct(sourceImg1, mysaveKey, oriImgSaveDir + mysaveKey + "/" + mySN + myDataTime + ".bmp");
|
||
|
||
//-------检测流程,根据实际项目修改---------
|
||
AIdetect(sourceImg1, mysaveKey, oriImgSaveDir, mySN, myDataTime);
|
||
//sourceImg1.release();
|
||
|
||
mysaveKey.clear();
|
||
myDir2D.clear();
|
||
mySN.clear();
|
||
myDataTime.clear();
|
||
qApp->processEvents();
|
||
|
||
} while (ifThreadRunDetect);
|
||
|
||
}
|
||
|
||
static int runNum = 0;
|
||
bool Hd_AI_2DHanHou_module::mianZhenEvaluate(cv::Mat& srcImg, cv::Mat& showImg, QString SN, QMutex* showImgLocker) {
|
||
QMutexLocker lock(showImgLocker);
|
||
HDevEngineCpp::HDevProcedure proc("check_short_CapSide");
|
||
HDevProcedureCall procedureCall(proc);
|
||
|
||
HalconCpp::HObject in_Image = Mat2HObject(srcImg);
|
||
HalconCpp::HObject output_Image;
|
||
//HalconCpp::HTuple in_pra[5] = { 70, 30, 40, 300, 0.7 };
|
||
HalconCpp::HTuple in_pra;
|
||
in_pra[0] = 70;
|
||
in_pra[1] = 30;
|
||
in_pra[2] = 40;
|
||
in_pra[3] = 300;
|
||
in_pra[4] = 0.7;
|
||
HalconCpp::HTuple out_res;//, 算法的执行结果, 0: 算法执行异常 1 : 算法执行成功
|
||
HalconCpp::HTuple out_Height;
|
||
std::vector<double> vec_Height;
|
||
//std::vector<string> send_result;
|
||
procedureCall.SetInputCtrlParamTuple("in_pra", in_pra);//传给算法参数
|
||
procedureCall.SetInputIconicParamObject("in_image", in_Image);//传给算法图片
|
||
procedureCall.Execute();//执行halcon程序
|
||
|
||
|
||
out_res = procedureCall.GetOutputCtrlParamTuple("out_res");//获取输出控制类变量
|
||
qDebug() << __FUNCTION__ << "line:" << __LINE__ << SN << "_HanHou2D_out_res()::" << out_res.I();
|
||
output_Image = procedureCall.GetOutputIconicParamObject("out_image");//获取输出控制类变量
|
||
showImg = HObject2Mat(output_Image);
|
||
if (out_res.D() == 1)//NG返回true,OK返回false
|
||
return false;
|
||
else
|
||
return true;
|
||
}
|
||
bool Hd_AI_2DHanHou_module::handleAiEvaluate(cv::Mat& srcImg, cv::Mat& showImg, QString SN, QMutex* showImgLocker) {
|
||
clock_t handleAiEvaluateStart = clock();
|
||
bool handleAiResult = true;
|
||
try {
|
||
clock_t start = clock();
|
||
cv::Mat imgForDet;
|
||
srcImg.copyTo(imgForDet);
|
||
cv::Rect detRect;
|
||
//cv::Mat det_Image = LocalCutImg_DET(det_hanhou, imgForDet, 1, 0, detRect);
|
||
cv::Mat det_Image = imgForDet(cv::Rect(1800,1250,1660,250));
|
||
//addImgToSaveStruct(det_Image, "0", savePath_sorce + "/" + SN + "_det_" + sdataTime_name + ".bmp");//保存seg图
|
||
cv::Mat segImg = ~seg_hanhou->Evaluate(det_Image);//一定要取反,因为seg默认抓到的区域为黑色
|
||
addImgToSaveStruct(segImg, "0", savePath_sorce + "/" + SN + "_seg_" + sdataTime_name + ".bmp");//保存seg图
|
||
clock_t stop = clock();
|
||
qDebug() << __FUNCTION__ << "line:" << __LINE__ << SN << "_det_Seg used time::" << stop - start;
|
||
std::vector<std::vector<cv::Point>>contours;//点集
|
||
stop = clock();
|
||
cv::findContours(segImg, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); // 寻找白色区域并储存为点集
|
||
for (int i = 0; i < contours.size(); i++)
|
||
cv::drawContours(det_Image, contours, i, cv::Scalar(0, 0, 255), 3);
|
||
QMutexLocker lock(showImgLocker);;//加锁防止和halcon线程冲突
|
||
for (auto contour : contours)//遍历点集
|
||
{
|
||
cv::Rect rect = cv::boundingRect(contour);//最小外接矩形
|
||
if( (rect.height > 25 || rect.width > 50) && (rect.height > 10 && rect.width > 10))// 过滤 只有都大于10像素才进检测
|
||
{
|
||
auto clsimgs = dingGaiHanClsCut(rect, det_Image);
|
||
bool isNg = false;
|
||
int cnt = 0;
|
||
for (auto& clsImg : clsimgs)
|
||
{
|
||
int clsResult = cls_hanhou->Evaluate(clsImg);//cls检测 得出的是int型 从0开始
|
||
if (clsResult != 0)
|
||
isNg = true;
|
||
addImgToSaveStruct(clsImg, "0", savePath_sorce + "/" + SN + "_cls_" + sdataTime_name + ".bmp");
|
||
cnt++;
|
||
}
|
||
if (isNg == false)
|
||
{
|
||
|
||
}
|
||
else
|
||
{
|
||
resultOKNG = 2;
|
||
rectangle(showImg, cv::Rect{ rect.x + detRect.x,rect.y + detRect.y,rect.width ,rect.height }, cv::Scalar(0, 0, 255), 2, 8, 0);
|
||
handleAiResult = false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
auto clsimgs = dingGaiHanClsCut(rect, det_Image);
|
||
int cnt = 0;
|
||
for (auto& clsImg : clsimgs)
|
||
{
|
||
addImgToSaveStruct(clsImg, "0", savePath_sorce + "/CLSSMALL_" + SN + "_" + sdataTime_name + ".bmp");
|
||
cnt++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
catch (...) {
|
||
qDebug() << __FUNCTION__ << "line:" << __LINE__ << SN << "_HANDLEAI error";
|
||
}
|
||
clock_t handleAiEvaluateStop = clock();
|
||
qDebug() << __FUNCTION__ << "line:" << __LINE__ << SN << "_HanHou2D_modeltime::" << handleAiEvaluateStop - handleAiEvaluateStart;
|
||
return handleAiResult;
|
||
}
|
||
bool Hd_AI_2DHanHou_module::run()
|
||
{
|
||
try
|
||
{
|
||
clock_t start = clock();
|
||
//if (HikMat.isEmpty())
|
||
//{
|
||
// cv::Mat null_mat = cv::Mat::zeros(cv::Size(100, 100), CV_8UC3);
|
||
// resMat.push_back(null_mat);
|
||
// qDebug() << __FUNCTION__ << "line:" << __LINE__ << "[INFO] " << SN << "_HikMat.isEmpty";
|
||
// return true;
|
||
//}
|
||
|
||
//if (HikMat[0].rows == 100 || HikMat[0].cols == 100)
|
||
//{
|
||
// cv::Mat null_mat = cv::Mat::zeros(cv::Size(100, 100), CV_8UC3);
|
||
// resMat.push_back(null_mat);
|
||
// qDebug() << __FUNCTION__ << "line:" << __LINE__ << "[INFO] " << SN << "_HikMat is 100 * 100";
|
||
// return true;
|
||
//}
|
||
//
|
||
cv::Mat srcImg, showImg;
|
||
//HikMat[0].copyTo(srcImg);
|
||
srcImg = cv::imread("D:\\img\\04QCB30601000JF4M0001918_ori_2025-04-23-01-09-57-443.bmp", -1);
|
||
if (srcImg.channels() == 3)
|
||
srcImg.copyTo(showImg);
|
||
else
|
||
cv::cvtColor(showImg, showImg, cv::COLOR_GRAY2BGR);
|
||
QMutex* locker = new QMutex();
|
||
auto halconFutrue = std::async(std::launch::async, &Hd_AI_2DHanHou_module::mianZhenEvaluate, this, srcImg, showImg, SN, locker);
|
||
auto handleAiFutrue = std::async(std::launch::async, &Hd_AI_2DHanHou_module::handleAiEvaluate, this, srcImg, showImg, SN, locker);
|
||
halconFutrue.wait();
|
||
halconResultIsNG = halconFutrue.get();
|
||
handleAiFutrue.wait();
|
||
handleAiResultNG = handleAiFutrue.get();
|
||
delete locker;
|
||
if (handleAiResultNG == true)
|
||
{
|
||
putTextZH(showImg, "OK", cv::Point(showImg.cols - 500, 0), cv::Scalar(0, 255, 0), 150, "微软雅黑", false, false);
|
||
resultOKNG = 1;
|
||
}
|
||
else
|
||
{
|
||
putTextZH(showImg, "NG", cv::Point(showImg.cols - 500, 0), cv::Scalar(0, 0, 255), 150, "微软雅黑", false, false);
|
||
resultOKNG = 2;
|
||
}
|
||
resMat.push_back(showImg);
|
||
clock_t stop = clock();
|
||
qDebug() << __FUNCTION__ << "line:" << __LINE__ << SN << "_run end HanHou2D ::runNum::" << runNum++ << " runTime::" << stop - start;
|
||
}
|
||
catch (...)
|
||
{
|
||
qDebug() << __FUNCTION__ << "line:" << __LINE__ << SN << "_Algo error";
|
||
return true;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool Hd_AI_2DHanHou_module::checkStatus()
|
||
{
|
||
status = moduleStatus;
|
||
return status;
|
||
}
|
||
|