工业缺陷检测项目实战(一)——基于opencv的工件缺陷检测C++和python实现

基于opencv的工件缺陷检测C++和python实现

作为研究生,每一个项目都很重要,这里给大家分享一个好入门项目,代码纯自己写,网上都是python的,但是有些企业要求C++编写项目,所以希望大家能学到东西。
一. 问题陈述
工件的展示,这是一个视频,然后工件一个个经过,要检测出哪个工件有缺陷,并且分类缺陷的种类。可以看到缺陷是不止一种。
在这里插入图片描述
在这里插入图片描述
二. 代码步骤
1.读取图像,转为灰度图并二值化

cvtColor(img, gray, COLOR_BGR2GRAY);
threshold(gray, thresh, 127, 255, THRESH_TOZERO_INV);、

2.寻找轮廓

std::vector<Vec4i> hireachy;
std::vector<std::vector<Point>> contours;
findContours(thresh, contours, hireachy, RETR_LIST, CHAIN_APPROX_NONE);

3.遍历轮廓,对工件圈进行统计,防止重复标记
原理是计算图像矩,可以确定图像的灰度中心,根据每个时刻每个工件的中心位置的变换,可以判断画面里是否出现新的工件。同时,也要记得更新每个时刻每个工件的位置。具体代码实现可以参考完整工程文件。这里贴出部分:

for (size_t cnt = 0; cnt < contours.size(); cnt++)
        {
            double area = contourArea(contours[cnt]); //求轮廓面积(大约的)
            if (area > 18000 & area < 28000)          //把工件圈出来
            {
                mu[cnt] = moments(contours[cnt], false); //计算图像矩,表示工件的位置
                //计算图像质心位置
                double cx = mu[cnt].m10 / mu[cnt].m00;
                double cy = mu[cnt].m01 / mu[cnt].m00;
                boundRect[cnt] = boundingRect(Mat(contours[cnt])); //计算外接矩形

                new_object = true;
                //通过质心位置判断视频出现的工件是否是最新的
                if (cx > 100) //工件要全部出现
                {
                    if (products.size() > 0) //判断出现的工件是不是新的
                    {
                        for (size_t i = 0; i < products.size(); i++)
                        {
                            //存在一个
                            if (fabs(cx - products[i].getX()) <= 35 && fabs(cy - products[i].getY()) <= 35)
                            {
                                new_object = false;
                                //更新位置参数
                                products[i].updateCoords(cx, cy, boundRect[cnt].x, boundRect[cnt].y, boundRect[cnt].width, boundRect[cnt].height);
                            }
                        }
                    }
                    if (new_object == true)
                    {
                        Product p(pid, cx, cy, boundRect[cnt].x, boundRect[cnt].y, boundRect[cnt].width, boundRect[cnt].height);
                        p.save_pic(img);
                        products.emplace_back(p);
                        p.count = pid;
                        defects = p.defect_detect(); //缺陷检测
                        pid += 1;
                    }
                }
                //圈出来
                rectangle(img, boundRect[cnt].tl(), boundRect[cnt].br(), Scalar(0, 0, 255), 2, 8, 0);
            }
        }

效果是这样:
在这里插入图片描述

4.遍历轮廓,把工件圈出来,顺便保存调试,如下图。圈出来的原理也不难,其实就是通过函数

boundRect[cnt] = boundingRect(Mat(contours[cnt])); 

计算外接矩形,就可以获得矩形的左上角和右下角坐标,就可以画了。效果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.对每个框进行缺陷提取,原理依然是轮廓检测加面积判断。效果如下:
在这里插入图片描述
在这里插入图片描述
6.缺陷类型判断,这里利用直方图统计。即统计0-255中每个像素的个数,根据个数,转为百分比,接着我们设定一个阈值,就可以判断出缺陷类型。部分代码:

          //------------------------直方图计算
            Mat hist;
            //设定像素取值范围
            int histSize = 256;
            float range[] = {0, 256};
            const float *histRanges = {range};
            // hist索引为像素,值为像素点的个数
            calcHist(&resul, 1, 0, Mat(), hist, 1, &histSize, &histRanges, true, false);
            //-------------------------判断缺陷
            float sum = 0;
            for (int i = 0; i < 256; i++)
            {
                float bin_val = hist.at<float>(i); //遍历hist元素(注意hist中是float类型)
                sum = sum + bin_val;               //计算总的个数
            }
            // std::cout << "sum:"
            //           << sum << "\n";
            //计算各个像素点的个数百分比
            for (int i = 0; i < 256; i++)
            {
                if (hist.at<float>(i) > 0) //像素点个数大于0的时候
                {
                    hist.at<float>(i) = hist.at<float>(i) / sum;
                }
                // std::cout << "hist:"
                //           << hist.at<float>(i) << "\n";
            }
            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<float>(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<float>(i);
            }
            std::cout << "hist_sum_blot:"
                      << hist_sum_blot << "\n";
            if (hist_sum_scratch >= hist_sum_blot)
            {
                Defect d(1, boundRect[cnt].x, boundRect[cnt].y, boundRect[cnt].width, boundRect[cnt].height);
                Result.emplace_back(d);
                state = 1;
                std::cout << "此处缺陷划痕"
                          << "\n";
            }
            if (hist_sum_scratch < hist_sum_blot)
            {
                Defect d(2, boundRect[cnt].x, boundRect[cnt].y, boundRect[cnt].width, boundRect[cnt].height);
                Result.emplace_back(d);
                state = 2;
                std::cout << "此处缺陷污渍"
                          << "\n";
            }

