-
条码规范介绍
1.1 EAN-13条码规范概述
在超级市场与零售业这一类的编码还是十分常见的,身边从超市买来的商品都是该编码规范。
图1-1
这一编码可以从两个方面划分,即按条形码的区域划分和按照数字代码划分。
按照区域:
图1-2
按照数字:
图 1-3
1.2 EAN-13条码编码解析
对于编码的过程分析十分重要它对于我们后期的编程有着十分重要的作用。
首先在编码前要搞清楚模块的概念,每个编码宽度应该是固定的,单位是模块,在本次作业中我的一个模块宽度是两个像素宽度。
1.左侧空白区
位于条形码的最左侧,拥有至少11个模块宽度,为了能够轻易的与其他条码区别。
2.前置码
国家代码的第一位,在条形码中并不直接进行编码,但对整个编码过程有着深刻的影响。
3.起始符
位于左侧空白区的右侧,由三个条形模块组成,“101”其中“1”为竖黑线。
4.左侧数据符
自认为是编码过程中最为复杂的一部分,它有六位数字信息,每个占用七个模块。
具体首先它拥有“A”“B”两种数字编码形式,那具体何时使用呢,这就与前置码有关了,对应关系如下。
图 1-4
A与B编码规则
图 1-5
其中左侧数据符号包含了国家代码的后两位,国家代码查表可得,这里不赘述。
5.中间分隔符
用于划分左右数据区,由五个条形模块组成,为“01010”
6.右侧数据符
右侧数据不似左侧编码如此复杂,一共五个数据位,每个也是七个模块宽。编码规则如下:
图1-6
7.校验符
校验符号只有一位,采用的是与右侧数据符号一样的编码方式,虽然在数字代码中显示,但其实它是被计算出来的。计算过程如下:
图 1-7
8.终止符
确保终止,三个模块长,逻辑方式“101”
9.右侧空白区
功能与左侧空白区类似,至少有七个条码模块宽度
-
代码设计框架
具体的代码设计框架如上图所示:
- 进入PixelModify函数,主要完成的功能是执行对一个空白24位BMP图像的二进制方式读取,然后获取图像的高宽以及相应的图像指针
- PixelModify函数执行了readBmp读取与修改完的saveBmp函数,还有一个DrawEan13是画出条形码的函数
- 在DrawEan13函数中主要执行了三个函数,用来完善相应编码规范
- 在所有的绘画函数中主要采用了DrawVerticalLine函数用以完成竖线的实现。
-
关键代码
3.1主函数
void main()//程序入口,主要完成了信息介绍,条码输入功能
{
cout << "图片自动输出到当前目录" << endl<< endl << "请输入13位EAN-13编码(使用空格隔开)"<<endl << endl << "或前十二位(最后一位错误自动纠正):" << endl << endl;
for (int i = 0;i < 13;i++)
{
cin >> BarCode[i];
}
cout << endl << endl << "请输入噪声类型" << endl << endl<< "0.无噪声 1.随机噪点" << endl << endl ;
cin >> NoiseControl;
if (NoiseControl == 1)
{
cout << "请输入翻转概率" << endl << endl ;
cin >> NoiseSeed;
}
PixelModify();
}
3.2主进程函数void PixelModify()
void PixelModify()
{
//读入指定BMP文件进内存
char readPath[] = "./canvas.BMP";
readBmp(readPath);
//输出图像的信息
cout << "width=" << CanvasWidth << " height=" << CanvasHeight << " biBitCount=" << biBitCount << endl;
//每行字节数
int lineByte = (CanvasWidth*biBitCount / 8 + 3) / 4 * 4;
//循环变量,针对彩色图像,遍历每像素的三个分量
int m = 0, n = 0, count_xiang_su = 0;
//画出EAN-13编码标准的条形码
DrawEan13();
//将图像数据存盘
char writePath[] = "./aftereffect.BMP";//图片处理后再存储
saveBmp(writePath, pCanvas, CanvasWidth, CanvasHeight, biBitCount, pColorTable);
//清除缓冲区,pCanvas和pColorTable是全局变量,在文件读入时申请的空间
delete[]pCanvas;
}
3.3 EAN13编码函数DrawEan13()
void DrawEan13()
{
//BarCode[13]
//画起始符号
DrawVerticalLine(pCanvas, 12, 180);
DrawVerticalLine(pCanvas, 14, 180);
//画左侧数据区
DrawLeftData();
//画中间分隔符
DrawVerticalLine(pCanvas, 58, 180);
DrawVerticalLine(pCanvas, 60, 180);
//画右侧数据区
DrawRightData();
//画出校验符
DrawCheckCharacter();
//画出终止符
DrawVerticalLine(pCanvas, 104, 180);
DrawVerticalLine(pCanvas, 106, 180);
}
3.4单模块(两像素)竖线函数DrawVerticalLine()
void DrawVerticalLine(unsigned char *pBmp,int pStartSize,int pLength)//处理一个模块的竖线,输入参数为图像指针,开始像素和竖线长度
{
int lineByte = (CanvasWidth*biBitCount / 8 + 3) / 4 * 4;
for (int i = CanvasHeight-10;i>CanvasHeight-pLength-10;i--)
{
for (int j = pStartSize*2-1;j<=pStartSize*2;j++)
{
for (int k = 0;k<3;k++)//每像素RGB三个分量分别置0才变成黑色
{
*(pBmp + i*lineByte + j * 3 + k) -= 255;
}
}
}
}
3.8读写BMP文件函数bool readBmp() bool saveBmp()
bool readBmp(char *bmpName)
{
FILE *fp = fopen(bmpName, "rb");//二进制读方式打开指定的图像文件
if (fp == 0) return 0;
//跳过位图文件头结构BITMAPFILEHEADER
fseek(fp, sizeof(BITMAPFILEHEADER), 0);
//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), 1, fp); //获取图像宽、高、每像素所占位数等信息
CanvasWidth = head.biWidth;
CanvasHeight = head.biHeight;
biBitCount = head.biBitCount;//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
int lineByte = (CanvasWidth * biBitCount / 8 + 3) / 4 * 4;//灰度图像有颜色表,且颜色表表项为256
if (biBitCount == 8)
{
//申请颜色表所需要的空间,读颜色表进内存
pColorTable = new RGBQUAD[256];
fread(pColorTable, sizeof(RGBQUAD), 256, fp);
}
//申请位图数据所需要的空间,读位图数据进内存
pCanvas = new unsigned char[lineByte * CanvasHeight];
fread(pCanvas, 1, lineByte * CanvasHeight, fp);
fclose(fp);//关闭文件
return 1;//读取文件成功
}
bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height, int biBitCount, RGBQUAD *pColorTable)
{
//如果位图数据指针为0,则没有数据传入,函数返回
if (!imgBuf)
return 0;
//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
int colorTablesize = 0;
if (biBitCount == 8)
colorTablesize = 1024;
//待存储图像数据每行字节数为4的倍数
int lineByte = (width * biBitCount / 8 + 3) / 4 * 4;
//以二进制写的方式打开文件
FILE *fp = fopen(bmpName, "wb");
if (fp == 0) return 0;
//申请位图文件头结构变量,填写文件头信息
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;//bmp类型
//bfSize是图像文件4个组成部分之和
fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ colorTablesize + lineByte*height;
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
//bfOffBits是图像文件前3个部分所需空间之和
fileHead.bfOffBits = 54 + colorTablesize;
//写文件头进文件
fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);
//申请位图信息头结构变量,填写信息头信息
BITMAPINFOHEADER head;
head.biBitCount = biBitCount;
head.biClrImportant = 0;
head.biClrUsed = 0;
head.biCompression = 0;
head.biHeight = height;
head.biPlanes = 1;
head.biSize = 40;
head.biSizeImage = lineByte*height;
head.biWidth = width;
head.biXPelsPerMeter = 0;
head.biYPelsPerMeter = 0;
//写位图信息头进内存
fwrite(&head, sizeof(BITMAPINFOHEADER), 1, fp);
//如果灰度图像,有颜色表,写入文件
if (biBitCount == 8)
fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
//写位图数据进文件
fwrite(imgBuf, height*lineByte, 1, fp);
//关闭文件
fclose(fp);
return 1;
}
-
噪声分析
5.1 代码实现void RandNoise()
使用系统时钟与随机数生成函数生成一系列随机数,然后再图像上画出,在主函数加入相应的人机交互代码并将输入的概率转化为需要生成的点传入函数。
void RandNoise(unsigned char *pBmp, int seed)//参数为图像指针和产生的噪点数
{
int i, j;
int lineByte = (CanvasWidth*biBitCount / 8 + 3) / 4 * 4;
srand((unsigned)time(NULL));
for (int z = 0;z < seed;z++)
{
i = random(0, CanvasHeight);
j = random(0, CanvasWidth);
for (int k = 0;k<3;k++)//每像素RGB三个分量分别置0才变成黑色
{
if(*(pBmp + i*lineByte + j * 3 + k)== 0)
*(pBmp + i*lineByte + j * 3 + k) += 255;
else if (*(pBmp + i*lineByte + j * 3 + k) == 255)
*(pBmp + i*lineByte + j * 3 + k) -= 255;
}
}
}
5.2 噪声影响分析
1.当概率为0.01时
图 5-1
2.当概率为0.03时
图 5-2
3.当概率为0.1时
图 5-3
4.噪声分析
经过大量的条码比较,此处的概率是指在所有像素的情况下,有相应概率会有噪声出现,当然也会有可能出现重复的噪点。大约在0.03以上的时刻,条码将不再能识别,0.03以下能够轻松的识别出信息,不会出现错误。由此可见,EAN13条形码具备一定的识别错误的能力,但是对于二维码这样具有50%纠错能力的码来说,还不具备良好的抗噪声性能。
-
资源分享
- 资源暂不支持分享