工業視覺檢測項目的算法實現

一、基本情況
在較好的成像質量下,可以通過ROI直接完成初略定位;而後通過手工定義特徵提取信息;完成量化和評價。

 

 

這次項目能夠實現的首要原因:
一是因爲在已經佈置了成像系統,能夠採集獲得高質量圖片;
二是在圖像處理領域的積累,讓我能夠解決一些問題。
這是我第一次較爲系統地解決“視覺檢測”問題,我認爲解決了一些問題,有很多收穫。
二、技術細節
0、ROI定位,使用MarkMan+NotePade,非常高效;

 

 

 

 

1、左側大螺絲
通過HoughCircles能夠非常準確地識別出螺絲孔洞和異常情況。這個思路的來源是因爲在做鋼管識別的過程中,對幾個找圓算法進行了分析研究。
     
       screwMat[i] = src(cv::Rect(screwLocation[i], screwSize[i]));
            vector<KeyPoint> keypoints;
            detector->detect(screwMat[i], keypoints);
            if (keypoints.size() == 0) {
                Mat gray; Mat bin;
                vector<Vec3f> vec3f_method_hough;
                cvtColor(screwMat[i], gray, COLOR_BGR2GRAY);
                threshold(gray, bin, 100, 255, THRESH_OTSU);
                HoughCircles(gray, vec3f_method_hough, HOUGH_GRADIENT, 2, p_radius * 2, 100, 33, p_radius - 2, p_radius + 2);
                if (vec3f_method_hough.size() >= 1)
                    putText(draw, "Y1", screwLocation[i], FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(255, 0, 0), 7);
                else
                {
                    //當探測到 ERROR的時候推出循環
                    putText(draw, "E1", screwLocation[i], FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 255, 0), 7);
                    bError = true;
                }        
                vec3f_method_hough.clear();

 

