.利用opencv来识别图片中的矩形
其中遇到的问题主要是识别轮廓时矩形内部的形状导致轮廓不闭合。
1. 对输入灰度图片进行高斯滤波
2. 做灰度直方图,提取阈值,做二值化处理
3. 提取图片轮廓
4. 识别图片中的矩形
5. 提取图片中的矩形
(1)approxPolyDP 多边形逼近
approxPolyDP 主要功能是把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合
(2)boundingRect 最小外接矩形
(3) minEnclosingCircle 最小外接圆形
(4)arcLength 计算周长
(5)contourArea 计算面积
(6)FitEllipse 最小外接椭圆
(7)cvFindDominantPoints 寻找关键点
(8)轮廓匹配
有10个图案,包括三角形,方形和圆。你把这三种图案识别出来,每一种图形画上不同颜色的轮廓,并提取出每个轮廓的重心坐标。
0.通过摄像头或是加载图片的方式获取到源图;
1.调用erode , dilate函数进行腐蚀膨胀,去掉图片上面黑色字体的影响(注意内核元素的定义)!
2.调节HSV的值,通过morphologyEx函数连接一些连通域;
3.通过第2步的操作得到只保留与目标形状相近部分的图片;
4.通过cvFindContours函数检测所有轮廓;
5.通过GetAreaMaxContour函数找到最大的轮廓(加上轮廓面积条件做判断,优化识别效果);
6.如果是圆,通过cvDrawContours函数画出轮廓;如果是矩形或是三角形,通过cvApproxPoly函数进行多边形逼近;
7.如果是圆的话,用handlecicle函数进行识别,参数根据实际图片进行调节。矩形与三角形的判断方法可参考网上的
凸包检测是给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它是能包含点集中的所有点的。理解物体形状或轮廓的一种比较有用的方法便是计算一个物体的凸包,然后计算其凸缺陷。
OpenCV中提供了函数convesHull()用于对物体轮廓凸包进行检测,对形状凸包缺陷分析时使用convexityDefects()函数,每个缺陷区包含四个特征量:起始点、结束点、距离和最远点。
下面介绍这两个函数:
void convexHull(InputArray points, OutputArray hull, bool clockwise=false, bool returnPoints=true )
points – Input 2D point set, stored in std::vector or Mat.
hull – Output convex hull. It is either an integer vector of indices or vector of points. In the first case, the hull elements are 0-based indices of the convex hull points in the original array (since the set of convex hull points is a subset of the original point set). In the second case, hull elements aree the convex hull points themselves.
clockwise – Orientation flag. If it is true, the output convex hull is oriented clockwise. Otherwise, it is oriented counter-clockwise. The usual screen coordinate system is assumed so that the origin is at the top-left corner, x axis is oriented to the right, and y axis is oriented downwards.
returnPoints – Operation flag. In case of a matrix, when the flag is true, the function returns convex hull points. Otherwise, it returns indices of the convex hull points. When the output array is std::vector, the flag is ignored, and the output depends on the type of the vector: std::vector implies returnPoints=true, std::vector implies returnPoints=false.
ConvexityDefects(const CvArr* contour, const CvArr* convexhull, CvMemStorage* storage=NULL )
contour – Input contour.
convexhull – Convex hull obtained using ConvexHull2() that should contain pointers or indices to the contour points, not the hull points themselves (the returnPoints parameter in ConvexHull2() should be zero).
storage – Container for the output sequence of convexity defects. If it is NULL, the contour or hull (in that order) storage is used.
convexhull可以得到vector
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
//设置全局参数
Mat srcImage, srcGray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
void thresh_callback(int, void*)
{
Mat srcTemp = srcImage.clone();
Mat threMat;
//轮廓检测参数
vector > contours;
vector hierarchy;
//阈值化操作
threshold(srcGray, threMat, thresh, 255, THRESH_BINARY);
//轮廓检测
findContours(threMat, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
//凸包及缺陷检测参数
vector > pointHull(contours.size());
vector > intHull(contours.size());
vector > 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_8UC1);
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, color, 1, 8, vector(), 0, Point());
drawContours(drawing, pointHull, i, color, 1, 8, vector(), 0, Point());
//绘制缺陷
size_t count = contours[i].size();
if (count < 300)
continue;
//设置凸包缺陷迭代器
vector::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(0, 255, 0), 2);
line(drawing, ptEnd, ptFar, CV_RGB(0, 255, 0), 2);
circle(drawing, ptStart, 4, Scalar(255, 0, 100), 2);
circle(drawing, ptEnd, 4, Scalar(255, 0, 100), 2);
circle(drawing, ptFar, 4, Scalar(100, 0, 255), 2);
}
iterDefects++;
}
}
imshow("result", drawing);
}
int main()
{
Mat srcImage = imread("D:\\3.jpg");
if (!srcImage.data)
return -1;
cvtColor(srcImage, srcGray, CV_BGR2GRAY);
blur(srcGray, srcGray, Size(3, 3));
char* source_window = "Source";
namedWindow(source_window, CV_WINDOW_AUTOSIZE);
imshow(source_window, srcImage);
createTrackbar("Thewshold:", "Source", &thresh, max_thresh, thresh_callback);
thresh_callback(0, 0);
waitKey(0);
return 0;
}
// 操作系统: Windows 10 64
// IDE版本:Visual Studio 2017
// OpenCV版本: 3.4
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
using namespace cv;
using namespace std;
//找轮廓
void findcontours()
{
vector > g_vContours;
vector g_vHierarchy;
Mat srcimg = imread("1.jpg", 1);
//Rect roi1(424, 315, 2000, 1400);
//srcimg = srcimg(roi1);
cvtColor(srcimg, srcimg, COLOR_BGR2GRAY);
medianBlur(srcimg, srcimg, 5);
threshold(srcimg, srcimg, 100, 255, CV_THRESH_BINARY_INV);
Canny(srcimg, srcimg, 100, 255, 3);
findContours(srcimg, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
vector mu(g_vContours.size()); //计算矩
for (unsigned int i = 0; i < g_vContours.size(); i++)
{
mu[i] = moments(g_vContours[i], false);
}
vector mc(g_vContours.size()); //计算中心矩
for (unsigned int i = 0; i < g_vContours.size(); i++)
{
mc[i] = Point2f(static_cast(mu[i].m10 / mu[i].m00), static_cast(mu[i].m01 / mu[i].m00));
}
Mat drawing = Mat::zeros(srcimg.size(), CV_8UC3); //绘制轮廓
for (unsigned int i = 0; i< g_vContours.size(); i++)
{
Scalar color = Scalar(255, 0, 0);
drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point()); //绘制外层和内层轮廓
circle(drawing, mc[i], 4, color, -1, 8, 0);
}
printf("\t 输出内容: 面积和轮廓长度\n"); //输出内容
for (unsigned int i = 0; i< g_vContours.size(); i++)
{
printf(" 通过m00计算出轮廓[%d]的面积: (M_00) = %.2f \n计算出的面积=%.2f , 长度: %.2f \n", i, mu[i].m00, contourArea(g_vContours[i]), arcLength(g_vContours[i], true));
Scalar color2 = Scalar(255, 255, 0);
drawContours(drawing, g_vContours, i, color2, 2, 8, g_vHierarchy, 0, Point());
circle(drawing, mc[i], 4, color2, -1, 8, 0);
}
}
void main()
{
findcontours();
waitKey(0);
system("pause");
}
读取灰度图,视频图,并灰度化
Mat srcImg, tempImg;
srcImg = imread("test_img/ocr_src.jpg");
Mat gray;
cvtColor(srcImg, gray, CV_RGB2GRAY);
中值滤波,均值滤波,高斯滤波,二值化阈值分割,模板匹配,形态学操作等
//7.滤波操作
int main()
{
Mat img = imread("dog.jpg");
imshow("原始图", img);
Mat out;
//方框滤波
boxFilter(img, out, -1, Size(5, 5));//-1指原图深度
imshow("方框滤波", out);
Mat out2;
blur(img, out2,Size(5, 5));//-1指原图深度
imshow("均值滤波", out2);
Mat out3;
GaussianBlur(img, out3, Size(3, 3), 0, 0);
imshow("高斯滤波", out3);
Mat out4;
medianBlur(img, out4, 7);//第三个参数表示孔径的线性尺寸,它的值必须是大于1的奇数
imshow("中值滤波", out4);
Mat out5;
bilateralFilter(img, out5, 25, 25 * 2, 25 / 2);
imshow("双边滤波", out5);
waitKey(0);
}
腐蚀和膨胀是最基本的形态学运算。
腐蚀和膨胀是针对白色部分(高亮部分)而言的。
膨胀就是对图像高亮部分进行“领域扩张”,效果图拥有比原图更大的高亮区域;腐蚀是原图中的高亮区域被蚕食,效果图拥有比原图更小的高亮区域。
膨胀
膨胀就是求局部最大值的操作,从图像直观看来,就是将图像光亮部分放大,黑暗部分缩小。
#include
#include
using namespace std;
using namespace cv;
//膨胀
int main()
{
Mat img = imread("lol1.jpg");
namedWindow("原始图", WINDOW_NORMAL);
imshow("原始图", img);
Mat out;
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
//膨胀操作
dilate(img, out, element);
namedWindow("膨胀操作", WINDOW_NORMAL);
imshow("膨胀操作", out);
waitKey(0);
}
图像原来光亮的部分被放大了,黑暗的部分被缩小了。
腐蚀
#include
#include
using namespace std;
using namespace cv;
//腐蚀
int main()
{
Mat img = imread("lol1.jpg");
namedWindow("原始图", WINDOW_NORMAL);
imshow("原始图", img);
Mat out;
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
//腐蚀操作
erode(img, out, element);
namedWindow("腐蚀操作", WINDOW_NORMAL);
imshow("腐蚀操作", out);
waitKey(0);
}
开运算:先腐蚀再膨胀,用来消除小物体
闭运算:先膨胀再腐蚀,用于排除小型黑洞
形态学梯度:就是膨胀图与俯视图之差,用于保留物体的边缘轮廓。
顶帽:原图像与开运算图之差,用于分离比邻近点亮一些的斑块。
黑帽:闭运算与原图像之差,用于分离比邻近点暗一些的斑块。
opencv里有一个很好的函数getStructuringElement,我们只要往这个函数传相应的处理参数,就可以进行相应的操作了,使用起来非常方便。
下面列举一下相应的操作宏定义。
#include
#include
using namespace std;
using namespace cv;
//高级形态学处理
int main()
{
Mat img = imread("lol1.jpg");
namedWindow("原始图", WINDOW_NORMAL);
imshow("原始图", img);
Mat out;
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
//高级形态学处理,调用这个函数就可以了,具体要选择哪种操作,就修改第三个参数就可以了。这里演示的是形态学梯度处理
morphologyEx(img, out, MORPH_GRADIENT, element);
namedWindow("形态学处理操作", WINDOW_NORMAL);
imshow("形态学处理操作", out);
waitKey(0);
}
形状特征,颜色,纹理,概率,描述算子,梯度直方图等。
图像的矩
图像的矩到底是什么?
矩是概率与统计中的一个概念,是随机变量的一种数字特征。
有点抽象,简而言之,矩就是图像的特征信息,比如大小、位置、方向等。
OpenCV提供了一些函数来计算图像的矩:
详见这里 轮廓查找和多边形包围轮廓
最后提供参考代码:
#include
#include
#include
#include
using namespace cv;
using namespace std;
//定义滑动条初始值
int g_nThresholdValue = 100; //阈值初始值
int g_nThresholdType = 0; //阈值模式初始值
//0: THRESH_BINARY - 当前点值大于阈值时,取Maxval,否则设置为0
//1 : THRESH_BINARY_INV - 当前点值大于阈值时,设置为0,否则设置为Maxval
//2 : THRESH_TRUNC - 当前点值大于阈值时,设置为阈值,否则不改变
//3 : THRESH_TOZERO - 当前点值大于阈值时,不改变,否则设置为0
//4 : THRESH_TOZERO_INV - 当前点值大于阈值时,设置为0,否则不改变
int g_ImgH = 0;
int g_ImgW = 0;
Mat g_srcImage, g_grayImage, g_dstImage;
#define WINDOW_NAME "阈值处理"
void on_Threshold(int, void*);//回调函数
RNG rng(12345);
void getDist(int x, int y, int& distX, int& distY);
/*
Mat srcImagel;
Mat srcGray;
int max_thresh = 255;
int thresh = 100;
RNG rng(12345);
//鼠标回调事件
void thresh_callback(int, void*)
{
Mat canny_output;
vector> contours;
vector hierarchy;
//用canny算子检测边缘
Canny(srcGray, canny_output, thresh, thresh * 2, 3);
//寻找轮廓
findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
//绘出轮廓
Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
for (int 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, color, 2, 8, hierarchy, 0, Point());
}
//显示轮廓结果
namedWindow("Contours", CV_WINDOW_AUTOSIZE);
imshow("Contours", drawing);
}
int test()
{
Mat srcImage = imread("test_img/ocr.jpg");
if (!srcImage.data)
return -1;
cvtColor(srcImage, srcGray, COLOR_BGR2GRAY);
blur(srcGray, srcGray, Size(3, 3));
//创建窗体
namedWindow("srcImage", CV_WINDOW_AUTOSIZE);
imshow("srcImage", srcImage);
//滑动条控制Canny阈值
createTrackbar(" thresh:", "srcImage", &thresh, max_thresh, thresh_callback);
thresh_callback(0, 0);
waitKey(0);
return 0;
}
*/
void on_Threshold(int, void*)
{
//进行阈值分割
threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, g_nThresholdType);
//显示结果
imshow("阈值处理", g_dstImage);
}
void getDist(int x,int y,int& distX,int& distY)
{
if (x < int(g_ImgW/2) )
{
distY = x;
distX = y < int(g_ImgH / 2) ? y : g_ImgH - y;
}
else
{
distY = g_ImgW - x;
distX = y < int(g_ImgH / 2) ? y : g_ImgH - y;
}
}
int main()
{
Mat srcImg, tempImg;
srcImg = imread("test_img/ocr_src.jpg");
g_ImgH = srcImg.rows;
g_ImgW = srcImg.cols;
if (!srcImg.data)
{
cout << "no Img" << endl;
return -1;
}
Mat gray;
//resize(srcImg, tempImg, Size(srcImg.cols / 2, srcImg.rows / 2), 0, 0);
cvtColor(srcImg, gray, CV_RGB2GRAY);
//medianBlur(gray, gray, 3);// 中值滤波:去椒盐噪声
//imshow("medianBlur", gray);
blur(gray, gray, Size(6, 6));//均值滤波
namedWindow("Blur", CV_WINDOW_NORMAL);
imshow("Blur", gray);
//二值化阈值分割
Mat thresh;
threshold(gray, thresh, 100, 255, THRESH_OTSU);
//namedWindow("thresh", CV_WINDOW_NORMAL);
imshow("thresh", thresh);
//形态学梯度
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(19,19));//MORPH_RECT表示矩形卷积核,还有椭圆,交叉型的
//高级形态学处理
Mat out;
//morphologyEx(thresh, out, MORPH_CLOSE, element);
dilate(thresh, out, element);//膨胀
//namedWindow("膨胀", CV_WINDOW_NORMAL);
imshow(" 膨胀", out);
//canny边缘检测
Mat canny_output;
Canny(out, canny_output, 100, 200, 3);
//namedWindow("Canny", CV_WINDOW_NORMAL);
imshow(" Canny", canny_output);
//提取轮廓
vector area;
vector> contours;
vector hierarchy;
Mat srcThresh;
srcThresh = canny_output.clone();
//寻找轮廓
findContours(srcThresh, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
//计算轮廓矩
vector mu(contours.size());
//计算轮廓的质心
vector mc(contours.size());
char text[100] = { 0 };
//绘出轮廓
Mat drawing = Mat::zeros(srcThresh.size(), CV_8UC3);
for (int 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, color, 2, 8, hierarchy, 0, Point());
area.push_back(contourArea(contours[i]));
if (area[i] > 1600)// 80
{
cout << "面积" << i << ":" << area[i] << endl;
mu[i] = moments(contours[i], false);
mc[i] = Point2d(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
cout << "center:" << i << ":" << Point(mc[i].x, mc[i].y) << endl;
//sprintf_s(text, "(%0.0f,%0.0f)", mc[i].x, mc[i].y);
//putText(drawing, text, Point(mc[i].x, mc[i].y), FONT_HERSHEY_SIMPLEX, 0.4, cvScalar(255, 0, 255), 1);
int distX=0, distY=0;
getDist(mc[i].x, mc[i].y, distX, distY);
cout << "the minimum margins are" << distX << "," << distY << endl;
sprintf_s(text, "(%d,%d)", distX, distY);
putText(srcImg, text, Point(mc[i].x, mc[i].y), FONT_HERSHEY_SIMPLEX, 1, cvScalar(255, 0, 255), 2);
}
}
namedWindow("drawing", CV_WINDOW_NORMAL);
imshow("Contours", srcImg);
imwrite("test_img/resultImg.jpg", srcImg);
waitKey();
return 0;
}
参考文献:
1.https://blog.csdn.net/lcy597692327/article/details/79753455
2.https://blog.csdn.net/xuxunjie147/article/details/76577298
3.https://blog.csdn.net/spw_1201/article/details/53690321
4.https://blog.csdn.net/kakiebu/article/details/79824856