1. 程式人生 > >使用opencv調用24*24點陣字庫和8*16ASCII字庫在圖片顯示文字數字

使用opencv調用24*24點陣字庫和8*16ASCII字庫在圖片顯示文字數字

使用 sca show alt 編碼的轉換 獲取 引用 raw 格式

課程實驗:編程讀漢字點陣字庫,把自己的名字和學號疊加到圖片的右下位置。

主要步驟分為三部分

第一部分:讀取圖片(文件讀取)

第二部分:讀取文字並從字庫中提取相應的編碼(字庫的存儲原理)

第三部分:將相應的編碼映射到圖片的相應位置實現文字“寫在圖片上”(提取編碼的轉換映射)

第一部分:讀取圖片(文件讀取)

可以利用opencv提供的函數cvLoadImage().(這裏的將字庫一並導入)

 1 /******************************************************
 2 函數名稱:  openfile
 3 函數功能:  打開字庫和圖片
 4 傳入參數: 
5 返 回 值: 6 建立時間: 2018-05-07 7 修改時間: 8 建 立 人: 重交親爸爸 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 void ShowName::openfile(){ 13 char pbuf[100]; 14 _getcwd(pbuf, 100); 15 strcat(pbuf, "/HZKf2424.hz"); 16 char pbufASC[100]; 17 _getcwd(pbufASC, 100); 18 strcat(pbufASC, "
/Asci0816.zf"); 19 // 讀取圖片 20 if ((img = cvLoadImage("test.png")) == NULL)exit(1); 21 // 打開字體文件 22 if ((HZK24 = fopen(pbuf, "rb")) == NULL)exit(1); 23 //打開asci8*16文件 24 if ((ASI816 = fopen(pbufASC, "rb")) == NULL)exit(1); 25 }

這裏提醒各位記得要將這些字庫關閉。養成好的習慣

 1 /****************************************************** 
2 函數名稱: ~ShowName 3 函數功能: 釋放空間 4 傳入參數: 5 返 回 值: 6 建立時間: 2018-05-07 7 修改時間: 8 建 立 人: 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 ShowName::~ShowName(){ 13 cvReleaseImage(&img); 14 fclose(HZK24); 15 fclose(ASI816); 16 if (CONTERNER!=NULL) 17 fclose(CONTERNER); 18 img = NULL; 19 HZK24 = NULL; 20 ASI816 = NULL; 21 CONTERNER = NULL; 22 }

第二部分:讀取文字並從字庫中提取相應的編碼(字庫的存儲原理)

這裏先介紹下字庫的存儲方式:以下為引用的資料:

漢字點陣獲取

1. 利用區位碼獲取漢字
漢字點陣字庫是根據區位碼的順序進行存儲的,因此,我們可以根據區位來
獲取一個字庫的點陣,它的計算公式如下:

點陣起始位置 = ((區碼- 1)*94 + (位碼 – 1)) * 漢字點陣字節數


獲取點陣起始位置後,我們就可以從這個位置開始,讀取出一個漢字的點陣。
2利用漢字機內碼獲取漢字
前面我們己經講過,漢字的區位碼和機內碼的關系如下:

機內碼高位字節 = 區碼 + 20H + 80H(或區碼 + A0H)
機內碼低位字節 = 位碼 + 20H + 80H(或位碼 + AOH)
反過來說,我們也可以根據機內碼來獲得區位碼:
區碼 = 機內碼高位字節 - A0H
位碼 = 機內碼低位字節 - AOH
將這個公式與獲取漢字點陣的公式進行合並計就可以得到漢字的點陣位置。

3.以下為漢字的存儲原理:

對於 24*24的點陣字庫,存放格式如下: 縱向存放 3 個字節(24 位),橫向存放24 個字節,每個字模占 72 個字節 字符排列順序如下:

1 4 7 10 ......

2 5 8 11 ......

3 6 9 12 ......

對於 16*16 的點陣字庫,存放格式如下: 橫向存放 2 個字節(16 位),其中第二個字節沒有多余的數據 縱向存放 16個字節,

每個字模占 32 個字節 字符排列順序如下:

1 2

3 4

5 6

......

對於 14*14 的點陣字庫,存放格式如下: 橫向存放 2 個字節(16 位),其中第二個字節的後 2 位是多余的數據 縱向存放 14個字節,每個字模占 28 個字節

字符排列順序如下:

1 2

3 4

5 6

......

對於 12*12 的點陣字庫,存放格式如下: 橫向存放 2 個字節(16 位),其中第二個字節的後 4 位是多余的數據 縱向存放 12個字節,每個字模占 24 個字節 字符排列順序如下:

1 2

3 4

5 6 ......

******************************************************/資料到此

