opencv——识别A4纸

  • Post author:
  • Post category:其他


小编这篇给大家带来的是如何用opencv在视野中识别出A4纸或者是某一个具体的靶标什么的,同样的算法改改可以用来识别其他的形状的物体,先来看看实际的效果:



由于小编是在虚拟机下调用摄像头出现了一些问题,还没有试试连续过程中的识别情况,等摄像头到了,小编会再后续中测试。

在程序开始之前我们先梳理一下解决这个问题的流程:

1、图像预处理(这里考虑到处理速度问题采用的是高斯滤波)

2、边缘检测(本文采用的canny算子)

3、膨胀(尽量使边缘闭合)

4、寻找A4边框(采用的是findContours算法)

5、对findContours识别出来的轮廓进行排除得到我们需要的轮廓,这是最为核心的一步。譬如本文是筛选类似A4纸书本这样的框,采用的方法是先对得到的轮廓进行凸包拟合,然后以四边形和每个夹角大于60作为这样的一个初步筛选。然后从筛选出来的轮廓中选择最大的那个就是我们想要的了。(注:由于本文是一个demo,筛选条件比较简单,读者结合自己的应用常见和识别对象可以利用譬如直方图统计这样的增加筛选成功率)

6、得到满足筛选条件的最大轮廓的四个顶点(方便后面做透视校正,小编会在后面陆续写出分享)

7、绘出我们的轮廓

下面给出具体代码,有以问的欢迎留言交流:

#include

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp> //图像处理头文件

#include <cv.h>

#include <highgui.h>

#include

using namespace cv;

using namespace std;

//根据三个点计算中间那个点的夹角,这里是用三角变换,感兴趣的可以求一下公式的推导 pt1 pt0 pt2

double getAngle(cv::Point pt1, cv::Point pt2, cv::Point pt0)

{


double dx1 = pt1.x – pt0.x;

double dy1 = pt1.y – pt0.y;

double dx2 = pt2.x – pt0.x;

double dy2 = pt2.y – pt0.y;

return (dx1

dx2 + dy1

dy2)/sqrt((dx1

dx1 + dy1

dy1)

(dx2

dx2 + dy2*dy2) + 1e-10);

}

//寻找最大边框

int findLargestSquare(const vector<vectorcv::Point >& squares, vectorcv::Point& biggest_square)

{


if (!squares.size()) return -1;

int max_width = 0;

int max_height = 0;

int max_square_idx = 0;

for (int i = 0; i < squares.size(); i++)

{


cv::Rect rectangle = boundingRect(Mat(squares[i]));

if ((rectangle.width >= max_width) && (rectangle.height >= max_height))

{


max_width = rectangle.width;

max_height = rectangle.height;

max_square_idx = i;

}

}

biggest_square = squares[max_square_idx];

return max_square_idx;

}

//主函数,小编这里反省一下,没有把一些函数封装起来,显得比较乱,小编建议大家养成把处理得函数都封装起来,主函数就不会像小编这样看上去很乱了。

int main(int argc, char *argv[])

{


Mat src,dst,gray,binarization,open,dilate_img,gauss_img,edges_img;

src = imread(“/home/shangbinbin/opencv_linux_test/test3/4.jpg”);

//处理为灰度图

cvtColor(src,gray,CV_BGR2GRAY);

//高斯滤波

GaussianBlur(gray,gauss_img,Size(3,3),2,2);

//边缘检测

Canny(gauss_img,edges_img,50,150,3);

//膨胀

dilate(edges_img, dilate_img, Mat(), cv::Point(-1, -1), 3, 1, 1);

//定义容器类型得变量,不太明白得可以看看C++的容器这一部分知识

vector<vectorcv::Point > contours, squares, hulls;

//寻找出所有闭合的边框

findContours(dilate_img, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);

vectorcv::Point hull, approx;

//筛选边框

for (int i = 0; i < contours.size(); i++)

{


//获得轮廓的凸包

convexHull(contours[i], hull);

//多边形拟合凸包边框(此时的拟合的精度较低)

//arcLength得到轮廓的长度

approxPolyDP(Mat(hull), approx, arcLength(Mat(hull), true)*0.02, true);

//筛选出四边形的各个角度都接近直角的凸四边形

if (approx.size() ==4 && isContourConvex(Mat(approx))&& fabs(contourArea(Mat(approx))) < 1166400)

{


double maxCosine = 0;

for (int j = 2; j < 5; j++)

{


double cosine = fabs(getAngle(approx[j%4], approx[j-2], approx[j-1]));

maxCosine = MAX(maxCosine, cosine);

}

//角度大概72度

if (maxCosine < 0.4)

{


squares.push_back(approx);

hulls.push_back(hull);

}

           }

}

vectorcv::Point largest_square;

//找出外接矩形最大的四边形

int idex = findLargestSquare(squares, largest_square);

//画图

for (int i = 0; i < 4; i++)

{


// circle(src,squares[idex][i],5,Scalar(0,0,255), 5);

if(i<3)

{


line(src, squares[idex][i], squares[idex][i+1], Scalar(0,0,255), 5);

}

else

line(src, squares[idex][3], squares[idex][0], Scalar(0,0,255), 5);

}

//显示出来

namedWindow(“draw”, CV_WINDOW_NORMAL);

imshow(“draw”,src);

waitKey(0);

return 0;

}

关注微信公众号share space,有超多资料和实用教程等你来。



版权声明:本文为weixin_43053387原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。