机器学习实践:动物图片识别-1

机器学习实践:动物图片识别

1、实验描述

  • SVM(支持向量机)是一种常用的机器学习分类算法。使用HOG+SVM算法和OpenCV实现一个图片分类器,通过训练分类器,达到可以判断任意图片是否是动物的效果;本节实验是利用hog来计算图像数据的特征描述,从而获得基于图片的特征向量,再利用SVM分类超平面来实现数据分类,并能够利用训练好的模型进行图像预测。

  • 实验时长:90分钟

  • 主要步骤:

    • 数据准备

    • 编写图片识别代码

    • 编写模型训练代码

    • 编写模型预测代码

    • 运行程序,查看结果

2、实验环境

  • 虚拟机数量:1
  • 系统版本:CentOS 7.5
  • Python版本:Python 2.7
  • OpenCV-Python:2.4.5

3、相关技能

  • Python原理
  • 使用vim
  • HOG+SVM分类器
  • OpenCV
  • 训练分类器

4、相关知识点

  • HOG方向梯度直方图
  • SVM支持向量机
  • OpenCV处理图片
  • 灰度化
  • Gamma校正法
  • Cell
  • Cell descriptor
  • HOG特征descriptor
  • 训练分类器,以得到适合自己项目的分类器
  • Python文件操作

5、效果图

  • 最后执行编写的代码,得到最终的分类结果,如下图

机器学习实践:动物图片识别-1_第1张图片

图 1

6、实验步骤

6.1数据准备

6.1.1本次实验使用系统自带的python2.7的环境

6.1.2进入虚拟环境,打开命令行终端;切换到root用户,密码是zkpk;然后在Linux终端安装OpenCV

[zkpk@master ~]$ su
[root@master zkpk]# yum install -y opencv-python
[root@master zkpk]# exit	# 回退到普通用户

6.1.3解压**./experiment/MLcase**包下的数据文件

[zkpk@master ~]$ cd experiment/MLcase
[zkpk@master MLcase]$ unzip cat.zip
[zkpk@master MLcase]$ unzip other.zip
[zkpk@master MLcase]$ unzip predict.zip
[zkpk@master MLcase]$ ls

在这里插入图片描述

6.1.3.1数据介绍:cat包全是猫的图片,other包中不包括猫的其它图片,以及predict包用于测试SVM分类器的数据集

6.1.3.2这些图片都是测试图片,如果想使用自己上传的图片也可以,但是需要设置成固定像素64*128大小的图片

6.2编写代码解析训练数据集并训练模型

6.2.1我们使用HOG+SVM算法进行训练,需要先计算每张图片的HOG值;HOG值再作为SVM分类器的输入向量。计算该值的一般过程如下:

6.2.1.1灰度化(OpenCV处理图像时,一般会将图像处理为灰度图像,忽略颜色干扰);

6.2.1.2采用Gamma校正法对输入图像进行颜色空间的标准化(归一化);目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪声的干扰;

6.2.1.3计算图像每个像素的梯度(包括大小和方向);主要是为了捕获轮廓信息,同时进一步弱化光照的干扰;

6.2.1.4将图像划分成小cells(例如6*6像素/cell);

6.2.1.5统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor;

6.2.1.6将每几个cell组成一个block(例如3*3个cell/block),一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor;

6.2.1.7将图像image内的所有block的HOG特征descriptor串联起来就可以得到该image(要检测的目标)的HOG特征descriptor。此结果就是最终可供分类算法使用的特征向量。

6.2.2在/home/zkpk目录下创建脚本文件Animal.py (本实验随后的代码都写入此文件中);以下使用vim文本编辑器编辑文件,若不熟悉vim的使用,也可以使用gedit代替

[zkpk@master MLcase]$ cd
[zkpk@master ~]$ vim Animal.py

6.2.3在Animal.py文件中开始编写代码;首先导入numpy包、opencv包、os系统路径相关包、sys包和glob包

import numpy as np
import cv2
from os.path import dirname, join, basename
import sys
from glob import glob

6.2.4设置梯度值为16*16

bin_n = 16*16

6.2.5定义hog方法,该方法读取图片,计算图片的特征向量(HOG值);

接下来涉及大量opencv的编程,若有需要,可访问opencv官网docs.opencv.org查看相应版本的文档,本实验使用OpenCV-Python 2.4.5版本

6.2.5.1定义x轴和y轴的像素值

