嵌入式系统基础:点阵汉字的字模读取与显示
點陣漢字的字模讀取與顯示
虛擬機:VitrualBox 6
系統:Ubuntu 18
函數庫:OpenCV 3.4(涉及到圖像編程)
文章目錄
- 點陣漢字的字模讀取與顯示
- 一、漢字點陣字庫原理
- 1. 漢字編碼與漢字點陣獲取
- 1.1 區位碼:
- 區位碼來獲取字庫點陣
- 1.2 機內碼:
- 機內碼獲取字庫點陣
- 2. 點陣字庫結構
- 2.1 點陣字庫存儲
- 2.2 16*16 點陣字庫
- 2.3 14 *14 與 12 *12 點陣字庫
- 二、使用OpenCV庫讓漢字點陣呈現在圖像上
- 1. 建立項目
- 2. 編寫代碼
- 三·、總結
- 四、參考文章
一、漢字點陣字庫原理
在嵌入式設備 LCD 上顯示的漢字大多數都屬于點陣漢字。常用的點陣字庫來自 UCDOS。大家可以去網上下載一個 UCDOS 的完全安裝版本,里面可以找到很多點陣字庫 文件。下面幾個字庫文件是常用的:
- HZK12 : 12 點陣漢字庫(寬度 x 高度 = 12x12)
- ASC12 : 12 點陣 ASCII 字庫(寬度 x 高度 = 6x12)
- HZK16 : 16 點陣漢字庫(寬度 x 高度 = 16x16) 最常用的中文字庫
- ASC16 : 16 點陣 ASCII 字庫(寬度 x 高度 = 8x16)最常用的 ASCII 字庫
- HZK24 : 24 點陣漢字庫(寬度 x 高度 = 24x24) 票據打印機用得較多
1. 漢字編碼與漢字點陣獲取
1.1 區位碼:
在國標 GD2312—80 中規定,所有的國標漢字及符號分配在一個 94 行、94 列的方 陣中,方陣的每一行稱為一個“區”,編號為 01 區到 94 區,每一列稱為一個“位”,編號為 01 位到 94 位,方陣中的每一個漢字和符號所在的區號和位號組合在一起形成的四個阿拉伯數字就是它們的“區位碼”。區位碼編輯規則:區位碼的前兩位是它的區號,后兩位是它的位號。用區位碼就 可以唯一地確定一個漢字或符號,反過來說,任何一個漢字或符號也都對應著一個唯一的 區位碼。
漢字“母”字的區位碼是 3624,表明它在方陣的 36 區 24 位,問號“?”的區位碼為 0331,則它在 03 區 3l 位。
區位碼來獲取字庫點陣
漢字點陣字庫是根據區位碼的順序進行存儲的,因此,我們可以根據區位來獲取一個字庫的點陣,它的計算公式如下:
*點陣起始位置 = ((區碼- 1)94 + (位碼 – 1)) * 漢字點陣字節數
獲取點陣起始位置后,我們就可以從這個位置開始,讀取出一個漢字的點陣。
1.2 機內碼:
漢字的機內碼是指在計算機中表示一個漢字的編碼。機內碼與區位碼稍有區別。如上所 述,漢字區位碼的區碼和位碼的取值均在 1~94 之間,如直接用區位碼作為機內碼,就會 與基本 ASCII 碼混淆。為了避免機內碼與基本 ASCII 碼的沖突,需要避開基本 ASCII 碼 中的控制碼(00H~1FH),還需與基本 ASCII 碼中的字符相區別。為了實現這兩點,可以 先在區碼和位碼分別加上 20H,在此基礎上再加 80H(此處“H”表示前兩位數字為十六進制 數)。經過這些處理,用機內碼表示一個漢字需要占兩個字節,分別 稱為高位字節和低位字節。
這兩位字節的機內碼編輯規則:
高位字節 = 區碼 + 20H + 80H(或區碼 + A0H)
低位字節 = 位碼 + 20H + 80H(或位碼 + A0H)
機內碼 = 高位字節H + 低位字節H
由于漢字的區碼與位碼的取值范圍的十六進制數均為 01H~5EH(即十進制的 01~9 4),所以漢字的高位字節與低位字節的取值范圍則為 A1H~FEH(即十進制的 161~254)。 例如,漢字“啊”的區位碼為 1601,區碼和位碼分別用十六進制表示即為 1001H,它 的機內碼的高位字節為 B0H,低位字節為 A1H,機內碼就是 B0A1H。
機內碼獲取字庫點陣
反過來說,我們也可以根據機內碼來獲得區位碼:
區碼 = 機內碼高位字節 - A0H
位碼 = 機內碼低位字節 - A0H
將這個公式與獲取漢字點陣的公式進行合并計就可以得到漢字的點陣位置。
2. 點陣字庫結構
2.1 點陣字庫存儲
在漢字的點陣字庫中,每個字節的每個位都代表一個漢字的一個點,每個漢 字都是由一個矩形的點陣組成,0 代表沒有,1 代表有點,將 0 和 1 分別用不同 顏色畫出,就形成了一個漢字,常用的點陣矩陣有 12x12, 14x14, 16x16 三 種字庫。
字庫根據字節所表示點的不同有分為橫向矩陣和縱向矩陣,目前多數的字庫 都是橫向矩陣的存儲方式(用得最多的應該是早期 UCDOS 字庫),縱向矩陣一 般是因為有某些液晶是采用縱向掃描顯示法,為了提高顯示速度,于是便把字庫 矩陣做成縱向,省得在顯示時還要做矩陣轉換。我們接下去所描述的都是指橫向矩陣字庫。
2.2 16*16 點陣字庫
對于 16x16 的矩陣來說,它所需要的位數共是 16*16=256 個位,每個字 節為 8 位,因此,每個漢字都需要用 256/8=32 個字節來表示。
即每兩個字節代表一行的 16 個點,共需要 16 行,顯示漢字時,只需一次 性讀取 32 個字節,并將每兩個字節為一行打印出來,即可形成一個漢字。
點陣結構如下圖所示:
2.3 14 *14 與 12 *12 點陣字庫
對于 1414 和 1212 的字庫,理論上計算,它們所需要的點陣分別為(14 14/8)=25, (1212/8)=18 個字節,但是,如果按這種方式來存儲,那么取 點陣和顯示時,由于它們每一行都不是 8 的整位數,因此就會涉到點陣的計 算處理問題,會增加程序的復雜度,降低程序的效率。
方式來存儲,那么取 點陣和顯示時,由于它們每一行都不是 8 的整位數,因此就會涉到點陣的計 算處理問題,會增加程序的復雜度,降低程序的效率。
為了解決這個問題,有些點陣字庫會將 1414 和 1212 的字庫按 1614 和 1612 來存儲,即每行還是按兩個字節來存儲,但是 1414 的字庫,每 兩個字節的最后兩位是沒有使用,1212 的字節,每兩字節的最后 4 位是沒有 使用,這個根據不同的字庫會有不同的處理方式,所以在使用字庫時要注意這個 問題,特別是 14*14 的字庫。
二、使用OpenCV庫讓漢字點陣呈現在圖像上
1. 建立項目
通過控制臺命令或者直接點擊操作創建一個新建文件夾,并將事先準備好的漢字點陣庫和需要打印的LOGO文本以及圖片放入文件夾中:
其中 logo文件中只有一行
圖片大小是350*350,小圖片是便于寫代碼時設置點陣起始位置
可以使用虛擬機與主機間的共享文件來快速完成這一步。
2. 編寫代碼
進入項目文件目錄后使用此命令創建并編輯代碼
gedit CN.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); //繪制起點坐標 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); //將漢字弄上圖片,x,y表示起點位置坐標,以圖片左上角為0點 int main(){String image_path="/home/karmen/ChineseTest/IMAGE.png"; //通過絕對路徑定位圖像文件char* logo_path=(char*)"/home/karmen/ChineseTest/logo.txt"; //通過絕對路徑定位文本文件put_text_to_image(60,270,image_path,logo_path);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("/home/karmen/ChineseTest/Asci0816.zf", "rb")) == NULL){printf("Can't open ASCII.zf,Please check the path!");exit(0);}fseek(ASCII, offset, SEEK_SET); //移動ASCII的讀寫指針SEEK_SET偏移offsetfread(buff, 16, 1, ASCII); //從ASCII文件流中讀取1個16大小的單元到buff中去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(63, 72, 204), -1);p1.x++;circle(image, p1, 0, Scalar(63, 72, 204), -1);p1.y++;circle(image, p1, 0, Scalar(63, 72, 204), -1);p1.x--;circle(image, p1, 0, Scalar(63, 72, 204), -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("/home/karmen/ChineseTest/HZKs2424.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(167, 132, 171), -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 length=18;//要打印的字符長度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<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個字節來表示如趙字區位碼是5352十六進制位3534機內碼就是d5d4d5-af=38(十進制),因為是從漢字啊開始的,所以減去的是af而不是a0,38+15等于53與區碼相對應d4-a0=52*/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;}}cv::imshow("image", image);cv::waitKey(); }寫入之后保存并退出編輯器
接著輸入下列代碼生成可執行文件:
編譯如果未報錯便輸入下列命令運行:
./CN
得到如圖所示的效果
三·、總結
通過本次實驗筆者學到了漢字點陣的原理,以及機內碼,區位碼的編輯規則,并且鞏固了OpenCV圖像編程的庫函數。
四、參考文章
平行葉子: Ubuntu+OpenCV學習漢字點陣
一只特立獨行的豬: 點陣漢字的字模讀取與顯示
總結
以上是生活随笔為你收集整理的嵌入式系统基础:点阵汉字的字模读取与显示的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转自网络
- 下一篇: 获取客户端浏览器信息