手写识别

news/2025/11/11 23:25:48/文章来源:https://www.cnblogs.com/5488cxr/p/19211902

import os
import random
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms

解决OMP冲突
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
device = torch.device('cpu')

配置参数
CHARS = ['一', '二', '三', '十', '人', '口', '手', '日', '月', '水']
TRAIN_NUM = 200
TEST_NUM = 50
IMG_SIZE = 64
DATA_SAVE_DIR = 'hanzi_data'
BATCH_SIZE = 32
EPOCHS = 30
LEARNING_RATE = 0.005

-------------------------- 关键改进:使用PIL默认字体生成汉字(无需额外安装) --------------------------
class HanziDatasetGenerator:
def init(self):

不依赖系统字体,使用PIL的默认字体+手动调整位置确保汉字显示

self.font = ImageFont.load_default()
print("提示:使用默认字体生成汉字(可能显示较简单,但能保证运行)")

def _generate_single_img(self, char):
"""生成简单但可区分的汉字图像"""
img = Image.new('L', (IMG_SIZE, IMG_SIZE), color=255) # 白底
draw = ImageDraw.Draw(img)

# 针对默认字体调整位置(确保汉字完整显示)
# 不同汉字手动调整偏移量,保证特征差异
char_offsets = {'一': (5, 25), '二': (5, 15), '三': (5, 10),'十': (20, 15), '人': (10, 20), '口': (15, 15),'手': (5, 10), '日': (15, 15), '月': (10, 15), '水': (5, 10)
}
x, y = char_offsets[char]# 固定较大字体尺寸,确保笔画清晰
font_size = 40
try:# 再次尝试系统字体,失败则用默认font = ImageFont.truetype('simsun.ttc', size=font_size)  # 尝试宋体draw.text((x, y), char, font=font, fill=0, stroke_width=2)
except:# 用默认字体,手动加粗笔画确保可区分draw.text((x, y), char, font=self.font, fill=0, stroke_width=3)# 二次绘制增强笔画(避免默认字体太细)draw.text((x+1, y), char, font=self.font, fill=0, stroke_width=2)# 轻微旋转增加差异
rotation = random.randint(-10, 10)
img = img.rotate(rotation, expand=False, fillcolor=255)return img

def generate_dataset(self):
"""生成数据集目录和图片"""
if os.path.exists(DATA_SAVE_DIR):
for root, dirs, files in os.walk(DATA_SAVE_DIR, topdown=False):
for f in files: os.remove(os.path.join(root, f))
for d in dirs: os.rmdir(os.path.join(root, d))
os.rmdir(DATA_SAVE_DIR)

# 创建目录
for split in ['train', 'test']:for char in CHARS:os.makedirs(os.path.join(DATA_SAVE_DIR, split, char), exist_ok=True)# 生成样本
print("生成数据集...")
for char in CHARS:for i in range(TRAIN_NUM):img = self._generate_single_img(char)img.save(os.path.join(DATA_SAVE_DIR, 'train', char, f'{i}.png'))for i in range(TEST_NUM):img = self._generate_single_img(char)img.save(os.path.join(DATA_SAVE_DIR, 'test', char, f'{i}.png'))
print(f"数据集生成完成:{os.path.abspath(DATA_SAVE_DIR)}")

-------------------------- 数据集加载 --------------------------
class HanziDataset(Dataset):
def init(self, split='train'):
self.split = split
self.data_dir = os.path.join(DATA_SAVE_DIR, split)
self.char_list = CHARS
self.char2idx = {c:i for i,c in enumerate(self.char_list)}
self.images, self.labels = self._load_data()
self.transform = transforms.ToTensor()

def _load_data(self):
images = []
labels = []
for char in self.char_list:
char_dir = os.path.join(self.data_dir, char)
for img_name in os.listdir(char_dir):
images.append(os.path.join(char_dir, img_name))
labels.append(self.char2idx[char])
return images, labels

def len(self):
return len(self.images)

def getitem(self, idx):
img = Image.open(self.images[idx]).convert('L')
return self.transform(img), self.labels[idx]
-------------------------- 模型 --------------------------
class FeatureCNN(nn.Module):
def init(self, num_classes=10):
super().init()
self.features = nn.Sequential(
nn.Conv2d(1, 8, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2, 2), # 64→32

    nn.Conv2d(8, 16, kernel_size=3, padding=1),nn.ReLU(),nn.MaxPool2d(2, 2)  # 32→16
)
self.classifier = nn.Linear(16 * 16 * 16, num_classes)

def forward(self, x):
x = self.features(x)
x = x.view(-1, 16 * 16 * 16)
x = self.classifier(x)
return x
-------------------------- 训练与识别 --------------------------
def main():

生成数据集(关键:即使没有中文字体也能生成可区分的图像)

generator = HanziDatasetGenerator()
generator.generate_dataset()

加载数据

train_dataset = HanziDataset('train')
test_dataset = HanziDataset('test')
train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=0)
test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=0)

模型与优化器

model = FeatureCNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)

训练

print("\n开始训练...")
best_acc = 0.0
for epoch in range(EPOCHS):
model.train()
total_loss = 0.0

for imgs, labels in train_loader:imgs, labels = imgs.to(device), labels.to(device)optimizer.zero_grad()outputs = model(imgs)loss = criterion(outputs, labels)loss.backward()optimizer.step()total_loss += loss.item() * imgs.size(0)avg_loss = total_loss / len(train_dataset)# 测试
model.eval()
correct = 0
total = 0
with torch.no_grad():for imgs, labels in test_loader:imgs, labels = imgs.to(device), labels.to(device)outputs = model(imgs)_, preds = torch.max(outputs, 1)total += labels.size(0)correct += (preds == labels).sum().item()acc = 100 * correct / total
print(f"轮次{epoch+1:2d} | 损失:{avg_loss:.4f} | 准确率:{acc:.2f}%")if acc > best_acc:best_acc = acctorch.save(model.state_dict(), 'best_model.pth')if acc >= 85:print(f"达标!准确率:{acc:.2f}%")break