這個資料有點抽象,不過沒關系。我們使用實例圖片進行說話。

這個是8*16點陣的ASCII碼字庫

技術分享圖片

這個是16*16點陣漢字庫的字

技術分享圖片

這個是24*24點陣字庫的字

技術分享圖片

非常明顯的看出8*16和16*16是正放的,而24*24的是側著,而且是反過來的字體。

對於數字和英文來說,有ASCII作為背景,就比較簡單。

其中數字的

點陣起始位置offset=incode[0]*16L(獲取的ASCII碼*16L就直接轉換了)

 1 /******************************************************
 2 函數名稱:  getasi
 3 函數功能:  獲取asci碼
 4 傳入參數:
 5 返 回 值:
 6 建立時間:  2018-05-08
 7 修改時間:
 8 建 立 人:  
 9 修 改 人:
10 其它說明:
11 ******************************************************/
12 void ShowName::getasi(char incode[]){
13     unsigned char qh, wh;
14     unsigned long offset;
15     offset = incode[0]*16L;
16     fseek(ASI816, offset, SEEK_SET);
17     fread(num_mat, 16, 1, ASI816);
18 }

但是對於24*24點陣字庫的話,以上的原理就不適用了

對於區碼位碼的話有所改變:

區碼 = 機內碼高位字節 - AFH
位碼 = 機內碼低位字節 - AOH

對於尋找字庫起始位置:

offset=offset = (94 * (qh - 1) + (wh - 1)) * 72L(72是怎麽來的?24*24/8)

 1 /******************************************************
 2 函數名稱:  get_mat
 3 函數功能:  通過漢字的區碼和位碼進行寫到mat中
 4 傳入參數:    qh, wh
 5 返 回 值:
 6 建立時間:  2018-05-07
 7 修改時間:
 8 建 立 人:  
 9 修 改 人:
10 其它說明:
11 ******************************************************/
12 void ShowName::get_mat(unsigned char qh, unsigned char wh){
13     long offset;
14     offset = (94 * (qh - 1) + (wh - 1)) * 72L;
15     // 讀取數據存入數組
16     fseek(HZK24, offset, SEEK_SET);
17     fread(mat, 72, 1, HZK24);
18 }

第三部分:將相應的編碼映射到圖片的相應位置實現文字“寫在圖片上”(提取編碼的轉換映射)

將數字進行映射

 1 /******************************************************
 2 函數名稱:  draw_code
 3 函數功能:  繪制學號
 4 傳入參數:
 5 返 回 值:
 6 建立時間:  2018-05-08
 7 修改時間:
 8 建 立 人:  
 9 修 改 人:
10 其它說明:
11 ******************************************************/
12 void ShowName::draw_code(int num){
13     int width, height;
14     width = img->width;
15     height = img->height;
16     // 開始的x y像素點
17     int start_x, start_y, size, current_start_x, current_start_y;
18     size = MAPSIZE; //+INTERSIZE;
19     //int numsize = 8;
20     start_x = width - sum_word * size;//開始位置一定要找好,確認開始位置
21     start_y = height - 16- INTERSIZE;
22     // 開始繪制
23 
24     CvScalar cs;
25     for (int i = 0; i < 16; ++i)
26     for (int k = 0; k < 8; k++)
27     if ((num_mat[i]&(0x80>>k)) != NULL)
28     {
29         current_start_x = k + start_x + size * num;
30         current_start_y = start_y + i;
31         cs = cvGet2D(img, current_start_y, current_start_x);//獲取圖像相對位置的RGB的值
32         cs.val[0] = 0;//變黑
33         cs.val[1] = 0;//這裏可以改成你喜歡的顏色
34         cs.val[2] = 0;
35         cvSet2D(img, current_start_y, current_start_x, cs);//重新設值
36     }
37 
38 }