def hog(img):
    x_pixel,y_pixel=194,259

6.2.5.2分别计算x轴和y轴方向上的Sobel导数

    gx = cv2.Sobel(img, cv2.CV_32F, 1, 0)	# 边沿检测;img作为输入图片;第二个参数指定输出图片的深度;第三个参数dx、第四个参数dy表示这个方向的求导的阶数;0表示这个方向没有求导,一般为0、1、2
    gy = cv2.Sobel(img, cv2.CV_32F, 0, 1)

6.2.5.3将直角坐标转换为极坐标,获取每个像素的梯度和方向

    mag, ang = cv2.cartToPolar(gx, gy)	# cart表示cartesian笛卡尔;polar极地的;第一个参数gx为x轴坐标组成的数组;第二个参数gy是y轴坐标组成的数组;magnitue极径,返回值mag是一个array,其中保存着一系列极径,mag的size与shape与参数gx的相同;angle极角,返回值ang是一个array,其中保存着一系列极角,它的size与shape也与参数gx的相同

6.2.5.4将梯度量化成16*16个整数值

    bins = np.int32(bin_n*ang/(2*np.pi))  

6.2.5.5将图像分成4个子图块

    bin_cells = bins[:x_pixel/2,:y_pixel/2], bins[x_pixel/2:,:y_pixel/2], bins[:x_pixel/2,y_pixel/2:], bins[x_pixel/2:,y_pixel/2:]
    mag_cells = mag[:x_pixel/2,:y_pixel/2], mag[x_pixel/2:,:y_pixel/2], mag[:x_pixel/2,y_pixel/2:], mag[x_pixel/2:,y_pixel/2:]

6.2.5.6对于每个子图块,计算它的bin的个数,4个子模块共返回16*16*4个bin的向量值

    hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]	# 使用了python的列表解析语法;bincount计算输入数组中每个值出现的次数,有三个参数x, weights, minlength,第一个参数表示输入的一维数组,第二个表示weights权重,与x的shape一样,第三个表示输出数组中元素的最小个数。关于方法,若有疑问可查阅docs.scipy.org中的文档

6.2.5.7将每个模块返回的向量值利用numpy进行水平堆叠(按列),然后返回该值

    hist = np.hstack(hists)  
    return hist

6.2.6定义字典img用于存储图片,定义num表示字典的key

img={}
num=0

6.2.7循环读取实验文件中的cat文件夹下的所有jpg文件,利用imread方法参数设置为0,读取图片黑白数据,也称作灰度化

# dirname函数获得路径;join函数将多个路径连接起来;glob函数返回由路径组成的list
for fn in glob(join(dirname('/home/zkpk/experiment/MLcase/cat/'), '*.jpg')):
    img[num] = cv2.imread(fn,0)	# imread函数用于读取图片文件,第二个参数flag若为0,表示返回3-channel颜色的图片 
    num=num+1

6.2.8读取cat文件夹下的图片后,我们打印下num看看有多少个文件,并将其赋值给positive

print num,' num'
positive=num

6.2.9同样,循环读取实验文件中的other文件夹下的所有jpg文件,利用imread方法参数设置为0,读取图片黑白数据

for fn in glob(join(dirname('/home/zkpk/experiment/MLcase/other/'), '*.jpg')):
    img[num] = cv2.imread(fn,0)	
    num=num+1

6.2.10我们再次打印出num的值和positive的值

print num,' num'
print positive,' positive'

6.2.11遍历字典中的值,按顺序添加到列表trainpic中

trainpic=[]
for i in img:
    trainpic.append(img[i])

6.2.12设置SVM模型的参数

6.2.12.1kernel_type:SVM的内核类型

6.2.12.2svm_type:指定SVM的类型

6.2.12.3C是惩罚系数,即对误差的宽容度。c越高,说明越不能容忍出现误差,容易过拟合。C越小,容易欠拟合。C过大或过小,泛化能力变差

6.2.12.4gamma是选择RBF函数作为kernel后,该函数自带的一个参数。隐含地决定了数据映射到新的特征空间后的分布,gamma越大,支持向量越少,gamma值越小,支持向量越多。支持向量的个数影响训练与预测的速度

svm_params = dict( kernel_type = cv2.SVM_LINEAR, svm_type = cv2.SVM_C_SVC, C=2.67, gamma=5.383 )