识别

model.load_state_dict(torch.load('best_model.pth'))
print(f"\n最优准确率:{best_acc:.2f}%")

while True:
path = input("\n输入图片路径(q退出):")
if path.lower() == 'q':
break
if not os.path.exists(path):
print("路径错误")
continue

try:img = Image.open(path).convert('L').resize((64,64))img_tensor = transforms.ToTensor()(img).unsqueeze(0).to(device)with torch.no_grad():output = model(img_tensor)pred_char = CHARS[torch.argmax(output).item()]confidence = torch.softmax(output, dim=1).max().item() * 100print(f"识别结果:{pred_char} | 可信度:{confidence:.2f}%")
except Exception as e:print(f"错误:{e}")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/962821.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

团队作业2-《文章管理系统》需求规格说明书

条目 内容这个作业属于哪个课程 [软件工程]https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience这个作业要求在哪里 点我查看作业要求(https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerSc…

01人月神话读后感--软件中的“焦油坑”

读《人月神话》第一章“焦油坑”有感 读完这一章,我的第一个感觉就是:太真实了,说得太准了! 布鲁克斯用的那个“焦油坑”的比喻,简直绝了。我想所有干过软件项目的人,应该都能立刻明白那种感觉:表面上看:我们的…

线程池FAQ

线程池FAQ线程池FAQ0. 你将学到什么?线程池到底是什么、解决什么问题线程池怎么接任务→排队→扩容→执行→回收SingleThreadExecutor 的作用与用法有界队列是啥,和 maximumPoolSize 有啥关系拒绝策略为什么存在、怎…

Python Threading new thread

import threading import time import uuid from datetime import datetimedef print_time_uuid(num=1000):for a in range(1,num+1):print(f{a},{datetime.now()}_{uuid.uuid4().hex}\n)time.sleep(1)def print_uuid_…

从同步耦合到异步解耦:消息中间件如何重塑系统间的通信范式?

从同步耦合到异步解耦:消息中间件如何重塑系统间的通信范式?当成百上千的服务需要相互协作时,它们之间的通信模式变得至关重要。如果服务间采用紧密耦合的同步调用,一个服务的延迟或故障,就可能引发连锁反应,导致…

深入理解OpenWrt生态:LuCI、UCI、ubus与rpcd的协同工作机制 - 实践

深入理解OpenWrt生态:LuCI、UCI、ubus与rpcd的协同工作机制 - 实践2025-11-11 23:02 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !…

251111重点

251111重点在以前人们学习知识要逐字逐句,究根问底,才能在脑海里有一个框架的概念,归根结底,是缺乏好的老师,但是现在用心理学的话说就是搁置,不明白的地方有人知道(ai),可以先搁置,用自然语言理解,很容易就有…

第22天(简单题中等题 二分查找)

打卡第二十二天 1道简单题+3道中等题题目:思路:代码: class Solution { public:bool search(vector<int> &nums, int target) {int n = nums.size();if (n == 0) {//数组为空直接返回 falsereturn false;}if…

In the name of capitalists

So we have Harvard University, Stanford University. Whatever its Japan, Korea, or China, India, I never found University names that are more narcissistic than Americans. Indians are usually quiet just …

2025.11.11总结

今天在百度智能云上找api接口,因为大作业要求,需要做一个集文本生成,语音合成,ai作画三合一的人工智能的项目。 对于第一次使用模型来说,比较艰难,界面不熟悉,文档看不懂,没有现成可参考的视频,较新的手把手教…

K8S百万资源预list加载数据方案

K8S百万资源预list加载数据方案联邦控制器资源已经超过百万,每次重启时,需要一次性list所有资源,需要10分钟,给服务启动带来很多风险。 现通过主从预list加载数据,避免升级或者leader切换过程中长时间拉取数据。/…

102302105汪晓红数据采集作业2

第二次作业 作业①: 作业代码和图片: 核心代码:点击查看代码 # 主程序 url = "http://www.weather.com.cn/weather/101010100.shtml" city = "北京"# 初始化数据库 conn = setup_database()try:…

【数据结构】:链表的核心实现与运行解析

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

11.11每日总结

今天主要的课程有人机交互技术和软件构造。软考结束了 唉 已经做好下学期再努力试一次的准备了。今天还完善了心理健康平台的设计,继续加油

Meta AI 推出全语种语音识别系统,支持 1600+语言;谢赛宁、李飞飞、LeCun 联手发布「空间超感知」AI 框架丨日报

开发者朋友们大家好:这里是 「RTE 开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的技术」、「有亮点的产品」、「有思考的文章」、「有态度…

Python Socket网络编程

1. Socket参数介绍 服务器端server = socket.socket(AF.INET,sock.SOCK_STREAM)Socket Typessocket.SOCK_STREAM #for tcpsocket.SOCK_DGRAM #for udp socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、…

拥护向量机(SVM)(二)---优化算法

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

英语翻译题做法

🔹快速浏览全文和完形填空一样,做翻译题前,我先快速浏览一遍全文,大概知道文章讲什么、关于什么。如果时间充裕,可以这样做;但如果考试时间紧张,直接读划线句的翻译部分。重点看首尾两段,也能大致把握主旨。�…

Python show memory

py -m pip install psutil import os import psutil import asyncio import time from datetime import datetimeclass Book:def __init__(self,id,name,author,isbn,title,topic):self.id=idself.name=nameself.autho…

LeetCode - 1171. - 教程

LeetCode - 1171. - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &q…