一维条形码应用广泛,本文选择最具代表性的EAN-13条形码,它采用互相平行、不同宽窄的59个黑白条纹构成,可以解码为数字0~9。条形码的识别功能实现过程如下图所示,一共分为7个过程,分别为:灰度转换、边沿检测、图像分条、角度计算、条形码定位、条形码识别和校验。
参考代码示例: https://github.com/BluesYu/MarStech_Vision_Sensor/tree/master/bar_mode
具体的实现原理如下图所示:
一、条形码识别算法
条形码识别主要有三种方法,分别为宽度测量法,平均值法和相似边距离的测量方法,其中,用相似边距离算法进行识别。相似边距离的优点是,即使有噪音干扰,导致上一步计算的条和空宽度不准确,也不影响正确的识别。
其中C1,C2,C3,C4分别为相应的条形码条和白色空格的宽度,有如下关系:
。
由于条形码的起始符每个黑条和白色空白代表一个条形码模块的宽度,黑条的宽有N个模块则表示二进制数中N个数值1,白色空表示有N个数值0,对应的前置符不进行编码,一共是7个模块宽度。同时,左侧的数据字符可以分为偶编码与奇编码,右侧的数据字符仅有偶编码。
字符数据 | 左侧字符数据 | 右侧字符数据 | |
奇编码 | 偶编码 | 偶编码 | |
0 | 0001101(53) | 0100111(23) | 1110010(53) |
1 | 0011001(44) | 0011001(44) | 1100110(44) |
2 | 0010011(33) | 0010011(33) | 0010011(33) |
3 | 0111101(55) | 0100001(25) | 0100001(25) |
4 | 0100011(24) | 0011101(54) | 1011100(24) |
5 | 0110001(35) | 0111001(45) | 1001110(35) |
6 | 0101111(22) | 0000101(52) | 1010000(22) |
7 | 0111011(44) | 0010001(34) | 1000100(44) |
8 | 0110111(33) | 0001001(43) | 1001000(33) |
9 | 0001011(42) | 0001011(42) | 0001011(42) |
上述计算得到的t数值与实际字符数据不是一一对应,例如,数字1和7对应左侧奇编码数值都为44,数字2和8对应左侧奇编码数值都为33。此时,需要进一步的判断,根据表3.3中数字1和7对应的左侧奇编码分别为0011001和0111011,即数字1的C1+C3>C2+C4 ,数字7的C1+C3<C2+C4 ,同理可以区分出数字2和8.
二,条形码识别实现
代码函数主要有:
(1) 获取参数:
/******************************************************************************************************
* 函数(Function):get_bar_num(Mat &srcImg, string &result)
* 功能(Description): 获取参数
* 调用函数(Calls):cvtColor()、threshold()、bitwise_not()、expand_dis_binary_image()、
get_bar_contours()、get_bar()、getDigitsEAN13()、parseEAN13()
* 输入参数(parameter):Mat &srcImg:输入图像(反转后的二值图像)
string &result:输出结果
* 返回数值(return): return="ERROR":错误
return=result :条形码数据
* 其他(Others): bar_rate可以调节,代表取值的参数,每张图一共扫描5次。
********************************************************************************************************/
string get_bar_num(Mat &srcImg, string &result);
(2)将目标图像扩大,检测区域:
/******************************************************************************************************
* 函数(Function):expand_dis_binary_image(Mat &srcImg, Mat &dstImg)
* 功能(Description): 将目标图像扩大,检测区域
* 调用函数(Calls):erode()、dilate()
* 输入参数(parameter):Mat &srcImg:输入图像(反转后的二值图像)
Mat &dstImg:输出图像
* 返回数值(return): 无
* 其他(Others): 可以修改模板,形态学模板越大计算越多。
********************************************************************************************************/
void expand_dis_binary_image(Mat &srcImg, Mat &dstImg);
(3)获取区域:
/******************************************************************************************************
* 函数(Function):get_bar_contours(Mat &mat_temp, vector<vector<Point2f>> &vertices)
* 功能(Description): 将目标图像扩大,检测区域
* 调用函数(Calls):findContours()、contourArea()
* 输入参数(parameter):Mat &mat_temp:输入图像(反转后的二值图像)
vector<Point2f> &vertices):输出旋转矩形参数点
* 返回数值(return): 返回0:无轮廓
返回4:代表4个点
* 其他(Others):
********************************************************************************************************/
int get_bar_contours(Mat &mat_temp, vector<Point2f> &vertices) ;
(4)获取矩形代码(0或者1)
/******************************************************************************************************
* 函数(Function):getDigitsEAN13(IplImage* source, int lineHight)
* 功能(Description): 获取矩形代码(0或者1)
* 调用函数(Calls):getBaseAndStart()、cvGet2D()、getNumOfPixel()
* 输入参数(parameter):IplImage* source:输入图像(灰度图)
int lineHight:获取的Y轴图像
* 返回数值(return): return="ERROR":失败
return=code(0、1代码):成功
* 其他(Others): 调整参数lineHight
********************************************************************************************************/
string getDigitsEAN13(IplImage* source, int lineHight);
(5)统计相同类型(条/空白)的像素的个数
/******************************************************************************************************
* 函数(Function): getNumOfPixel(IplImage* source, int lineHight, int threshold, int* index)
* 功能(Description): 统计相同类型(条/空白)的像素的个数
* 调用函数(Calls):
* 输入参数(parameter):IplImage* source:输入图像(灰度图)
int lineHight:获取的Y轴图像
int threshold:二值化阈值
int* index:起始点(x轴)
* 返回数值(return): return="ERROR1":失败
return=code(0、1代码):成功
* 其他(Others): 调整参数lineHight
********************************************************************************************************/
int getNumOfPixel(IplImage* source, int lineHight, int threshold, int* index) ;
(6)获取条形码起始的位置和宽度
/******************************************************************************************************
* 函数(Function): getBaseAndStart(IplImage* source, int lineHight, int threshold, int* base, int* start)
* 功能(Description):获取条形码起始的位置和宽度
* 调用函数(Calls):cvGet2D()
* 输入参数(parameter):IplImage* source:输入图像(灰度图)
int lineHight:获取的Y轴图像
int threshold:二值化阈值
int* base:基础宽度
int* start:起始的位置
* 返回数值(return): return=-2:失败
return=-1:失败
return= 0:成功,返回参数
* 其他(Others): 判断宽度的算法还可以改进
********************************************************************************************************/
int getBaseAndStart(IplImage* source, int lineHight, int threshold, int* base, int* start) ;
(7) 获取目标图像
/******************************************************************************************************
* 函数(Function):get_bar(Mat &gray, vector<Point2f> &vertices,Mat &final_result,const int &add_pixel)
* 功能(Description): 获取目标图像
* 调用函数(Calls):temp_expand()、warpAffine()
* 输入参数(parameter):Mat &gray:输入灰度图像(二值图像也可)
vector<Point2f> &vertices:输入矩形点
Mat &final_result:输出的目标图像
const int &add_pixel:扩张图像(用于旋转)
* 返回数值(return): return=-1:失败,无图像
return= 4:正常
* 其他(Others): add_pixel参数可以修改,根据图像进行调整。
********************************************************************************************************/
int get_bar(Mat &gray, vector<Point2f> &vertices, Mat &final_result, const int &add_pixel);
(8)解析EAN13的间接code
/******************************************************************************************************
* 函数(Function): parseEAN13(string code)
* 功能(Description):解析EAN13的间接code
* 调用函数(Calls):
* 输入参数(parameter):string code:根据得到的参数,进行解析
* 返回数值(return): return=code(数字):成功
return= "ERROR1":参数范围错误
return= "ERROR2":参数解析错误
return= "ERROR3":第一个参数解析错误
return= "ERROR4":校验失败
* 其他(Others): 基本参数不需要修改
********************************************************************************************************/
string parseEAN13(string code) ;
三,使用代码
主要代码为bar_mode.cpp和bar_mode.h ,测试代码为 bar_mode_test.cpp,使用注意事项为:
1,需要使用摄像头;
2,采集图像信息,通过串口发送;
3,依赖简易的opencv库函数,需要白平衡算法;
4,需要调节摄像头焦距:10cm左右,效果较好;
5, 测试函数编译命令为:g++ bar_mode_test.cpp bar_mode.cpp uart_io/uart_io.cpp -o bar_mode_test -lopencv_core -lopencv_highgui -lopencv_imgproc -lpthread -std=c++11
使用代码:
string result = ""; //参数解析
if (get_bar_num(frame, result) != "ERROR")
具体视觉传感器测试、购买可以咨询:火星人俱乐部官网(https://www.imarsclub.com/web/index),电话或邮件联系即可。传感器已经申请专利,商业使用需要授权。
火星人视觉传感器是一个开放平台,相关电路版图、代码对外开放,可以自行下载,代码地址:https://github.com/BluesYu/MarStech_Vision_Sensor,欢迎star和fork,有问题可以再github上交流。
本项目为开源项目,不以盈利为目的,开源社区需要大家一起努力,欢迎大家一起来开发!