您的位置:首页 > 运维架构

基于OpenCV的PCB缺损判断研究

2016-03-09 18:58 239 查看
本人模式识别小硕一枚,目前帝都某校研一在读。寒假自己用opencv做了一个对PCB板的好坏的检测,拿出来和大家一起学习讨论,这篇博文也是我在CSDN上发表的第一篇文章,欢迎各路大神指导。



基本思想是通过定焦的工业摄像头,对放置于卡槽中的PCB进行拍摄并取ROI,与标准的PCB图片进行模板匹配,两者二值化后相减并中值滤波,在缺损处用红色矩形标出,最后只命名输出缺损PCB图片。

为了给大家更好的演示,我将程序改为直接读取图片

程序如下:

(可能有些杂乱,本人水平还需提高)

[code]/********************************************************************************/
    /*
     * Copyright(c)2016
     * ALL rights  reserved.
     *
     * file name:  Lab_Identification_Program.cpp
     * file description:    
     *

     * Abstract:
     * current version:2.0
     * author: L T
     * date: 2016.2.3
     *
     * replacement version:
     * original author:
     * date:
     */
/********************************************************************************/
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;

//------------------------------------------------------
//  全局变量
//------------------------------------------------------
Mat frame, grayimage, StandardPhoto, WaitJudgePhoto, g_resultImage, srcImage;
Mat img,dst,edge,out,mask,out1,out2,out3,out4;
Mat img_result,img_result1,img_result2,Wback,WbackClone,WbackClone1;

int ConditionJudgment;
int g_nMatchMethod = 5;

#define pic01 "AFTER2.jpg"  //需要比对的图
#define pic02 "PRO5.jpg"
#define WINDOW_NAME1 "【原始图片】"        //为窗口标题定义的宏 
#define WINDOW_NAME2 "【匹配窗口】" 

//------------------------------------------------------
//  二值化(需先灰度化)
//------------------------------------------------------
void Binarization()
{

    threshold(grayimage, out, 90, 255, 0);//THRESH_BINARY = 0
    threshold(StandardPhoto, out1, 90, 255, 0);//将标准图二值化
    threshold(WaitJudgePhoto, out2, 90, 255, 0);
}

//------------------------------------------------------
//  读入图片
//------------------------------------------------------
void ReadPic()
{
    StandardPhoto = imread( pic02, 0 );//完美标准图 (灰度图)
    WaitJudgePhoto = imread( pic01, 0 );//读入“待判断”图片 (灰度图)
    out3 = imread( pic01, 1 );//再读入一张无修改图
    mask = imread( pic01, 0 );//mask图必须读灰度图
    Wback = imread("WhiteBack02.jpg",1);//同分辨率白底板
    WbackClone = Wback.clone();
    WbackClone1 = Wback.clone();
}

//------------------------------------------------------
//  模板匹配
//------------------------------------------------------
void TempMatch()
{
    out1.copyTo( srcImage );
    int resultImage_cols =  out1.cols - out2.cols + 1;
    int resultImage_rows = out1.rows - out2.rows + 1;
    g_resultImage.create( resultImage_cols, resultImage_rows, CV_32FC1 );
    matchTemplate( out1, out2, g_resultImage, g_nMatchMethod );
    normalize( g_resultImage, g_resultImage, 0, 1, NORM_MINMAX, -1, Mat() );
    double minValue; double maxValue; Point minLocation; Point maxLocation;
    Point matchLocation;
    minMaxLoc( g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation, Mat() );
    if( g_nMatchMethod  == CV_TM_SQDIFF || g_nMatchMethod == CV_TM_SQDIFF_NORMED )
    { matchLocation = minLocation; }
    else
    { matchLocation = maxLocation; }
    rectangle( out1, matchLocation, Point( matchLocation.x + out2.cols , matchLocation.y + out2.rows ), Scalar(0,0,255), 2, 8, 0 );
    rectangle( g_resultImage, matchLocation, Point( matchLocation.x + out2.cols , matchLocation.y + out2.rows ), Scalar(0,0,255), 2, 8, 0 );
    Mat imageROI = out1(Rect(matchLocation.x,matchLocation.y,out2.cols,out2.rows));
    out2.copyTo(imageROI,mask);
    Mat imageROI1 = WbackClone(Rect(matchLocation.x,matchLocation.y,out3.cols,out3.rows));
    out3.copyTo(imageROI1,mask);
    cvtColor(WbackClone,out4,CV_BGR2GRAY);//灰度化
    threshold(out4, WbackClone, 90, 255, 0);//二值化
    Mat img2 = imread(pic01);
    Mat imageROI2 = WbackClone1(Rect(matchLocation.x,matchLocation.y,out3.cols,out3.rows));
    out3.copyTo(imageROI2,mask);
    imshow("【彩色ROI位置待判断图】",WbackClone1);
}

