文章目录

  • 一. 汉字点阵字库实现原理
    • 1. 汉字编码
      • 1.1 区位码
      • 1.2 机内码
    • 2. 点阵字库结构
      • 2.1 点阵字库存储
      • 2.2 16*16点阵字库
    • 3. 汉字点阵获取
      • 3.1 利用区位码获取汉字
      • 3.2 利用汉字机内码获取汉字
  • 二. 代码实现
    • 1. C++
    • 2. python
  • 三. 总结

环境:

  • ubuntu18.04 desktop
  • opencv 3.4.15

opencv是不支持中文的。

这里C++代码是采用替换原图的像素点来实现的,实现之前我们先了解一下汉字点阵字库。

python代码是采用将opencv格式转为PIL格式实现的。

不想了解汉字点阵字库的,可以直接看代码

一. 汉字点阵字库实现原理

1. 汉字编码

1.1 区位码

在国标 GD2312—80 中规定,所有的国标汉字及符号分配在一个 94 行、94 列的方阵中,方阵的每一行称为一个“区”,编号为 01 区到 94 区,每一列称为一个“位”,编号为 01 位到 94 位,方阵中的每一个汉字和符号所在的区号和位号组合在一起形成的四个阿拉伯数字就是它们的“区位码”。

区位码的前两位是它的区号,后两位是它的位号。

用区位码就可以唯一地确定一个汉字或符号,反过来说,任何一个汉字或符号也都对应着一个唯一的 区位码。

汉字“母”字的区位码是 3624,表明它在方阵的 36 区 24 位,问号“?”的区位码为 0331,则它在 03 区 3l 位。

1.2 机内码

汉字的机内码是指在计算机中表示一个汉字的编码。

机内码与区位码稍有区别

如上所述,汉字区位码的区码和位码的取值均在 1-94 之间,如直接用区位码作为机内码,就会与基本 ASCII 码混淆。

为了避免机内码与基本 ASCII 码的冲突,需要避开基本 ASCII 码中的控制码(00H~1FH),还需与基本 ASCII 码中的字符相区别。

为了实现这两点,可以先在区码和位码分别加上 20H,在此基础上再加 80H(此处“H”表示前两位数字为十六进制数)。经过这些处理,用机内码表示一个汉字需要占两个字节,分别称为高位字节和低位字节,这两位字节的机内码按如下规则表示:

高位字节 = 区码 + 20H + 80H(或区码 + A0H)

低位字节 = 位码 + 20H + 80H(或位码 + AOH)

由于汉字的区码与位码的取值范围的十六进制数均为 01H~5EH(即十进制的 01~94),所以汉字的高位字节与低位字节的取值范围则为 A1H~FEH(即十进制的 161~254)。 例如,汉字“啊”的区位码为 1601,区码和位码分别用十六进制表示即为 1001H,它 的机内码的高位字节为 B0H,低位字节为 A1H,机内码就是 B0A1H。

2. 点阵字库结构

2.1 点阵字库存储

在汉字的点阵字库中,每个字节的每个位都代表一个汉字的一个点,每个汉字都是由一个矩形的点阵组成,0 代表没有,1 代表有点,将 0 和 1 分别用不同 颜色画出,就形成了一个汉字,常用的点阵矩阵有 12 * 12, 14 * 14, 16 * 16 三种字库。

字库根据字节所表示点的不同有分为横向矩阵和纵向矩阵

目前多数的字库都是横向矩阵的存储方式(用得最多的应该是早期 UCDOS 字库),纵向矩阵一般是因为有某些液晶是采用纵向扫描显示法,为了提高显示速度,于是便把字库矩阵做成纵向,省得在显示时还要做矩阵转换。我们接下去所描述的都是指横向矩阵字库。

2.2 16*16点阵字库

对于 16*16 的矩阵来说,它所需要的位数共是 16*16=256 个位,每个字节为 8 位,因此,每个汉字都需要用 256/8=32 个字节来表示。 即每两个字节代表一行的 16 个点,共需要 16 行,显示汉字时,只需一次性读取 32 个字节,并将每两个字节为一行打印出来,即可形成一个汉字。 点阵结构如下图所示:

3. 汉字点阵获取

3.1 利用区位码获取汉字

汉字点阵字库是根据区位码的顺序进行存储的,因此,我们可以根据区位来获取一个字库的点阵,它的计算公式如下:

*点阵起始位置 = ((区码- 1)94 + (位码 – 1)) * 汉字点阵字节数

获取点阵起始位置后,我们就可以从这个位置开始,读取出一个汉字的点阵。

3.2 利用汉字机内码获取汉字

