樹莓派下利用opencv在圖片上畫漢字點陣(寫一句話)

一、 點陣字庫的原理

一、 漢字編碼

1. 區位碼

在國標 GD2312—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~4),所以漢字的高位字節與低位字節的取值範圍則爲 A1H~FEH(即十進制的 161~254)。

例如,漢字“啊”的區位碼爲 1601,區碼和位碼分別用十六進制表示即爲 1001H,的機內碼的高位字節爲 B0H,低位字節爲 A1H,機內碼就是 B0A1H。

二、 點陣字庫結構

1. 點陣字庫存儲

在漢字的點陣字庫中,每個字節的每個位都代表一個漢字的一個點,每個漢 字都是由一個矩形的點陣組成,0 代表沒有,1 代表有點,將 0 和 1 分別用不同 顏色畫出,就形成了一個漢字,常用的點陣矩陣有 1212, 1414, 16*16 種字庫。
字庫根據字節所表示點的不同有分爲橫向矩陣和縱向矩陣,目前多數的字庫 都是橫向矩陣的存儲方式(用得最多的應該是早期UCDOS 字庫),縱向矩陣般是因爲有某些液晶是採用縱向掃描顯示法,爲了提高顯示速度,於是便把字矩陣做成縱向,省得在顯示時還要做矩陣轉換。我們接下去所描述的都是指橫矩陣字庫。

2. 16*16 點陣字庫

1616 點陣字庫 對於 1616 的矩陣來說,它所需要的位數共是 16*16=256 個位,每個字 節爲 8 位,因此,每個漢字都需要用 256/8=32 個字節來表示。 即每兩個字節代表一行的 16 個點,共需要 16行,顯示漢字時,只需一次 性讀取 32 個字節,並將每兩個字節爲一行打印出來,即可形成一個漢字。

點陣結構如下圖所示:
在這裏插入圖片描述

3 1414 與 1212 點陣字庫

對於 1414 和 1212 的字庫,理論上計算,它們所需要的點陣分別爲(14 14/8)=25, (1212/8)=18 個字節,但是,如果按這種方式來存儲,那麼取 點陣和顯示時,由於它們每一行都不是 8 的整位數,因此,就會涉到點陣的算處理問題,會增加程序的複雜度,降低程序的效率。 爲了解決這個問題,有些點陣字庫會將 1414 和 1212 的字庫按 1614 和 1612 來存儲,即,每行還是按兩個字節來存儲,但是 1414 的字庫,每 兩個字節的最後兩位是沒有使用,1212 的字節,每兩字節的最後 4 位是沒使用,這個根據不同的字庫會有不同的處理方式,所以在使用字庫時要注意這個 問題,特別是 14*14 的字庫。

三、 漢字點陣獲取

1. 利用區位碼獲取漢字

漢字點陣字庫是根據區位碼的順序進行存儲的,因此,我們可以根據區位獲取一個字庫的點陣,它的計算公式如下: 點陣起始位置 = ((區碼- 1)*94 + (位碼 – 1)) * 漢字點陣字節數獲取點陣起始位置後,我們就可以從這個位置開始,讀取出一個漢字的點陣。

2. 利用漢字機內碼獲取漢字

前面我們己經講過,漢字的區位碼和機內碼的關係如下:
機內碼高位字節 = 區碼 + 20H + 80H(或區碼 + A0H)
機內碼低位字節 = 位碼 + 20H + 80H(或位碼 + AOH)
反過來說,我們也可以根據機內碼來獲得區位碼:
區碼 = 機內碼高位字節 - A0H
位碼 = 機內碼低位字節 - AOH
將這個公式與獲取漢字點陣的公式進行合併計就可以得到漢字的點陣位置。
我們可以使用軟件ultraedit配合字庫查看工具來查看每一個文字在字庫中的位置。

使用教程:

先創建了一個文本文件並添加了我自己的名字,並以ANSI的編碼格式保存。
在這裏插入圖片描述

然後用UltraEdit使用十六進制模式編輯這個文本文件,就會得到每個字體在字庫中的位置。
在這裏插入圖片描述

接着打開我們下載的字庫查看工具,打開字庫後依次查找每一個漢字。

在這裏插入圖片描述

以上圖片中的漢字代碼和UltraEdit中使用16進制查看文字得到的代碼一致,因此驗證成功。

二、代碼


