目录
一,问题背景
在做人智学科基础的期末考察作业,虽然老师说可以网上找代码,但搜了一圈发现啥都看不懂(不是大佬写得不好,而是我们Python还在学基础语法,实在太菜),无法完成论文撰写。
尝试文心一言,并结合所学,从头到尾(伪)手搓代码,获益匪浅。故写下人生第一篇博文,从32*32像素的图片开始,
涉及内容:图片处理、Python代码详细注释、相似度计算判断等
期待路过的好心大佬指点!❤
二,实验过程
1.原理及简要思路:
(1)原理:
灰度图像像素的值与像素点的亮度有关。
在灰度图像中,每个像素点的值只有一个,表示其亮度级别,范围从0到255(0表示黑色,255表示白色)。
灰度值越高,像素点越亮,反之则越暗。
(2)简要思路:
①图片处理:将图像转为灰度图像——对图像进行归一化(灰度值/255,使每个像素点的值都在0和1之间,个人觉得这步可以跳过)——得到32*32的二维数组
②相似度计算思路:从0-9的图片中,分别挑一张图片作为标准,并处理得到其二维数组,设名为A——将测试图片同样处理成二维数组,设名为B——对于每个B,计算其与10个A的距离,距离最小的,即为测试图片所属数字
③距离计算:欧几里得距离,是在n维空间中两个点之间的真实距离。在二维中体现为直角坐标系中两点距离的计算。会通过numpy来直接实现。
2.数据准备:
(1)数据获取
老师给的,包含许多32*32像素的手写图片
链接:https://pan.baidu.com/s/1VBBaGNsbXjzI7BUkY_xiuw?pwd=dspc
提取码:dspc
我在学写代码的时候,为了方便搞懂,新建了“测试数据”和“训练数据”两个文件夹,每个里面只挑了3张0和1,(1)到(3)的代码会先基于此情况编写
(2)将图像转化为灰度图像
from PIL import Image
import numpy as np
import glob
import os
#遍历图片文件夹,获取图片路径和对应的标签
image_folder = r'E:\桌面\期末\代码\尝试时所用数据\训练数据\零' # 替换为你的图片文件夹路径
for image_path in glob.glob(os.path.join(image_folder, '*.bmp')): # 假设图片为bmp格式,如有其他格式请相应修改
with open(image_path, 'rb') as f:
# 读取图像文件
image = Image.open(f)
# 将彩色图像转换为灰度图像
gray_image = image.convert('L')
# 将灰度图像转换为NumPy数组,并重塑为一维数组
pixel_values = np.array(gray_image).flatten()
# 将一维数组重塑为32x32的二维数组
reshaped_image = pixel_values.reshape((32, 32))
(3)将图片数据和标签分别存储在两个列表中
(仅代码片段,帮助理解。融合后有变量名、删除二进制信息等部分改动。完整版见末尾代码)
import os
import glob
image_data = [] # 创建一个空列表来存储图片数据
labels = [] # 创建一个空列表来存储标签
# 遍历图片文件夹,获取图片路径和对应的标签
image_folder = 'E:\桌面\期末\代码\尝试时所用数据\训练数据\零' # 替换为你的图片文件夹路径
for image_path in glob.glob(os.path.join(image_folder, '*.bmp')): # 假设图片为bmp格式,如有其他格式请相应修改
with open(image_path, 'rb') as f:
image_data.append(f.read()) # 将图片数据读取到列表中
label = int(list(os.path.basename(image_path))[-5]) # 假设图片名称代表数字,例如0.png、1.png等,根据实际情况修改提取标签的方式
labels.append(label) # 将标签添加到列表中
#print(image_data)
#print(labels)
其中,关于“list(os.path.basename(image_path))[-5]”的解释:
os.path.basename()
是一个Python的os模块函数,用于返回指定路径的基本文件名。image_path
是一个包含图片完整路径的字符串。例如:'E:\桌面\期末\代码\尝试时所用数据\训练数据\零\001-0.bmp'- 当我们调用
os.path.basename(image_path)
,它会返回基本文件名,即 ’001-0.bmp'。 - 转列表后去第-5个元素,得到“0”作为图片标签。
- 为什么不用split()而是要转列表操作?不知道的话,建议实操一下+文心一言
(4) 编写函数,便于批量处理标准图片及测试图片
def get_32D_and_labels(path):
data = [] # 创建一个空列表来存储图片数据
labels = [] # 创建一个空列表来存储图片标签
# 遍历图片文件夹,获取图片路径和对应的标签
image_folder = path #
for image_path in glob.glob(os.path.join(image_folder, '*.bmp')): # 假设图片为bmp格式,如有其他格式请相应修改
with open(image_path, 'rb') as f:
# 读取图像文件
image = Image.open(f)
# 将彩色图像转换为灰度图像
gray_image = image.convert('L')
# 将灰度图像转换为NumPy数组,并重塑为一维数组
pixel_values = np.array(gray_image).flatten()
# 将一维数组重塑为32x32的二维数组
reshaped_image = pixel_values.reshape((32, 32))
reshaped_image = reshaped_image / 255.0 # 归一化到[0, 1]范围
data.append(reshaped_image) # 将归一化后的图片数据添加到列表中
if '标准数据' in path:
label = int(list(os.path.basename(image_path))[-5]) # 得到数字标签
labels.append(label) # 将标签添加到列表中
else:
pass
return labels,data
3.计算相似度(距离)并判断
编写函数,通过计算欧式距离并判断其最小值,得到数字类型
def get_kind(array1):
dist_data= []
# 遍历标准数据,计算其与测试数据的欧式距离,并用列表记录
for array2 in stan_data:
dist = np.linalg.norm(array1 - array2)
dist_data.append(dist)
dist_min=min(dist_data) # 得到最小距离的值
kind_index = dist_data.index(dist_min) #类型在标签列表中的索引==最小距离值在距离列表中的索引
kind = stan_labels[kind_index]
return kind
三,实验结果及分析
1.完整代码及输出结果
from PIL import Image
import numpy as np
import glob
import os
def get_32D_and_labels(path):
data = [] # 创建一个空列表来存储图片数据
labels = [] # 创建一个空列表来存储图片标签
# 遍历图片文件夹,获取图片路径和对应的标签
image_folder = path #
for image_path in glob.glob(os.path.join(image_folder, '*.bmp')): # 假设图片为bmp格式,如有其他格式请相应修改
with open(image_path, 'rb') as f:
# 读取图像文件
image = Image.open(f)
# 将彩色图像转换为灰度图像
gray_image = image.convert('L')
# 将灰度图像转换为NumPy数组,并重塑为一维数组
pixel_values = np.array(gray_image).flatten()
# 将一维数组重塑为32x32的二维数组
reshaped_image = pixel_values.reshape((32, 32))
reshaped_image = reshaped_image / 255.0 # 归一化到[0, 1]范围
data.append(reshaped_image) # 将归一化后的图片数据添加到列表中
if '标准数据' in path:
label = int(list(os.path.basename(image_path))[-5]) # 得到数字标签
labels.append(label) # 将标签添加到列表中
else:
pass
return labels,data
def get_kind(array1):
dist_data= []
# 遍历标准数据,计算其与测试数据的欧式距离,并用列表记录
for array2 in stan_data:
dist = np.linalg.norm(array1 - array2)
dist_data.append(dist)
dist_min=min(dist_data) # 得到最小距离的值
kind_index = dist_data.index(dist_min) #类型在标签列表中的索引==最小距离值在距离列表中的索引
kind = stan_labels[kind_index]
return kind
stan_labels,stan_data = get_32D_and_labels('E:\桌面\期末\代码\尝试时所用数据\标准数据')
test_labels,test_data = get_32D_and_labels('E:\桌面\期末\代码\尝试时所用数据\测试数据')
#得到标准数字的32维数组列表及对应标签列表、测试数字的32维数组列表及空标签列表,空标签列表将用于记录数字类别
for test_32D in test_data:
test_kind = get_kind(test_32D)
test_labels.append(test_kind)
print(stan_labels)
print(test_labels) # 根据我的数据库,打印内容应与stan_labels一致
输出结果:
2.结果分析
根据我的数据库,两个列表应该一致。但2、5、8没能成功识别,正确率只有70%