前面我们己经讲过,汉字的区位码和机内码的关系如下:

机内码高位字节 = 区码 + 20H + 80H(或区码 + A0H)

机内码低位字节 = 位码 + 20H + 80H(或位码 + AOH)

反过来说,我们也可以根据机内码来获得区位码:

区码 = 机内码高位字节 - A0H

位码 = 机内码低位字节 - AOH

将这个公式与获取汉字点阵的公式进行合并计就可以得到汉字的点阵位置。

二. 代码实现

1. C++

  • 创建文件夹 DrawChinese 并进入此文件夹

  • 准备中文点阵字库,以及文本文件、图片。

中文点阵字库及显示工具程序百度网盘

链接:https://pan.baidu.com/s/1gCtiVbHrr2y6teo-bG8rEw
提取码:2413

windows上文本文件默认编码是ANSI。
ANSI即扩展的ASCII编码,简体中文操作系统中ANSI编码代表GB2312编码。linux系统默认编码UTF-8,两种编码对中文的处理不一致,因此在linux中显示中文乱码。

因为代码是采用ascii字库以及hz中文点阵字库,中文字库编码基本采用GB2312(扩展的ASCll编码),所以不能将ANSI改为UTF-8。

  • 在当前文件夹里创建文件 drawchinese.cpp,编写代码

c++的代码不是我编的,因为c++的opencv过于复杂。
参考我们老师给的资料。

 vim drawchinese.cpp
#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);
//绘制字符,采用ASCll字库
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,char* logo_path);int main()
{   string imagePath="liqin.jpg";char* txtPath=(char*)"logo.txt";//参数分别为:文本添加的位置(x,y)、图片路径、所添加的文本路径put_text_to_image(100,250,imagePath,txtPath);     return 0;
}//绘制字符
void paint_ascii(Mat& image,int x_offset,int y_offset,unsigned long offset){Point p;p.x = x_offset;p.y = y_offset;//存放ascii字膜char buff[16];           //打开ascii字库文件FILE *ASCII;if ((ASCII = fopen("Asci0816.zf", "rb")) == NULL){printf("Can't open ascii.zf,Please check the path!");//getch();exit(0);}fseek(ASCII, offset, SEEK_SET);fread(buff, 16, 1, ASCII);int i, j;Point p1 = p;for (i = 0; i<16; i++)                  //十六个char{p.x = x_offset;for (j = 0; j < 8; j++)              //一个char八个bit{p1 = p;if (buff[i] & (0x80 >> j))    /*测试当前位是否为1*/{/*由于原本ascii字膜是8*16的,不够大,所以原本的一个像素点用4个像素点替换,替换后就有16*32个像素点ps:感觉这样写代码多余了,但目前暂时只想到了这种方法*/circle(image, p1, 0, Scalar(0, 0, 255), -1);p1.x++;circle(image, p1, 0, Scalar(0, 0, 255), -1);p1.y++;circle(image, p1, 0, Scalar(0, 0, 255), -1);p1.x--;circle(image, p1, 0, Scalar(0, 0, 255), -1);}                     p.x+=2;            //原来的一个像素点变为四个像素点,所以x和y都应该+2}p.y+=2;}
}
//绘制汉字
void paint_chinese(Mat& image,int x_offset,int y_offset,unsigned long offset){Point p;p.x=x_offset;p.y=y_offset;FILE *HZK;char buff[72];//72个字节,用来存放汉字的if((HZK=fopen("HZKf2424.hz","rb"))==NULL){printf("Can't open HZKf2424.hz,Please check the path!");exit(0);//退出}fseek(HZK, offset, SEEK_SET);/*将文件指针移动到偏移量的位置*/fread(buff, 72, 1, HZK);/*从偏移量的位置读取72个字节,每个汉字占72个字节*/bool mat[24][24];//定义一个新的矩阵存放转置后的文字字膜int i,j,k;for (i = 0; i<24; i++)                 /*24x24点阵汉字,一共有24行*/{for (j = 0; j<3; j++)                /*横向有3个字节,循环判断每个字节的*/for (k = 0; k<8; k++)              /*每个字节有8位,循环判断每位是否为1*/if (buff[i * 3 + j] & (0x80 >> k))    /*测试当前位是否为1*/{mat[j * 8 + k][i] = true;          /*为1的存入新的字膜中*/}else {mat[j * 8 + k][i] = false;}}for (i = 0; i < 24; i++){p.x = x_offset;for (j = 0; j < 24; j++){      if (mat[i][j])circle(image, p, 1, Scalar(255, 0, 0), -1);         //写(替换)像素点p.x++;                                                //右移一个像素点}p.y++;                                                    //下移一个像素点}
}
//在图片上添加文本
void put_text_to_image(int x_offset,int y_offset,String image_path,char* logo_path)
{//x和y就是第一个字在图片上的起始坐标//通过图片路径获取图片Mat image=imread(image_path);int text_length=17;         //文本的字节长度unsigned char qh,wh;      //定义区号,位号unsigned long offset;           //偏移量unsigned char hexcode[30];      //用于存放记事本读取的十六进制,记得要用无符号FILE* file_logo;if ((file_logo = fopen(logo_path, "rb")) == NULL){printf("Can't open txtfile,Please check the path!");//getch();exit(0);}fseek(file_logo, 0, SEEK_SET);fread(hexcode, length, 1, file_logo);int x =x_offset,y = y_offset;//x,y:在图片上绘制文字的起始坐标for(int m=0;m<text_length;){if(hexcode[m]==0x23){break;//读到#号时结束}else if(hexcode[m]>0xaf){qh=hexcode[m]-0xaf;//使用的字库里是以汉字啊开头,而不是以汉字符号开头wh=hexcode[m+1] - 0xa0;//计算位码offset=(94*(qh-1)+(wh-1))*72L;paint_chinese(image,x,y,offset);/*计算在汉字库中的偏移量对于每个汉字,使用24*24的点阵来表示的一行有三个字节,一共24行,所以需要72个字节来表示*/m=m+2;//一个汉字的机内码占两个字节,x+=24;//一个汉字为24*24个像素点,由于是水平放置,所以是向右移动24个像素点}else{//当读取的字符为ASCII码时wh=hexcode[m];offset=wh*16l;//计算英文字符的偏移量paint_ascii(image,x,y,offset);m++;//英文字符在文件里表示只占一个字节,所以往后移一位就行了x+=16;}}imwrite("image_text.jpg",image);    //保存为新图片 image_text.jpgimshow("image",image);          //展示图片waitKey(0);
}