6.2.13调用hog方法,将trainpic中的图片都分别计算其特征向量,打印输出看下hogdata的结构

hogdata = map(hog,trainpic)
print np.float32(hogdata).shape,' hogdata'

6.2.14将所有图片的特征向量赋值给trainData,打印输出

trainData = np.float32(hogdata).reshape(-1,bin_n*4)
print trainData.shape,' trainData'

6.2.15利用numpy的repeat方法生成一列值全为1.0的数据,个数为trainData的行数,也就是89;再将从positive下标到89下标的数据置为-1.0;因为从上面输出的num和positive我们知道,是猫的图片是positive个;最后这一个新创建的列将作为标签值进行模型训练,我们先打印输出responses的结构看下

responses = np.float32(np.repeat(1.0,trainData.shape[0])[:,np.newaxis])	# np.newaxis相当于None
responses[positive:trainData.shape[0]]=-1.0
print responses.shape,' responses'

6.2.16调用cv2的SVM方法,创建支持向量机模型,再利用该模型对上面得到的训练数据、标签值和模型参数进行模型训练

svm = cv2.SVM()
svm.train(trainData,responses, params=svm_params)

6.2.17模型训练完成之后,我们就可以读取一张测试文件夹下的图片进行测试;例如读取predict文件夹下01.jpg,通过hog方法计算出其特征向量,打印输出,然后利用svm模型的predict方法对该特征向量进行预测,得到它的结果(标签值);我们还可以自定义一些自己的判断和输出等

img = cv2.imread('/home/zkpk/experiment/MLcase/predict/01.jpg',0)
hogdata = hog(img)
testData = np.float32(hogdata).reshape(-1,bin_n*4)
print testData.shape,' testData'
result = svm.predict(testData)
print result
if result > 0:
    print 'this pic is a cat!'

6.2.18这次我们对predict下的所有文件进行分类预测;首先我们定义一个列表testpic;然后循环读取predict文件夹下的所有图片文件(灰度化);并添加到列表testpic中;打印输出testpic的长度

testpic=[]
for fn in glob(join(dirname('/home/zkpk/experiment/MLcase/predict/'), '*.jpg')):
    img=cv2.imread(fn,0)
    testpic.append(img)
print len(testpic),' len(tespic)'

6.2.19再利用map方法将testpic中的所有图片数据,调用hog方法,计算出每张图片的特征向量值,格式化后作为测试数据testData,打印输出查看testData的结构

hogdata = map(hog,testpic)	# 其中hog为之前定义的函数
testData = np.float32(hogdata).reshape(-1,bin_n*4)
print testData.shape,' testData'

6.2.20利用for循环从testData中读取每张图片的特征向量值,利用svm的predict方法做分类预测,并将结果保存到result列表中,打印结果列表

result = [svm.predict(eachone) for eachone in testData]
print result

6.2.21至此,基于SVM的动物图片识别代码编写完毕

6.2.22编辑完成后,利用Esc键回到命令行模式,再输入shift+:,输入wq保存文件并退出vim编辑器

6.3运行程序

[zkpk@master ~]$ python2.7 Animal.py

6.3.1注意文件路径

6.3.2查看终端输出结果

机器学习实践:动物图片识别-1_第2张图片

图 2

6.3.2.1从代码中不难发现我们定义SVM分类器中1是猫,而-1不是猫,并且我们提供的predict测试数据集里,没有一张图片是猫,而分类器认为第3张和第11张是猫,其余不是猫。这明显是误判了。一次训练的结果肯定达不到完全分类正确,所以才需要迭代重复训练,让机器再次学习。把分类器认错的图片,丢进other文件夹下,再训练一次,下一次遇到该图片就能够识别了

7、参考答案

  • 代码清单Animal.py

机器学习实践:动物图片识别-1_第3张图片

图 3

8、总结

在本节实验中我们使用SVM机器学习算法和OpenCV实现了一个判断一张图片是否是猫的分类器,并通过该分类器对测试集图片进行了图像预测。通过本实验的学习,我们应当充分理解了hog方向梯度直方图处理图片特征、SVM分类器的原理等,并可以达到可自己建立的图片分类器,训练分类器达到合适的分类精度的效果。
全分类正确,所以才需要迭代重复训练,让机器再次学习。把分类器认错的图片,丢进other文件夹下,再训练一次,下一次遇到该图片就能够识别了

你可能感兴趣的:(人工智能,机器学习,支持向量机,opencv)