本文主要介绍,如何通过**C++**语言在Ubuntu操作系统下的OpenCV中将一个图片叠加上文字
通过opencv在图片上叠加上文字
一、汉字点阵字库原理
1.汉字的三种编码
1.1区位码
GB2312是一种中文的编码方式,具体是由区位码来实现的。GB2312将所有的汉字编入一个 94*94 的二维表, 行就是 “区”、列就是 “位”, 其中的一个汉字由区、位唯一定位,其对应的区、位合并就为区位码。
譬如 “万” 字在 45 区 82 位, 所以 “万” 字的区位码是: 4582.
00-09 区(682个): 是符号、数字、英文字符…制表符等;
10-15 区: 空白, 留待扩展;
16-55 区(3755个): 常用汉字(也有叫一级汉字), 按拼音排序;
56-87 区(3008个): 非常用汉字(也有叫二级汉字), 这是按部首排序的;
88-94 区: 空白, 留待扩展.
1.2 国标码
将 “区” 和 “位” 分别加上32(十六进制表示: 20H) 作为国标码
这样我们可以算出(45+32, 82+32):
“万” 字的国标码是 77114(4D72.H)
77 = 0111 0111
114 = 0111 0010
1.3内码
不过这还不能在计算机上使用,因为这样会和早已通用的 ASCII 码混淆(导致乱码)。譬如: 77 是 ASCII 的 “M”, 114 是 ASCII 的 “r”。所以又有规定把每个字节的最高位都从 0 换成 1(这之前它们都是 0),或者说把每个字节(区和位)都再加上 128(十六进制的: 80H),从而得到 “机内码”, 也就是前面所说的 “内码”。
77 + 128 = 205 = CD
114+ 128 = 242 = F2
打开记事本输入 “万” 字,保存(编码选择 ANSI); 然后用二进制编辑器(譬如:UltraEdit) 打开,会看到: CD F2, 这就是 “万” 字的内码!
1.4编码总结
总结一下:从区位码(国家标准定义) —> 区和位分别 +32(20H) 得到国标码—> 再分别 +128 (80H)得到内码(与ACSII也不再混淆); 区位码的区和位分别 +160 即可得到内码。用十六进制表示:区位码 + A0A0H = 内码。
为什么要加上2020H和8080H?
区位码、内码、国标码怎么转换非常简单,但是令人迷惑的是为什么要那么转换?这种转换不可能平白无故地那样转换!我搜索很多资料,找到最好的解释,总结如下:
首先,注意到一点,GB2312虽说是对中文编码,但是里面有对26个英文字母和一些特殊符号的编码,按理说这和ASCII重合的部分应该无需设置,沿用ASCII中不就行了?但是当时在制定GB2312之前,就决定覆盖掉ASCII中符号和英文字母部分,所以将其中的英文字母和符号重新编入GB2312中。而对于ASCII中前32个控制字符则继续沿用。所以保留前32字符,就需要将汉字编码向后偏移32,十六进制20H,这也就是区位码要加上20H得到国标码,这就是GB2312的编码规范
而这样产生一个弊端,某些早期用ASCII码编码的英文文章无法打开,一打开就是乱码,也就是说应该要兼容早期ASCII码而不是覆盖它!为了解决这个问题,将字节的最高位设为1,因为ASCII中使用7位,最高位为0。这样就区分开了ASCII和GB2312。这也是为什么要加上8080H。
其实我们说国标码才是GB2312的规范编码,后来的内码是微软为了解决冲突问题而采用的方式,本质上是修改了GB2312的编码标准,而这种方法最后产生的编码最后就被一些教科书称为内码。
2. 点阵字库结构
2.1 点阵字库存储
在汉字的点阵字库中,每个字节的每个位都代表一个汉字的一个点,每个汉字都是由一个矩形的点阵组成,0 代表没有,1 代表有点,将 0 和 1 分别用不同颜色画出,就形成了一个汉字,常用的点阵矩阵有 1212, 1414, 16*16 三 种字库。
字库根据字节所表示点的不同有分为横向矩阵和纵向矩阵,目前多数的字库都是横向矩阵的存储方式(用得最多的应该是早期 UCDOS 字库),纵向矩阵一 般是因为有某些液晶是采用纵向扫描显示法,为了提高显示速度,于是便把字库矩阵做成纵向,省得在显示时还要做矩阵转换。我们接下去所描述的都是指横向矩阵字库。
2.2 16*16 点阵字库
对于 1616 的矩阵来说,它所需要的位数共是 1616=256 个位,每个字节为 8 位,因此,每个汉字都需要用 256/8=32 个字节来表示。
即每两个字节代表一行的 16 个点,共需要 16 行,显示汉字时,只需一次性读取 32 个字节,并将每两个字节为一行打印出来,即可形成
2.3 1414 与 1212 点阵字库
对于 1414 和 1212 的字库,理论上计算,它们所需要的点阵分别为(1414/8)=25, (1212/8)=18 个字节,但是,如果按这种方式来存储,那么取点阵和显示时,由于它们每一行都不是 8 的整位数,因此,就会涉到点阵的计算处理问题,会增加程序的复杂度,降低程序的效率。
为了解决这个问题,有些点阵字库会将 1414 和 1212 的字库按 1614和 1612 来存储,即,每行还是按两个字节来存储,但是 1414 的字库,每两个字节的最后两位是没有使用,1212 的字节,每两字节的最后 4 位是没有使用,这个根据不同的字库会有不同的处理方式,所以在使用字库时要注意这个问题,特别是 14*14 的字库。
3.汉字点阵的获取
3.1 利用区位码获取汉字
汉字点阵字库是根据区位码的顺序进行存储的,因此,我们可以根据区位来获取一个字库的点阵,它的计算公式如下:
点 阵 起 始 位 置 = ( ( 区 码 − 1 ) ∗ 94 + ( 位 码 – 1 ) ) ∗ 汉 字 点 阵 字 节 数 点阵起始位置 \ = \ ((区码- 1) \ *94 \ + \ (位码 – 1)) \ * \ 汉字点阵字节数
点阵起始位置 = ((区码−1) ∗94 + (位码–1)) ∗ 汉字点阵字节数
获取点阵起始位置后,我们就可以从这个位置开始,读取出一个汉字的点阵。
3.2 利用汉字机内码获取汉字
前面我们己经讲过,汉字的区位码和机内码的关系如下:
高 位 字 节 = 区 码 + 2 0 H + 8 0 H ( 或 区 码 + A 0 H ) 低 位 字 节 = 位 码 + 2 0 H + 8 0 H ( 或 位 码 + A 0 H ) 高位字节 = 区码 + 2 \ 0H + 8 \ 0H(或区码 + A \ 0H) \ 低位字节 = 位码 + 2 \ 0H + 8 \ 0H(或位码 + A \ 0H)
高位字节=区码+2 0H+8 0H(或区码+A 0H)
低位字节=位码+2 0H+8 0H(或位码+A 0H)
反过来说,我们也可以根据机内码来获得区位码:
区 码 = 机 内 码 高 位 字 节 − A 0 H 位 码 = 机 内 码 低 位 字 节 − A 0 H 区码 = 机内码高位字节 - A \ 0H \ 位码 = 机内码低位字节 - A \ 0H
区码=机内码高位字节−A 0H
位码=机内码低位字节−A 0H
将这个公式与获取汉字点阵的公式进行合并计就可以得到汉字的点阵位置。
二丶在Ubuntu中使用opencv在图上叠加文字
首先我们得准备一个图片文件和一个包含自己名字的txt文件
图片:
txt:
创建一个C++文件,输入以下代码
其中文件的位置范围为(0-1500,0-1000)是从上到下从左到右。
#include<iostream>
#include<opencv/cv.h>
#include"opencv2/opencv.hpp"
#include<opencv/cxcore.h>
#include<opencv/highgui.h>
#include<math.h>
using namespace cv;
using namespace std;
void paint_chinese(Mat& image,int x_offset,int y_offset,unsigned long offset);
void paint_ascii(Mat& image,int x_offset,int y_offset,unsigned long offset);
void put_text_to_image(int x_offset,int y_offset,String image_path,