Opencv 改進的外接矩形合並拼接方法


上一篇中的方法存在的問題是矩形框不夠精確,而且效果不能達到要求

這里使用凸包檢測的方法,並將原來膨脹系數由20縮小到5,達到了更好的效果

效果圖:

效果圖:

代碼:

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;
//設置全局參數
Mat srcImage, srcGray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
Mat thresh_callback(int, void*)
{
    Mat srcTemp = srcImage.clone();
    Mat threMat;
    //輪廓檢測參數
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    //閾值化操作
    threshold(srcGray, threMat, thresh, 255, THRESH_BINARY);
    //輪廓檢測
    findContours(threMat, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
    //凸包及缺陷檢測參數
    vector<vector<Point> > pointHull(contours.size());
    vector<vector<int> > intHull(contours.size());
    vector<vector<Vec4i> > hullDefect(contours.size());
    for (size_t i = 0; i < contours.size(); i++)
    {
        //Point類型凸包檢測
        convexHull(Mat(contours[i]), pointHull[i], false);
        //int 類型凸包檢測
        convexHull(Mat(contours[i]), intHull[i], false);
        //凸包缺陷檢測
        convexityDefects(Mat(contours[i]), intHull[i], hullDefect[i]);
    }
    //繪制凸包及缺陷檢測
    Mat drawing = Mat::zeros(threMat.size(), CV_8UC3);
    for (size_t i = 0; i < contours.size(); i++)
    {
        Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
        drawContours(drawing, contours, i, Scalar(255,255,255), -1, 8, vector<Vec4i>(), 0, Point());
        drawContours(drawing, pointHull, i, Scalar(255,255,255), -1, 8, vector<Vec4i>(), 0, Point());
        //繪制缺陷
        size_t count = contours[i].size();
        if (count < 300)
            continue;
        //設置凸包缺陷迭代器
        vector<Vec4i>::iterator iterDefects = hullDefect[i].begin();
        //遍歷得到4個特征量
        while (iterDefects != hullDefect[i].end())
        {
            Vec4i& v = (*iterDefects);
            //起始位置
            int startidx = v[0];
            Point ptStart(contours[i][startidx]);
            //終止位置
            int endidx = v[1];
            Point ptEnd(contours[i][endidx]);
            //內凸殼最遠的點缺陷
            int faridx = v[2];
            Point ptFar(contours[i][faridx]);
            //凸點之間的最遠點
            int depth = v[3] / 256;
            //繪制相應的線與圓檢測結果
            if (depth > 20 && depth < 80)
            {
                line(drawing, ptStart, ptFar, CV_RGB(255,255,255), 2);
                line(drawing, ptEnd, ptFar, CV_RGB(255,255,255), 2);
            }
            iterDefects++;
        }
    }
    return drawing;
}
Mat change(Mat src)
{
    int cPointR,cPointG,cPointB,cPoint;
    for(int i=0; i<src.rows; i++)
    {
        for(int j=0; j<src.cols; j++)
        {
            cPointB=src.at<Vec3b>(i,j)[0]=src.at<Vec3b>(i,j)[0];
            cPointG=src.at<Vec3b>(i,j)[1]=src.at<Vec3b>(i,j)[1];
            cPointR=src.at<Vec3b>(i,j)[2]=src.at<Vec3b>(i,j)[2];
            if(cPointR>250||cPointG>250||cPointB>250)
            {
                src.at<Vec3b>(i,j)[0]=0;
                src.at<Vec3b>(i,j)[1]=0;
                src.at<Vec3b>(i,j)[2]=0;
            }
            else
            {
                src.at<Vec3b>(i,j)[0]=255;
                src.at<Vec3b>(i,j)[1]=255;
                src.at<Vec3b>(i,j)[2]=255;
            }
            cPointB=src.at<Vec3b>(i,j)[0]=src.at<Vec3b>(i,j)[0];
            cPointG=src.at<Vec3b>(i,j)[1]=src.at<Vec3b>(i,j)[1];
            cPointR=src.at<Vec3b>(i,j)[2]=src.at<Vec3b>(i,j)[2];
        }
    }
    return src;
}
///矩形測距
int Distance(Rect rect1,Rect rect2)
{
    // 用於判斷rect1在rect2的第三象限里 用於反轉X軸用
    bool isInversion;
    // 保存兩個比較的點
    Point point1;
    Point point2;
    // 判斷 rect1 在rect2的上面還是下面 也就是說是在第一、二象限還是在三四象限
    if(rect1.y<rect2.y)
    {
        // 判斷rect1 在rect2的左邊還是右邊 也就是說是在 一象限還是二象限
        isInversion= rect1.x<rect2.x;
        if(isInversion )
        {
            // 取rect1的右上點
            point1 = Point(rect1.x+rect1.width,rect1.y+rect1.height);
            // 取rect2的左下點
            point2 = Point(rect2.x,rect2.y);
        }
        else
        {
            // 取rect1的左上點
            point1 = Point(rect1.x,rect1.y+rect1.height);
            // 取rect2的右下點
            point2 = Point(rect2.x+rect2.width,rect2.y);
        }
    }
    else
    {
        // 判斷rect1 在rect2的左邊還是右邊 也就是說是在 三象限還是四象限
        isInversion = rect1.x>rect2.x;
        if(isInversion)
        {
            // 取rect2的右上點
            point1 = Point(rect2.x+rect2.width,rect2.y+rect2.height);
            // 取rect1的左下點
            point2 = Point(rect1.x,rect1.y);
        }
        else
        {
            // 取rect2的左上點
            point1 = Point(rect2.x,rect2.y+rect2.height);
            // 取rect1的右下點
            point2 = Point(rect1.x+rect1.width,rect1.y);
        }
    }
    // 做向量減法
    Point dPoint = point2 -point1;
    // 如果反轉x軸
    dPoint.x = isInversion? dPoint.x:-dPoint.x;
    // 如果這個向量在第三象限里 那么這兩個矩形相交 返回-1
    if(dPoint.x<0&& dPoint.y<0)
        return -1;
    // 如果x<0 返回y
    if(dPoint.x<0)
        return dPoint.y;
    // 如果y小於0 返回x
    if(dPoint.y<0)
        return dPoint.x;
    // 返回這個向量的長度
    return abs(sqrt((point1.x-point2.x)*(point1.x-point2.x)+(point1.y-point2.y)*(point1.y-point2.y)));
}
int main()
{
    //freopen("stdout.txt","w",stdout);
    ///讀圖
    //srcImage = imread("C:\\Users\\Administrator\\Desktop\\1-gl300c.png",1);
    //srcImage = imread("C:\\Users\\Administrator\\Desktop\\2-P330D.png",1);
    srcImage = imread("C:\\Users\\Administrator\\Desktop\\3-spark.png",1);
    Mat outImage=srcImage;
    if (!srcImage.data)
        return -1;

    ///腐蝕去噪處理
    Mat erosion_dst,temp;
    int erosion_size=5;
    Mat element = getStructuringElement( MORPH_RECT,Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                                         Point( erosion_size, erosion_size ) ); //腐蝕去噪處理參數
    erode( srcImage,erosion_dst, element ); //腐蝕去噪處理
    //imshow( "腐蝕去噪處理", erosion_dst );

    ///像素變換
    Mat change_dst=change(erosion_dst);
    srcImage=erosion_dst;

    ///轉灰度圖
    cvtColor(srcImage, srcGray, CV_BGR2GRAY);
    blur(srcGray, srcGray, Size(3, 3));

    ///凸包檢測
    Mat image=thresh_callback(0, 0);
    //imwrite("C:\\Users\\Administrator\\Desktop\\image.png", image);

    ///轉單通道灰度圖
    Mat imageSource;
    cvtColor(image,imageSource,CV_BGR2GRAY);
    blur(imageSource,image,Size(3,3));
    threshold(image,image,0,255,CV_THRESH_OTSU);

    ///尋找最外層輪廓
    vector<vector<Point> > contours0;
    vector<Vec4i> hierarchy0;
    findContours(image,contours0,hierarchy0,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point());
    cout<<contours0.size()<<endl;

    ///連接矩形區域
    for(int i=0; i<contours0.size(); i++)
    {
        RotatedRect rect_i=minAreaRect(contours0[i]);
        Point2f P_i[4];
        rect_i.points(P_i);
        int lable=0;
        for(int j=i+1; j<contours0.size(); j++)
        {
            RotatedRect rect_j=minAreaRect(contours0[j]);
            Point2f P_j[4];
            rect_j.points(P_j);
            double recArea_i=contourArea(contours0[i]);
            double recArea_j=contourArea(contours0[j]);
            //cout<<"兩矩形坐標:("<<P_i[1].x<<","<<P_i[1].y<<")  ("<<P_i[3].x<<","<<P_i[3].y<<") --> ("<<P_j[1].x<<","<<P_j[1].y<<")  ("<<P_j[3].x<<","<<P_j[3].y<<")  ";
            Rect r_j = rect_j.boundingRect();
            Rect r_i = rect_i.boundingRect();
            //cout<<"兩矩形面積:"<<recArea_i<<" -> "<<recArea_j<<"     距離:"<<Distance(r_i,r_j)<<"     ";
            if(Distance(r_i,r_j)<=100&&(recArea_i<2500&&recArea_j<2500))
            {
                lable=1;
                int minx=min(P_i[2].x,P_j[2].x);
                int maxx=max(P_i[3].x,P_j[3].x);
                int miny=min(P_i[2].y,P_j[2].y);
                int maxy=max(P_i[0].y,P_j[0].y);
                rectangle(image,Point(minx,miny),Point(maxx,maxy),Scalar(255,255,255),-1,1);//畫實心矩形
                //cout<<"yes";
            }
            //cout<<endl;
        }
        //cout<<"---------------------------------------------------"<<endl;
        if(lable==0&&contourArea(contours0[i])<110)
            rectangle(image,Point(P_i[1].x,P_i[1].y),Point(P_i[3].x,P_i[3].y),Scalar(0,0,0),-1,1);
        else
            rectangle(image,Point(P_i[1].x,P_i[1].y),Point(P_i[3].x,P_i[3].y),Scalar(255,255,255),-1,1);
    }
    //imwrite("C:\\Users\\Administrator\\Desktop\\image2.png", image);

    ///繪制輪廓
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    findContours(image,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point());
    Mat imageContours=Mat::zeros(image.size(),CV_8UC1);    //最小外接矩形畫布
    for(int i=0; i<contours.size(); i++)
    {
        ///繪制輪廓
        //drawContours(imageContours,contours,i,Scalar(0,0,0),1,8,hierarchy);
        ///繪制輪廓的最小外結矩形
        RotatedRect rect=minAreaRect(contours[i]);
        Point2f P[4];
        rect.points(P);

        int minx=min(P[1].x,P[2].x)+3;
        int maxx=max(P[3].x,P[0].x)-3;
        int miny=min(P[2].y,P[3].y)+3;
        int maxy=max(P[1].y,P[0].y)-3;
        rectangle(outImage,Point(minx,miny),Point(maxx,maxy),Scalar(0,0,0),2,1);//二值圖繪線

    }
    imwrite("C:\\Users\\Administrator\\Desktop\\image.png", outImage);

    waitKey(0);
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM