opencv(23)---轮廓特征属性及应用之最小外接矩形

轮廓最小外接矩形—minAreaRect()

函数原型

RotatedRect minAreaRect( InputArray points );

points: 输入的二维点集, 可以填Mat类型或std::vector 
返回值: RotatedRect类矩形对象, 外接旋转矩形主要成员有center、size、 angle、points

注意点

在opencv中,坐标的原点在左上角,与x轴平行的方向为角度为0,逆时针旋转角度为负,顺时针旋转角度为正。而RotatedRect类是以矩形的哪一条边与x轴的夹角作为角度的呢?angle 是水平轴(x轴)逆时针旋转,与碰到的第一个边的夹角,而opencv默认把这个边的边长作为width,angle的取值范围必然是负的

这里写图片描述

代码

   Mat srcImg = imread("D:\\1\\10.png");
    imshow("src", srcImg);
    Mat dstImg = srcImg.clone();

    cvtColor(srcImg, srcImg, CV_BGR2GRAY);
    threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY); //二值化
    imshow("threshold", srcImg);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarcy;

    findContours(srcImg, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    cout<<"num="<<contours.size()<<endl;
    vector<Rect> boundRect(contours.size());  //定义外接矩形集合
    vector<RotatedRect> box(contours.size()); //定义最小外接矩形集合
    Point2f rect[4];
    for(int i=0; i<contours.size(); i++)
    {
        box[i] = minAreaRect(Mat(contours[i]));  //计算每个轮廓最小外接矩形
        boundRect[i] = boundingRect(Mat(contours[i]));

        circle(dstImg, Point(box[i].center.x, box[i].center.y), 5, Scalar(0, 255, 0), -1, 8);  //绘制最小外接矩形的中心点
        box[i].points(rect);  //把最小外接矩形四个端点复制给rect数组

        rectangle(dstImg, Point(boundRect[i].x, boundRect[i].y), Point(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height), Scalar(0, 255, 0), 2, 8);
        for(int j=0; j<4; j++)
        {
            line(dstImg, rect[j], rect[(j+1)%4], Scalar(0, 0, 255), 2, 8);  //绘制最小外接矩形每条边
        }

    }
    imshow("dst", dstImg);

    waitKey(0);

运行结果

这里写图片描述

知识点分析

绘制最小外接矩形的轮廓

for(int j=0; j<4; j++)
   {
       line(dstImg, rect[j], rect[(j+1)%4], Scalar(0, 0, 255), 2, 8);  //绘制最小外接矩形每条边
   }

应用一—粗略计算长宽(像素)

代码

 Mat srcImg = imread("D:\\1\\phone.jpg");
    imshow("src", srcImg);
    Mat dstImg = srcImg.clone();
    //进行了两次滤波
    medianBlur(srcImg, srcImg, 5);
    GaussianBlur(srcImg, srcImg, Size(3, 3), 0, 0);

    cvtColor(srcImg, srcImg, CV_BGR2GRAY);
    threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY_INV); 
    imshow("threshold", srcImg);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarcy;

    findContours(srcImg, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    cout<<"num="<<contours.size()<<endl;
    vector<Rect> boundRect(contours.size());
    vector<RotatedRect> box(contours.size());
    Point2f rect[4];
    for(int i=0; i<contours.size(); i++)
    {
        box[i] = minAreaRect(Mat(contours[i]));
        boundRect[i] = boundingRect(Mat(contours[i]));
        cout<<box[i].angle<<endl;
        cout<<box[i].center<<endl;
        cout<<box[i].size.width<<endl;
        cout<<box[i].size.height<<endl;
        circle(dstImg, Point(box[i].center.x, box[i].center.y), 5, Scalar(0, 255, 0), -1, 8);

        char width[20], height[20];

        sprintf(width, "width=%0.2f", box[i].size.width);
        sprintf(height, "height=%0.2f", box[i].size.height);

        box[i].points(rect);

        rectangle(dstImg, Point(boundRect[i].x, boundRect[i].y), Point(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height), Scalar(0, 255, 0), 2, 8);

        for(int j=0; j<4; j++)
        {
            line(dstImg, rect[j], rect[(j+1)%4], Scalar(0, 0, 255), 2, 8);
        }

        putText(dstImg, width, Point(235, 260), CV_FONT_HERSHEY_COMPLEX_SMALL, 0.85, Scalar(0, 255, 0), 2, 8);
        putText(dstImg, height, Point(235, 285), CV_FONT_HERSHEY_COMPLEX_SMALL, 0.85, Scalar(0, 255, 0), 2, 8);

    }
    imshow("dst", dstImg);

    waitKey(0);

运行结果

这里写图片描述

应用二—-旋转矫正

代码

Mat srcImg = imread("D:\\1\\qrcode.jpg");
imshow("src", srcImg);
Mat dstImg = srcImg.clone();
//高斯滤波
GaussianBlur(srcImg, srcImg, Size(3, 3), 0, 0);
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
//边缘检测
Canny(srcImg, srcImg, 100, 200);
//threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY_INV); //二值化
//adaptiveThreshold(srcImg, srcImg, 255, ADAPTIVE_THRESH_GAUSSIAN_C,  CV_THRESH_BINARY_INV, 15, 3);
imshow("threshold", srcImg);
Mat element = getStructuringElement(MORPH_RECT, Size(11, 11), Point(-1, -1)); //定义结构元素
dilate(srcImg, srcImg, element); //膨胀,将二维码区域连接起来
imshow("dilate", srcImg);
erode(srcImg, srcImg, element);
imshow("erode", srcImg);

vector<vector<Point>> contours;
vector<Vec4i> hierarcy;

findContours(srcImg, contours, hierarcy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
cout<<"num="<<contours.size()<<endl;
vector<Rect> boundRect(contours.size());
vector<RotatedRect> box(contours.size());
Point2f rect[4];
for(int i=0; i<contours.size(); i++)
{
   box[i] = minAreaRect(Mat(contours[i]));
   boundRect[i] = boundingRect(Mat(contours[i]));
   //利用长宽来选择符合条件的轮廓
   if(box[i].size.width < 100 || box[i].size.height<100)
       continue;
   circle(dstImg, Point(box[i].center.x, box[i].center.y), 5, Scalar(0, 255, 0), -1, 8);
   cout<<"num="<<box[i].angle<<endl;
   angle = box[i].angle;

   char width[20], height[20];
   sprintf(width, "width=%0.2f", box[i].size.width);
   sprintf(height, "height=%0.2f", box[i].size.height);

   box[i].points(rect);
   rectangle(dstImg, Point(boundRect[i].x, boundRect[i].y), Point(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height), Scalar(0, 255, 0), 2, 8);

   for(int j=0; j<4; j++)
   {
       line(dstImg, rect[j], rect[(j+1)%4], Scalar(0, 0, 255), 2, 8);
   }

   putText(dstImg, width, Point(235, 260), CV_FONT_HERSHEY_COMPLEX_SMALL, 0.85, Scalar(0, 255, 0), 2, 8);
   putText(dstImg, height, Point(235, 285), CV_FONT_HERSHEY_COMPLEX_SMALL, 0.85, Scalar(0, 255, 0), 2, 8);
   imshow("temp", dstImg);
   //经验值
   if (0< abs(angle) && abs(angle)<=45)  //逆时针
       angle = angle;
   else if (45< abs(angle) && abs(angle)<90) //顺时针
       angle = 90 -  abs(angle);

   Point2f center = box[i].center;  //定义旋转中心坐标
   double angle0 = angle;
   double scale = 1;
   Mat roateM;
   roateM = getRotationMatrix2D(center, angle0, scale);  //获得旋转矩阵
   warpAffine(dstImg, dstImg, roateM, dstImg.size()); //利用放射变换进行旋转


}
imshow("dst", dstImg);

waitKey(0);

运行结果

原图

这里写图片描述

阈值化图

这里写图片描述

膨胀图

这里写图片描述

腐蚀图

这里写图片描述

结果图

这里写图片描述

旋转图

这里写图片描述

知识点讲解

1.不同灰度处理方式处理后的灰度图

Canny(srcImg, srcImg, 100, 200);
threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY_INV); //二值化
adaptiveThreshold(srcImg, srcImg, 255, ADAPTIVE_THRESH_GAUSSIAN_C,  CV_THRESH_BINARY_INV, 15, 3);

canny

这里写图片描述

threshold

这里写图片描述

adaptiveThreshold

这里写图片描述

2.腐蚀膨胀

Mat element = getStructuringElement(MORPH_RECT, Size(11, 11), Point(-1, -1)); //定义结构元素
dilate(srcImg, srcImg, element); //膨胀,将二维码区域连接起来
imshow("dilate", srcImg);
erode(srcImg, srcImg, element);
imshow("erode", srcImg);

先进行膨胀,使所有的二维码连接成一个整体 
在进行腐蚀,使得二维码大小不进行改变

3.筛选

if(box[i].size.width < 100 || box[i].size.height<100)
     continue;

4. 旋转角度

//经验值
if (0< abs(angle) && abs(angle)<=45)  //逆时针
  angle = angle;
else if (45< abs(angle) && abs(angle)<90) //顺时针
  angle = 90 -  abs(angle);

5.对二维码进行旋转

Point2f center = box[i].center;  //定义旋转中心坐标
double angle0 = angle;
double scale = 1;
Mat roateM;
roateM = getRotationMatrix2D(center, angle0, scale);  //获得旋转矩阵
warpAffine(dstImg, dstImg, roateM, dstImg.size()); //利用放射变换进行旋转
  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值