2、左側大螺絲防護罩
採用的是閾值、投影的方法進行解決。這個思路的來源,應該是答題卡。
        
    //識別螺絲防護罩
            coverMat[i] = src(Rect(coverLocation[i], coverSize[i]));
            cvtColor(coverMat[i], tmpCoverMat[i], COLOR_BGR2GRAY);
            threshold(tmpCoverMat[i], tmpCoverMat[i], 100, 255, THRESH_BINARY);
            //取中間一列的投影
            Mat tmp = tmpCoverMat[i].col(tmpCoverMat[i].cols / 2);
            int istart = 0; int  iend = tmp.rows - 1;
            //開頭
            for (int irow = 0; irow < tmp.rows - 3; irow++)
            {
                if (0 == tmp.at<uchar>(irow, 0) && tmp.at<uchar>(irow + 1, 0) > 0 && tmp.at<uchar>(irow + 2, 0) > 0 && tmp.at<uchar>(irow + 3, 0) > 0)
                {
                    istart = irow;
                    break;
                }
            }
            //結尾
            for (int irow = tmp.rows - 1; irow > 4; irow--)
            {
                if (0 == tmp.at<uchar>(irow, 0) && tmp.at<uchar>(irow - 1, 0) > 0 && tmp.at<uchar>(irow - 2, 0) > 0 && tmp.at<uchar>(irow - 3, 0) > 0)
                {
                    iend = irow;
                    break;
                }
            }
            if (istart <= tmp.rows * 0.1 || iend >= tmp.rows*0.92)//注意這裏的0.1和0.9可能是需要設置的
            {
                putText(draw, "N2", cv::Point(coverLocation[i].x + 150, coverLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7);
            }

 

 

3、白色隔弧板
閾值、輪廓(面積)分析的方法進行。這個思路的來源,也應該是鋼管識別。
//在正確的基礎上,進一步識別白色隔弧板
                wbMat[i] = src(Rect(whiteBoardLocation[i], whiteBoardSize[i]));
                cvtColor(wbMat[i], wbMat[i], COLOR_BGR2GRAY);
                threshold(wbMat[i], wbTmpMat[i], 50, 255, THRESH_OTSU);
                //1、去沾粘;
                cv::rectangle(wbTmpMat[i], cv::Rect(0, 0, wbTmpMat[i].cols, wbTmpMat[i].rows), cv::Scalar(0),3);
                Mat element = getStructuringElement(MORPH_ELLIPSE, Size(7, 3));
                morphologyEx(wbTmpMat[i], wbTmpMat[i], cv::MORPH_OPEN, element);
                //2、分輪廓
                cv::Mat binDraw;
                int iRight = 0;
                vector<VP> binVP =  connection2(wbTmpMat[i], binDraw);
                //3、以面積等特徵篩選
                for (int indexBindVP=0;indexBindVP<binVP.size();indexBindVP++)
                {
                    double dArea =  cv::contourArea(binVP[indexBindVP]);
                    if (dArea >= 250 && dArea <= 480)//可能區間
                        iRight++;
                    //printf("%d__%f\n", indexBindVP, (float)dArea);
                }

 

在實現的過程中,通過系統方式有效去粘連,這個也是來源於之前項目

 

 

4、手柄
採用的是閾值、投影的方法進行解決。
Mat handleMat = src(Rect(946, 884, 541, 378));
        Mat handleTmp;
        cvtColor(handleMat, handleTmp, COLOR_BGR2GRAY);
        threshold(handleTmp, handleTmp, 100, 255, THRESH_OTSU);
        cv::dilate(handleTmp, handleTmp, Mat());
        Mat handleTmpRow = handleTmp.row(handleTmp.rows / 2);
        int iHandleStart = 0; int  iHandleEnd = handleTmpRow.cols - 1;
        int iHandleUp = 0;
        //開頭
        for (int i = 0; i < handleTmpRow.cols - 2; i++)
        {
            if (0 == handleTmpRow.at<uchar>(0, i) && handleTmpRow.at<uchar>(0, i + 1) > 0 && handleTmpRow.at<uchar>(0, i + 2) > 0)
                iHandleUp++;
        }
        if (iHandleUp > 7)//超級參數
        {
            putText(draw, "N4", cv::Point(946 + 150, 884), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7);
        }
        else {
            putText(draw, "Y4", cv::Point(946 + 150, 884), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(255, 0, 0), 7);
        }

 

5、隔膜
閾值、輪廓(外界矩形長寬比)分析的方法進行。
//識別隔膜
            filmMat[i] = src(Rect(filmLocation[i], filmSize[i]));
            cv::rectangle(draw, Rect(filmLocation[i], filmSize[i]), cv::Scalar(0, 0, 255));
            cv::cvtColor(filmMat[i], filmMat[i], COLOR_BGR2GRAY);
            cv::threshold(filmMat[i], tmpFilmMat[i], 100, 255, cv::THRESH_OTSU);
            cv::dilate(tmpFilmMat[i], tmpFilmMat[i], cv::Mat());
            //1、去粘連
            cv::rectangle(tmpFilmMat[i], cv::Rect(0, 0, tmpFilmMat[i].cols, tmpFilmMat[i].rows), Scalar(0));
            //2、找最大輪廓
            vector<cv::Point> biggestContour = FindBigestContour(tmpFilmMat[i]);
            Rect boundRect = boundingRect(Mat(biggestContour)); //獲得輪廓最小外接矩形
            cv::rectangle(draw,cv::Rect(filmLocation[i].x+boundRect.x, filmLocation[i].y+boundRect.y, boundRect.width, boundRect.height), cv::Scalar(0, 255, 0));
            //3、進行判斷
            float fScale = (float)boundRect.width / (float)filmSize[i].width;
            if (fScale >= 0.65 && boundRect.x>=8)
            {
                putText(draw, "Y5", cv::Point(filmLocation[i].x + 150, filmLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(255, 0, 0), 7);
            }
            else
            {
                putText(draw, "N5", cv::Point(filmLocation[i].x + 150, filmLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7);
            }
6、小螺絲
採用的是閾值、投影的方法進行解決。所不同的這裏是豎向投影。
        
     littleScrewMat[i] = src(Rect(LittleScrewLocation[i], LittleScrewSize[i]));
            cvtColor(littleScrewMat[i], tmpLittleMat[i], COLOR_BGR2GRAY);
            threshold(tmpLittleMat[i], tmpLittleMat[i], 100, 255, THRESH_BINARY);
            //取一列的投影
            Mat tmp = tmpLittleMat[i].col(tmpLittleMat[i].cols * 0.7);
            int iLittleScrewMat = 0;
            for (int i = 0; i < tmp.rows - 2; i++)
            {
                if (0 == tmp.at<uchar>(i, 0) && tmp.at<uchar>(i + 1, 0) > 0 && tmp.at<uchar>(i + 2, 0) > 0)
                    iLittleScrewMat++;
            }
            if (iLittleScrewMat <= 0)//超級參數
            {
                putText(draw, "N6", cv::Point(LittleScrewLocation[i].x + 150, LittleScrewLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7);
            }
            else {
               ……
            }
7、小螺絲紅漆
採用的是顏色(紅色)分析的方法,然後做了一些面積比對。這個思路的來源是“你的名字”
            
     littleScrewMat[i] = src(Rect(LittleScrewLocation[i], LittleScrewSize[i]));
            cvtColor(littleScrewMat[i], tmpLittleMat[i], COLOR_BGR2GRAY);
            threshold(tmpLittleMat[i], tmpLittleMat[i], 100, 255, THRESH_BINARY);
            //取一列的投影
            Mat tmp = tmpLittleMat[i].col(tmpLittleMat[i].cols * 0.7);
            int iLittleScrewMat = 0;
            for (int i = 0; i < tmp.rows - 2; i++)
            {
                if (0 == tmp.at<uchar>(i, 0) && tmp.at<uchar>(i + 1, 0) > 0 && tmp.at<uchar>(i + 2, 0) > 0)
                    iLittleScrewMat++;
            }
            if (iLittleScrewMat <= 0)//超級參數
            {
                putText(draw, "N6", cv::Point(LittleScrewLocation[i].x + 150, LittleScrewLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7);
            }
            else {
               ……
            }

 

 

 

8、黑色隔弧板
採用的是閾值、投影的方法進行解決。
bbMat[i] = src(Rect(blackBoardLocation[i], blackBoardSize[i]));
            cvtColor(bbMat[i], bbTmpMat[i], COLOR_BGR2GRAY);
            threshold(bbTmpMat[i], bbTmpMat[i], 100, 255, cv::THRESH_OTSU);
            bitwise_not(bbTmpMat[i], bbTmpMat[i]);
            //1、投影分析
            Mat tmp = bbTmpMat[i].row(bbTmpMat[i].rows/2);
            int ibbMat = 0;
            for (int i = 0; i < tmp.cols - 2; i++)
            {
                if (0 == tmp.at<uchar>(0,i) && tmp.at<uchar>(0,i + 1) > 0 && tmp.at<uchar>(0,i + 2) > 0)
                    ibbMat++;
            }
9、右側大螺絲 
同問題1。
10、滅弧室板
採用的是閾值、輪廓(數量)分析的方法解決,再輔助投影分析。
cutMat[icutMat] = src(Rect(cutLocation[icutMat], cutSize[icutMat]));
            cvtColor(cutMat[icutMat], cutTmpMat[icutMat], COLOR_BGR2GRAY);
            threshold(cutTmpMat[icutMat], cutTmpMat[icutMat], 100, 255, cv::THRESH_OTSU);
            bitwise_not(cutTmpMat[icutMat], cutTmpMat[icutMat]);
            vector<VP> vpTmpMat= connection2(cutTmpMat[icutMat]);
            //printf("vp size is%d\n", vpTmpMat.size());
            //補充一個投影
            tmpCutRow[icutMat] = cutTmpMat[icutMat].row(cutTmpMat[icutMat].rows * 0.7);
            int iCut = 0;
            for (int icutTmpMat = 0; icutTmpMat < tmpCutRow[icutMat].cols - 1; icutTmpMat++)
            {
                if ( tmpCutRow[icutMat].at<uchar>(0, icutTmpMat)>0 && 0 == tmpCutRow[icutMat].at<uchar>(0, icutTmpMat + 1) )
                    iCut++;
            }
            if (vpTmpMat.size() < 11 && iCut < 3)
            {
                putText(draw, "N10", cv::Point(cutLocation[icutMat].x + 150, cutLocation[icutMat].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7);
            }
            else
            {
                putText(draw, "Y10", cv::Point(cutLocation[icutMat].x + 150, cutLocation[icutMat].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(255, 0, 0), 7);
            }

以上是檢測類項目中的常用方法

在這種“量化”的項目中,越是簡單的算法越能夠得出穩定有效的結論;


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