//------------------------------------------------------
//  缺损判断
//------------------------------------------------------
void DefectJudgment()
{
    int rowNumber = img_result.rows;//行数
    int colcolNumber = img_result.cols;//列数
    int colNumber = img_result.cols*img_result.channels();  //列数 x 通道数=每一行元素的个数
    ConditionJudgment=1;
    for(int i = 0; i < rowNumber && ConditionJudgment; i++)  //行循环
    {  
        uchar* data = img_result.ptr<uchar>(i);  //获取第i行的首地址
        for(int j = 0;j < colNumber;j++)   //列循环
        {   
            // ---------【开始处理每个像素】-------------     
            //data[j] = data[j]/div*div + div/2;  
            if(data[j]==255)//如果白色
            {
                //imshow("【效果图】Canny边缘检测", grayImage);
                imwrite("有问题的PCB.jpg",WbackClone1);
                ConditionJudgment=0;
                break;
            }

            // ----------【处理结束】---------------------
        }  //行处理结束
    }  
}

//------------------------------------------------------
//  主程序
//------------------------------------------------------
int main()
{
    //CallCamera();//调用摄像头

    namedWindow("【滤波前二值化效果】", 2);
    namedWindow("【滤波后缺损二值化显示】", 2);
    namedWindow("【对拍摄图缺损位置进行标注】", 2);
    namedWindow("【彩色ROI位置待判断图】", 2);

    ReadPic();//读入图片

    Binarization();//二值化

    TempMatch();//模板匹配

    subtract(srcImage,WbackClone,img_result1);//相减
    subtract(WbackClone,srcImage,img_result2);

    medianBlur(img_result1,img_result,3);//小噪点使用中值滤波  或  erode + dilate方案   3
    medianBlur(img_result2,img_result2,3);
    //GaussianBlur(img_result1,img_result,Size(3,3),0,0);//高斯滤波
    //adaptiveThreshold(img, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY,3,5);

    add(img_result,img_result2,img_result);//两幅互减图叠加

    Mat ele = getStructuringElement(MORPH_RECT, Size(5,5));
    dilate(img_result,img_result,ele);

    Mat threshold_output;
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;

    threshold( img_result, threshold_output, 50, 255, THRESH_BINARY );//二值化

    findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

    vector<vector<Point> > contours_poly( contours.size() );
    vector<Rect> boundRect( contours.size() );

    for( unsigned int i = 0; i < contours.size(); i++ )
    { 
        approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
        boundRect[i] = boundingRect( Mat(contours_poly[i]) );
        //minEnclosingCircle( contours_poly[i], center[i], radius[i] );
    }

    for( int unsigned i = 0; i<contours.size( ); i++ )
    {
        Scalar color = Scalar( 0, 0, 255 );
        //rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );//效果图上绘制矩形
        rectangle( WbackClone1, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );//原图上绘制矩形
    }

    DefectJudgment();//缺损判断

    imshow( "【滤波前二值化效果】", img_result1 );
    imshow( "【滤波后缺损二值化显示】", img_result );
    imshow( "【对拍摄图缺损位置进行标注】", WbackClone1 );

    waitKey(0);
}


效果如下图所示:




【完好无损的PCB】




【有缺损的PCB】(焊盘缺失或缺损)




【处理后未滤波的图片】




【滤波后图像】




【缺损判断标记】(图上红色矩形框)

程序中Wback是读入的一张纯白色底板图片,这个底板分辨率和标准模板图片一致,为了在模板匹配后相减时防止检测图片因拍摄或放PCB板入卡槽时,位置的改变而处理的。

还有太多的东西需要学习,欢迎大神前辈们指教。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: