1868【毕设课设】基于机器视觉的玻璃缺陷检测算法研究与设计

基于机器视觉的玻璃缺陷检测算法研究与设计

摘要

本文研究并设计了一种基于机器视觉的玻璃缺陷检测算法,利用OpenCV库实现玻璃镜面缺陷的检测、缺陷信息的提取和标记。本文首先介绍了系统环境和依赖库的配置,然后详细描述了检测算法的设计与实现过程,最后通过实验验证了算法的有效性。

关键词

机器视觉, 玻璃缺陷检测, OpenCV, 图像处理, 连通区域
在这里插入图片描述
在这里插入图片描述

完整资料下载地址:
https://docs.qq.com/doc/p/ffc421513a628b30450ae742547c0b850456d7c8

1. 引言

玻璃缺陷检测在工业生产中具有重要意义。传统的人工检测方法效率低下且易受人为因素影响,而基于机器视觉的自动检测系统能够提高检测速度和准确性。本文设计了一种基于OpenCV的玻璃缺陷检测算法,通过图像处理技术实现缺陷的检测与标记。

2. 系统环境与配置

2.1 系统环境

  • 操作系统:Windows 10
  • 集成开发环境:Visual Studio 2015
  • 图像处理库:OpenCV 3.0

2.2 环境配置

本文使用OpenCV 3.0进行图像处理,具体的环境配置可以通过百度搜索相关教程进行设置。

3. 算法设计

3.1 变量定义

// 定义灰度图像变量
IplImage *g_GrayImage = NULL;
// 定义二值化图片变量
IplImage *g_BinaryImage = NULL;
// 定义二值化窗口标题
const char *WindowBinaryTitle = "二值化图片";
// 创建源图像窗口标题变量
const char *WindowSrcTitle = "灰度图像";
// 创建滑块标题变量
const char *TheSliderTitle = "二值化阀值";
const char *SrcPath = "3.jpg"; // 定义图片路径

IplImage *g_pGrayImage_liantong = NULL;
IplImage *g_pBinralyImage_liantong = NULL;

int contour_num = 0; // 数字编号
char number_buf[10]; // 数字编号存入数组,puttext

#define num_col 11 // 二维数组的列,每一个点缺陷信息的详细信息

long int liantong_all_area = 0; // 连通区域总面积
long int Rect_all_area = 0;     // 保存最小外接矩形总的面积

3.2 结构体定义

struct my_struct1 {
    double scale;                                  // 定义显示图像的比例
    const int threshold_value_binaryzation;        // 定义第一次二值化阀值
    const int threshold_value_second_binaryzation; // 定义第二次二值化阀值
};
my_struct1 picture = {0.3, 50, 100};

struct my_struct2 {
    int Model1_k1; // 图像膨胀腐蚀
    int Model1_k2; // 图像膨胀腐蚀
    int Model2_k1; // 图像膨胀腐蚀
    int Model2_k2; // 图像膨胀腐蚀
};
my_struct2 value = {5, 2, 3, 2};

struct my_struct3 {
    double maxarea; // 最大缺陷面积
    double minarea; // 最小显示保留的缺陷面积
    double font_scale;  // 字体大小
    int font_thickness; // 字体粗细
    const int Feature_value2_number; // 定义一个二维数组的列,即缺陷的个数
};
my_struct3 value2 = {0, 4, 0.6, 0.8, 100};

struct my_struct4 {
    const int hough_Canny_thresh1;
    const int hough_Canny_thresh2;
    const int hough_Canny_kernel;
    const int cvHoughLines2_thresh; // 像素值大于多少才显示,值越大,显示的线段越少
    const int cvHoughLines2_param1; // 显示线段的最小长度
    const int cvHoughLines2_param2; // 线段之间的最小间隔
};
my_struct4 Hough = {50, 100, 3, 50, 20, 10};

3.3 图像处理与缺陷检测

