Files
eUi/include/simpleAlgo.h
RCiRoot 9210b37b44 V20251116-2.2.0
优化文件结构,删除较老代码文件。
优化参数修改界面,更改的参数都会实时更新到程序以及json中。
优化SQL相关功能。
更改了存图格式。
2025-11-16 19:29:29 +08:00

835 lines
27 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <HalconCpp.h>
#include <opencv.hpp>
#include <ios>
#include <algorithm>
#include <handleAI.h>
#include <QString>
#include <QByteArray>
#include <chrono>
#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 = std::max(std::max(boundingBox.width, boundingBox.height), 128);
// 期望在原图上的 ROI(可能越界)
cv::Rect expectRect(mid.x - sideLength / 2, mid.y - sideLength / 2, sideLength, sideLength);
// 计算需要的边界填充(反射填充)
const int left = std::max(0, -expectRect.x);
const int top = std::max(0, -expectRect.y);
const int right = std::max(0, expectRect.x + expectRect.width - imgInput.cols);
const int bottom = std::max(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 = std::min(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(std::max(rect1.x + rect1.width, rect2.x + rect2.width), std::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 = std::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;
}
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 = std::max(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 = std::max(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()));
}