找工作过程中发现要求会C++的工业检测,用一个项目熟悉整个流程
#include
#include
#include
// 定义一个结构体来存储缺陷信息
struct Defect {
int type; // 缺陷类型,1代表划痕,2代表污渍
int x; // 缺陷的x坐标
int y; // 缺陷的y坐标
int width; // 缺陷的宽度
int height;// 缺陷的高度
// 构造函数,初始化缺陷信息
Defect(int t, int x, int y, int w, int h) : type(t), x(x), y(y), width(w), height(h) {}
};
// 存储检测到的缺陷
std::vector Result;
// 函数:加载参考图像并转换为灰度图
cv::Mat loadReferenceImage(const std::string& referenceImagePath) {
cv::Mat referenceImage = cv::imread(referenceImagePath, cv::IMREAD_GRAYSCALE); // 以灰度模式读取图像
if (referenceImage.empty()) {
std::cerr << "Reference image not found!" << std::endl;
exit(1); // 如果图像未找到,退出程序
}
return referenceImage;
}
// 函数:检测图像中的缺陷
cv::Mat detectDefects(const cv::Mat& inputImage, const cv::Mat& referenceImage) {
cv::Mat diff; // 存储输入图像和参考图像的差异
cv::absdiff(referenceImage, inputImage, diff); // 计算差异
cv::Mat thresh; // 存储二值化后的差异图像
cv::threshold(diff, thresh, 30, 255, cv::THRESH_BINARY); // 二值化处理
return thresh;
}
int main() {
// 加载参考图像
cv::Mat reference = loadReferenceImage("reference_image.png");
// 读取待检测的工件图像
cv::Mat input = cv::imread("input_image.png");
if (input.empty()) {
std::cerr << "Input image not found!" << std::endl;
return -1; // 如果图像未找到,退出程序
}
// 检测缺陷
cv::Mat defects = detectDefects(input, reference);
// 查找缺陷轮廓
std::vector> contours;
cv::findContours(defects.clone(), contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
// 创建一个掩码,用于标记已经处理过的区域
cv::Mat mask = cv::Mat::zeros(defects.size(), CV_8UC1);
int工件数量 = 0; // 存储检测到的工件数量
// 遍历每个轮廓
for (size_t i = 0; i < contours.size(); i++) {
cv::Rect boundRect = cv::boundingRect(contours[i]); // 获取轮廓的边界矩形
cv::drawContours(mask, contours, static_cast(i), cv::Scalar(255), cv::FILLED); // 在掩码上标记轮廓
// 从缺陷图像中裁剪出当前轮廓对应的区域
cv::Mat resul(defects.size(), CV_8UC1);
resul = defects(boundRect).clone();
// 直方图计算
cv::Mat hist; // 存储直方图结果
int histSize = 256; // 直方图的bin数量
float range[] = {0, 256}; // 像素值范围
const float* histRanges = {range}; // 直方图的值域
cv::calcHist(&resul, 1, 0, cv::Mat(), hist, 1, &histSize, &histRanges, true, false); // 计算直方图
// 判断缺陷
float sum = 0; // 存储直方图的总和
for (int i = 0; i < 256; i++) {
float bin_val = hist.at(i); // 获取直方图的第i个bin值
sum = sum + bin_val; // 计算直方图总和
}
// 计算直方图的百分比
for (int i = 0; i < 256; i++) {
if (hist.at(i) > 0) {
hist.at(i) = hist.at(i) / sum; // 计算百分比
}
}
// 分别计算较灰和较黑像素的百分比总和
float hist_sum_scratch = 0; // 较灰像素的百分比总和
float hist_sum_blot = 0; // 较黑像素的百分比总和
for (int i = 90; i < 135; i++) {
hist_sum_scratch = hist_sum_scratch + hist.at(i); // 较灰像素
}
std::cout << "hist_sum_scratch:" << hist_sum_scratch << "\n";
for (int i = 15; i < 90; i++) {
hist_sum_blot = hist_sum_blot + hist.at(i); // 较黑像素
}
std::cout << "hist_sum_blot:" << hist_sum_blot << "\n";
// 根据直方图百分比判断缺陷类型
if (hist_sum_scratch >= hist_sum_blot) {
Defect d(1, boundRect.x, boundRect.y, boundRect.width, boundRect.height); // 划痕
Result.emplace_back(d);
std::cout << "此处缺陷划痕" << "\n";
}
if (hist_sum_scratch < hist_sum_blot) {
Defect d(2, boundRect.x, boundRect.y, boundRect.width, boundRect.height); // 污渍
Result.emplace_back(d);
std::cout << "此处缺陷污渍" << "\n";
}
工件数量++; // 增加工件数量
}
std::cout << "检测到的工件数量: " << 工件数量 << std::endl;
cv::imshow("Mask", mask); // 显示掩码图像
cv::waitKey(0); // 等待按键
return 0;
}
这段代码提供了一个完整的流程,从加载图像、检测缺陷、计算直方图、判断缺陷类型,到显示结果。
问:他是通过什么方法判断缺陷类型的
答:在提供的代码中,缺陷类型的判断是通过分析图像的灰度直方图来实现的。具体的方法如下:
1. **直方图计算**:
- 首先,代码计算了缺陷区域的灰度直方图。直方图是一个统计图像中每个灰度级出现次数的图形表示。在这个例子中,直方图的每个bin对应一个灰度值,从0(黑)到255(白)。
2. **直方图归一化**:
- 然后,代码将直方图的每个bin值转换为该灰度值出现的相对频率(百分比)。这是通过将每个bin值除以直方图的总和来实现的。
3. **缺陷类型判断**:
- 代码中定义了两个灰度范围来区分划痕和污渍:
- **划痕**:被认为是较灰的区域,代码中检查直方图中90到135之间的灰度值的累积和(`hist_sum_scratch`)。
- **污渍**:被认为是较黑的区域,代码中检查直方图中15到90之间的灰度值的累积和(`hist_sum_blot`)。
- 如果`hist_sum_scratch`大于或等于`hist_sum_blot`,则判断该缺陷为划痕。
- 如果`hist_sum_scratch`小于`hist_sum_blot`,则判断该缺陷为污渍。
这种方法的基本思想是:
- **划痕**:通常表现为图像中的亮度变化,因此在较灰的灰度值范围内有更多的像素。
- **污渍**:通常表现为图像中的暗区域,因此在较黑的灰度值范围内有更多的像素。
### 注意事项
- 这种方法的有效性取决于图像的质量和缺陷的明显性。在实际应用中,可能需要调整灰度范围的阈值,以适应不同的图像和缺陷类型。
- 直方图方法对光照变化和阴影比较敏感,可能需要额外的预处理步骤来减少这些因素的影响。
- 在更复杂的应用中,可能需要结合其他图像特征和机器学习方法来提高缺陷检测的准确性和鲁棒性。