Python构建轻量级手写数字识别数据库管理系统

Evan
Evan
发布时间:2026-10-13 | 2人浏览过
原创

本文介绍如何使用Python构建一个轻量级的图片存储管理系统,用于优化手写数字识别流程。通过将图像转换为1x10000向量并存储于CSV文件中,系统能够智能识别新增图片,仅对增量数据进行向量化处理,避免重复计算。文章详细解析了从文件对比、向量生成到数据合并的完整逻辑,并提供了核心代码实现,旨在提升大量图片处理时的效率。

写在前面

在上一篇关于Python实现手写数字识别及图像处理的教程中,我们详细讲解了如何对图片进行裁剪和拉伸,最终将每张图片转化为一个大小为1x10000的向量。然而,如果每次运行程序都重新计算这些向量,当图片数量庞大时,将会消耗大量的计算时间。

为了解决这一问题,我们需要将这些向量持久化存储到文件中。系统的运行逻辑是:每次启动时,首先检查图库中是否有新增图片。若有新增,则将这些新图片转换为1x10000的向量并追加存入文件;若无新增,则直接读取文件中已有的全部图片向量进行后续计算。这种机制显著节省了重复计算的时间。

因此,本文旨在从零开始构建这样一个图片存储管理系统。

实现逻辑

第一次读入图片

我们的图库中包含大量手写数字图片,每张图片的名称遵循“[数字内容]_[序号]”的格式。例如,文件名“2_3”表示该图片中的数字是2,且是数字为2的图片序列中的第3张。

我们使用一个名为Data.csv的CSV文件作为基础图片数据库。

首先,程序读取图库中所有图片的文件名,并将其保存在变量fileNames中。同时,读取Data.csv文件中的所有数据。

接着,提取Data.csv的最后一列(该文件共有10002列,其中第10001列表示图片对应的数字,第10002列存储图片名称)。我们将这一列提取出来,存储在变量item中,它代表了数据库中已存储的所有图片名称。

我们将图库中新增的图片名称保存在newFileNames中。如果Data.csv文件为空,则直接将fileNames赋值给newFileNames,这意味着如果数据库初始为空,图库中的所有图片均被视为新增图片。

如果Data.csv不为空,则将item中的内容与fileNames进行比对。若fileNames中存在而item中不存在的名称,则将这些名称加入newFileNames;若item中存在而fileNames中不存在的名称,则忽略不计。

简而言之,我们设定数据库遵循“只进不出”的原则。

至此,我们获得了新增图片的名称列表newFileNames。

将newFileNames中的图片名称传入上一篇文章介绍的GetTrainPicture函数进行处理,得到一个nx10001的矩阵。该矩阵的每一行代表一张新增图片,前10000列为图片向量,第10001列为该图片对应的数字,结果保存在变量pic中。

将这些新图片的数据追加存入数据库的末尾。

读取数据库中原有的图片向量,并与新获取的pic矩阵合并,从而得到当前所有训练图片的完整向量集合pic。

以上即为本章的核心内容,下文将结合代码进行详细解释。

代码解析

主文件

import os
import numpy as np
import OperatePicture as OP
import OperateDataBase as OD
import csv

##Essential vavriable 基础变量
#Standard size 标准大小
N = 100
#Gray threshold 灰度阈值
color = 100/255

#读取原CSV文件
reader = list(csv.reader(open('DataBase.csv', encoding = 'utf-8')))
#清除读取后的第一个空行
del reader[0]
#读取num目录下的所有文件名
fileNames = os.listdir(r"./num/")
#对比fileNames与reader,得到新增的图片newFileNames
newFileNames = OD.NewFiles(fileNames, reader)
print('New pictures are: 'newFileNames)
#得到newFilesNames对应的矩阵
pic = OP.GetTrainPicture(newFileNames)
#将新增图片矩阵存入CSV中
OD.SaveToCSV(pic, newFileNames)
#将原数据库矩阵与新数据库矩阵合并
pic = OD.Combination(reader, pic)

我将两节内容分别封装在两个Python文件中:上一篇文章涉及的图片切割与处理逻辑放置在OperatePicture文件中,而本节涉及的数据库处理逻辑则放置在OperateDatabase文件中。

