基于LeNet与YOLOV8在2024年mm杯甲骨文图像文字识别数据集研究

1、数据展示与处理

  • 数据集下载:https://pan.baidu.com/s/1cp-DUwgS3c_d8EFhBwXRHw?pwd=pf1t
  • 数据选择:下载后的数据集分为4个文件1_Pre_test 、2_Train、3_Test、4_Recognize
    由于本文只讲解如何搭建一个较为完整的图像识别分类模型,所以选取的数据集为2_Train与4_Recognize这二份数据集。
  • 其中2_Train文件夹下有对应的image以及json数据类型。但在json中所展示的数据集如下:
    {"img_name": "b02519Z", "ann": [[64.0, 194.0, 134.0, 277.0, 1.0], [150.0, 168.0, 204.0, 344.0, 1.0], [206.0, 170.0, 259.0, 349.0, 1.0]]}。ann每一行所对应的信息是对应img_name的一个图像框,也即为已经标注好的甲骨文信息。同时在4_Recongnize文件夹下有二个子文件夹,在训练集下有已经分好的图片类别,在测试集下有待识别文字的图片。接下来我们讨论如何处理这些数据集。
  • 在YOLOV8训练数据集中,数据类型应该为YOLO TXT类型,具体可参考这位博主:https://blog.csdn.net/wlh156423/article/details/118861987
    在处理的同时,可以看出在json文件下,并没有为我们识别文字信息为那一类,故而需要采取一类模型对文字进行分类处理,将训练好的模型对上述信息进行分类,最后转为YOLO TXT类型再进行YOLOV8的模型训练,即可得到想要模型。
  • 图像分类模型有多具体可以参考:https://zhuanlan.zhihu.com/p/562609779
    本文采用LeNet模型对所含信息进行分类。

2、具体实现过程

在开始实现以上操作时,我们需要准备python环境,具体流程可以参考:(本文所采用python=3.9的环境)https://blog.csdn.net/weixin_43366149/article/details/132206526

第一步训练LeNet模型

搭建LeNet模型

本文将此代码保存为LeNet.py。

import torch
import torch.nn as nn

class LeNet(nn.Module):
    def __init__(self, num_classes=10):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, kernel_size=5)  # 输入通道数为1(灰度图像),输出通道数为6,卷积核大小为5x5
        self.conv2 = nn.Conv2d(6, 16, kernel_size=5)  # 输入通道数为6,输出通道数为16,卷积核大小为5x5
        self.fc1 = nn.Linear(16*5*5, 120)  # 全连接层,输入大小为16*5*5,输出大小为120
        self.fc2 = nn.Linear(120, 84)  # 全连接层,输入大小为120,输出大小为84
        self.fc3 = nn.Linear(84, num_classes)  # 全连接层,输入大小为84,输出大小为类别数

    def forward(self, x):
        # 输入数据经过卷积层1,使用ReLU激活函数,再经过2x2最大池化层
        x = torch.max_pool2d(torch.relu(self.conv1(x)), (2, 2))
        # 输入数据经过卷积层2,使用ReLU激活函数,再经过2x2最大池化层
        x = torch.max_pool2d(torch.relu(self.conv2(x)), (2, 2))
        # 将特征图展平成一维向量
        x = x.view(-1, 16*5*5)
        # 经过全连接层1和ReLU激活函数
        x = torch.relu(self.fc1(x))
        # 经过全连接层2和ReLU激活函数
        x = torch.relu(self.fc2(x))
        # 经过全连接层3
        x = self.fc3(x)
        return x

# # 创建LeNet模型实例
# model = LeNet(num_classes=10)
# # 输出模型结构
# print(model)

 处理数据集

本文将其命名为Mydata.py。

import os
from PIL import Image
from torch.utils.data import Dataset