3.3.1 图像预处理
int **on_trackbar() {
    CvSeq *contour = 0;
    CvSeq *_contour = contour;

    // 定义存放数组的二维数组,返回指针数组
    int **Feature_value2 = 0;
    Feature_value2 = new int *[value2.Feature_value2_number];

    IplImage *SrcImage_or;
    CvSize src_sz;
    
    // 载入原图
    IplImage *SrcImage_origin = cvLoadImage(SrcPath, CV_LOAD_IMAGE_UNCHANGED);

    // resize
    src_sz.width = SrcImage_origin->width * picture.scale;
    src_sz.height = SrcImage_origin->height * picture.scale;
    SrcImage_or = cvCreateImage(src_sz, SrcImage_origin->depth, SrcImage_origin->nChannels);
    cvResize(SrcImage_origin, SrcImage_or, CV_INTER_CUBIC);

    cvNamedWindow("原图", CV_WINDOW_AUTOSIZE);
    cvShowImage("原图", SrcImage_origin);

    // 单通道灰度化处理
    g_GrayImage = cvCreateImage(cvSize(SrcImage_or->width, SrcImage_or->height), IPL_DEPTH_8U, 1);
    cvCvtColor(SrcImage_or, g_GrayImage, CV_BGR2GRAY);

    // 创建二值化原图
    g_BinaryImage = cvCreateImage(cvGetSize(g_GrayImage), IPL_DEPTH_8U, 1);

    cvThreshold(g_GrayImage, g_BinaryImage, picture.threshold_value_binaryzation, 255, CV_THRESH_BINARY);
    cvShowImage(WindowBinaryTitle, g_BinaryImage);
3.3.2 图像膨胀腐蚀
    g_BinaryImage = cvCloneImage(g_BinaryImage);

    IplImage *temp_cvDilate = cvCreateImage(cvGetSize(g_BinaryImage), IPL_DEPTH_8U, 1);
    IplImage *temp_cvErode = cvCreateImage(cvGetSize(g_BinaryImage), IPL_DEPTH_8U, 1);
    IplImage *temp_cvErode_cvErode = cvCreateImage(cvGetSize(g_BinaryImage), IPL_DEPTH_8U, 1);

    IplConvKernel *myModel1;
    myModel1 = cvCreateStructuringElementEx(value.Model1_k1, value.Model1_k1, value.Model1_k2, value.Model1_k2, CV_SHAPE_ELLIPSE);
    IplConvKernel *myModel2;
    myModel2 = cvCreateStructuringElementEx(value.Model2_k1, value.Model2_k1, value.Model2_k2, value.Model2_k2, CV_SHAPE_RECT);

    cvDilate(g_BinaryImage, temp_cvDilate, myModel1, 1);
    cvErode(temp_cvDilate, temp_cvErode_cvErode, myModel2, 1);

    namedWindow("膨胀腐蚀", CV_WINDOW_AUTOSIZE);
    cvShowImage("膨胀腐蚀", temp_cvErode_cvErode);

    g_BinaryImage = cvCloneImage(temp_cvErode_cvErode);
3.3.3 连通区域检测
    CvMemStorage *liantong_storage = cvCreateMemStorage();
    IplImage *liantogn_dst = cvCreateImage(cvGetSize(g_BinaryImage), 8, 3);
    cvFindContours(g_BinaryImage, liantong_storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
    cvZero(liantogn_dst);

    int n = -1, m = 0;

    for (; contour != 0; contour = contour->h_next) {
        double tmparea = fabs(cvContourArea(contour));
        if (tmparea <= value2.minarea) {
            cvSeqRemove(contour, 0);
            continue;
        } else {
            liantong_all_area = liantong_all_area + tmparea;
        }
        CvRect aRect = cvBoundingRect(contour, 0);
        if (tmparea > value2.maxarea) {
            value2.maxarea = tmparea;
            n = m;
        }
        m++;
        CvScalar color = CV_RGB(0, 255, 255);
        cvDrawContours(liantogn_dst, contour, color, color, -1, -1, 8);
    }

    long int sizeof_pic = liantogn_dst->width * liantogn_dst->height;
    cvNamedWindow("连通区域", 1);
    cvShowImage("连通区域", liantogn_dst);
3.3.4 缺陷标记与统计
    IplImage *label_liantogn_dst_origin = cvCloneImage(liantogn_dst);
    Mat label_liantogn_dst = cvarrToMat(label_liantogn_dst_origin);

    IplImage *g_BinaryImage_fanse_origin = NULL;
    g_pGrayImage_liantong = cvCreateImage(cvGetSize(liantogn_dst), IPL_DEPTH_8U, 1);
    cvCvtColor(liantogn_dst, g_pGrayImage_liantong, CV_BGR2GRAY);

    g_pBinralyImage_liantong = cvCreateImage(cvGetSize(g_pGrayImage_liantong), IPL_DEPTH_8U, 1);
    cvThreshold(g_pGrayImage_liantong, g_pBinralyImage_liantong, picture.threshold_value_second_binaryzation, 255, CV_THRESH_BINARY);

    cvNamedWindow("连通二值化", CV_WINDOW_AUTOSIZE);
    cvShowImage("连通二值化", g_pBinralyImage_liantong);

    Mat g_pBinralyImage_liantong_2 = cvarrToMat(g_pBinralyImage_liantong);

    IplImage *fanse_origin = cvCloneImage(g_pBinralyImage_liantong);

    CvMemStorage *storage = cvCreateMemStorage();
    CvSeq *seq = NULL;
    int cnt = cvFindContours(g_pBinralyImage_liantong, storage, &seq);
    seq = seq->h_next;
    double length = cvArcLength(seq);
    double area = cvContourArea(seq);
    CvRect rect = cvBoundingRect(seq, 1);
    CvBox2D box = cvMinAreaRect2(seq, NULL);

    IplImage *dst_min_rec = cvLoadImage(SrcPath, 1);
    CvSize sz;
    sz.width = dst_min_rec->width * picture.scale;
    sz.height = dst_min_rec->height * picture.scale;

    IplImage *SrcImage = cvCreateImage(sz, dst_min_rec->depth, dst_min_rec->nChannels);
    cvResize(dst_min_rec, SrcImage, CV_INTER_CUBIC);
    dst_min_rec = cvCloneImage(SrcImage);

    cvFindContours(g_pBinralyImage_liantong, storage, &contour, sizeof(CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
    for (; contour != 0; contour = contour->h_next) {
        CvBox2D rect = cvMinAreaRect2(contour, storage);
        CvPoint2D32f rect_pts0[4];
        cvBoxPoints(rect, rect_pts0);

        int npts = 4, k = 0;
        int aaa = 0, bbb = 0;
        CvPoint rect_pts[4], *pt = rect_pts;
        int sum_rect_x = 0, sum_rect_y = 0;

        Feature_value2[contour_num] = new int[num_col];

        for (int i = 0; i < 4; i++) {
            rect_pts[i] = cvPointFrom32f(rect_pts0[i]);

            Feature_value2[contour_num][i] = rect_pts[i].x;
            Feature_value2[contour_num][i + 4] = rect_pts[i].y;

            sum_rect_x += rect_pts[i].x;
            sum_rect_y += rect_pts[i].y;

            aaa = (int)sqrt((pow((rect_pts[0].x - rect_pts[1].x), 2) + pow((rect_pts[0].y - rect_pts[1].y), 2)));
            bbb = (int)sqrt((pow((rect_pts[0].x - rect_pts[3].x), 2) + pow((rect_pts[0].y - rect_pts[3].y), 2)));

            if (aaa < bbb) {
                k = aaa;
                aaa = bbb;
                bbb = k;
            }
        }

        Feature_value2[contour_num][8] = aaa;
        Feature_value2[contour_num][9] = bbb;
        Feature_value2[contour_num][10] = aaa * bbb;

        Rect_all_area = Rect_all_area + aaa * bbb;

        int font_face = cv::FONT_HERSHEY_COMPLEX;

        cv::Point origin;
        origin.x = sum_rect_x / 4;
        origin.y = sum_rect_y / 4;

        sprintf(number_buf, "%3d", contour_num);
        string number_buf_string = number_buf;
        putText(label_liantogn_dst, number_buf_string, origin, font_face, value2.font_scale, cv::Scalar(0, 255, 255), value2.font_thickness, 8, 0);

        cvPolyLine(dst_min_rec, &pt, &npts, 1, 1, CV_RGB(255, 0, 0), 1);

        contour_num++;
    }

    cvNamedWindow("label_liantogn_dst_result", CV_WINDOW_AUTOSIZE);

    line(label_liantogn_dst, Point(0, dst_min_rec->height * 0.25), Point(dst_min_rec->width, dst_min_rec->height * 0.25), Scalar(89, 90, 90), 1);
    line(label_liantogn_dst, Point(0, dst_min_rec->height * 0.5), Point(dst_min_rec->width, dst_min_rec->height * 0.5), Scalar(89, 90, 90), 1);
    line(label_liantogn_dst, Point(0, dst_min_rec->height * 0.75), Point(dst_min_rec->width, dst_min_rec->height * 0.75), Scalar(89, 90, 90), 1);

    line(label_liantogn_dst, Point(dst_min_rec->width * 0.25, 0), Point(dst_min_rec->width * 0.25, dst_min_rec->height), Scalar(89, 90, 90), 1);
    line(label_liantogn_dst, Point(dst_min_rec->width * 0.5, 0), Point(dst_min_rec->width * 0.5, dst_min_rec->height), Scalar(89, 90, 90), 1);
    line(label_liantogn_dst, Point(dst_min_rec->width * 0.75, 0), Point(dst_min_rec->width * 0.75, dst_min_rec->height), Scalar(89, 90, 90), 1);

    Point p2;
    p2.x = 10;
    p2.y = 10;
    circle(label_liantogn_dst, p2, 5, Scalar(0, 0, 255), -1);

    imshow("连通标记", label_liantogn_dst);

    printf("连通区域个数:%4d \n", contour_num);
    cvNamedWindow("外接矩形", CV_WINDOW_AUTOSIZE);
    cvLine(dst_min_rec, cvPoint(0, 50), cvPoint(dst_min_rec->width, 50), CV_RGB(255, 0, 0), 1);
    cvShowImage("外接矩形", dst_min_rec);

    Mat dst_min_rec_result = cvarrToMat(dst_min_rec);
    cvSaveImage("外接矩形.jpg", dst_min_rec);

    float temp_percent = 0.0;

    printf("连通区域面积:%d\r\n", liantong_all_area);
    printf("图像矩形面积:%d\r\n", Rect_all_area);
    printf("整副图像面积:%d\r\n", sizeof_pic);

    temp_percent = (float)liantong_all_area / sizeof_pic * 100;
    printf("缺陷面积占比:%0.2f %%\r\n", temp_percent);
3.3.5 霍夫变换检测直线
    IplImage *lines_dst;
    IplImage *color_dst;
    CvMemStorage *lines_storage = cvCreateMemStorage(0);
    CvSeq *lines = 0;
    int hough_i;

    lines_dst = cvCreateImage(cvGetSize(liantogn_dst), 8, 1);
    color_dst = cvCreateImage(cvGetSize(liantogn_dst), 8, 3);
    cvCanny(liantogn_dst, lines_dst, Hough.hough_Canny_thresh1, Hough.hough_Canny_thresh2, Hough.hough_Canny_kernel);
    cvCvtColor(lines_dst, color_dst, CV_GRAY2BGR);

    lines = cvHoughLines2(lines_dst, lines_storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, Hough.cvHoughLines2_thresh, Hough.cvHoughLines2_param1, Hough.cvHoughLines2_param2);
    for (hough_i = 0; hough_i < lines->total; hough_i++) {
        CvPoint *line = (CvPoint *)cvGetSeqElem(lines, hough_i);
        cvLine(dst_min_rec, line[0], line[1], CV_RGB(0, 255, 255), 3, CV_AA, 0);
    }

    cvNamedWindow("Hough", 1);
    cvShowImage("霍夫变换", dst_min_rec);

    cvSaveImage("霍夫变换.jpg", liantogn_dst);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值