目录
案例训练数据:cifar 10
图像下载地址(后面我们要用这批数据作为训练数据):CIFAR-10 and CIFAR-100 datasetshttp://www.cs.toronto.edu/~kriz/cifar.html本文参考链接:HOG + SVM 进行图片分类(python)_程序员的点滴-CSDN博客_svm图片二分类pythonhttps://blog.csdn.net/q1242027878/article/details/74271694什么是HOG:
一文讲解方向梯度直方图(hog)https://zhuanlan.zhihu.com/p/85829145
特征描述符就是通过提取图像的有用信息,并且丢弃无关信息来简化图像的表示。HOG特征描述符可以将3通道的彩色图像转换成一定长度的特征向量。
在HOG特征描述符中,梯度方向的分布,也就是梯度方向的直方图被视作特征。图像的梯度(x和y导数)非常有用,因为边缘和拐角(强度突变的区域)周围的梯度幅度很大,并且边缘和拐角比平坦区域包含更多关于物体形状的信息。
图像处理——梯度直方图HOG_lanling1996的博客-CSDN博客_梯度直方图 图像处理之特征提取:HOG特征简单梳理 HOG方向梯度直方图,这里分解为方向梯度与直方图。 一、方向梯度 梯度:在向量微积分中,标量...https://blog.csdn.net/lanling1996/article/details/112589519HOG主要应用于行人检测方面,以行人照片为例:
完整代码、数据处理和样例,可去这里下载:基于Hog 的 SVM Pytorch图像分类任务
一、训练数据准备:cifar转图像操作
案例操作中使用的训练数据是cifar10的数据,这里我们需要先把原始数据,转为图像数据,进行本地化保存,下载好数据后,进行转图操作,转图代码如下:
import cv2
import numpy as np
import os
def unpickle(file):
import _pickle as cPickle
with open(file, 'rb') as f:
dict = cPickle.load(f, encoding='iso-8859-1')
return dict
def main(cifar10_data_dir):
train_txt=[]
test_txt=[]
for i in range(1, 2):
train_data_file = os.path.join(cifar10_data_dir, 'data_batch_' + str(i))
print(train_data_file)
data = unpickle(train_data_file)
print('unpickle done', data)
for j in range(10000):
img = np.reshape(data['data'][j], (3, 32, 32))
img = img.transpose(1, 2, 0)
img_name = 'train/' + str(data['labels'][j]) + '_' + str(j + (i - 1) * 10000) + '.jpg'
cv2.imwrite(os.path.join(cifar10_data_dir, img_name), img)
train_txt.append(str(data['labels'][j]) + '_' + str(j + (i - 1) * 10000) + '.jpg' + ' ' + str(data['labels'][j]))
np.savetxt(r"./train.txt", np.reshape(train_txt, -1), delimiter=',', fmt='%5s')
test_data_file = os.path.join(cifar10_data_dir, 'test_batch')
data = unpickle(test_data_file)
for i in range(10000):
img = np.reshape(data['data'][i], (3, 32, 32))
img = img.transpose(1, 2, 0)
img_name = 'test/' + str(data['labels'][i]) + '_' + str(i) + '.jpg'
cv2.imwrite(os.path.join(cifar10_data_dir, img_name), img)
test_txt.append(str(data['labels'][i]) + '_' + str(i) + '.jpg' + ' ' + str(data['labels'][i]))
np.savetxt(r"./test.txt", np.reshape(test_txt, -1), delimiter=',', fmt='%5s')
if __name__ == "__main__":
main('cifar-10-batches-py')
如果遇到这个问题:
python 3以上版本使用pickle.load读取文件报UnicodeDecodeError: 'ascii' codec can't decode byte 0x8b in position 6
二、训练和验证部分
直接方代码,边训练边学习这段代码的含义,主要思路就是一下几点:
- 原图转hog梯度图
- 加载特征和标签进行训练
- 测试评估
# -*- 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
# 第一个是你的类别 第二个是类别对应的名称 输出结果的时候方便查看
label_map = {0: 'airplane',
1: 'automobile',
2: 'bird',
3: 'cat',
4: 'deer',
5: 'dog',
6: 'frog',
7: 'horse',
8: 'ship',
9: 'truck,'
}
# 训练集图片的位置
train_image_path = './database/train'
# 测试集图片的位置
test_image_path = './database/test'
# 训练集标签的位置
train_label_path = os.path.join('./labels', 'train.txt')
# 测试集标签的位置
test_label_path = os.path.join('./labels', 'test.txt')
image_height = 32
image_width = 32
train_feat_path = './feature/train/'
test_feat_path = './feature/test/'
model_path = 'model/'
# 获得图片列表
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):
i = 0
for image in image_list:
try:
# 如果是灰度图片 把3改为-1
image = np.reshape(image, (image_height, image_width, -1))
except:
print('发送了异常,图片大小size不满足要求:', name_list[i])
continue
#gray = rgb2gray(image) / 255.0
# 如果直接就是灰度图
gray = image
# 这句话根据你的尺寸改改
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]]))
fd_name = name_list[i] + '.feat'
fd_path = os.path.join(savePath, fd_name)
joblib.dump(fd, fd_path)
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("label必须为数字,得到的是:",label_list[-1], "程序终止,请检查文件")
exit(1)
return name_list, label_list
# 提取特征
def extra_feat():
train_name, train_label = get_name_label(train_label_path)
test_name, test_label = get_name_label(test_label_path)
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)
get_feat(test_image, test_name, test_label, test_feat_path)
# 创建存放特征的文件夹
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')):
data = joblib.load(feat_path)
features.append(data[:-1])
labels.append(data[-1])
############ 训练部分 ############
"""
print("Training a Linear LinearSVM Classifier.")
clf = LinearSVC()
clf.fit(features, labels)
# 下面的代码是保存模型的
if not os.path.exists(model_path):
os.makedirs(model_path)
joblib.dump(clf, model_path + 'model')
"""
############ 训练完验证部分 ############
# 下面的代码是加载模型 可以注释上面的代码 直接进行加载模型 不进行训练
clf = joblib.load(model_path+'model')
print("训练之后的模型存放在model文件夹中")
# exit()
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)
print(result)
result_list.append(image_name + ' ' + str(result[0]) + '\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里面')
if __name__ == '__main__':
mkdir() # 不存在文件夹就创建
# need_input = input('是否手动输入各个信息?y/n\n')
if sys.version_info < (3,):
need_extra_feat = input('是否需要重新获取特征?y/n\n')
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()
cifar10的训练和测试结果,如下:
是否需要重新获取特征?y/n
y
read label from ./labels\train.txt
read label from ./labels\test.txt
read image from ./database/train
read image from ./database/test
Test features are extracted and saved.
Test features are extracted and saved.
Training a Linear LinearSVM Classifier.
训练之后的模型存放在model文件夹中
每张图片的识别结果存放在result.txt里面
准确率是: 0.460400
耗时是 : 12.241030
三、hog梯度图可视化
直观的查看下hog梯度方向直方图的样式,以一张其它图像为例进行展示,你也可以替换为你自己的图像,如下:
from skimage.feature import hog
from skimage import io
from PIL import Image
import cv2
img = cv2.cvtColor(cv2.imread('./database/train_medical/DRCrop_3.png'), cv2.COLOR_BGR2GRAY)
print(img.shape)
normalised_blocks, hog_image = hog(img, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(8, 8),
block_norm='L2-Hys', visualize=True)
io.imshow(hog_image)
io.show()
四、SVM
支持向量机(Support Vector Machine, SVM)是一种监督学习算法,常用于分类和回归问题。它的基本思想是在训练数据集中找到一个超平面,使得超平面尽可能地将不同类别的数据分开,同时尽量缩小超平面与数据点之间的间隔。
在分类问题中,支持向量机通常使用线性分类器,即找到一个超平面使得其将数据点分为两个类别。在非线性分类问题中,支持向量机可以通过将数据映射到高维空间,再在高维空间中找到超平面,来解决非线性分类问题。
在回归问题中,支持向量机使用支持向量回归(Support Vector Regression, SVR)来预测连续型输出变量。SVR的基本思想是找到一条拟合数据的直线或曲线,使得直线或曲线与数据点之间的误差尽可能小。
支持向量机具有许多优点,例如:
1. 泛化能力强、
2. 不需要太多的训练数据(因为最终的解仅由少数支持向量决定)
3. 可以解决高维问题等。
但是,支持向量机也有一些缺点,例如:
1. 对缺失数据敏感、
2. 对参数调整敏感等。
问:分类间隔是由所有训练集样本所决定的吗?
答:不是!分类间隔是由距离分类决策边界最近的那些少量样本决定的。而比这些样本距离分类决策边界更远的大量样本,其实都不会影响到最优权向量的求取。这些作用十分特殊的样本,就被称为“支持向量(Support Vector)”,表示是他们支撑起了线性分类器在最大分类间隔意义下的最优解。这也是为什么这种算法被称为“支持向量机(Support Vector Machine,SVM)”的原因。
推荐更详尽的学习链接,强烈推荐:
如果最后测试阶段的准确率不好,可以调整核函数,可以参考这个函数,对核函数进行修改:SVMhttps://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html#sklearn.svm.SVC