IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    在线手写数据集 CASIA-OLHWDB:联机手写识别的宝藏资源

    Veen Zhao发表于 2024-07-22 14:59:00
    love 0

    CASIA简介

    CASIA是一个非常有价值的手写单字数据集。它涵盖了众多的汉字以及字母、数字和符号。由中科院自动化研究所在 2007-2010 年间收集;包含1020人书写的脱机(联机)手写中文单字样本和手写文本;联机数据是采用 Anoto 笔在点阵纸上书写后扫描、分割得到。主要由离线(HWDB)和在线(OLHWDB)两部分。

    具体可以前往官网了解:https://nlpr.ia.ac.cn/databases/handwriting/Home.html

    这里只介绍在线手写数据集部分,离线的网上介绍比较多。在线手写数据集单字主要包括OLHWDB1.0 与 OLHWDB1.2。一共收录汉字 7185 个,涵盖了 GB2312 中全部 6763 个汉字。这个数据集以.pot文件存储,其数据组织形式独特。这些版本的数据集可以用于训练和测试在线手写汉字识别模型。CASIA - OLHWDB 为手写汉字识别、字形分析等相关任务提供了丰富的数据资源。

    pot文件的解析

    pot文件的解析相关教程并不多,官网也只给出了文件头信息,和一个预览的软件。

    m1yoph33.png

    使用预览软件打开某个pot文件的结果:
    m1yoqsjl.png

    我这里按照文件头规则直接写了一版python的,会将在线的点集转换为json文件存储,方便后期使用,文件夹的命名使用的是这个汉字字符对应的md5编码,当然你也可以修改为其他的,解析时间很长,因为一个字符大概有100多个样本。

    import hashlib
    import json
    import os
    import struct
    
    
    def parse_pot_file(filename):
        with open(filename, 'rb') as f:
            while True:
                # 读取样本大小
                sample_size_data = f.read(2)
                if not sample_size_data:
                    break  # 文件读取结束
                sample_size = struct.unpack('<H', sample_size_data)[0]
    
                # 读取字符的 GBK 编码
                tag_code_data = f.read(4)
                tag_code = struct.unpack('<I', tag_code_data)[0]
                gbk_code = tag_code & 0xFFFF  # 提取低16位
                character = gbk_code.to_bytes(2, 'big').decode('gbk', errors='ignore').strip('\x00')
                # print(f"Character: {character}")
                # c.append(character)
                # 读取笔画数量
                stroke_count_data = f.read(2)
                stroke_count = struct.unpack('<H', stroke_count_data)[0]
                # print(f"Number of strokes: {stroke_count}")
    
                # 读取每个笔画
                strokes = []
                for _ in range(stroke_count):
                    stroke = []
                    while True:
                        # 读取一个坐标对
                        x, y = struct.unpack('<hh', f.read(4))
                        if (x, y) == (-1, 0):  # 笔画结束标志
                            break
                        stroke.append((x, y))
                    strokes.append(stroke)
    
                # 将字符编码为md5
                md5_hash = hashlib.md5(character.encode('utf-8')).hexdigest()
                # 创建以MD5为名的文件夹
                folder_path = os.path.join(output_dir, md5_hash)
                os.makedirs(folder_path, exist_ok=True)
                # 计算已有文件数量,确定当前文件名
                existing_files = os.listdir(folder_path)
                json_filename = f"{len(existing_files) + 1}.json"
                # 将strokes保存为JSON文件
                json_file_path = os.path.join(folder_path, json_filename)
                with open(json_file_path, 'w', encoding='utf-8') as json_file:
                    json.dump(strokes, json_file, ensure_ascii=False, separators=(',', ':'))
    
                # print(f"Strokes: {strokes}")
    
                # 检查字符结束标志 (-1, -1)
                end_marker = struct.unpack('<hh', f.read(4))
                if end_marker != (-1, -1):
                    raise ValueError("Invalid character end marker.")
    
    
    output_dir = 'D:\\VeenCode\\MyCode\\DataSet\\OLHWDB\\PotTrainJson'
    input_dir = 'D:\\VeenCode\\MyCode\\DataSet\\OLHWDB\\Pot1.0Train'
    # 遍历文件夹中的所有 .pot 文件
    for root, _, files in os.walk(input_dir):
        for file in files:
            if file.endswith('.pot'):
                file_path = os.path.join(root, file)
                parse_pot_file(file_path)
                print(os.path.join(root, file))

    文件夹Pot1.0Train用来存放全部pot文件,会将结果输出到PotTrainJson文件夹。

    结果预览

    将pot转换成json后,处理起来就方便太多了,随机来一首诗预览一下字符:
    m1ypc4zt.png
    下面是预览的代码,大家也可以拿去玩玩,使用了Tk库,在canvas上绘制的:

    import hashlib
    import json
    import tkinter as tk
    import os
    import random
    # 读取JSON文件并解析数据
    def load_json(file_path):
        with open(file_path, 'r') as file:
            data = json.load(file)
        return data
    
    
    # 归一化点的坐标,放缩到90x90区域并居中
    def normalize_data(strokes, offset_x, offset_y, word_size=90, box_size=100):
        # 获取所有点的最大值和最小值,以便归一化
        all_x = [point[0] for stroke in strokes for point in stroke]
        all_y = [point[1] for stroke in strokes for point in stroke]
    
        min_x, max_x = min(all_x), max(all_x)
        min_y, max_y = min(all_y), max(all_y)
    
        # 归一化到90x90的绘制区域
        scale_x = word_size / (max_x - min_x) if max_x - min_x != 0 else 1
        scale_y = word_size / (max_y - min_y) if max_y - min_y != 0 else 1
    
        # 计算居中的偏移量
        center_x_offset = (box_size - word_size) / 2
        center_y_offset = (box_size - word_size) / 2
    
        normalized_strokes = []
        for stroke in strokes:
            normalized_stroke = []
            for x, y in stroke:
                norm_x = (x - min_x) * scale_x + offset_x + center_x_offset
                norm_y = (y - min_y) * scale_y + offset_y + center_y_offset
                normalized_stroke.append((norm_x, norm_y))
            normalized_strokes.append(normalized_stroke)
    
        return normalized_strokes
    
    
    # 在canvas上绘制笔画
    def draw_strokes(canvas, strokes):
        for i, stroke in enumerate(strokes):
            color = "red" if i == 0 else "black"
            for j in range(len(stroke) - 1):
                x1, y1 = stroke[j]
                x2, y2 = stroke[j + 1]
                canvas.create_line(x1, y1, x2, y2, fill=color, width=2)
    
    
    # 主函数:随机挑选10个JSON文件并渲染到画布上
    def main(selected_files):
        # Canvas的大小设置为500x500
        canvas_width = 500
        canvas_height = 500
        box_size = 100  # 每个字的框大小为100x100
        word_size = 50  # 字的大小为90x90
        # 创建Tkinter窗口和Canvas
        root = tk.Tk()
        root.title("Drawing Random Strokes")
    
        canvas = tk.Canvas(root, width=canvas_width, height=canvas_height)
        canvas.pack()
    
        # 按照5列2行的顺序排列和绘制笔画
        num_cols = 5
    
        for index, file_name in enumerate(selected_files):
            # 计算子区域的左上角的坐标
            col = index % num_cols
            row = index // num_cols
            offset_x = col * box_size
            offset_y = row * box_size
    
            # 加载和归一化数据
            strokes = load_json(os.path.join(folder_path, file_name))
            normalized_strokes = normalize_data(strokes, offset_x, offset_y, word_size, box_size)
    
            # 在子区域内绘制笔画
            draw_strokes(canvas, normalized_strokes)
    
        # 运行Tkinter主循环
        root.mainloop()
    
    
    # 示例调用
    if __name__ == "__main__":
        # 给定字符
        chars = "白日依山尽黄河入海流欲穷千里目更上一层楼"
        json_path = []
        # 遍历字符并生成对应的JSON文件路径
        for char in chars:
            # 将字符md5加密
            char_md5 = hashlib.md5(char.encode()).hexdigest()
            folder_path = os.path.join(r"D:\VeenCode\MyCode\DataSet\OLHWDB\PotTrainJson", char_md5)
            # 随机从folder_path文件夹中挑选一个json文件
            json_files = [f for f in os.listdir(folder_path) if f.endswith('.json')]
            json_file = random.choice(json_files)
            json_path.append(os.path.join(folder_path, json_file))
        main(json_path)


沪ICP备19023445号-2号
友情链接