#include <unistd.h>
#include <stdio.h>
#include <opencv/cxcore.h>
#include <sys/types.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;
const int CC_SIZE = 16;
const int SAFE_WIDTH = 10;
const int CC_NUMBER = 15;
// 姓名
unsigned char name[7] ="王尉銘";
unsigned int name_code[3][2];

// 學號
unsigned int id_code[12][2] = { { 0xa3, 0xb6 },{ 0xa3, 0xb3 },{ 0xa3, 0xb1 },{ 0xa3, 0xb6 },{ 0xa3, 0xb0 },{ 0xa3, 0xb7 },{ 0xa3, 0xb0 },{ 0xa3, 0xb4 },{ 0xa3, 0xb0 },{ 0xa3, 0xb3 },{ 0xa3, 0xb0 },{ 0xa3, 0xb5} };

// 保存字節的數組
unsigned char mat[16][2];
FILE* HZK16;
IplImage* img;
void get_area_position_codes();
void get_mat(int a_code, int p_code);
void open_file();
void draw_one_cc(int num);
void release();
int main()
{
   open_file();
   get_area_position_codes();
   // 寫名字
   int i, j;
   for (i = 0; i < 3; ++i)
   {
   	get_mat(name_code[i][0], name_code[i][1]);
   	draw_one_cc(i);
   }
   // 寫學號
   for (j = 0; j < 12; ++j)
   {
   	get_mat(id_code[j][0] - 0xa0, id_code[j][1] - 0xa0);
   	draw_one_cc(i + j);
   }
   // 顯示圖片
   cvShowImage("IMAGE", img);
   cvWaitKey(0);
   release();
   return 0;
}

void get_area_position_codes()
{
   for (int i = 0; i < 3; ++i)
   	for (int j = 0; j < 2; ++j)
   		name_code[i][j] = name[i * 2 + j] - 0xa0;
}

void get_mat(int a_code, int p_code)
{
   long offset;
   offset = (94 * (a_code - 1) + (p_code - 1)) * 32L;
   // 讀取數據存入數組
   fseek(HZK16, offset, SEEK_SET);
   fread(mat, 32, 1, HZK16);
}

void open_file()
{
   char pbuf[100];
   getcwd(pbuf, 100);
   cout<<pbuf<<endl;
   strcat(pbuf, "/HZKf1616.hz");
   // 讀取圖片
   if ((img = cvLoadImage("test.jpg")) == NULL)exit(1);
   // 打開字體文件
   if ((HZK16 = fopen(pbuf, "rb")) == NULL)exit(1);
}

void draw_one_cc(int num)
{
   // 圖片的像素值
   int width, height;
   width = img->width;
   height = img->height;
   // 開始的x y像素點
   int start_x, start_y, size, current_start_x, current_start_y;
   size = CC_SIZE + SAFE_WIDTH;
   start_x = width - CC_NUMBER * size;
   start_y = height - CC_SIZE - SAFE_WIDTH;
   // 開始繪製

   CvScalar cs;
   for (int i = 0; i < 16; ++i)
   	for (int j = 0; j < 2; ++j)
   		for (int k = 0; k < 8; k++)
   			if (mat[i][j] & (0x80 >> k))
   			{
   				cout << mat[i][j] << endl;
   				// 繪點
   				current_start_x = j * 8 + k + start_x + size * num;
   				current_start_y = start_y + i;
   				cs = cvGet2D(img, current_start_y, current_start_x);
   				cs.val[0] = 0;
   				cs.val[1] = 0;
   				cs.val[2] = 0;
   				cvSet2D(img, current_start_y, current_start_x, cs);
   			}
}

void release()
{
   cvReleaseImage(&img);
   fclose(HZK16);
   img = NULL;
   HZK16 = NULL;
}

這段代碼中,我定義名字時:是在windows上寫好了再傳到樹莓派上編譯執行代碼,因爲在樹莓派上直接輸入名字或者使用vim定義名字都會出錯:因爲字符的編碼格式的不同(windows下使用GBK或者utf_8顯示中文,而樹莓派使用的是ISO 8859-1拉丁語顯示中文)

工具、字庫和代碼我都已經上傳在我的github中,有興趣的可以去下載使用:

(工具下載地址)https://github.com/Budexing/Tools/tree/master/ziku
(代碼下載地址)
https://github.com/Budexing/Embeded-System/tree/master/opencv
而UltraEdit可以直接百度下載試用30天。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章