首页 > 技术文章 > Opencv 改进的外接矩形合并拼接方法

dzzy 2018-07-09 17:03 原文

上一篇中的方法存在的问题是矩形框不够精确,而且效果不能达到要求

这里使用凸包检测的方法,并将原来膨胀系数由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;
}

 

推荐阅读