注意函数 void put_text_to_image() 中的
int text_length=17; //文本的字节长度,
改成自己的文本长度

字节长度计算:汉字两个字节,空格以及字符一个字节
如果按照这样计算,最后显示的文本还有多余,则你可能需要考虑文本内容是全角还是半角的问题。全角就是每个字符占两个字节。

  • 编译代码
g++ drawchinese.cpp -o test `pkg-config --cflags --libs opencv`

  • 执行程序,输出结果
./test

2. python

这里还是选择在 DrawChinese 文件夹中

准备字体,我采用simhei字体

百度网盘链接:https://pan.baidu.com/s/15SfvOaAUHnoVTjfhtUAwaA
提取码:7qzy

创建文件,编写代码

这个代码是我以前学python的时候编的,正好用上了,哈哈。

vim drawchinese.py
from PIL import Image, ImageDraw, ImageFont
import cv2
import numpy as np# cv2读取图片
img = cv2.imread('liqin.jpg') # 名称不能有汉字
cv2img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # cv2和PIL中颜色的hex码的储存顺序不同
#将cv2格式转化为PIL格式
pilimg = Image.fromarray(cv2img)# PIL图片上打印汉字
draw = ImageDraw.Draw(pilimg) # 图片上打印
# 参数1:字体文件路径,参数2:字体大小 ,参数3:编码格式
font = ImageFont.truetype("simhei.ttf", 60, encoding="utf-8")
# 参数1:打印坐标,参数2:文本,参数3:字体颜色,参数4:字体
draw.text((150, 250), "Hi,我是缔宇", (255, 0, 0), font=font) # PIL图片转cv2 图片
# cv2.cvtColor颜色空间转换函数,第一个参数是需要转换的图片,第二个参数是转换成何种格式,这里是将RGB换成BGR
cv2charimg = cv2.cvtColor(np.array(pilimg), cv2.COLOR_RGB2BGR)cv2.imwrite("image_python.jpg",cv2charimg)   #保存图片
cv2.imshow("photo", cv2charimg)   #展示图片
cv2.waitKey (0)
cv2.destroyAllWindows()

运行程序

python3 drawchinese.py

三. 总结

运用opencv添加中文,在C++中虽然也有多种方法添加中文,但都很复杂,而python的方法就很简单了。

