CASIA-HWDB2.x 數據集DGRL文件解析(python)

CASIA-HWDB 數據集是最常見的手寫漢字識別數據集,它包含脫機、聯機兩部分,分單字、文本行兩種類型:

  • HWDB1.x:脫機單字,1.0~1.2 三個版本,數據格式爲 .gnt
  • OLHWDB1.x:聯機單字,1.0~1.2 三個版本,
  • HWDB2.x:脫機文本行,1.0~1.2 三個版本,數據格式爲 .dgrl
  • OLHWDB1.x:聯機文本行,1.0~1.2 三個版本,

一般常用的漢字識別多爲脫機單字識別,該部分數據格式爲 .gnt,網絡上針對這種數據的解析文章也很多,這裏主要介紹文本行識別數據集,它的格式是 .dgrl,它的解析類似於 .gnt,但由於結構不同,具體的操作也不一樣!

1、DGRL 文件格式介紹

.dgrl 格式如下圖所示,它按照這種順序依次存儲,一張圖對應一個 DGRL 文件,大部分內容都有固定的長度,部分內容長度不固定但是也能通過其他數據推導出來,我們可以通過訪問文件特定位置的數據得到我們需要的內容:行文本標註,行圖像。

舉例來說:比如我們要讀取圖像中行數量,那首先要知道這個數據在哪個位置(就好像,python讀文件要知道數據在哪一行)(1)我們先讀取第一行的 4B 長度數據(4個字節),得到文件頭的長度(也就是文件頭佔據多少行),假設是 n 行;(2)然後讀取 n-1 行(第一行已經讀過了),就來到了圖像信息 Image Records 的位置,根據上圖的結構,再讀3個 4B 就可以拿到圖像中行數量的數據了!

2、python 代碼實現

讀取 .dgrl 時先以二進制方式打開文件:

f = open(dgrl, 'rb')

然後用 numpy 挨個去讀取,它的讀取方式跟 f.readline() 類似,一個一個讀,所以之前讀了多少個數據就很重要!所以要指定讀的格式、數量:

np.fromfile(f, dtype='uint8', count=4)

一般 dtype 都選擇 uint8count 需要根據上圖結構中的長度 Length 做相應變化。

要注意的一個地方是:行文本標註讀取出來以後,是一個 int 列表,要把它還原成漢字,一個漢字佔用兩個字節(具體由 code_length 決定),使用 struct 將其還原:

struct.pack('I', i).decode('gbk', 'ignore')[0]

上面的 i 就是提取出來的漢字編碼,解碼格式爲 gbk,有些行文本會有空格,解碼可能會出錯,使用 ignore 忽略。

import struct
import os
import cv2 as cv
import numpy as np

def read_from_agrl(dgrl):
    if not os.path.exists(dgrl):
        print('DGRL not exis!')
        return
    
    dir_name,base_name = os.path.split(dgrl)
    label_dir = dir_name+'_label'
    image_dir = dir_name+'_images'
    if not os.path.exists(label_dir):
        os.makedirs(label_dir)
    if not os.path.exists(image_dir):
        os.makedirs(image_dir)
    
    with open(dgrl, 'rb') as f:
        # 讀取表頭尺寸
        header_size = np.fromfile(f, dtype='uint8', count=4)
        header_size = sum([j<<(i*8) for i,j in enumerate(header_size)])
		# print(header_size)
        
        # 讀取表頭剩下內容,提取 code_length
        header = np.fromfile(f, dtype='uint8', count=header_size-4)
        code_length = sum([j<<(i*8) for i,j in enumerate(header[-4:-2])])
		# print(code_length)
        
        # 讀取圖像尺寸信息,提取圖像中行數量
        image_record = np.fromfile(f, dtype='uint8', count=12)
        height = sum([j<<(i*8) for i,j in enumerate(image_record[:4])])
        width = sum([j<<(i*8) for i,j in enumerate(image_record[4:8])])
        line_num = sum([j<<(i*8) for i,j in enumerate(image_record[8:])])
        print('圖像尺寸:')
        print(height, width, line_num)
        
        # 讀取每一行的信息
        for k in range(line_num):
            print(k+1)

            # 讀取該行的字符數量
            char_num = np.fromfile(f, dtype='uint8', count=4)
            char_num = sum([j<<(i*8) for i,j in enumerate(char_num)])
            print('字符數量:', char_num)
            
            # 讀取該行的標註信息
            label = np.fromfile(f, dtype='uint8', count=code_length*char_num)
            label = [label[i]<<(8*(i%code_length)) for i in range(code_length*char_num)]
            label = [sum(label[i*code_length:(i+1)*code_length]) for i in range(char_num)]
            label = [struct.pack('I', i).decode('gbk', 'ignore')[0] for i in label]
            print('合併前:', label)
            label = ''.join(label)
            label = ''.join(label.split(b'\x00'.decode()))	# 去掉不可見字符 \x00,這一步不加的話後面保存的內容會出現看不見的問題
            print('合併後:', label)
            
            # 讀取該行的位置和尺寸
            pos_size = np.fromfile(f, dtype='uint8', count=16)
            y = sum([j<<(i*8) for i,j in enumerate(pos_size[:4])])
            x = sum([j<<(i*8) for i,j in enumerate(pos_size[4:8])])
            h = sum([j<<(i*8) for i,j in enumerate(pos_size[8:12])])
            w = sum([j<<(i*8) for i,j in enumerate(pos_size[12:])])
			# print(x, y, w, h)
            
            # 讀取該行的圖片
            bitmap = np.fromfile(f, dtype='uint8', count=h*w)
            bitmap = np.array(bitmap).reshape(h, w)
            
            # 保存信息
            label_file = os.path.join(label_dir, base_name.replace('.dgrl', '_'+str(k)+'.txt'))
            with open(label_file, 'w') as f1:
                f1.write(label)
            bitmap_file = os.path.join(image_dir, base_name.replace('.dgrl', '_'+str(k)+'.jpg'))
            cv.imwrite(bitmap_file, bitmap)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章