赞
踩
SVM(支持向量机)是一种常用的机器学习分类算法。本课程使用HOG+SVM算法和OpenCV实现一个图片分类器,通过自己训练分类器,达到可以判断任意图片是否是猫咪的效果。
HOG+SVM
分类器的基本原理OpenCV
处理图片Python
文件操作python2.7
Xfce
终端 本课程难度为中等,适合掌握Python
基础的用户,建立对SVM
分类器的基础知识。
你可以通过下面命令将代码下载到实验楼环境中,作为参照对比进行学习。
- $ wget http://labfile.oss.aliyuncs.com/courses/794/train.py
- $ wget http://labfile.oss.aliyuncs.com/courses/794/predict.py
SVM
(支持向量机)分类器的原理是利用“分类超平面”来实现数据分类。在利用“分类超平面”对数据进行划分时,遵循“间距最大”原则。例如,将二维平面内的两组数据分类,可以确定很多个“分类超平面”,在二维维度下,超平面退化为一条直线:
上图中使用绿线将蓝色圆圈和红色方块进行分类,可以有多种方式。那么根据SVM
原理,哪一条线是最佳分类线呢?答案是,最佳分类线因该是距离蓝色圆圈和红色方框的距离都是最大的那一条,即找到两组数据的最大间距,在最大间距中点画一条线,如下:
如果分类3维数据,我们就使用一个平面来分割数据。如果分类4维数据,我们将会使用一个体来分割数据。以此类推,如果分类1024维数据,我们将使用1023维平面来分割数据。1023维的平面是什么样子?天知道。所以这个时候,将1023维度的平面命名为“分类超平面”。
SVM
是一个由分类超平面定义的判别分类器。也就是说给定一组带标签的训练样本,算法将会输出一个最优超平面对新样本(测试样本)进行分类。
这也是监督类型机器学习的特点,即,把一堆带有标签的数据输入到机器中,让机器根据给定的数据计算出规则,再利用这个规则,去对未知数据进行分类。说白了,就是先积累几年工作经验,然后去工作。
本实验是读入输入图片的灰度图,即黑白的。然后计算该图片的hog
值,将计算得到的结果作为向量来代表该图片。对由很多张图片组成的向量集进行计算,找到最大间距的分类超平面,进而分类数据。
hog
的全称是Histogram of Oriented Gradient, HOG
,即方向梯度直方图
。它是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。HOG+SVM进行行人检测的方法是法国研究人员Dalal在2005的CVPR上提出的,今天的很多行人检测算法基本都是以HOG+SVM的思路。
打开Xfce终端,下载并安装 OpenCV
的相关依赖。
- $ sudo pip install numpy
- $ sudo apt-get install python-opencv
遇到是否安装的询问时,输入y,按回车键继续安装。安装时间较长,并且视网络状态而定。
下载实验所需的图片数据:
- $ wget http://labfile.oss.aliyuncs.com/courses/794/cat.zip
- $ wget http://labfile.oss.aliyuncs.com/courses/794/other.zip
- $ wget http://labfile.oss.aliyuncs.com/courses/794/predict.zip
这三组数据分别是含有猫的图片,没有猫的图片,以及用于测试SVM
分类器的数据集。
下载后,解压得到图片:
- $ unzip cat.zip
- $ unzip other.zip
- $ unzip predict.zip
这些图片都是从网上下载的。如果想使用自己下载的图片,也没有问题。需要注意爹是,输入到分类器的图片都是固定像素的。我们需要对下载的图片数据进行处理,使其符合我们程序的要求。将大图片裁减成固定像素的小图片的程序如下:
- # -*- coding: utf-8 -*-
- import numpy as np
- import cv2
- from os.path import dirname, join, basename
- from glob import glob
-
- num=0
- for fn in glob(join(dirname(__file__)+'\other', '*.jpg')):
- img = cv2.imread(fn)
- res=cv2.resize(img,(64,128),interpolation=cv2.INTER_AREA)
- cv2.imwrite(r'D:\ECLIPSE-PROJECT\Python\my_opencv\other_64_128\test'+str(num)+'.jpg',res)
- num=num+1
-
- print 'all done!'
-
- cv2.waitKey(0)
- cv2.destroyAllWindows()
使用程序时,请替换输出路径为一个已存在的路径,即替换这一句中的路径:
cv2.imwrite(r'D:\ECLIPSE-PROJECT\Python\my_opencv\other_64_128\test'+str(num)+'.jpg',res)
这段代码会扫描Python
脚本所在的文件夹的子文件夹other
文件夹下的所有.jpg
文件,然后使用OpenCV
读取图片数据,并按照指定的大小进行缩放,将缩放后的结果写入到指定目录下的指定图片中。
首先,我们根据已经分类好的数据集来对分类器进行训练。我们的cat
文件夹下全是猫的照片,而other
文件夹下全不是猫,已经完成了贴标签这个过程了。让程序从这两组数据里学习,计算分类的方法。
使用HOG+SVM
算法进行训练前,需要先计算每张图片的HOG
值以得到供SVM
分类器使用的输入向量。计算该值的算法实现的一般过程为:
OpenCV
处理图像时,一般都处理为灰度图像,忽略颜色干扰) 在本实验中,没有严格按照上述的过程实现,我们采用了下述方法:我们在每个cell
内计算X
和Y
方向的Sobel
导数。然后找到每个像素的梯度和方向。此梯度被量化为16*16个整数值。把每个图像分成四个子图方块。对于每个子正方形,计算加权其幅度的方向(16*16bins)的直方图。因此,每个子图给我们一个包含16*16个值的向量。四个这样的向量(分别代表四个子图的16*16向量)一起给我们一个特征向量包含1024个值。这就是我们用来训练数据的特征向量。这部分的代码如下所示:
- bin_n = 16*16 # Number of bins
-
- def hog(img):
- x_pixel,y_pixel=194,259
- gx = cv2.Sobel(img, cv2.CV_32F, 1, 0)
- gy = cv2.Sobel(img, cv2.CV_32F, 0, 1)
- mag, ang = cv2.cartToPolar(gx, gy)
- bins = np.int32(bin_n*ang/(2*np.pi)) # quantizing binvalues in (0...16)
- 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:]
- hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
- hist = np.hstack(hists) # hist is a 64 bit vector
- # print hist.shape
- # print type(hist)
- return hist
完整的代码如下所示,程序首先扫描cat
和other
文件夹内的图片,然后用灰度方式读入,计算每个图片的hog
值,然后建立SVM
分类器,使用输入的数据进行训练,将训练结果保存于svm_cat_data.dat
文件中。
- #file name:train.py
- import numpy as np
- import cv2
- #from matplotlib import pyplot as plt
- from os.path import dirname, join, basename
- import sys
- from glob import glob
-
-
- bin_n = 16*16 # Number of bins
-
- def hog(img):
- x_pixel,y_pixel=194,259
- gx = cv2.Sobel(img, cv2.CV_32F, 1, 0)
- gy = cv2.Sobel(img, cv2.CV_32F, 0, 1)
- mag, ang = cv2.cartToPolar(gx, gy)
- bins = np.int32(bin_n*ang/(2*np.pi)) # quantizing binvalues in (0...16)
- 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:]
- hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
- hist = np.hstack(hists) # hist is a 64 bit vector
- # print hist.shape
- # print type(hist)
- return hist
-
- #print glob(join(dirname(__file__)+'/cat','*.jpg'))
- img={}
- num=0
- for fn in glob(join(dirname(__file__)+'/cat', '*.jpg')):
- img[num] = cv2.imread(fn,0)#参数加0,只读取黑白数据,去掉0,就是彩色读取。
- # print img[num].shape
- num=num+1
- print num,' num'
- positive=num
- for fn in glob(join(dirname(__file__)+'/other', '*.jpg')):
- img[num] = cv2.imread(fn,0)#参数加0,只读取黑白数据,去掉0,就是彩色读取。
- # print img[num].shape
- num=num+1
- print num,' num'
- print positive,' positive'
-
- trainpic=[]
- for i in img:
- # print type(i)
- trainpic.append(img[i])
-
- svm_params = dict( kernel_type = cv2.SVM_LINEAR,
- svm_type = cv2.SVM_C_SVC,
- C=2.67, gamma=5.383 )
- #img = cv2.imread('02.jpg',0)
- #hist_full = cv2.calcHist([img],[0],None,[256],[0,256])
- #print hist_full
- #plt.plot(hist_full)
- #plt.show()
-
- #img1 = cv2.imread('02.jpg',0)
- #temp=img[0].ravel()
- #print temp
- #print len(temp)
- temp=hog(img[0])
- print temp.shape
-
- #hogdata = [map(hog,img[i]) for i in img]
- hogdata = map(hog,trainpic)
- print np.float32(hogdata).shape,' hogdata'
- trainData = np.float32(hogdata).reshape(-1,bin_n*4)
- print trainData.shape,' trainData'
- responses = np.float32(np.repeat(1.0,trainData.shape[0])[:,np.newaxis])
- responses[positive:trainData.shape[0]]=-1.0
- print responses.shape,' responses'
- print len(trainData)
- print len(responses)
- print type(trainData)
-
- svm = cv2.SVM()
- svm.train(trainData,responses, params=svm_params)
- svm.save('svm_cat_data.dat')
注意,如果想要运行此程序并得到正确的结果,需要在控制台输入:
$ python /home/shiyanlou/train.py
如果只输入:
$ python train.py
也能看到输出,只不过执行到一半会报错。
这其中的原因,主要是程序中使用了glob
包来枚举文件夹下的某个类型的文件。
正常运行后,可以见到文件夹下生成的数据:
同时也会看到,当导入OpenCV
包的时候,程序报错libdc1394 error: Failed to initiallize libdc1394
,这个错误,是因为没有加载摄像头驱动。实验楼使用的云服务器,很可能没有摄像头,所以报错。但这个错误并不影响我们的实验,所以忽略即可。在自己的笔记本上或者台式机上,只要正确安装了驱动,不会有这个错误。
SVM
分类器进行分类机器学习是一个不断迭代的过程。训练的数据集越大越好,训练时间当然也是越长效果越好。当机器认错了图片的时候,我们要把这个图片拿出来,标记正确,输入机器再训练一遍,如此迭代下去。本实验只训练了一次以演示原理。
我们得到了训练好的数据svm_cat_data.dat
后,可以用它来分类测试图片。
建立程序如下:
- #file name:predict.py
- import numpy as np
- import cv2
- #from matplotlib import pyplot as plt
- from os.path import dirname, join, basename
- import sys
- from glob import glob
-
- #my_svm=cv2.SVM()
- #my_svm
- #bin_n = 16 # Number of bins
- bin_n = 16*16 # Number of bins
-
- def hog(img):
- x_pixel,y_pixel=194,259
- gx = cv2.Sobel(img, cv2.CV_32F, 1, 0)
- gy = cv2.Sobel(img, cv2.CV_32F, 0, 1)
- mag, ang = cv2.cartToPolar(gx, gy)
- bins = np.int32(bin_n*ang/(2*np.pi)) # quantizing binvalues in (0...16)
- 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:]
- hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
- hist = np.hstack(hists) # hist is a 64 bit vector
- # print hist.shape
- # print type(hist)
- return hist
-
- #print glob(join(dirname(__file__)+'/cat','*.jpg'))
- img={}
- num=0
- for fn in glob(join(dirname(__file__)+'/cat', '*.jpg')):
- img[num] = cv2.imread(fn,0)#参数加0,只读取黑白数据,去掉0,就是彩色读取。
- # print img[num].shape
- num=num+1
- print num,' num'
- positive=num
- for fn in glob(join(dirname(__file__)+'/other', '*.jpg')):
- img[num] = cv2.imread(fn,0)#参数加0,只读取黑白数据,去掉0,就是彩色读取。
- # print img[num].shape
- num=num+1
- print num,' num'
- print positive,' positive'
-
- trainpic=[]
- for i in img:
- # print type(i)
- trainpic.append(img[i])
-
- svm_params = dict( kernel_type = cv2.SVM_LINEAR,
- svm_type = cv2.SVM_C_SVC,
- C=2.67, gamma=5.383 )
-
- temp=hog(img[0])
- print temp.shape
-
- #hogdata = [map(hog,img[i]) for i in img]
- hogdata = map(hog,trainpic)
- print np.float32(hogdata).shape,' hogdata'
- trainData = np.float32(hogdata).reshape(-1,bin_n*4)
- print trainData.shape,' trainData'
- responses = np.float32(np.repeat(1.0,trainData.shape[0])[:,np.newaxis])
- responses[positive:trainData.shape[0]]=-1.0
- #print responses[40:80]
- print responses.shape,' responses'
- print len(trainData)
- print len(responses)
- print type(trainData)
-
- svm = cv2.SVM()
- svm.load('svm_cat_data.dat')
-
- img = cv2.imread('/home/shiyanlou/predict/01.jpg',0)
- #print img.shapes,' img_test0'
- 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!'
-
- test_temp=[]
- for fn in glob(join(dirname(__file__)+'/predict', '*.jpg')):
- img=cv2.imread(fn,0)#参数加0,只读取黑白数据,去掉0,就是彩色读取。
- test_temp.append(img)
- print len(test_temp),' len(test_temp)'
-
- hogdata = map(hog,test_temp)
- testData = np.float32(hogdata).reshape(-1,bin_n*4)
- print testData.shape,' testData'
- result = [svm.predict(eachone) for eachone in testData]
- print result
运行该程序,同样,需要提供完整的路径:
$ python /home/shiyanlou/predict.py
程序输出结果如下:
注意SVM
分类器认为1
是猫,而-1
不是猫。我们提供的测试数据集里,没有一张图片是猫,而分类器认为第3张和第11张是猫,其余不是猫。这明显是误判了。一次训练的结果肯定达不到100%
分类正确,所以才需要迭代重复训练。把分类器认错的图片,再丢进other
文件夹下,再训练一次,下次再遇到,就认识了。
使用SVM
机器学习算法和OpenCV
实现了一个判断一张图片是否是猫的分类器。通过本课程的学习,学员应理解SVM
分类器的原理,可以建立自己的图片分类器,训练分类器达到合适的分类精度。
在本实验中,分类器误判了两张图片,请重复训练让分类器正确分类它们;增加图片数量,训练一个更加精确的分类器;使用HOG+SVM
进行行人检测。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。