前言
前一段时间帮别人做项目,这个题目是数码管识别,这个最简单的办法就是准备图片,放到SVM中训练,就会得到不错的效果,但是,我想起以前做过这个项目,当时还没接触机器学习,用的是传统方法识别的。主要思想就是三次扫描一个数目管,判断他的7个发光二极管的亮灭,就可以识别出结果,但当时由于获取到的数码管图像太小,误识别很高,就放弃了,不过现在这个题目得到的图片大,清楚,不容易误识别,就可以用这个方法识别,顺便复习一下当时的思路和算法。
识别思想
采用三次扫描法,可以实现数码管数字的识别,识别方法的思想也简单:
第一次:扫描五号位置和一号位置
第二次:扫描四号位置和二号位置
第三次:扫描零号位置和六号位置三号位置
主要思路如图所示:
扫描结束后,会得到一个码表,比如数字二,它的编码为 0 1 0 1 1 0 1 1,转换为十六进制为0x5b,得到编码之后,就可以查表,识别数字。
下面给出一个可以运行的demo,大家可以运行一下,看下效果(前提配置好OpenCV的C++环境)。
链接:https://download.csdn.net/download/qq_36327203/11221814
效果截图:
代码
这种方法的代码实现如下:
//2 3 4 5 6 7 8 9 0
static unsigned char model_num2[9] = { 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x47, 0x7f, 0x6f, 0x3f };
int match_num(Mat &img)
{
// 6 5 4 3 2 1 0
//2 0x5b 1 0 1 1 0 1 1
//3 0x4f 1 0 0 1 1 1 1
//4 0x66 1 1 0 0 1 1 0
//5 0x6d 1 1 0 1 1 0 1
//6 0x7d 1 1 1 1 1 0 1
//7 0x47 0 1 0 0 1 1 1 0x07
//8 0x7f 1 1 1 1 1 1 1
//9 0x6f 1 1 0 1 1 1 1
//0 0x3f 0 1 1 1 1 1 1
//2 3 4 5 6 7 8 9 0
//unsigned char model_num2[9] = { 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x47, 0x7f, 0x6f, 0x3f };
Mat img_temp = img.clone();
/*
---------------------00000-------------------------
--------------------5-----1------------------------
--------------------5-----1------------------------
---------------------66666-------------------------
--------------------4-----2------------------------
--------------------4-----2------------------------
---------------------33333-------------------------
*/
rectangle(img_temp, Rect(0, 0, img_temp.cols, img_temp.rows), Scalar(0), 1); //处理边界
int y1 = img_temp.rows >> 2; //y方向1/4处
int y2 = (img_temp.rows >> 2) * 3;; //y方向3/4处
int xx = img_temp.cols >> 1; //x方向1/2处
int yy = img_temp.rows >> 1; //x方向1/2处
unsigned char temp_value = 0;
//识别到1
if ((double)img.rows / (double)img.cols > 2.5)
return 1;
//两条横线检测遍历
for (int y = y1; y < img_temp.rows; y += (y1 * 2))
{
uchar *pp = img_temp.ptr<uchar>(y);
for (int x = 0; x < img_temp.cols - 1; x++)
{
if (pp[x] == 0)
{
if (pp[x + 1] == 255)
{
if (x < xx && y < yy) temp_value |= (0x01 << 5);
if (x > xx && y < yy) temp_value |= (0x01 << 1);
if (x < xx && y > yy) temp_value |= (0x01 << 4);
if (x > xx && y > yy) temp_value |= (0x01 << 2);
}
}
}
}
//一条竖线检测遍历
for (int y = 0; y < img_temp.rows - 1; y++)
{
uchar *pp = img_temp.ptr<uchar>(y);
if (pp[xx] == 0)
{
if (pp[xx + img_temp.step] == 255)
{
if (y < y1) temp_value |= (0x01 << 0);
if (y > y2) temp_value |= (0x01 << 3);
if (y > y1 && y < y2) temp_value |= (0x01 << 6);
}
}
}
//查表匹配
for (int model_i = 0; model_i < 9; model_i++)
{
if (temp_value == model_num2[model_i])
{
if (model_i == 8) return 0; //0为特殊情况
else return model_i + 2;
}
}
return -1;
}
反思与改进
图形必须预处理好,不能有其他明显的噪声。
这种方法是基于擦混同方法识别的,稳定性方面不好,复杂环境下效果不好。