opencv在图片上添加中文汉字(c++以及python)相关推荐

  1. JAVA Opencv在图片上添加中文

    问题描述: 将图片进行均值.中值.高斯滤波,高斯边缘检测,并在图片上添加中文文字. 一.算法思想 首先经过opencv的一系列操作,例如高斯模糊.均值模糊等操作后.用Imgcodecs.imwrite ...

  2. opencv在图片上添加文字

    import cv2img=cv2.imread('1.jpg',cv2.IMREAD_COLOR) # 打开文件font = cv2.FONT_HERSHEY_DUPLEX # 设置字体# 图片对象 ...

  3. 图片上绘制点阵汉字(C++)

    在Ubuntu下用C/C++(或python) 调用opencv库编程显示一张图片,并打开一个名为"logo.txt"的文本文件(其中只有一行文本文件,包括你自己的名字和学号),按 ...

  4. 基于Ubuntu系统,调用opencv在图片上显示数字和汉字

    文章目录 一.汉字编码的介绍 1.汉字编码的发展过程 (1)汉字编码产生的原因 (2)常用的汉字编码 2.区位码 (1)产生原因 (2)概念简述 (3)区域分布情况 3.机内码 (1)基本概念 (2) ...

  5. 基于Ubuntu系统调用opencv——在图片上显示汉字和数字

    文章目录 一. 认识汉字编码 (1)区位码 (2)机内码 (3)点阵字库结构 (4)汉字点阵获取 二. 实例--调用opencv在图片上显示文字 (1)准备工作 (2)撰写代码 (3)编译运行 三. ...

  6. opencv怎么在图片上添加文字?

    在 OpenCV 中,可以使用 cv2.putText() 函数在图像上添加文本.这个函数需要指定文本.文本位置.字体.字体比例.颜色.线宽等参数. opencv怎么在图片上添加文字? 下面是一个示例 ...

  7. python 使用opencv在图片上绘制矩形、圆形以及中英文

    最近看了下python的第三方库opencv,在python中用来对图片进行一些简单处理还是非常好用的,比如在图形上画矩形框,圆框,指示线,椭圆以及文字等,只需要调用几个相应的函数,设置好起止坐标点以 ...

  8. 通过opencv在图片上加上文字_基于Ubuntu16

    本文主要介绍,如何通过**C++**语言在Ubuntu操作系统下的OpenCV中将一个图片叠加上文字 通过opencv在图片上叠加上文字 一.汉字点阵字库原理 1.汉字的三种编码 1.1区位码 1.2 ...

  9. python怎么加图片_怎么在图片旁边加文字 python如何在图片上添加文字 - 励志 - 52资讯网...

    Python在图片上添加文字的两种方法:OpenCV和PIL 一.OpenCV方法 1.安装cv2 pip install opencv-python 2.利用putText方法来实现在图片的指定位置 ...

最新文章

  1. 快速排序的三种实现方法
  2. Subversion的权限控制
  3. python基本数据类型及语法
  4. linux 查找文件_LINUX常用命令全集
  5. 无向图中两点之间的距离_九上数学:二次函数图像,一动点到两定点距离和最小...
  6. 48.怎样消除桌面图标上的小箭头:
  7. RK3288_Android7.1接eDP屏休眠之后led状态灯没有亮红色
  8. ai人工智能可以干什么_我们可以使人工智能更具道德性吗?
  9. 7.4 倒向随机微分方程-Feynman-Kac公式
  10. ENSP教程---配置DHCP
  11. day2 craps赌博游戏
  12. 永恒之蓝攻击处置方法
  13. 烤仔TVのCCW | 智能合约间的四种调用(下)
  14. SqlServer误删数据恢复
  15. btwdins.exe
  16. 全程15分钟 详解如何为MacBook Pro 15寸 加装固态硬
  17. 飞飞CMS最新版本DC04电脑端网站模板
  18. 介绍Namada:链间资产不可知(Asset-agnostic)隐私
  19. Adobe Illustrator CC 2018制作七巧板
  20. PTA 使我精神焕发

热门文章

  1. 《Netty+JavaFx实战:仿桌面版微信聊天》代码开源、上云部署、视频讲解,只为让你给点个Star!
  2. GNU开发工具简介(一)
  3. 安装python3出现报错信息:configure:error:cannot run C complied programs
  4. “经济逆风”袭来 十年轮回IT行业再入下行周期(草根网)
  5. 计算机基础---电脑的原理
  6. 数据结构——【队列】知识介绍+基本操作代码
  7. Linux nl 命令
  8. 快递分拣机器人系统运用了哪些计算机技术,自动分拣十几秒完成 快递机器人何日满城“跑”...
  9. Android系统开发 默认壁纸的定制 主题风格的开发及定制 DDMS 常用adb 命令 抓取Log
  10. 北京中医药大学22春《中医基础理论Z》平时作业5【专职辅导答案】