[OpenCV+VS2015]表计读数识别(一):表计位置检测
本文是基于传统视频图像处理办法检测表计读数,作者资历尚浅,如有不足之处,欢迎指正,谢谢!
1 位置检测的思路
当时拿到手有如下的几个思路想法:
a、表计都是圆的,可以用hough变换圆形检测,再用mask掩膜提取,就可以完成位置的确定
b、表盘的颜色有一定的分布,比如外圈是黑色原框,内部是白色表盘底。可以先用聚类的方式让颜色聚类分开,便于区域分割。然后设定一些RGB判据进行二值化,就可以得出表盘轮廓从而提取。
c、模板匹配:这里分为直接模板匹配和surf特征点匹配找到对应匹配点,前者是不具有旋转拉伸不变性。
然后我拿到图,裂开
这嘛玩意歪成这样,好吧,我直接颜色聚类简单点处理算了。
所以我直接方案b走起,其他方法我也想过,可以操作但是有点麻烦,而且本人太菜!
2 位置提取
2.1 kmeans颜色聚类
keamns简介和代码,我之前写过,这里建个传送门
这里的核心思想就是表盘颜色接近白色和周围颜色有明显不同,所以kmeans聚类之后,使用二值化判断的方法把表轮廓的部分选择出来
这个图就是我二值化的结果,表盘部分很明显是内部白色外部黑色的圆形轮廓
2.2 表盘轮廓提取
这个时候就可以选择
1、可以hough变换找圆形或者拟合椭圆形去寻找
2、可以直接findcontour找到轮廓,然后用矩形框选,但是框选的时候应该添加一些条件,比如:
- 表计的长方形框长宽比接近1:1;
- 表计位置不会出出现在图片的边缘位置;
- 表计不会过于狭窄;
- 表计框选内部的颜色分量应该是白色居多;
等等可以按照自己的需要添加条件
3 结果
最后截取效果
表盘就出来啦
4 代码
这是主要部分的代码
void Gauge_Detection(Mat& src, Mat& dst)
{
Mat binary_image;
Image trans_in, trans_out;
MattoImage(src, trans_in);
RGB_kmeans(trans_in, trans_out);
ImagetoMat(trans_out, dst);
//namedWindow("【kmeans聚类图像】", 0);
//resizeWindow("【kmeans聚类图像】", 640, 480);
//imshow("【kmeans聚类图像】",dst);
Binary(dst, binary_image);
namedWindow("【二值化图像】", 0);
resizeWindow("【二值化图像】", 640, 480);
imshow("【二值化图像】", binary_image);
DrawFire(src, binary_image);
//namedWindow("【kmeans聚类图像】", 0);
//resizeWindow("【kmeans聚类图像】", 640, 480);
//imshow("【kmeans聚类图像】", dst);
//namedWindow("【二值化图像】", 0);
//resizeWindow("【二值化图像】", 640, 480);
//imshow("【二值化图像】", binary_image);
}
/*将导入的mat型变换到image*/
void MattoImage(Mat &src, Image &dst) {
for (int i = 0; i < src.rows; i++) {
Row row;
for (int j = 0; j < src.cols; j++) {
auto temp = src.at<Vec3b>(i, j);
row.emplace_back(Color(temp[2], temp[1], temp[0]));
}
dst.push_back(row);
}
}
void RGB_kmeans(Image &src, Image &dst)
{
int i, j, s, it, t, pos;
double diff;
double mindiff = _mindiff;
int Maxitr = _Maxitr;
assert(!src.empty());
for (i = 1; i < src.size(); i++) {
assert(src[i].size() == src[0].size());
}
int rows = src.size();
int cols = src[0].size();
Mat cluster(src.size(), src[0].size(), CV_8UC1);
dst.clear();
//给定初始质心
double temp = src.size() / k;
if (temp < 1 || temp<0)
{
printf("the input 'k' error.please try again!");
}
int interval = (src.size() - src.size() % k) / k;
int krow[k] = {
0 };
int kcol[k] = {
0 };//用于存放初始化随机k个质心点位置
int kr[k];
int kg[k];
int kb[k]