opencv——识别A4纸

小编这篇给大家带来的是如何用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 (dx1dx2 + dy1dy2)/sqrt((dx1dx1 + dy1dy1)(dx2dx2 + 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,有超多资料和实用教程等你来。

猜你喜欢

转载自blog.csdn.net/weixin_43053387/article/details/84927889