CASIA-HWDB2.x 数据集DGRL文件解析(python)

    技术2022-07-10  125

    CASIA-HWDB 数据集是最常见的手写汉字识别数据集,它包含脱机、联机两部分,分单字、文本行两种类型:

    HWDB1.x:脱机单字,1.0~1.2 三个版本,数据格式为 .gntOLHWDB1.x:联机单字,1.0~1.2 三个版本,HWDB2.x:脱机文本行,1.0~1.2 三个版本,数据格式为 .dgrlOLHWDB1.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 都选择 uint8,count 需要根据上图结构中的长度 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_dgrl(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)
    Processed: 0.012, SQL: 9