【实战类】Hog SVM进行图像分类任务

目录

一、训练数据准备:cifar转图像操作

二、训练和验证部分

三、hog梯度图可视化


案例训练数据:cifar 10

图像下载地址(后面我们要用这批数据作为训练数据):CIFAR-10 and CIFAR-100 datasetsicon-default.png?t=N7T8http://www.cs.toronto.edu/~kriz/cifar.html本文参考链接:HOG + SVM 进行图片分类(python)_程序员的点滴-CSDN博客_svm图片二分类pythonicon-default.png?t=N7T8https://blog.csdn.net/q1242027878/article/details/74271694什么是HOG:

一文讲解方向梯度直方图(hog)icon-default.png?t=N7T8https://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

 可参考这里解决:python 3以上版本使用pickle.load读取文件报UnicodeDecodeError: 'ascii' codec can't decode byte 0x8b in position 6_IT人生-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_33144323/article/details/80042273

二、训练和验证部分

直接方代码,边训练边学习这段代码的含义,主要思路就是一下几点:

  1. 原图转hog梯度图
  2. 加载特征和标签进行训练
  3. 测试评估
# -*- 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()

 参考文档:使用skimage提取图像hog特征_人工智能讲师团-CSDN博客_skimage.feature.hogicon-default.png?t=N7T8https://blog.csdn.net/mago2015/article/details/85320347

 四、SVM

支持向量机(Support Vector Machine, SVM)是一种监督学习算法,常用于分类和回归问题。它的基本思想是在训练数据集中找到一个超平面,使得超平面尽可能地将不同类别的数据分开,同时尽量缩小超平面与数据点之间的间隔。

在分类问题中,支持向量机通常使用线性分类器,即找到一个超平面使得其将数据点分为两个类别。在非线性分类问题中,支持向量机可以通过将数据映射到高维空间,再在高维空间中找到超平面,来解决非线性分类问题。

在回归问题中,支持向量机使用支持向量回归(Support Vector Regression, SVR)来预测连续型输出变量。SVR的基本思想是找到一条拟合数据的直线或曲线,使得直线或曲线与数据点之间的误差尽可能小。

支持向量机具有许多优点,例如:
1. 泛化能力强、
2. 不需要太多的训练数据(因为最终的解仅由少数支持向量决定)
3. 可以解决高维问题等。

但是,支持向量机也有一些缺点,例如:
1. 对缺失数据敏感、
2. 对参数调整敏感等。

问:分类间隔是由所有训练集样本所决定的吗?
答:不是!分类间隔是由距离分类决策边界最近的那些少量样本决定的。而比这些样本距离分类决策边界更远的大量样本,其实都不会影响到最优权向量的求取。这些作用十分特殊的样本,就被称为“支持向量(Support Vector)”,表示是他们支撑起了线性分类器在最大分类间隔意义下的最优解。这也是为什么这种算法被称为“支持向量机(Support Vector Machine,SVM)”的原因。

推荐更详尽的学习链接,强烈推荐:

  1. 【线性分类器】(四)万字长文解释拉格朗日乘子与支持向量机
  2. 【机器学习】支持向量机 SVM(非常详细)

如果最后测试阶段的准确率不好,可以调整核函数,可以参考这个函数,对核函数进行修改:SVMicon-default.png?t=N7T8https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html#sklearn.svm.SVC

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钱多多先森

你的鼓励,是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值