一 开篇说明:
这个是我找到的博客的svm,
本篇文章就是对这个博客进行进一步详细解读
https://blog.csdn.net/q1242027878/article/details/74271694
github: https://github.com/CHNicelee/HOG_SVM
二 原者的readme:
# HOG+SVM
使用说明:[点我查看博客](https://blog.csdn.net/q1242027878/article/details/74271694)
上面的hog_svm.py是用于训练的,通过提取图片的hog特征,然后通过SVM进行训练得到model,最后通过model进行预测,将结果写入result.txt文件之中。
代码不难,大家根据需要自己改改。
不要将hog的参数设置的太复杂。不然提取的特征会非常大,然后训练的时候会占满内存,导致机器死机。
三 原作者的解释说明
注意事项:
1. 你的图片长宽可以不相等,设置好
image_height
和image_width
即可。
如果图片大小不相等,可以使用change_size.py,把所有图片大小resize成一样的。ps:我没看到这个文件啊2. 你图片对应的标签必须是这样的:
001.jpg 1
003.jpg 2前面是图片名称,后面是对应的类别(类别用数字表示),中间要用空格隔开,每个标签占一行。
你要准备两个文件,一个是训练用的,一个是测试用的。
训练样本标签和预测的都是一样的格式
大家可以看github上面的实例.(image文件夹)
1) 这里是github下载的压缩包的样子
2) 这些是解压之后的文件
image 文件里面是正方形图片
image128 里面是没有经过resize的长方形图片
3) 打开image文件夹4) 打开train.txt文件
3. 你的训练和测试的图片可以放在同一个文件夹下面,也可以不同,设置好 train_image_path 和 test_image_path 即可。
4. 你要根据你图片的大小,对这行代码进行一些调整,这个调整需要你先了解hog的知识:
fd = hog (gray, orientations=18, pixels_per_cell=[8,8], cells_per_block=[4,4], visualise=False, transform_sqrt=True)
这是我为128x128大小图片设置的提取 hog 特征的参数,你需要适当改变一些,到时候的效果也不同。
orientations我是选9或18,即梯度方向的个数
一般来说,图片越大,pixels_per_cell 和 cells_per_block 里面的值可以相应变大。
5. 如果你要进行多次,建议你把文件位置的参数写死
#训练集图片的位置 train_image_path = '/home/icelee/Downloads/dataset/small_shixun/' #测试集图片的位置 test_image_path = '/home/icelee/Downloads/dataset/small_shixun/' #训练集标签的位置 train_label_path = '/home/icelee/Downloads/dataset/mydata.txt' #测试集标签的位置 test_label_path = '/home/icelee/Downloads/dataset/test.txt' #图片大小 image_height = 128 image_width = 128
6. 你需要安装 sk-learn库,hog,PIL库 等。可能还有一些零碎的库,大家用pip安装就好。
7. 实验都是彩色图片,如果你的图片是纯黑白的,很有可能需要改一下代码,看一下代码注释即可
采用这个测试cifar-10,准确率有50%多一点点(乱猜的准确率是10%),所以效果还是可以的,虽然比不上深度学习。
为了方便大家查看,代码放在了github https://github.com/CHNicelee/HOG_SVM
可能出现的错误
ValueError: Expected 2D array, got 1D array instead: array=[]. Reshape your data either using array.reshape(-1, 1) if your data has a single feature or array.reshape(1, -1) if it contains a single sample.
出现上面的错误说明没有正常读取到图片,所以请检查图片宽高是不是和设置的size一样,路径是否正确。
更改之后运行程序时,请选择重新获取特征。
四 原作者的hog_svm.py文件
# -*- coding=utf-8 -*- import glob import platform import time from PIL import Image from skimage.feature import hog import numpy as np import os import joblib from sklearn.svm import LinearSVC import shutil import sys # /=== === === ===> 1准备工作-start <=== === === ===\ # 第一个是你的类别 第二个是类别对应的名称 输出结果的时候方便查看 label_map = {1: 'cat', 2: 'chick', 3: 'snack', } # 训练集图片的位置 train_image_path = 'image' # 测试集图片的位置 test_image_path = 'image' # 训练集标签的位置 train_label_path = os.path.join('image', 'train.txt') # 此指令用于链接路径os.path.join # 测试集标签的位置 test_label_path = os.path.join('image', 'train.txt') # print(train_label_path) =>image128/train.txt size = 128 # 用于调整图片大小 train_feat_path = 'train/' test_feat_path = 'test/' model_path = 'model/' # 用于存储模型路径的构成,模型被放在model文件夹里 # \=== === === ===> 1准备工作-end <=== === === ===/ # /=== === === ===> 2定义函数-start <=== === === ===\ # 获得图片列表 def get_image_list(filePath, nameList): print('read image from ', filePath) img_list = [] for name in nameList: temp = Image.open(os.path.join(filePath, name)) img_list.append(temp.copy()) temp.close() return img_list # 提取特征并保存 def get_feat(image_list, name_list, label_list, savePath, size): i = 0 for image in image_list: try: # 如果是灰度图片 把3改为-1 image = np.reshape(image, (size, size, 3)) except: print('发送了异常,图片大小size不满足要求:', name_list[i]) continue gray = rgb2gray(image) / 255.0 # 这句话根据你的尺寸改改,orientations是梯度的方向的个数 fd = hog(gray, orientations=12, block_norm='L1', pixels_per_cell=[8, 8], cells_per_block=[4, 4], visualize=False, transform_sqrt=True) fd = np.concatenate((fd, [label_list[i]])) print(fd.shape) # (32448+1,) fd_name = name_list[i] + '.feat' # cat fd_path = os.path.join(savePath, fd_name) # savePath为: train_feat_path= 'train/' 和test_feat_path= 'test/' joblib.dump(fd, fd_path) # 保存fd模型 到 train_feat_path= 'train/' #TODO i += 1 print("Test features are extracted and saved.") # 变成灰度图片 def rgb2gray(im): gray = im[:, :, 0] * 0.2989 + im[:, :, 1] * 0.5870 + im[:, :, 2] * 0.1140 return gray # 获得图片名称与对应的类别 def get_name_label(file_path): print("read label from ", file_path) name_list = [] label_list = [] with open(file_path) as f: for line in f.readlines(): # 一般是name,后缀.label 三部分,所以至少长度为3 所以可以通过这个忽略空白行 if len(line) >= 3: name_list.append(line.split(' ')[0]) label_list.append(line.split(' ')[1].replace('\n', '').replace('\r', '')) if not str(label_list[-1]).isdigit(): print("labeol必须为数字,得到的是:", label_list[-1], "程序终止,请检查文件") exit(1) return name_list, label_list # 提取特征 def extra_feat(): train_name, train_label = get_name_label(train_label_path) # train_label_path='/train/train.txt' # name_list=[cat.jpg,smallCat.jpg] label_list=[1,1] test_name, test_label = get_name_label(test_label_path) # test_label_path='/train/test.txt' train_image = get_image_list(train_image_path, train_name) test_image = get_image_list(test_image_path, test_name) get_feat(train_image, train_name, train_label, train_feat_path, size) get_feat(test_image, test_name, test_label, test_feat_path, size) # 创建存放特征的文件夹 def mkdir(): if not os.path.exists(train_feat_path): os.mkdir(train_feat_path) if not os.path.exists(test_feat_path): os.mkdir(test_feat_path) # 训练和测试 def train_and_test(): t0 = time.time() features = [] labels = [] correct_number = 0 total = 0 for feat_path in glob.glob(os.path.join(train_feat_path, '*.feat')): # train_feat_path='train/' # glob.glob()这里是一个列表,feat_path是其中一个元素,就是路径 data = joblib.load(feat_path) # 读取fd模型 从 feat_path='train/*.feat'#TODO # print(feat_path)#特征的路径 train/yellowCat.jpg.feat # print(data)#yellocat的特征 features.append(data[:-1]) # 其实就是去除了这行文本的最后一个字符(此处删除的是最后的标签)后剩下的部分 # print(features) labels.append(data[-1]) # 把最后一个标签给了labels # print(labels) 标签列表['1', '2', '3', '2', '3', '1', '1', '3'] print("Training a Linear LinearSVM Classifier.") clf = LinearSVC() # TODO clf.fit(features, labels) # 下面的代码是保存模型的 if not os.path.exists(model_path): os.makedirs(model_path) joblib.dump(clf, model_path + 'model') # 保存clf模型 到 model/model#TODO # 下面的代码是加载模型 可以注释上面的代码 直接进行加载模型 不进行训练 # clf = joblib.load(model_path+'model') print("训练之后的模型存放在model文件夹中") # exit() result_list = [] for feat_path in glob.glob(os.path.join(test_feat_path, '*.feat')): # test/*.feat total += 1 if platform.system() == 'Windows': symbol = '\\' else: symbol = '/' image_name = feat_path.split(symbol)[1].split('.feat')[0] # test/cat.jpg.feat得到cat data_test = joblib.load(feat_path) # test/cat.jpg.feat读取到特征到data_test data_test_feat = data_test[:-1].reshape((1, -1)).astype(np.float64) # 去掉最后的标签 result = clf.predict(data_test_feat) # 预测测试特征 print(result) # ???result[0] result_list.append(image_name + ' ' + label_map[int(result[0])] + '\n') # cat 对应查找字典里面名字\n if int(result[0]) == int(data_test[-1]): correct_number += 1 write_to_txt(result_list) rate = float(correct_number) / total t1 = time.time() print('准确率是: %f' % rate) print('耗时是 : %f' % (t1 - t0)) def write_to_txt(list): with open('result.txt', 'w') as f: f.writelines(list) print('每张图片的识别结果存放在result.txt里面') # \=== === === ===> 2定义函数-end <=== === === ===/ if __name__ == '__main__': mkdir() # 不存在文件夹就创建 # need_input = input('是否手动输入各个信息?y/n\n') # if need_input == 'y': # train_image_path = input('请输入训练图片文件夹的位置,如 /home/icelee/image\n') # test_image_path = input('请输入测试图片文件夹的位置,如 /home/icelee/image\n') # train_label_path = input('请输入训练集合标签的位置,如 /home/icelee/train.txt\n') # test_label_path = input('请输入测试集合标签的位置,如 /home/icelee/test.txt\n') # size = int(input('请输入您图片的大小:如64x64,则输入64\n')) if sys.version_info < (3,): need_extra_feat = raw_input('是否需要重新获取特征?y/n\n') # 判断系统版本,此指令用python2 else: need_extra_feat = input('是否需要重新获取特征?y/n\n') if need_extra_feat == 'y': shutil.rmtree(train_feat_path) shutil.rmtree(test_feat_path) mkdir() extra_feat() # 获取特征并保存在文件夹 train_and_test() # 训练并预测
四 我改进之后的代码: 拆解解读 & 文件夹详情介绍
注意:我们文件名为 hog_svm.py
这里是完整代码:https://blog.csdn.net/zjc910997316/article/details/99005087
1 首先是导入包
# -*- coding=utf-8 -*-
import glob
import platform
import time
from PIL import Image
from skimage.feature import hog # 用来提取hog特征
import numpy as np
import os
import joblib
from sklearn.svm import LinearSVC
# 这个就是svm在机器学习库里面的名字, 关于sklearn.svm包中的SVC(kernel=”linear“)和LinearSVC的区别:
# # 请看https://blog.csdn.net/zjc910997316/article/details/84594763
import shutil
import sys
2 准备工作
# 第一个是你的类别 第二个是类别对应的名称 输出结果的时候方便查看
label_map = {1: 'tbj',
2: 'dw',
3: 'zsy',
4: 'zjc',
}
# 训练集图片的位置, 这里是相对位置
train_image_path = 'image_train'
# 测试集图片的位置
test_image_path = 'image_test'
# 训练集标签的位置
train_label_path = os.path.join('image_train', 'train.txt')
print(train_label_path)
# 测试集标签的位置
test_label_path = os.path.join('image_test', 'test.txt')
image_height = 128
image_width = 64
train_feat_path = 'train/'
test_feat_path = 'test/'
model_path = 'model/'
# 训练集标签的位置
=> train_label_path = os.path.join('image_train', 'train.txt') # 此指令用于链接路径os.path.join
print(train_label_path)
image_train/train.txt
# 测试集标签的位置
test_label_path = os.path.join('image_test', 'train.txt')
=> size = 128 # 用于调整图片大小
=> train_feat_path = 'train/'
=> test_feat_path = 'test/'
=> model_path = 'model/' # 用于存储模型路径的构成,模型被放在model文件夹里
=> label_map = {1: 'tbj',
2: 'dw',
3: 'zsy',
4: 'zjc',
}
#我们这里是给ssd检测出来的人进行分类,分类成是哪个同学 (比如 tbj dw zsy ..)
# 这里是字典的形式保存了一类图片的 label (比如 tbj dw zsy 等)
# 字典在这里的意义是让数字和标签对应上, 训练的时候只需要告诉机器1 就知道是 tbj 同学
# 训练集图片的位置,
=> train_image_path = 'image_train'
# 测试集图片的位置
=> test_image_path = 'image_test'
# 这里是相对位置,这样写可以让hog_svm.py文件运行时找到辅助工具 => Change_Name.py, Write_Label_txt.py
这里有8个文件:
依次介绍image_train 存放训练图片 =>里面有train.txt, eg: 1-001.jpg 1 图片名,标签格式存储
image_test 存放测试图片 =>里面有test.txt, eg: 1-001.jpg 1 图片名,标签格式存储
model 保存的模型
train 保存训练图片的特征
test 保存测试图片的特征
hog_svm.py 主程序
README.md 原作者的读我
result 运行结果展示
辅助工具文件夹 里面有 =>ChangName.py 图片批量命名,改名程序
=>Write_Label_txt.py 写test.txt, train.txt的程序
image_train
存放训练的图片这里是4个同学的照片:
命名方式为
tbj同学: 第一张图片031-1.jpg,...,最后一张图片 245-1.jpgdw同学: 第一张图片001-2.jpg,...,最后一张图片 162-2.jpg
zsy同学: 第一张图片001-3.jpg,...,最后一张图片 308-3.jpg
zjc同学: 第一张图片001-4.jpg,...,最后一张图片 337-4.jpg
这里的
-1表示tbj
-2表示dw
-3表示zsy
-4表示zjc每个人的照片数量不同,tbj的前30张被用来做测试,在后面
局部图片示意train.txt文件里面有:图片全名 和 标签 (部分截图)(前面30张被用来做测试了)
image_test
存放测试的图片
这里是4个同学的照片:
命名方式为
tbj同学: 第一张图片001-1.jpg,...,最后一张图片 030-1.jpg (我这里只用30张图片做测试)
局部图片示意test.txt文件夹 (全部截图)
model
此文件夹里面存放模型
train
存放训练图片的特征
(部分)
test
存放测试图片的特征
(全部)
hog_svm.py 主程序,后面会有介绍
训练
https://blog.csdn.net/zjc910997316/article/details/98943969
预测
https://blog.csdn.net/zjc910997316/article/details/99004997
README.md 原作者的读我文件,看我的博客就不需要看这个了
result 运行结果展示
可见有很多都是预测错的, 60%的正确率, -1表示tbj
辅助工具 => ChangName.py
图片批量改名程序
需要放在image_train,image_test文件夹里面使用博客专门介绍
https://blog.csdn.net/zjc910997316/article/details/99069988
SVM从零开始 (四) 图片批量改名代码
辅助工具 => Write_Label_txt.py
写test.txt, train.txt的程序博客专门介绍
https://blog.csdn.net/zjc910997316/article/details/99076273
SVM从零开始 (五) 写训练集train.txt, 测试集test.txt 的代码
3 定义函数
1 创建路径
# /=== === === ===> 1:创建路径-start <=== === === ===\
# => 分别创建训练路径,测试路径
def creat_fold():
mkdir_train() # 不存在文件夹就创建
mkdir_test()
# => 创建存放训练图片路径的文件夹
def mkdir_train():
if not os.path.exists(train_feat_path):
os.mkdir(train_feat_path)
# => 创建存放测试图片路径的文件夹
def mkdir_test():
if not os.path.exists(test_feat_path):
os.mkdir(test_feat_path)
# \=== === === ===> 1:创建路径-end <=== === === ===/
2 训练部分
# /=== === === ===> 2:训练stage-start <=== === === ===\
# /=== ===> 1)训练准备,提取特征,训练 <=== ===\
def train_stage(train_feat_path):
shutil.rmtree(train_feat_path) # 递归删除文件夹下所有文件子文件,包括此文件train
mkdir_train() # 删除之后重新创建 train
extra_feat_train() # 获取训练特征并保存在文件夹 === ===> 2)提取特征
nk_train() # 训练 === ===> 3)训练
# \=== ===> 1)训练准备,提取特征,训练 <=== ===/
# /=== ===> 2)提取特征 <=== ===\
def extra_feat_train():
t_extra0 = time.time() # 计时start
train_name, train_label = get_name_label(train_label_path) # =>获得图片名称,对应的类别
# train_label_path = image_train\train.txt
train_image = get_image_list(train_image_path, train_name) # =>获得图片列表
# train_image_path = image_train\
get_feat(train_image, train_name, train_label, train_feat_path)
t_extra1 = time.time() # 计时end
print('训练特征提取结束,', '特征提取耗时是 : %f' % (t_extra1 - t_extra0) + '\n')
# =>从txt获得'图片名称list',对应的'类别list'
def get_name_label(file_path): # file_path = train_label_path = image_train\train.txt
print("read label from ", file_path)
name_list = [] # 用于存储名字的列表
label_list = [] # 用于存储标签的列表
with open(file_path) as f:
for line in f.readlines():
# 一般是name label 三部分,所以至少长度为3 所以可以通过这个方法来忽略空白行
if len(line) >= 3:
name_list.append(line.split(' ')[0]) # 以空格划分为两部分,前面部分为[0], 后面为[1]
label_list.append(line.split(' ')[1].replace('\n', '').replace('\r', ''))
# \r代表回车,\n代表换行, 都替换成'', 也就是去掉
if not str(label_list[-1]).isdigit():
# 因为[-1]是最后一个, 而每次append的最后一个都是最新的,检查它如果不是数字就报错
print("label必须为数字,得到的是:", label_list[-1], "程序终止,请检查文件")
exit(1)
return name_list, label_list
# 得到了两个列表,分别对应顺序放置 名字,标签 也是就是形如:
# name_list=['tbj1.jpg', 'tbj2.jpg',..., 'dw1.jpg']
# label_list=[1,1,...,2]
# =>获得'图片list'
def get_image_list(filePath, nameList):
# x1: filePath = train_image_path = image_train\
# x2: nameList = train_name = name_list 也就是上一个函数的第一个返回值, name_list=['tbj1.jpg', 'tbj2.jpg',..., 'dw1.jpg']
print('read image from ', filePath)
img_list = []
for name in nameList:
temp = Image.open(os.path.join(filePath, name)) # 形成一个完整的具体图片路径image_train\tbj1, 然后用Image打开
# print('测试是否有后缀.jpg', os.path.join(filePath, name)) # txt文件在起名字的时候就要写成dw1.jpg
# 测试是否有后缀.jpg image_train/337-4.jpg
# 这里我为了统计4个人一个人有几个照片起名方式为:337-4.jpg, 4表示第4个人zjc, 337表示这个人的第337个照片,上面列表dw1.jpg只是示意
img_list.append(temp.copy()) # 把这个图片copy出来,送进列表
temp.close() # 关掉这个,进行下一个图片的copy
# print('img_list[0]里面内容的格式', type(img_list[0]))
# img_list[0]里面内容的格式 <class 'PIL.Image.Image'>
# img_list = [<class 'PIL.Image.Image'>,...,<class 'PIL.Image.Image'>]
return img_list
# =>提取特征并保存
def get_feat(image_list, name_list, label_list, savePath):
# x1: image_list = train_image = img_list
# 也就上一个函数def get_image_list 的返回值list放到是<class 'PIL.Image.Image'>格式的图片
# x2: name_list = train_name 是def get_name_label的第一个返回值name_list=['tbj1.jpg', 'tbj2.jpg',..., 'dw1.jpg']
# x3: label_list = train_label 是def get_name_label的第二个返回值label_list = [1,1,...,2]
# x4: savePath = train_feat_path = train/ 特征被保存在这里
i = 0
for image in image_list:
try:
image = image.resize((64, 128)) # 调整图片大小,因为人高近似是宽的2倍
# 如果是灰度图片 把3改为-1
image = np.reshape(image, (image_height, image_width, 3))
except:
print('发送了异常,图片大小size不满足要求:', name_list[i])
continue
gray = rgb2gray(image) / 255.0
# 提取hog特征, 根据需求改, 这句话根据你的尺寸改改
fd = hog(gray, orientations=12, block_norm='L1', pixels_per_cell=[8, 8], cells_per_block=[4, 4],
visualize=False,
transform_sqrt=True)
'''
print('fd', fd) # fd [ 5.50173054e-04 5.32123593e-05 0.00000000e+00 ..., 2.04692958e-02]
print('fd.shape', fd.shape) # fd.shape (12480,)
print('type(fd)', type(fd)) # type(fd) <class 'numpy.ndarray'>
exit()
'''
fd = np.concatenate((fd, [label_list[i]])) # =>形如:label_list = [1,1,...,2]
'''
print('[label_list[i]]', [label_list[i]]) # [label_list[i]] ['1']
print('type([label_list[i]])', type([label_list[i]])) # type([label_list[i]]) <class 'list'>
print('type(label_list[i])', type(label_list[i])) # type(label_list[i]) <class 'str'>
exit()
'''
# =>fd就是后面 ===> 3)训练 <=== 里面的data = job.lib.load()
# 将image_list里面<class 'PIL.Image.Image'>格式图片resize, np.reshape之后与label_list = [1,1,...,2]对应
fd_name = name_list[i] + '.feat' # 对应名字name_list=['tbj1.jpg', 'tbj2.jpg',..., 'dw1.jpg']加后缀
fd_path = os.path.join(savePath, fd_name) # 前面加train/保存路径 = train/dw1.jpg.feat
# print('fd_path', fd_path) # train/001-1.jpg.feat
joblib.dump(fd, fd_path) # =>把相应的特征fd保存在对应的train/dw1.jpg.feat文件里面, 通过joblib的dump可以将模型保存到本地
i += 1
print("Test features are extracted and saved.")
# =>变成灰度图片
def rgb2gray(im):
gray = im[:, :, 0] * 0.2989 + im[:, :, 1] * 0.5870 + im[:, :, 2] * 0.1140
return gray
# \=== ===> 2)提取特征 <=== ===/
# /=== ===> 3)训练 <=== ===\
def nk_train():
t_train0 = time.time() # 计时start
features = []
labels = []
# train_feat_path = 'train/', 此目录下所有后缀为.feat的文件分别逐个给feat_path进行操作
for feat_path in glob.glob(os.path.join(train_feat_path, '*.feat')):
data = joblib.load(feat_path) # 通过joblib的load方法,加载保存的模型,在这里是加载hog特征eg: train/dw1.jpg.feat
features.append(data[:-1]) # 去掉最后的标签的特征, =>fd = np.concatenate((fd, [label_list[i]]))
labels.append(data[-1]) # 只要最后的标签
print("Training a Linear LinearSVM Classifier.")
clf = LinearSVC() # =>clf 1: 准备要训练的分类器clf
clf.fit(features, labels) # =>clf 2: 特征与标签匹配
# 下面的代码是保存模型的
if not os.path.exists(model_path): # 如果模型路径不存在, model_path = 'model/', 就创建路径
os.makedirs(model_path)
joblib.dump(clf, model_path + 'model') # =>clf 3: 通过joblib的dump可以将模型保存到本地'model/'的model文件中,clf是训练的分类器
print("训练之后的模型存放在model文件夹中")
# exit()
t_train1 = time.time() # 计时end
print('训练耗时是 : %f' % (t_train1 - t_train0) + '\n')
# \=== ===> 3)训练 <=== ===/
# \=== === === ===> 2:训练stage-end <=== === === ===/
预测部分
# /=== === === ===> 3:预测stage-start <=== === === ===\
# /=== ===> 1)测试准备,提取特征,预测 <=== ===\
def predict_stage(test_feat_path):
shutil.rmtree(test_feat_path)
mkdir_test()
extra_feat_test() # 获取测试特征并保存在文件夹
# nk_predict() # 多张图片的预测,非实时
clf = joblib.load(model_path + 'model') # 直接进行加载模型 不进行训练 joblib.load.predict
nk_predict()
# \=== ===> 1)测试准备,提取特征,测试 <=== ===/
# /=== ===> 2)提取特征 <=== ===\test
def extra_feat_test():
t_extra0 = time.time()
test_name, test_label = get_name_label(test_label_path)
test_image = get_image_list(test_image_path, test_name)
get_feat(test_image, test_name, test_label, test_feat_path)
t_extra1 = time.time()
print('测试特征提取结束,', '特征提取耗时是 : %f' % (t_extra1 - t_extra0) + '\n')
# =>下面部分: 与上面训练部分同, 因而注释掉
# =>从txt获得'图片名称list',对应的'类别list'
'''
def get_name_label(file_path): # file_path = train_label_path = image\train.txt
print("read label from ", file_path)
name_list = [] # 用于存储名字的列表
label_list = [] # 用于存储标签的列表
with open(file_path) as f:
for line in f.readlines():
# 一般是name label 三部分,所以至少长度为3 所以可以通过这个方法来忽略空白行
if len(line) >= 3:
name_list.append(line.split(' ')[0]) # 以空格划分为两部分,前面部分为[0], 后面为[1]
label_list.append(line.split(' ')[1].replace('\n', '').replace('\r', ''))
# \r代表回车,\n代表换行, 都替换成'', 也就是去掉
if not str(label_list[-1]).isdigit():
# 因为[-1]是最后一个, 而每次append的最后一个都是最新的,检查它如果不是数字就报错
print("label必须为数字,得到的是:", label_list[-1], "程序终止,请检查文件")
exit(1)
return name_list, label_list
# 得到了两个列表,分别对应顺序放置 名字,标签 也是就是形如:
# name_list=['tbj1.jpg', 'tbj2.jpg',..., 'dw1.jpg']
# label_list=[1,1,...,2]
'''
# =>获得'图片list'
'''
def get_image_list(filePath, nameList):
# x1: filePath = train_image_path = image\
# x2: nameList = train_name = name_list 也就是上一个函数的第一个返回值, name_list=['tbj1.jpg', 'tbj2.jpg',..., 'dw1.jpg']
print('read image from ', filePath)
img_list = []
for name in nameList:
temp = Image.open(os.path.join(filePath, name)) # 形成一个完整的具体图片路径image\tbj1, 然后用Image打开
# print('测试是否有后缀.jpg', os.path.join(filePath, name)) # txt文件在起名字的时候就要写成dw1.jpg
# 测试是否有后缀.jpg image/337-4.jpg
# 这里我为了统计4个人一个人有几个照片起名方式为:337-4.jpg, 4表示第4个人zjc, 337表示这个人的第337个照片,上面列表dw1.jpg只是示意
img_list.append(temp.copy()) # 把这个图片copy出来,送进列表
temp.close() # 关掉这个,进行下一个图片的copy
# print('img_list[0]里面内容的格式', type(img_list[0]))
# img_list[0]里面内容的格式 <class 'PIL.Image.Image'>
# img_list = [<class 'PIL.Image.Image'>,...,<class 'PIL.Image.Image'>]
return img_list
'''
# =>提取特征并保存
'''
def get_feat(image_list, name_list, label_list, savePath):
# x1: image_list = train_image = img_list
# 也就上一个函数def get_image_list 的返回值list放到是<class 'PIL.Image.Image'>格式的图片
# x2: name_list = train_name 是def get_name_label的第一个返回值name_list=['tbj1.jpg', 'tbj2.jpg',..., 'dw1.jpg']
# x3: label_list = train_label 是def get_name_label的第二个返回值label_list = [1,1,...,2]
# x4: savePath = train_feat_path = train/ 特征被保存在这里
i = 0
for image in image_list:
try:
image = image.resize((64, 128)) # 调整图片大小,因为人高近似是宽的2倍
# 如果是灰度图片 把3改为-1
image = np.reshape(image, (image_height, image_width, 3))
except:
print('发送了异常,图片大小size不满足要求:', name_list[i])
continue
gray = rgb2gray(image) / 255.0
# 提取hog特征, 根据需求改, 这句话根据你的尺寸改改
fd = hog(gray, orientations=12, block_norm='L1', pixels_per_cell=[8, 8], cells_per_block=[4, 4],
visualize=False,
transform_sqrt=True)
# =>fd就是后面 ===> 3)训练 <=== 里面的data = job.lib.load()
# 将image_list里面<class 'PIL.Image.Image'>格式图片resize, np.reshape之后与label_list = [1,1,...,2]对应
fd_name = name_list[i] + '.feat' # 对应名字name_list=['tbj1.jpg', 'tbj2.jpg',..., 'dw1.jpg']加后缀
fd_path = os.path.join(savePath, fd_name) # 前面加train/保存路径 = train/dw1.jpg.feat
# print('fd_path', fd_path) # train/001-1.jpg.feat
joblib.dump(fd, fd_path) # =>把相应的特征fd保存在对应的train/dw1.jpg.feat文件里面, 通过joblib的dump可以将模型保存到本地
i += 1
print("Test features are extracted and saved.")
'''
# =>变成灰度图片
'''
def rgb2gray(im):
gray = im[:, :, 0] * 0.2989 + im[:, :, 1] * 0.5870 + im[:, :, 2] * 0.1140
return gray
'''
# \=== ===> 2)提取特征 <=== ===/
# /=== ===> 3)预测 <=== ===\
def nk_predict():
correct_number = 0
total = 0
clf = joblib.load(model_path + 'model') # 直接进行加载模型 不进行训练
t_predict0 = time.time()
result_list = []
for feat_path in glob.glob(os.path.join(test_feat_path, '*.feat')):
total += 1
if platform.system() == 'Windows':
symbol = '\\'
else:
symbol = '/'
image_name = feat_path.split(symbol)[1].split('.feat')[0]
data_test = joblib.load(feat_path)
data_test_feat = data_test[:-1].reshape((1, -1)).astype(np.float64)
result = clf.predict(data_test_feat)
result_list.append(image_name + ' ' + label_map[int(result[0])] + '\n')
if int(result[0]) == int(data_test[-1]):
correct_number += 1
write_to_txt(result_list)
rate = float(correct_number) / total
t_predict1 = time.time()
print('准确率是: %f' % rate + ' ', '预测耗时是 : %f' % (t_predict1 - t_predict0))
# \=== ===> 3)预测 <=== ===/
# /=== ===> 4)txt记录 <=== ===\
def write_to_txt(list):
with open('result.txt', 'w') as f:
f.writelines(list)
print('每张图片的识别结果存放在result.txt里面' + '\n')
# \=== ===> 4)txt记录 <=== ===/
4 主函数
if __name__ == '__main__':
# === ===> 创建路径 <=== ===
creat_fold()
# === ===> 训练 <=== ===
train_stage(train_feat_path) # 提取特征,并训练模型
# === ===> 预测 <=== ===
predict_stage(test_feat_path) # 预测