class MyDataset(Dataset):
    def __init__(self, data_dir, transform=None):
        """
        初始化 MyDataset 类。

        Args:
            data_dir (str): 数据目录的路径。
            transform (callable, optional): 对图像进行预处理的转换函数。
        """
        self.data_dir = data_dir
        self.transform = transform

        # 获取类别文件夹和类别名称
        self.classes = sorted([d.name for d in os.scandir(data_dir) if d.is_dir()])
        self.class_to_idx = {cls_name: idx for idx, cls_name in enumerate(self.classes)}

        # 获取图像文件路径和对应的类别标签
        self.image_paths = []
        self.labels = []

        for class_name in self.classes:
            class_dir = os.path.join(data_dir, class_name)
            for img_name in os.listdir(class_dir):
                img_path = os.path.join(class_dir, img_name)
                self.image_paths.append(img_path)
                self.labels.append(self.class_to_idx[class_name])

    def __len__(self):
        """
        返回数据集中样本的数量。
        """
        return len(self.image_paths)

    def __getitem__(self, index):
        """
        根据索引获取样本(图像和标签)。

        Args:
            index (int): 样本的索引。

        Returns:
            tuple: (image, label) 图像和标签。
        """
        # 获取图像路径和标签
        img_path = self.image_paths[index]
        label = self.labels[index]

        # 打开图像并进行预处理
        image = Image.open(img_path)
        if self.transform:
            image = self.transform(image)

        return image, label

搭建训练主程序

本文将其命名为train_lenet.py。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from LeNet import LeNet
from Mydata import MyDataset
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter

# 定义训练参数
batch_size = 64
learning_rate = 0.001
num_epochs = 5

