汉字点阵原理字模读取与显示
目錄
- 一、漢字點陣字庫原理
- (一)漢字編碼
- (二)點陣字庫結構
- (三)漢字點陣獲取
- 二、點陣漢字的字模讀取與顯示
- 三、總結
- 參考文獻
一、漢字點陣字庫原理
(一)漢字編碼
- 國家標準信息交換用漢字字符集GB 2312-80共收錄了漢字、圖形符號等共7445個,其中漢字6763個,按照漢字使用的頻度分為兩級,其中一級漢字3755個,二級漢字3008個。漢字、圖形符號根據其位置將其分為94個“區”,每個區包含94個漢字字符,每個漢字字符又稱為“位”。其中“區”的序號由01區至94區,“位”的序號也由01位至94位。若以橫向表示“位”號,縱向表示“區” 號,則“區”和“位”構成一個二維坐標。給定一個“區”值和“位”值就可以確定一個惟一的漢字或圖形符號。即4位阿拉伯數字就可以惟一地確定一個漢字或符號。如“北”字的區位碼是“1717”,而京字的區位碼是“3009”。前兩位是“區”號,后兩位是“位”號。其中1至15區是各種圖形符號、制表符和一些主要國家的語言字母,16區至87區是漢字,其中16區至55區是一級漢字,56至87區是二級漢字。
- UCDOS軟件中的文件HZK16和文件ASC16分別為16×16的國際漢字點陣文件和8×16的ASCII碼點陣文件,HZK16中按漢字區位碼從小到大依次存放國標區位碼表中的所有漢字,每個漢字占用32字節,每個區為94個漢字。而asc16文件中按ascii碼從小到大依次存有8×16的ASCII碼點陣,每個ASCII碼占用16字節。
1.區位碼
在國標GB2312—80中規定,所有的國標漢字及符號分配在一個94行、94列的方陣中,方陣的每一行稱為一個“區”,編號為01區到94區,每一列稱為一個“位”,編號為01位到94位,方陣中的每一個漢字和符號所在的區號和位號組合在一起形成的四個阿拉伯數字就是它們的“區位碼”。區位碼的前兩位是它的區號,后兩位是它的位號。用區位碼就可以唯一地確定一個漢字或符號,反過來說,任何一個漢字或符號也都對應著一個唯一的區位碼。漢字“母”字的區位碼是3624,表明它在方陣的36區24位,問號“?”的區位碼為0331,則它在03區3l位。
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。
(二)點陣字庫結構
1.點陣字庫存儲
- 在漢字的點陣字庫中,每個字節的每個位都代表一個漢字的一個點,每個漢字都是由一個矩形的點陣組成,0代表沒有,1代表有點,將0和1分別用不同顏色畫出,就形成了一個漢字,常用的點陣矩陣有12x12, 14x14, 16x16三種字庫。
- 字庫根據字節所表示點的不同有分為橫向矩陣和縱向矩陣,目前多數的字庫都是橫向矩陣的存儲方式(用得最多的應該是早期UCDOS字庫),縱向矩陣一般是因為有某些液晶是采用縱向掃描顯示法,為了提高顯示速度,于是便把字庫矩陣做成縱向,省得在顯示時還要做矩陣轉換。我們接下去所描述的都是指橫向矩陣字庫。
(三)漢字點陣獲取
1.利用區位碼獲取漢字
漢字點陣字庫是根據區位碼的順序進行存儲的,因此,我們可以根據區位來獲取一個字庫的點陣,它的計算公式如下:
- 點陣起始位置 = ((區碼- 1)*94 + (位碼 – 1)) * 漢字點陣字節數
- 獲取點陣起始位置后,我們就可以從這個位置開始,讀取出一個漢字的點陣。
2.利用漢字機內碼獲取漢字
漢字的區位碼和機內碼的關系
- 機內碼高位字節 = 區碼 + 20H + 80H(或區碼 + A0H)
- 機內碼低位字節 = 位碼 + 20H + 80H(或位碼 + AOH)
也可以根據機內碼來獲得區位碼:
- 區碼 = 機內碼高位字節 - A0H
- 位碼 = 機內碼低位字節 - AOH
將這個公式與獲取漢字點陣的公式進行合并計就可以得到漢字的點陣位置。
二、點陣漢字的字模讀取與顯示
在Ubuntu下用C/C++(或python) 調用opencv庫編程顯示一張圖片,并打開一個名為"logo.txt"的文本文件(其中只有一行文本文件,包括你自己的名字和學號),按照名字和學號去讀取漢字24*24點陣字形字庫(壓縮包中的文件HZKf2424.hz)中對應字符的字形數據,將名字和學號疊加顯示在此圖片右下位置。
創建一個test.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);int main(){String image_path="test.png";char* logo_path="logo1.txt";put_text_to_image(20,300,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("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 length=16;//要打印的字符長度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(); }編譯一下
g++ test.cpp -o test `pkg-config --cflags --libs opencv`顯示結果
./test三、總結
每次實驗都會遇到一些不會的問題,想辦法去解決它,然后就收獲新知識。本次實驗需要在Ubuntu里解壓rar文件,解決問題過程中也學習了解壓命令。做了這次實驗也對漢字點陣的原理和結構有了更充分的了解。在漢字的點陣字庫中,每個字節的每個位都代表一個漢字的一個點,每個漢字都是由一個矩形的點陣組成,0代表沒有,1代表有點,將0和1分別用不同顏色畫出,就形成了一個漢字,常用的點陣矩陣有12x12, 14x14, 16x16三種字庫。
參考文獻
漢字點陣字庫原理
SPI與I2C接口下的OLED顯示
ubuntu下如何解壓rar文件
總結
以上是生活随笔為你收集整理的汉字点阵原理字模读取与显示的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: squid服务的应用 转
- 下一篇: 符合WEB标准的div+css导航下拉菜