由于整体代码逻辑已在上方梳理完毕,此处不再赘述。接下来将针对每个函数进行具体讲解。

OperateDatabase代码

从主文件中可以看到,首先调用了NewFiles函数,该函数主要用于对比fileNames和reader两个来源中图片名称的差异,并返回新增图片名称的列表。代码如下:

def NewFiles(fileNames, reader):
 '''判断是否有不同于数据库中的新文件加入'''
 #如果数据库中没有数据,则返回filenames
 if len(reader) == 0:
  return fileNames
 else:
  #从数据库中提取所有名称
  files = [item[10001] for item in reader]
  #需要加入的图片名称
  newFileNames = []
  for item in fileNames:
   #判断当前名称是否存在数据库中
   #如果不存在,则加入newFileNames
   if item not in files:
    newFileNames.append(item)
  return newFileNames

首先判断reader是否有内容。若无内容,说明是首次执行,直接返回fileNames;否则进入后续的比较逻辑。

获取newFileNames后,将该列表中的所有名称对应的图片通过GetTrainPicture函数处理,生成一个1x10001大小的矩阵。具体处理过程请参阅上一篇文章的讲解。

随后,为了将新数据存入CSV文件,我们利用SaveToCSV函数将pic矩阵存入文件,具体代码如下:

def SaveToCSV(pic, fileNames):
 '''将pic与对应的dileNames存入CSV文件'''
 writer = csv.writer(open('Database.csv', 'a', newline = ''), dialect = 'excel')
 #将fileNames变为列表
 f = [item for item in fileNames]
 #每一行依次写入文件中
 for i in range(len(pic)):
  #将改行图片向量转为list
  item = pic[i].tolist()
  #将这个图片向量对应的名称f放入列表最后一个
  item.append(f[i])
  writer.writerow(item)

函数运行后,会将pic矩阵对应的内容直接追加写入CSV文件,实现数据库的写入操作,且不会覆盖原有数据。

接下来,我们需要将数据库中原有的大量数据reader与新加入的数据pic合并到pic变量中,因此利用Combination函数将两个矩阵合并,代码如下:

def Combination(reader, pic):
 '''将两个矩阵reader与pic合并'''
 #两个矩阵的总行数
 l = len(reader) + len(pic)
 #初始化新的矩阵
 newPic = np.zeros(l*10001).reshape(l, 10001)
 #将reader最后的那个字符串名称去掉
 for item in reader:
  item.pop()
 #将reader转化为numpy的矩阵形式
 reader = np.array(reader)
 #新矩阵前半部分放reader,后半部分放pic
 if len(reader) != 0:
  newPic[0:len(reader), :] = reader
 newPic[len(reader):len(pic), :] = pic
 return newPic

由于reader的最后一行包含图片名称字符串,因此先利用pop方法将其去除,随后转换为矩阵形式,再直接放入新矩阵中。此处的矩阵操作可能较为少见,下面进行详细解释。

假设我现在有一个2x3的矩阵和一个2x2的矩阵

m = [[1 2 3]
  [4 5 6]]
n = [[7 8]
  [9 1]]

我可以执行如下操作

#操作一
m[:, 0:2] = n
print(m)
#操作二
m[:, 1:3] = n
print(m)

#以下为输出结果
#操作一
[[7 8 3]
 [9 1 6]]
#操作二
[[7 7 8]
 [9 9 1]]

可以看出,操作一直接将矩阵m的第1、2列替换为n,操作二将m的第2、3列替换为n。具体过程可查阅numpy矩阵操作文档或自行总结规律,此处不再赘述。

以上即为本篇的全部代码内容。

小结

本篇使用CSV文件构建了一个非常简陋的数据库,目前仅支持识别已有内容(NewFiles)与添加内容(SaveToCSV)两种操作,并未实现插入、删除或修改等功能。我认为这两个函数已满足当前需求,因此在功能已足够的前提下,不再进行拓展。

所有源代码已上传至我的GitHub仓库,欢迎下载参考,感谢阅读。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表