使用opencv调用24*24点阵字库和8*16ASCII字库在图片显示文字数字
课程实验:编程读汉字点阵字库,把自己的名字和学号叠加到图片的右下位置。
主要步骤分为三部分
第一部分:读取图片(文件读取)
第二部分:读取文字并从字库中提取相应的编码(字库的存储原理)
第三部分:将相应的编码映射到图片的相应位置实现文字“写在图片上”(提取编码的转换映射)
第一部分:读取图片(文件读取)
可以利用opencv提供的函数cvLoadImage().(这里的将字库一并导入)
1 /****************************************************** 2 函数名称: openfile 3 函数功能: 打开字库和图片 4 传入参数: 5 返 回 值: 6 建立时间: 2018-05-07 7 修改时间: 8 建 立 人: 重交亲爸爸 9 修 改 人: 10 其它说明: 11 ******************************************************/ 12 void ShowName::openfile(){ 13 char pbuf[100]; 14 _getcwd(pbuf, 100); 15 strcat(pbuf, "/HZKf2424.hz"); 16 char pbufASC[100]; 17 _getcwd(pbufASC, 100); 18 strcat(pbufASC, "/Asci0816.zf"); 19 // 读取图片 20 if ((img = cvLoadImage("test.png")) == NULL)exit(1); 21 // 打开字体文件 22 if ((HZK24 = fopen(pbuf, "rb")) == NULL)exit(1); 23 //打开asci8*16文件 24 if ((ASI816 = fopen(pbufASC, "rb")) == NULL)exit(1); 25 }
这里提醒各位记得要将这些字库关闭。养成好的习惯
1 /****************************************************** 2 函数名称: ~ShowName 3 函数功能: 释放空间 4 传入参数: 5 返 回 值: 6 建立时间: 2018-05-07 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ******************************************************/ 12 ShowName::~ShowName(){ 13 cvReleaseImage(&img); 14 fclose(HZK24); 15 fclose(ASI816); 16 if (CONTERNER!=NULL) 17 fclose(CONTERNER); 18 img = NULL; 19 HZK24 = NULL; 20 ASI816 = NULL; 21 CONTERNER = NULL; 22 }
第二部分:读取文字并从字库中提取相应的编码(字库的存储原理)
这里先介绍下字库的存储方式:以下为引用的资料:
汉字点阵获取
1. 利用区位码获取汉字
汉字点阵字库是根据区位码的顺序进行存储的,因此,我们可以根据区位来
获取一个字库的点阵,它的计算公式如下:
点阵起始位置 = ((区码- 1)*94 + (位码 – 1)) * 汉字点阵字节数
获取点阵起始位置后,我们就可以从这个位置开始,读取出一个汉字的点阵。
2利用汉字机内码获取汉字
前面我们己经讲过,汉字的区位码和机内码的关系如下:
机内码高位字节 = 区码 + 20H + 80H(或区码 + A0H)
机内码低位字节 = 位码 + 20H + 80H(或位码 + AOH)
反过来说,我们也可以根据机内码来获得区位码:
区码 = 机内码高位字节 - A0H
位码 = 机内码低位字节 - AOH
将这个公式与获取汉字点阵的公式进行合并计就可以得到汉字的点阵位置。
3.以下为汉字的存储原理:
对于 24*24的点阵字库,存放格式如下: 纵向存放 3 个字节(24 位),横向存放24 个字节,每个字模占 72 个字节 字符排列顺序如下:
1 4 7 10 ......
2 5 8 11 ......
3 6 9 12 ......
对于 16*16 的点阵字库,存放格式如下: 横向存放 2 个字节(16 位),其中第二个字节没有多余的数据 纵向存放 16个字节,
每个字模占 32 个字节 字符排列顺序如下:
1 2
3 4
5 6
......
对于 14*14 的点阵字库,存放格式如下: 横向存放 2 个字节(16 位),其中第二个字节的后 2 位是多余的数据 纵向存放 14个字节,每个字模占 28 个字节
字符排列顺序如下:
1 2
3 4
5 6
......
对于 12*12 的点阵字库,存放格式如下: 横向存放 2 个字节(16 位),其中第二个字节的后 4 位是多余的数据 纵向存放 12个字节,每个字模占 24 个字节 字符排列顺序如下:
1 2
3 4
5 6 ......
******************************************************/资料到此
这个资料有点抽象,不过没关系。我们使用实例图片进行说话。
这个是8*16点阵的ASCII码字库
这个是16*16点阵汉字库的字
这个是24*24点阵字库的字
非常明显的看出8*16和16*16是正放的,而24*24的是侧着,而且是反过来的字体。
对于数字和英文来说,有ASCII作为背景,就比较简单。
其中数字的
点阵起始位置offset=incode[0]*16L(获取的ASCII码*16L就直接转换了)
1 /****************************************************** 2 函数名称: getasi 3 函数功能: 获取asci码 4 传入参数: 5 返 回 值: 6 建立时间: 2018-05-08 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ******************************************************/ 12 void ShowName::getasi(char incode[]){ 13 unsigned char qh, wh; 14 unsigned long offset; 15 offset = incode[0]*16L; 16 fseek(ASI816, offset, SEEK_SET); 17 fread(num_mat, 16, 1, ASI816); 18 }
但是对于24*24点阵字库的话,以上的原理就不适用了
对于区码位码的话有所改变:
区码 = 机内码高位字节 - AFH
位码 = 机内码低位字节 - AOH
对于寻找字库起始位置:
offset=offset = (94 * (qh - 1) + (wh - 1)) * 72L(72是怎么来的?24*24/8)
1 /****************************************************** 2 函数名称: get_mat 3 函数功能: 通过汉字的区码和位码进行写到mat中 4 传入参数: qh, wh 5 返 回 值: 6 建立时间: 2018-05-07 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ******************************************************/ 12 void ShowName::get_mat(unsigned char qh, unsigned char wh){ 13 long offset; 14 offset = (94 * (qh - 1) + (wh - 1)) * 72L; 15 // 读取数据存入数组 16 fseek(HZK24, offset, SEEK_SET); 17 fread(mat, 72, 1, HZK24); 18 }
第三部分:将相应的编码映射到图片的相应位置实现文字“写在图片上”(提取编码的转换映射)
将数字进行映射
1 /****************************************************** 2 函数名称: draw_code 3 函数功能: 绘制学号 4 传入参数: 5 返 回 值: 6 建立时间: 2018-05-08 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ******************************************************/ 12 void ShowName::draw_code(int num){ 13 int width, height; 14 width = img->width; 15 height = img->height; 16 // 开始的x y像素点 17 int start_x, start_y, size, current_start_x, current_start_y; 18 size = MAPSIZE; //+INTERSIZE; 19 //int numsize = 8; 20 start_x = width - sum_word * size;//开始位置一定要找好,确认开始位置 21 start_y = height - 16- INTERSIZE; 22 // 开始绘制 23 24 CvScalar cs; 25 for (int i = 0; i < 16; ++i) 26 for (int k = 0; k < 8; k++) 27 if ((num_mat[i]&(0x80>>k)) != NULL) 28 { 29 current_start_x = k + start_x + size * num; 30 current_start_y = start_y + i; 31 cs = cvGet2D(img, current_start_y, current_start_x);//获取图像相对位置的RGB的值 32 cs.val[0] = 0;//变黑 33 cs.val[1] = 0;//这里可以改成你喜欢的颜色 34 cs.val[2] = 0; 35 cvSet2D(img, current_start_y, current_start_x, cs);//重新设值 36 } 37 38 }
对汉字进行映射:这里需要在映射的时候翻个身子
1 /****************************************************** 2 函数名称: draw_name 3 函数功能: 通过汉字的区码和位码进行写到mat中 4 传入参数: qh, wh 5 返 回 值: 6 建立时间: 2018-05-07 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ******************************************************/ 12 void ShowName::draw_name(int num){ 13 // 图片的像素值 14 int width, height; 15 width = img->width; 16 height = img->height; 17 // 开始的x y像素点 18 int start_x, start_y, size, current_start_x, current_start_y; 19 size = MAPSIZE;// +INTERSIZE; 20 start_x = width - sum_word * size; 21 start_y = height - MAPSIZE - INTERSIZE; 22 // 开始绘制 23 24 CvScalar cs; 25 26 for (int i = 0; i < 24; ++i) 27 for (int j = 0; j < 3; ++j) 28 for (int k = 0; k < 8; k++) 29 if (((mat[i* 3 + j] >> (7 - k)) & 0x1) != NULL) 30 { 31 // 绘点 32 current_start_x = start_x + i + size * num;//24*24的是纵向排列的i对应的是x 33 current_start_y = start_y + j * 8 + k; 34 cs = cvGet2D(img, current_start_y, current_start_x); 35 cs.val[0] = 0; 36 cs.val[1] = 0; 37 cs.val[2] = 0; 38 cvSet2D(img, current_start_y, current_start_x, cs); 39 } 40 }
最后进行重新显示绘制好的图片
1 /****************************************************** 2 函数名称: Runtodraw 3 函数功能: 启动绘制 4 传入参数: 5 返 回 值: 6 建立时间: 2018-05-07 7 修改时间: 8 建 立 人: 9 修 改 人: 10 其它说明: 11 ******************************************************/ 12 13 void ShowName::Runtodraw(){ 14 unsigned char mask = 0x80; 15 char tmpcode[3] = { 0 }; 16 while (*Name!=NULL)//汉字转换过程 17 { 18 tmpcode[0] = *Name; 19 tmpcode[1] = *(Name + 1); 20 if (tmpcode[0] & mask){ 21 unsigned char qh, wh; 22 qh = tmpcode[0] - 0xaf; //求区码 23 wh = tmpcode[1] - 0xa0;//求位码 24 get_mat(qh,wh); 25 draw_name(current_num++);//画字 26 Name += 2; 27 } 28 } 29 while (*code!=NULL) 30 { 31 tmpcode[0] = *code; 32 if (tmpcode[0]) 33 { 34 getasi(code); 35 draw_code(current_num++); 36 code++; 37 } 38 } 39 cvShowImage("bt", img); 40 cvWaitKey(); 41 }
结果:
ps:本文这里采用的是c++的面向对象的方式进行写的。
如需要源码请转移至码云:https://gitee.com/cjqbaba/MediaTest/tree/textimage进行源码克隆下载
如有问题请留言评论。转载请注明出处,谢谢。