基于OpenCV的水电表的刻度数读取及识别
本文分享一个机器视觉检测的小项目,目的是读取并识别水电表的读数。如下图:
图1.
图2.
图一水表,生活中主要是关注数码表的数字部分,另外带有指针的四个部分可以忽略;
图二电表,既要关注数码表的数字部分,也要关注带指针的部分。
本文主要分享图二中电表两部分读数的读取及识别。首先解决带指针部分的刻度读取识别,然后再分享数码表部分的数字读取识别。
一、指针部分的刻度读取识别
思路分析:
指针部分的刻度读取识别,大体分为三步:
1、//步骤一:找到目标区域,截取ROI,并进行倾斜矫正,得到包含目标的图像.
2、//步骤二:对步骤一中得到图像进行处理,获得包含长指针和刻度的最终目标图像.
3、//步骤三:对步骤二中得到的图像进行处理,识别指针刻度数.
先上结果图像,再详解步骤分析。
原图:图3.
最终结果图像:图4.
左边是原始图像,右边是经过处理之后、识别出刻度盘读数的图像。下面来进行一步步的拆解分析。
1、//步骤一:找到目标区域,截取ROI,并进行倾斜矫正,得到包含目标的图像.
1.1 截取包含目标的ROI区域
由于表盘本身是圆形的,所以根据这个特点,先制作一个圆形的mask,并进行位与操作,即可截取得到表盘的中间圆形玻璃部分。(也可以在原图上拟合找圆,然后截取包含目标的圆形区域)
//制作一个圆形mask图像
cv::Mat mask(i_Rows, i_Cols, CV_8UC1, cv::Scalar(0, 0, 0));
cv::circle(mask, cv::Point(i_Rows/2, i_Cols/2), (i_Rows/2 - 15), cv::Scalar(255, 255, 255), -1);
//执行位操作
cv::Mat roiIMG;
cv::bitwise_and(srcIMG, mask, roiIMG);
mask图像:图5.
ROI图像:图6.
1.2 倾斜矫正
二值化后,查找连通域,根据连通域大小(周长、面积……),筛选出表盘刻度部分;
表盘刻度图像:图7.
最小外接矩形(红色):图8. 绿色:外接矩形.
可以看出表盘刻度图像,背景干净,前景目标明显突出。我们可以直接求取其最小外接矩形,根据最小外接矩形得到其倾斜角度、旋转中心。然后进行旋转矫正即可。
//获取旋转中心、旋转角度
vector<cv::RotatedRect> box(contours.size()); //定义最小外接矩形集合
cv::Point2f rect[4];
double angle=0.0;
cv::Point2f center;
//经过筛选之后,这里 1==contours.size() .
for (int i = 0; i < contours.size(); i++)
{
box[i] = minAreaRect(cv::Mat(contours[i]));//计算每个轮廓的最小外接矩形
angle = box[i].angle;//倾斜角度
center = box[i].center;//定义旋转中心坐标
box[i].points(rect);//把最小外接矩形四个端点复制给rect数组
std::cout << "angle=" << angle << std::endl;
//最小外接矩形的宽高.
char q_width[20], q_height[20];
sprintf_s(q_width, "width=%0.2f", box[i].size.width);
sprintf_s(q_height, "height=%0.2f", box[i].size.height);
circle(src_copy, cv::Point(box[i].center.x, box[i].center.y), 5, cv::Scalar(0, 0, 255), -1, 8); //绘制最小外接矩形的中心点
for (int j = 0; j < 4; j++)
{
cv::line(src_copy, rect[j], rect[(j + 1) % 4], cv::Scalar(0, 0, 255), 2, 8); //绘制最小外接矩形每条边
cv::namedWindow("rectLined", 0);
cv::imshow("rectLined", src_copy);
cv::waitKey(0);
}
//在图像上绘制文字
cv::putText(src_copy, q_width, Point(235, 260), FONT_HERSHEY_COMPLEX_SMALL, 0.85, Scalar(0, 255, 0), 2, 8);
cv::putText(src_copy, q_height, cv::Point(235, 285), FONT_HERSHEY_COMPLEX_SMALL, 0.85, cv::Scalar(0, 255, 0), 2, 8);
}
矫正之后的图像:图9.
矫正后的图像:图10.
//进行旋转矫正
//图像围绕center旋转degree角度(原尺寸)
void f_rotateImage2(Mat src, Mat& img_rotate, Point2f center, double angle)
{
//最小外接矩形的中心点为旋转中心坐标
//经验值
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(src, img_rotate, roateM, src.size()); //利用放射变换进行旋转
//imshow("f_img_rotate2", img_rotate);
//waitKey(0);
}
2、//步骤二:对步骤一中得到图像进行处理,获得包含长指针和刻度的最终目标图像.
步骤一中的图9即为步骤二中的输入图像。经过二值化、寻找连通域、筛选(周长/面积等),即可得到下图:图11
最终目标图像:图11.
图12.
步骤二与步骤一也可以一起处理,在步骤一中通过二值化后,也可得到图12,经过筛选和旋转矫正后,亦可得到图11。
3、//步骤三:对步骤二中得到的图像进行处理,识别指针刻度数.
步骤三的输入图像为步骤二中的图11。经过左右边界点查找、筛选,指针上的点查找、筛选,并将筛选后的点(图13红色点)进行直线拟合,即可得到图14中的三条绿色直线。
筛选后的点:图13.
直线拟合:图14.
有了三条直线,通过夹角、及刻度与夹角的比例关系,即可算出当前的刻度值是多少。(图14中红字即为当前刻度读数)
源码下载地址:基于OpenCV的水电表的刻度数读取及识别waterMeter.zip
二、数码表部分的数字读取识别
数码表部分的数字读取及识别。
【第二部分在整理中,待续……】
YOLO 教程:https://www.youtube.com/watch?v=_FNfRtXEbr4
数据集:https://ieee-dataport.org/open-access/water-meter-dataset
参考文献: