嵌入式學習---點陣漢字的字模讀取與顯示
1.原理介紹
1.漢字編碼
1.機內碼
漢字的機內碼是指在計算機中表示一個漢字的編碼。機內碼與區位碼稍有區別。如上所述,漢字區位碼的區碼和位碼的取值均在 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)。
由於漢字的區碼與位碼的取值范圍的十六進制數均為 01H~5EH(即十進制的 01~94),所以漢字的高位字節與低位字節的取值范圍則為 A1H~FEH(即十進制的 161~254)。
2.區位碼
在國標 GD2312—80 中規定,所有的國標漢字及符號分配在一個 94 行、94 列的方陣中,方陣的每一行稱為一個“區”,編號為 01 區到 94 區,每一列稱為一個“位”,編號為01 位到 94 位,方陣中的每一個漢字和符號所在的區號和位號組合在一起形成的四個阿拉伯數字就是它們的“區位碼”。區位碼的前兩位是它的區號,后兩位是它的位號。用區位碼就可以唯一地確定一個漢字或符號,反過來說,任何一個漢字或符號也都對應着一個唯一的區位碼。漢字“母”字的區位碼是 3624,表明它在方陣的 36 區 24 位,問號“?”的區位碼為0331,則它在 03 區 3l 位。
2.代碼實現
1.首先在文件夾之中添加字庫文件,並自己准備一張圖片放入文件夾中
(ps:文件必須是要ansi格式的,建議直接從windows中復制到虛擬機之中去)
2.新建一個C++文件開始編寫代碼
代碼如下
#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="123.jpg";
char* logo_path=(char*)"213.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("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(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=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
機內碼就是d5d4
d5-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
完成了實驗的要求