基於OpenCV的PCB缺損判斷研究

本人模式識別小碩一枚,目前帝都某校研一在讀。寒假自己用opencv做了一個對PCB板的好壞的檢測,拿出來和大家一起學習討論,這篇博文也是我在CSDN上發表的第一篇文章,歡迎各路大神指導。

軟件流程圖如下

基本思想是通過定焦的工業攝像頭,對放置於卡槽中的PCB進行拍攝並取ROI,與標準的PCB圖片進行模板匹配,兩者二值化後相減並中值濾波,在缺損處用紅色矩形標出,最後只命名輸出缺損PCB圖片。

爲了給大家更好的演示,我將程序改爲直接讀取圖片

程序如下:
(可能有些雜亂,本人水平還需提高)

/********************************************************************************/
    /*
     * 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】
有缺損的PCB
【有缺損的PCB】(焊盤缺失或缺損)
處理後未濾波圖片
【處理後未濾波的圖片】
濾波後圖像
【濾波後圖像】
缺損判斷標記
【缺損判斷標記】(圖上紅色矩形框)

程序中Wback是讀入的一張純白色底板圖片,這個底板分辨率和標準模板圖片一致,爲了在模板匹配後相減時防止檢測圖片因拍攝或放PCB板入卡槽時,位置的改變而處理的。

還有太多的東西需要學習,歡迎大神前輩們指教。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章