- 实验目的
1、了解模板匹配算法进行数字识别的基本原理,理解bmp 图像在存储器中的存储形式。
2、实现bmp格式图像中印刷体数字的识别。
- 实验基本原理及步骤(或方案设计及理论计算)
1、基本原理
实验采用模板匹配中像素点重合的方法,将待测图像中的数字与模板库中的各个数字进行比对,认为模板库中与待测数字重合像素点最多的为数字识别结果。
注:当 bmp 图像读入DSP 实验板存储空间之后,imgbuf 指针指向其数据的首地址。
模板库的建立
将包含 0—9 十个数字的十张bmp 格式的图像(本模板库的图像均为白色背景黑色字体)读入,将其像素信息分别存入十个50 x 50的二维数组中。本实验中灰度图像的灰度值低于50的为有效像素点,其对应的二维数组中的数值定义为1,灰度值大于50 的认为是无效点,其对应的二维数组中的数值定义为0。
待检测图像中的数字分离
定义如下结构体:
struct{
int Left;
int Right;
}W[20];
struct{
int Up;
int Down;
}H[20];
如果输入图像包含n x m个数字,则需要将n x m个数字分离开之后,分别与模板库进行匹配并分别识别,将识别结果存入result[n x m]的数组中。
实验主要处理1 x m个数字的图像,先统计图像中每行每列有效像素点分别存入grayw[]和grayh[]中,然后逐行逐列扫描图像,通过W[].Left和W[].Right,H[].Up 和H[].Down 分别标记每个数字的左右上下可将数值分离。
模板的匹配与识别
依次识别每一位数字,将每一位数字的像素信息存入50´50的名为t 的二维数组中,有效像素点定为1,无效像素点定义0。将得到的分离后的数字的二维数组t 与各个模板进行匹配。由于模板和待检测数字中的有效像素点在数组中均表示为1,可通过对应点相加之后对2取余数再相加得到结果sum,比较待检测图像与十个模板得到的sum的值,sum 最小的表示待检测数字与其图像重合点最多,即认为待检测数字为该数字。
2、算法步骤
(1)模板库的建立,十个数字的十张bmp 格式的图像读入,并以二值形式分别存入十个50´50的二维数组中;
(2)通过分别标记每个数字的左右上下位置,将待检测图像中的
数字进行分离;
(3)统计待检测图像与十个模板得到的图像重合点数目,进行模
板的匹配与识别。
- 实验结果分析及回答问题(或测试环境及测试结果)
模板
需要识别的图片
识别的结果
如上图结果所示,其是由识别出来的每一个数字的结果拼接得到。该算法进行识别之前必须对数字进行很好的分离,否则将会出现很大识别误差。
- 实验代码
#include<stdio.h>
#include<cstring>
#include<math.h>
#include<windows.h>
#include <time.h>
#define imageW 288
#define imageH 36
int ImagePtr[10][1800];
int Height[10];int Width[10];
int bmpHeight, bmpWidth, biBitCount;
unsigned char *pBmpBuf;
RGBQUAD *pColorTable;
int image[10368];
int Print[10368];
int end[15];
int start[14][2];
void fun(){
end[0]=0;
int i,j,k,q;
//寻找起始点ox,oy,每个数字的终点end
for(k=0;k<14;k++){
//寻找数字k的起点ox
for(i=end[k];i<288;i++){
for(j=0;j<36;j++){
if (image[i+j*288]==0){
start[k][0]=i;
j=36;
i=288;
}
}
}
//寻找数字k的起点oy
for(j=0;j<36;j++){
for(i=end[k];i<288;i++){
if(image[i+j*288]==0){
start[k][1]=j;
j=36;
i=288;
}
}
}
//寻找数字k的终点end
for(i=start[k][0];i<288;i++){
j=0;
while(image[i+j*288]==255&&j<36){
j++;
}
if(j==36){
end[k+1]=i;
i=288;
}
}
}
//匹配
int sum=0;
int Lsum=0;
int res;
int m;
int n;
for(k=0;k<14;k++){
Lsum=0;
for(q=0;q<10;q++){
sum=0;
for(i=start[k][0];i<end[k+1];i++){
for(j=start[k][1];j<36;j++){
m=i-start[k][0];
n=j-start[k][1]+19;
int w = end[k+1] - start[k][0];
if(image[i+j*288]==ImagePtr[q][n * 40 + m]){
sum+=1;
}
}
}
if(Lsum<sum){
Lsum=sum;
res=q;
}
}
for(i=k*20;i<(k+1)*20;i++){
for(j=0;j<36;j++){
m=i-k*20;
n=j+9;
Print[i+j*288]=ImagePtr[res][m+n*40];
}
}
}
for(i=(k+1)*20;i<288;i++){
for(j=0;j<36;j++){
Print[i+j*288]=255;
}
}
}
void read_temp(){
for (int photonum = 0;photonum < 10;photonum++){
const char src0[] = "0.bmp";
const char src1[] = "1.bmp";
const char src2[] = "2.bmp";
const char src3[] = "3.bmp";
const char src4[] = "4.bmp";
const char src5[] = "5.bmp";
const char src6[] = "6.bmp";
const char src7[] = "7.bmp";
const char src8[] = "8.bmp";
const char src9[] = "9.bmp";
FILE *fp = fopen(src9, "rb");
if (photonum == 0) fp = fopen(src0, "rb");
else if (photonum == 1) fp = fopen(src1, "rb");
else if (photonum == 2) fp = fopen(src2, "rb");
else if (photonum == 3) fp = fopen(src3, "rb");
else if (photonum == 4) fp = fopen(src4, "rb");
else if (photonum == 5) fp = fopen(src5, "rb");
else if (photonum == 6) fp = fopen(src6, "rb");
else if (photonum == 7) fp = fopen(src7, "rb");
else if (photonum == 8) fp = fopen(src8, "rb");
// else if (photonum == 9) fp = fopen(src9, "rb");
if (fp == 0){printf("false\n");}
fseek(fp, sizeof(BITMAPFILEHEADER), 0);
BITMAPINFOHEADER head;
fread(&head, 40, 1, fp);
bmpHeight = head.biHeight;
bmpWidth = head.biWidth;
biBitCount = head.biBitCount;
fseek(fp, sizeof(RGBQUAD), 1);
int LineByte = (bmpWidth*biBitCount / 8 + 3) / 4 * 4;
pBmpBuf = new unsigned char[LineByte*bmpHeight];
fread(pBmpBuf, LineByte*bmpHeight, 1, fp);
fclose(fp);
int rgbMap[bmpHeight * bmpWidth][3];
for (int i = 0; i < bmpHeight;i++){ //取位图 rgb 三通道数据
for (int j = 0; j < bmpWidth;j++){
unsigned char *tmp;
tmp = pBmpBuf + i * LineByte + j * 3;
rgbMap[i * bmpWidth + j][0] = int(*(tmp));
rgbMap[i * bmpWidth + j][1] = int(*(tmp+1));
rgbMap[i * bmpWidth + j][2] = int(*(tmp+2));
}
}
for (int i = 0; i < bmpHeight * bmpWidth;i++){
ImagePtr[photonum][i] = rgbMap[i][0]*0.299 + rgbMap[i][1]*0.587 + rgbMap[i][2]*0.114;
}
Height[photonum] = bmpHeight;
Width[photonum] = bmpWidth;
}
}
int main(){
read_temp();
const char src[] = "test.bmp";
FILE *fp = fopen(src, "rb");
if (fp == 0){
printf("false\n");
return 0;
}
fseek(fp, sizeof(BITMAPFILEHEADER), 0);
BITMAPINFOHEADER head;
fread(&head, 40, 1, fp);
bmpHeight = head.biHeight;
bmpWidth = head.biWidth;
biBitCount = head.biBitCount;
fseek(fp, sizeof(RGBQUAD), 1);
int LineByte = (bmpWidth*biBitCount / 8 + 3) / 4 * 4;
pBmpBuf = new unsigned char[LineByte*bmpHeight];
fread(pBmpBuf, LineByte*bmpHeight, 1, fp);
fclose(fp);
int rgbMap[bmpHeight * bmpWidth][3];
for (int i = 0; i < bmpHeight;i++){ //取位图 rgb 三通道数据
for (int j = 0; j < bmpWidth;j++){
unsigned char *tmp;
tmp = pBmpBuf + i * LineByte + j * 3;
rgbMap[i * bmpWidth + j][0] = int(*(tmp));
rgbMap[i * bmpWidth + j][1] = int(*(tmp+1));
rgbMap[i * bmpWidth + j][2] = int(*(tmp+2));
}
}
for (int i = 0; i < bmpHeight * bmpWidth;i++){
image[i] = rgbMap[i][0]*0.299 + rgbMap[i][1]*0.587 + rgbMap[i][2]*0.114;
}
const char dst[] = "segResultTest.bmp";
FILE *fp1 = fopen(dst, "wb");
if (fp1 == 0){
printf("false\n");
return 0;
}
int LineByte1 = (bmpWidth * 8 / 8 + 3) / 4 * 4;
//修改文件头,其中有两项需要修改,分别为 bfSize 和 bfOffBits
BITMAPFILEHEADER bfhead;
bfhead.bfType = 0x4D42;
bfhead.bfSize = 14 + 40 + 256 * sizeof(RGBQUAD)+LineByte1*bmpHeight;
bfhead.bfReserved1 = 0;
bfhead.bfReserved2 = 0;
bfhead.bfOffBits = 14 + 40 + 256 * sizeof(RGBQUAD);//修改偏移字节数
fwrite(&bfhead, 14, 1, fp1); //将修改后的文件头存入 fp1;
BITMAPINFOHEADER head1;
head1.biBitCount = 8; //将每像素的位数改为 8
head1.biClrImportant = 0;
head1.biCompression = 0;
head1.biClrUsed = 0;
head1.biHeight = bmpHeight;
head1.biWidth = bmpWidth;
head1.biPlanes = 1;
head1.biSize = 40;
head1.biSizeImage = LineByte1*bmpHeight;//修改位图数据的大小
head1.biXPelsPerMeter = 0;
head1.biYPelsPerMeter = 0;
fwrite(&head1, 40, 1, fp1); //将修改后的信息头存入 fp1;
pColorTable = new RGBQUAD[256];
for (int i = 0; i < 256; i++){
pColorTable[i].rgbRed = i;
pColorTable[i].rgbGreen = i;
pColorTable[i].rgbBlue = i; //是颜色表里的 B、G、R 分量都相等,且等于索引值
}
fwrite(pColorTable, sizeof(RGBQUAD), 256, fp1); //将颜色表写入 fp1
//写位图数据
unsigned char *newBmp;
newBmp = new unsigned char[LineByte1*bmpHeight];
fun();
for (int i = 0; i < bmpHeight; i++){
for (int j = 0; j < bmpWidth; j++){
unsigned char *pb;
pb = newBmp + i * LineByte1 + j;
*pb = Print[i * bmpWidth + j];
}
}
fwrite(newBmp, LineByte1*bmpHeight, 1, fp1);
fclose(fp1);
system("pause");
return 0;
}