# 检查是否可以使用CUDA加速
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 创建数据集实例
data_dir = "4_Recognize/训练集"
transform = transforms.Compose([
    transforms.Resize((32, 32)),
    transforms.Grayscale(num_output_channels=1),  # Convert to grayscale
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

dataset = MyDataset(data_dir, transform=transform)
data_loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

# 创建LeNet模型实例
model = LeNet(num_classes=76)
model.to(device)

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

# 创建SummaryWriter实例
writer = SummaryWriter()

# 训练模型
total_steps = len(data_loader)
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(data_loader):
        images = images.to(device)
        labels = labels.to(device)

        # 前向传播
        outputs = model(images)
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if (i + 1) % 100 == 0:
            print(f'Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{total_steps}], Loss: {loss.item():.4f}')

            # 将损失写入日志
            writer.add_scalar('Loss/train', loss.item(), epoch * total_steps + i)

print('Finished Training')

# 关闭SummaryWriter
writer.close()

# 保存模型
torch.save(model.state_dict(), 'lenet_model.pth')

最后保存所需模型文件即可,本文主要讨论的是方法以及具体实现思路,本文未进行模型评估检验,感兴趣的小伙伴可以外加代码对模型精度进行检验。

第二步处理2_Train文件

JSON转YOLO TXT 

通过参考https://blog.csdn.net/wlh156423/article/details/118861987 ​​​​​​本文对其进行改进,即可得到如下代码实现:

import os
import json
import shutil
from PIL import Image

def convert_bbox(img_size, box):
    dw = 1. / img_size[0]
    dh = 1. / img_size[1]
    x = (box[0] + box[2]) / 2.0 - 1
    y = (box[1] + box[3]) / 2.0 - 1
    w = box[2] - box[0]
    h = box[3] - box[1]
    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    return (x, y, w, h)

def get_image_size(image_path):
    with Image.open(image_path) as img:
        width, height = img.size
    return width, height

def extract_data_from_json(json_folder, train_folder):
    images_folder = os.path.join(train_folder, 'images')
    labels_folder = os.path.join(train_folder, 'labels')
    os.makedirs(images_folder, exist_ok=True)
    os.makedirs(labels_folder, exist_ok=True)

    json_files = [f for f in os.listdir(json_folder) if f.endswith('.json')]

    for json_file in json_files:
        with open(os.path.join(json_folder, json_file)) as f:
            data = json.load(f)
            img_name = data.get('img_name')
            ann = data.get('ann')

            img_path = os.path.join('2_Train', img_name + '.jpg')
            img_w, img_h = get_image_size(img_path)
            shutil.copy(img_path, os.path.join(images_folder, img_name + '.jpg'))

            txt_path = os.path.join(labels_folder, img_name + '.txt')
            with open(txt_path, 'w') as txt_file:
                for bbox in ann:
                    bbox = convert_bbox((img_w, img_h), bbox[:-1])  # Ignore the class label for now
                    txt_file.write(f"0 {' '.join(map(str, bbox))}\n")  # Assuming class index is 0

if __name__ == "__main__":
    json_folder_path = '2_Train'
    train_folder_path = 'train'
    extract_data_from_json(json_folder_path, train_folder_path)

将JSON文件转化为所需要的YOLO TXT文件,且保存再同项目train文件夹下。其中txt数据类如下

0 0.3091482649842271 0.5269662921348315 0.22082018927444794 0.1865168539325843
0 0.555205047318612 0.5730337078651686 0.17034700315457413 0.39550561797752815
0 0.7302839116719243 0.5808988764044944 0.167192429022082 0.402247191011236

将对应的信息框进行分类

import torch
import torchvision.transforms as transforms
from PIL import Image
from LeNet import LeNet
import os

# 定义预处理操作
transform = transforms.Compose([
    transforms.Grayscale(),  # 转为灰度图像
    transforms.Resize((32, 32)),  # 改变尺寸以适配LeNet模型输入
    transforms.ToTensor(),  # 转为张量
])

# 初始化模型
model = LeNet(num_classes=76)  
model.load_state_dict(torch.load("lenet_model.pth"))
model.eval()  # 设置为评估模式

# 文件和文件夹路径
label_dir = 'B题/labels/train'
image_dir = 'B题/images/train'
output_dir = 'new_data'

if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# 遍历标签文件夹
for label_file in os.listdir(label_dir):
    label_path = os.path.join(label_dir, label_file)
    image_path = os.path.join(image_dir, label_file.replace('.txt', '.jpg'))  # 假设图片文件名与标签相同,仅扩展名不同

    if os.path.exists(image_path):
        img = Image.open(image_path)  # 加载图像
        with open(label_path, 'r') as file:
            lines = file.readlines()

        new_lines = []
        for line in lines:
            parts = line.strip().split()
            # 假设标签格式为:class x_center y_center width height
            _, x_center, y_center, width, height = map(float, parts)
            bbox = [
                (x_center - width / 2) * img.width,
                (y_center - height / 2) * img.height,
                width * img.width,
                height * img.height
            ]  # 计算实际像素坐标和尺寸

            # 裁剪并转换图像
            cropped_img = img.crop((bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3]))
            input_tensor = transform(cropped_img).unsqueeze(0)

            # 模型预测
            with torch.no_grad():
                output = model(input_tensor)
                predicted_class = output.argmax(1).item()

            # 更新行
            new_line = f"{predicted_class} {x_center} {y_center} {width} {height}\n"
            new_lines.append(new_line)

        # 保存新的标签文件
        new_label_path = os.path.join(output_dir, label_file)
        with open(new_label_path, 'w') as new_file:
            new_file.writelines(new_lines)

这段代码会将原本的标签进行处理并且以txt形式保存在new_data文件夹下,小伙伴们运行时记得更改路径为自己的路径。转化后的图片txt信息如下

59 0.3091482649842271 0.5269662921348315 0.22082018927444794 0.1865168539325843
69 0.555205047318612 0.5730337078651686 0.17034700315457413 0.39550561797752815
6 0.7302839116719243 0.5808988764044944 0.167192429022082 0.402247191011236

提取类别标签

基于yolov8训练数据集时需要类别标签,本文运用如下代码进行相关处理

import os

# 指定文件夹路径
folder_path = '4_Recognize/训练集'

# 获取子文件夹名称并按照指定格式保存
with open('name.txt', 'w') as file:
    for idx, subfolder in enumerate(sorted(os.listdir(folder_path))):
        file.write(f"{idx}: {subfolder}\n")

第三步进行YOLOV8模型训练

训练操作可以参考https://blog.csdn.net/qq_42452134/article/details/135149531

以下是实现过程

建立一个python文件且输入如下代码

from ultralytics import YOLO

# 加载模型
model = YOLO('yolov8n.yaml').load('yolov8n.pt')  # 从YAML构建并转移权重

if __name__ == '__main__':
    # 训练模型
    results = model.train(data='fnal.yaml', epochs=10, imgsz=256)

    metrics = model.val()

建立对应的.yaml文件,这里就需要用到我们提取的类别标签。(本文命名为fnal.yaml)

# 这里的path需要指向你项目中数据集的目录
path: E:/dell/比赛/new/train/
# 这里分别指向你训练、验证、测试的文件地址,只需要指向图片的文件夹即可。但是要注意图片和labels名称要对应
train: images/train  # train images (relative to 'path') 128 images
val: images/val  # val images (relative to 'path') 128 images
test: images/test # test images (optional)

# Classes
names:
  0: 万
  1: 丘
  2: 丙
  3: 丧
  4: 乘
  5: 亦
  6: 人
  7: 今
  8: 介
  9: 从
  10: 令
  11: 以
  12: 伊
  13: 何
  14: 余
  15: 允
  16: 元
  17: 兄
  18: 光
  19: 兔
  20: 入
  21: 凤
  22: 化
  23: 北
  24: 印
  25: 及
  26: 取
  27: 口
  28: 吉
  29: 囚
  30: 夫
  31: 央
  32: 宗
  33: 宾
  34: 尞
  35: 巳
  36: 帽
  37: 并
  38: 彘
  39: 往
  40: 御
  41: 微
  42: 旨
  43: 昃
  44: 木
  45: 朿
  46: 涎
  47: 灾
  48: 焦
  49: 爽
  50: 牝
  51: 牡
  52: 牧
  53: 生
  54: 田
  55: 疑
  56: 祝
  57: 福
  58: 立
  59: 羊
  60: 羌
  61: 翌
  62: 翼
  63: 老
  64: 艰
  65: 艺
  66: 若
  67: 莫
  68: 获
  69: 衣
  70: 逆
  71: 门
  72: 降
  73: 陟
  74: 雍
  75: 鹿

同时要注意文件夹类型的指向,文件夹的命名也很关键,具体操作可以看https://blog.csdn.net/qq_42452134/article/details/135181244这位博主所讲内容。

这样我们就可以开始训练了

3、所得结果

训练的图片展示

当我们进行训练后,可以在项目路径下的runs文件中观看所得参数图像,以及python运行框中查看模型是否训练完全。

预测

再训练好模型后如何进行预测呢?可以参考官方文档https://docs.ultralytics.com/modes/predict/#key-features-of-predict-mode

本文给出相应的二份预测处理的代码

from PIL import Image
from ultralytics import YOLO

# Load a pretrained YOLOv8n model
model = YOLO('yolov8n.pt')

# Run inference on 'bus.jpg'
results = model(['4_Recognize/测试集/w01790.jpg', '4_Recognize/测试集/w01791.jpg'])  # results list

# Visualize the results
for i, r in enumerate(results):
    # Plot results image
    im_bgr = r.plot()  # BGR-order numpy array
    im_rgb = Image.fromarray(im_bgr[..., ::-1])  # RGB-order PIL image

    # Show results to screen (in supported environments)
    r.show()

    # Save results to disk
    r.save(filename=f'results{i}.jpg')
# -*- coding: gbk -*-
import os
from PIL import Image
from tqdm import tqdm
from glob import glob
from ultralytics import YOLO

# 定义测试集图片文件夹和保存预测图片的文件夹
test_images_folder = "4_Recognize/测试集"
output_folder = "new_images"

# 加载训练好的YOLOv8模型
model = YOLO("yolov8n.pt")  # 替换为你训练好的模型的路径

# 确保保存预测图片的文件夹存在
os.makedirs(output_folder, exist_ok=True)

# 获取测试集中所有图片的路径
test_image_paths = glob(os.path.join(test_images_folder, "*.jpg")) + glob(os.path.join(test_images_folder, "*.png"))

# 对每张图片进行预测并保存预测后的图片
# 对每张图片进行预测并保存预测后的图片
for image_path in tqdm(test_image_paths, desc="Predicting"):
    # 读取图片
    image = Image.open(image_path)

    # 对图片进行预测
    predicted_images = model.predict(image)

    # 处理每个预测结果并保存到文件夹中
    for idx, predicted_image in enumerate(predicted_images):
        # 构建保存预测图片的路径,这里假设以预测结果的索引命名文件
        output_path = os.path.join(output_folder, f"{os.path.basename(image_path)}_{idx}.jpg")

        # 保存预测后的图片
        predicted_image.save(output_path)

print("预测完成,并且预测图片已保存到'new_images'文件夹中。")

4、结语

本文只是提供一种参考思路且对对应数据集进行了处理。在进行相关的图像识别中,最让人头疼的应该是数据集,例如本次比赛的数据集处理起来较为复杂,如果不熟悉YOLO数据类型,以及对应关系转化,难以将题目进行。

同时作者参与此次比赛收获颇多,虽然是赛后补救,但也希望提供给大家一种思路,以及运用YOLOV8的细节。

  1. YOLOV8运行需要用所需的YOLO TXT类型文件,(即txt文件名为图像文件名,且txt文件中装有标签类与信息框数据)
  2. YOLOV8进行数据训练需要注意.yaml的路径设置要注意,以及对应路径下所包含的文件名不能改变,且文件摆放不能改变具体可以参考上文我推荐的文章。

第一次发表文章,望对大家有所帮助。

  • 15
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
很高兴为您提供甲骨文学习小游戏的思路。下面是一个简单的游戏示例: 1. 游戏目标:学习甲骨文,了解甲骨文的基本知识和常用符号。 2. 游戏规则:玩家需要在限定时间内,回答尽可能多的甲骨文问题,每个问题包含一个甲骨文符号和四个选项,玩家需要选择正确的选项。每答对一题,得分加一分,答错不扣分。时间到后,游戏结束,统计得分排名。 3. 游戏设计:游戏界面应该简洁明了,包括游戏标题、计时器、得分统计、题目展示和选项选择等。游戏难度可以根据玩家的水平进行调整,可以设置不同的关卡和难度等级。同时,还可以增加一些游戏元素,比如音效、动画等,增强游戏的趣味性和可玩性。 4. 游戏开发:游戏可以使用Python等编程语言进行开发,可以使用Pygame等游戏开发库进行界面设计和交互操作。同时,还需要编写甲骨文数据集和问题库,设计游戏难度和关卡等。游戏还可以利用机器学习图像识别技术,自动生成甲骨文问题和选项,增加游戏的多样性和趣味性。 下面是一个简单的Python代码示例: ```python import pygame import random # 初始化Pygame pygame.init() # 设置游戏界面 WIDTH, HEIGHT = 800, 600 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption('甲骨文学习游戏') # 加载游戏资源 font = pygame.font.Font(None, 36) bg_color = (255, 255, 255) question_data = [('象形', '日', ['A. 月', 'B. 日', 'C. 星', 'D. 太阳'], 'B'), ('象形', '口', ['A. 口', 'B. 人', 'C. 门', 'D. 山'], 'A'), ('象形', '木', ['A. 山', 'B. 木', 'C. 金', 'D. 石'], 'B')] # 设置游戏计时器和得分统计 time_left = 60 score = 0 # 游戏循环 running = True while running: # 事件处理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: running = False # 更新游戏状态 time_left -= 1 if time_left == 0: running = False # 绘制游戏界面 screen.fill(bg_color) text = font.render('时间:{} 秒'.format(time_left), True, (0, 0, 0)) screen.blit(text, (20, 20)) text = font.render('得分:{} 分'.format(score), True, (0, 0, 0)) screen.blit(text, (20, 60)) if len(question_data) > 0: idx = random.randint(0, len(question_data) - 1) question = question_data.pop(idx) text = font.render('甲骨文符号:{}'.format(question[1]), True, (0, 0, 0)) screen.blit(text, (20, 100)) text = font.render('题目类型:{}'.format(question[0]), True, (0, 0, 0)) screen.blit(text, (20, 140)) text = font.render('1. {}'.format(question[2][0]), True, (0, 0, 0)) screen.blit(text, (20, 180)) text = font.render('2. {}'.format(question[2][1]), True, (0, 0, 0)) screen.blit(text, (20, 220)) text = font.render('3. {}'.format(question[2][2]), True, (0, 0, 0)) screen.blit(text, (20, 260)) text = font.render('4. {}'.format(question[2][3]), True, (0, 0, 0)) screen.blit(text, (20, 300)) pygame.display.flip() selection = '' while selection == '': for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: running = False elif event.key == pygame.K_1: selection = 'A' elif event.key == pygame.K_2: selection = 'B' elif event.key == pygame.K_3: selection = 'C' elif event.key == pygame.K_4: selection = 'D' if selection == question[3]: score += 1 # 刷新游戏界面 pygame.display.flip() # 游戏结束 text = font.render('游戏结束!得分:{} 分'.format(score), True, (0, 0, 0)) screen.blit(text, (WIDTH // 2 - 120, HEIGHT // 2 - 20)) pygame.display.flip() pygame.time.delay(2000) # 退出Pygame pygame.quit() ``` 需要注意的是,这只是一个简单的示例,实际的甲骨文学习小游戏还需要考虑很多问题,比如游戏设计和难度调整等。希望这个示例能够为您提供一些有用的思路。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值