到此就可以完成啦!!!
三. 最终效果:
在这里插入图片描述
可以看到,会在终端打印相关消息

C++代码纯自己写,有多文件,花了一些时间总结,大家可以私信我拿代码
另外,也准备了python代码,需要的也可以私信我。

因为花费不少时间,所以希望大家给点报酬支持一下,继续加油进入下一个实战项目分享

------------------------------------补充内容---------------------------------

由于很多小伙伴下载代码之后不知道怎么使用这代码,今天就补充一下。首先,我们需要安装opencv与dlib,参考下我的另外一篇文章:
ubuntu20+dlib19.22+0pencv4.5.0的机器视觉算法之路(一).
1.删除buiild文件夹里面的所有文件
2.路径打开到build, 执行

cmake ..

3.接着

make

4.执行

./result

搞定,完结,欢迎大家来学习,当作自己的一个项目,或者可以进一步去扩展成其他东西,懂的都懂。

  • 48
    点赞
  • 323
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 45
    评论
这是一个简单的基于OpenCV工业表面缺陷分类检测程序,它使用了SVM分类器来识别两种不同类型的缺陷:裂缝和凸起。 首先,我们需要导入必要的OpenCV库和头文件: ```c++ #include <opencv2/opencv.hpp> #include <opencv2/ml.hpp> using namespace cv; using namespace cv::ml; using namespace std; ``` 然后,定义一些常量和变量: ```c++ const int IMAGE_WIDTH = 64; const int IMAGE_HEIGHT = 64; const int NUM_TRAINING_SAMPLES = 40; const int NUM_TESTING_SAMPLES = 10; const int NUM_FEATURES = IMAGE_WIDTH * IMAGE_HEIGHT; const int NUM_CLASSES = 2; const string TRAINING_DATA_PATH = "training_data/"; const string TESTING_DATA_PATH = "testing_data/"; const string SVM_MODEL_PATH = "svm_model.xml"; Mat trainingData; Mat trainingLabels; Mat testingData; Mat testingLabels; Ptr<SVM> svm; ``` 接下来,我们定义一个函数,用于将图像转换为特征向量。 ```c++ Mat imageToFeatureVector(Mat image) { Mat grayImage; cvtColor(image, grayImage, COLOR_BGR2GRAY); resize(grayImage, grayImage, Size(IMAGE_WIDTH, IMAGE_HEIGHT)); Mat featureVector = grayImage.reshape(1, 1); return featureVector; } ``` 然后,我们定义一个函数,用于加载训练和测试数据。 ```c++ void loadData() { for (int i = 1; i <= NUM_TRAINING_SAMPLES; i++) { string imagePath = TRAINING_DATA_PATH + "crack_" + to_string(i) + ".jpg"; Mat image = imread(imagePath); Mat featureVector = imageToFeatureVector(image); trainingData.push_back(featureVector); trainingLabels.push_back(0); } for (int i = 1; i <= NUM_TRAINING_SAMPLES; i++) { string imagePath = TRAINING_DATA_PATH + "bump_" + to_string(i) + ".jpg"; Mat image = imread(imagePath); Mat featureVector = imageToFeatureVector(image); trainingData.push_back(featureVector); trainingLabels.push_back(1); } for (int i = 1; i <= NUM_TESTING_SAMPLES; i++) { string imagePath = TESTING_DATA_PATH + "crack_" + to_string(i) + ".jpg"; Mat image = imread(imagePath); Mat featureVector = imageToFeatureVector(image); testingData.push_back(featureVector); testingLabels.push_back(0); } for (int i = 1; i <= NUM_TESTING_SAMPLES; i++) { string imagePath = TESTING_DATA_PATH + "bump_" + to_string(i) + ".jpg"; Mat image = imread(imagePath); Mat featureVector = imageToFeatureVector(image); testingData.push_back(featureVector); testingLabels.push_back(1); } } ``` 然后,我们定义一个函数,用于训练SVM分类器。 ```c++ void trainSVM() { svm = SVM::create(); svm->setType(SVM::C_SVC); svm->setKernel(SVM::LINEAR); svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6)); Ptr<TrainData> trainData = TrainData::create(trainingData, ROW_SAMPLE, trainingLabels); svm->train(trainData); svm->save(SVM_MODEL_PATH); } ``` 最后,我们定义一个函数,用于测试SVM分类器。 ```c++ void testSVM() { int numCorrect = 0; for (int i = 0; i < testingData.rows; i++) { Mat featureVector = testingData.row(i); int trueLabel = testingLabels.at<int>(i); int predictedLabel = svm->predict(featureVector); if (predictedLabel == trueLabel) { numCorrect++; } } float accuracy = (float)numCorrect / testingData.rows; cout << "Accuracy: " << accuracy << endl; } ``` 现在,我们可以在main函数中调用这些函数来训练和测试SVM分类器。 ```c++ int main() { loadData(); trainSVM(); testSVM(); return 0; } ``` 这是一个简单的工业表面缺陷分类检测程序,你可以使用更多的训练数据和更复杂的特征来提高分类器的准确性。
评论 45
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏融化了这季节

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值