對漢字進行映射:這裏需要在映射的時候翻個身子

 1 /******************************************************
 2 函數名稱:  draw_name
 3 函數功能:  通過漢字的區碼和位碼進行寫到mat中
 4 傳入參數:    qh, wh
 5 返 回 值:
 6 建立時間:  2018-05-07
 7 修改時間:
 8 建 立 人:  
 9 修 改 人:
10 其它說明:
11 ******************************************************/
12 void ShowName::draw_name(int num){
13     // 圖片的像素值
14     int width, height;
15     width = img->width;
16     height = img->height;
17     // 開始的x y像素點
18     int start_x, start_y, size, current_start_x, current_start_y;
19     size = MAPSIZE;// +INTERSIZE;
20     start_x = width - sum_word * size;
21     start_y = height - MAPSIZE - INTERSIZE;
22     // 開始繪制
23 
24     CvScalar cs;
25     
26     for (int i = 0; i < 24; ++i)
27     for (int j = 0; j < 3; ++j)
28     for (int k = 0; k < 8; k++)
29     if (((mat[i* 3 + j] >> (7 - k)) & 0x1) != NULL)
30     {
31         // 繪點
32         current_start_x = start_x + i + size * num;//24*24的是縱向排列的i對應的是x
33         current_start_y = start_y + j * 8 + k;
34         cs = cvGet2D(img, current_start_y, current_start_x);
35         cs.val[0] = 0;
36         cs.val[1] = 0;
37         cs.val[2] = 0;
38         cvSet2D(img, current_start_y, current_start_x, cs);
39     }
40 }

最後進行重新顯示繪制好的圖片

 1 /******************************************************
 2 函數名稱:  Runtodraw
 3 函數功能:  啟動繪制
 4 傳入參數:    
 5 返 回 值:
 6 建立時間:  2018-05-07
 7 修改時間:
 8 建 立 人: 
 9 修 改 人:
10 其它說明:
11 ******************************************************/
12 
13 void ShowName::Runtodraw(){
14     unsigned char mask = 0x80;
15     char tmpcode[3] = { 0 };
16     while (*Name!=NULL)//漢字轉換過程
17     {
18         tmpcode[0] = *Name;
19         tmpcode[1] = *(Name + 1);
20         if (tmpcode[0] & mask){
21             unsigned char qh, wh;
22             qh = tmpcode[0] - 0xaf;    //求區碼   
23             wh = tmpcode[1] - 0xa0;//求位碼
24             get_mat(qh,wh);
25             draw_name(current_num++);//畫字
26             Name += 2;
27         }
28     }
29     while (*code!=NULL)
30     {
31         tmpcode[0] = *code;
32         if (tmpcode[0])
33         {
34             getasi(code);
35             draw_code(current_num++);
36             code++;
37         }
38     }
39     cvShowImage("bt", img);
40     cvWaitKey();
41 }

結果:

技術分享圖片

ps:本文這裏采用的是c++的面向對象的方式進行寫的。

如需要源碼請轉移至碼雲:https://gitee.com/cjqbaba/MediaTest/tree/textimage進行源碼克隆下載

如有問題請留言評論。轉載請註明出處,謝謝。

使用opencv調用24*24點陣字庫和8*16ASCII字庫在圖片顯示文字數字