利用HOG特征和SVM算法对图片进行二分类实现图像目标的检测

一、HOG方向梯度直方图

1.HOG简介

    方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中。HOG的思想就是在一副图像中,局部目标的表象和形状(appearance and shape)能够被梯度或边缘的方向密度分布很好地描述。

2.HOG基本步骤简述

(1)标准化gamma空间和颜色空间

    第一步,为了减少光照因素的影响,首先需要将整个图像进行规范化(归一化)。在图像的纹理强度中,局部的表层曝光贡献的比重较大,所以,这种压缩处理能够有效地降低图像局部的阴影和光照变化。因为颜色信息作用不大,通常先转化为灰度图;

(2)计算图像梯度

    第二步,计算图像横坐标和纵坐标方向的梯度,并据此计算每个像素位置的梯度方向值;求导操作不仅能够捕获轮廓,人影和一些纹理信息,还能进一步弱化光照的影响。

(3)为每个细胞单元构建梯度方向直方图

    第三步,为每个细胞单元构建梯度方向直方图的目的是为局部图像区域提供一个编码,同时能够保持对图像中人体对象的姿势和外观的弱敏感性。我们将图像分成若干个“单元格cell”,例如每个cell为66个像素。假设我们采用9个bin的直方图来统计这66个像素的梯度信息。但bin并不是连续的,而是20度为一个bin,然后相同bin是关于180度对称。

(4)把细胞单元组合成大的块(block),块内归一化梯度直方图

    第四步,由于局部光照的变化以及前景-背景对比度的变化,使得梯度强度的变化范围非常大。这就需要对梯度强度做归一化。归一化能够进一步地对光照、阴影和边缘进行压缩,方法是把各个细胞单元组合成大的、空间上连通的区间(blocks)。例如本次实验所使用的block是(16, 16),窗口(即图片)大小是(64, 128),block的滑动步长是(8,8),共有105个block,block内的cell是(8, 8),共有4个cell,而cell内的bin是9个,那么总共有36715=3780个特征。

(5)收集HOG特征

    最后一步就是将检测窗口中所有重叠的块进行HOG特征的收集,并将它们结合成最终的特征向量供分类使用,在opencv中使用相应的方法。图1 HOG检测步骤流程。

图1 HOG检测步骤流程

二、SVM支持向量机

    支持向量机(支持向量机(Support Vector Machine, SVM)是一类按监督学习(supervised learning)方式对数据进行二元分类的广义线性分类器(generalized linear classifier),将样本分为正样本和负样本,其决策边界是对学习样本求解的最大边距超平面(maximum-margin hyperplane)。SVM一般用于解决模式识别领域中的数据分类问题,属于有监督学习算法的一种。

三、实验流程

1.数据集准备

    数据集分为正负样本数据集,2个文件夹,分别存储在pos(pos1为作者的图片数据集)和neg文件夹下,正样本有820张图片,负样本有1931张图片,均是64*128的格式。

2.图像预处理

    对图像进行3以下3步的预处理:首先将视频分解为图片,其次是图片缩放,最后是将图片裁剪为64 * 128。

3.实验结果

    图2和图3分别是我仿造源作者代码的实验结果以及作者的实验结果,明显的看出我的实验结果并不准确。

图2 自己的实验结果
图3 原作的实验结果

四、程序代码

1.视频图像的预处理

(1)视频分解

'''
视频分解图片 video_Decomposition(video_name)
1 load 2 info 3 parse 4 imshow imwrite
负样本 64*128 正样本:64*128
'''
import cv2
import numpy as np
def video_Decomposition(video_name, begin):
    cap = cv2.VideoCapture(video_name)# 获取一个视频打开cap 1 file name
    isOpened = cap.isOpened # 判断是否打开
    print(isOpened)
    fps = cap.get(cv2.CAP_PROP_FPS)#帧率
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) #width
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))#height
    print(fps, width, height)
    i = 0 + 205 * (begin - 1) 
    while(isOpened):
        if(i == 205 * (begin - 1) + 205):
            break
        else:
            i = i + 1
        (flag, frame) = cap.read() #读取每一章 flag frame
        fileName = 'pos_Original//'  + str(i) + '.jpg'
        print(fileName)
        if flag == True:
            cv2.imwrite(fileName, frame, [cv2.IMWRITE_JPEG_QUALITY,100])
    print(i)
    print("video_Decomposition OK!")
for i in range(1, 5):
    video_Decomposition(str(i) + '.mp4', i)

(2)图片裁剪缩放

'''
裁剪缩放图片 def img_Resize(path, imgNum)
'''
import cv2
def img_Resize(pos_path, imgNum):
    #pos_path = 'pos_Original\\'
    for i in range(1, imgNum + 1):
        img = cv2.imread(pos_path + str(i) + '.jpg', 1)
        print(pos_path  + str(i) + '.jpg')
        imgInfo =  img.shape
        print(imgInfo)
        height = imgInfo[0]
        width = imgInfo[1]
        mode = imgInfo[2]
        #1 方法 缩小 2 等比例
        dstHeight = int(height * 0.2)
        dstWidth = int(width * 0.05)
        dst = cv2.resize(img, (dstWidth, dstHeight))
        imgInfo =  dst.shape
        print(imgInfo)
        #cv2.imshow('img', dst)
        cv2.imwrite('pos//'+ str(i) + '.jpg', dst) #路径是相对这个.ipynb文件的
        #cv2.waitKey(0)
    print("img_Resize OK!")
img_Resize('pos_Original//', 820)

(3)图片的裁剪

'''
图片的裁剪 def img_Spilt(pos_path, imgNum)
'''
def img_Spilt_and_Rotation(pos_path, imgNum):
    #pos_path = 'pos\\'
    for i in range(1, imgNum + 1):
        img = cv2.imread(pos_path + str(i) + '.jpg', 1)
        print(pos_path + str(i) + '.jpg')
        img = img[10:138, :]
        #img_rotate =np.rot90(img, -1)
        #imgInfo = img_rotate.shape
        imgInfo = img.shape
        print(imgInfo)
        #matRotate = cv2.getRotationMatrix2D((height * 0.5, width * 0.5), 270, 0)
        #dst = cv2.warpAffine(dst, matRotate, (height,width))
        cv2.imwrite("pos//" + str(i) + '.jpg', img)
    print(imgInfo[0])
    print("img_Spilt OK!!")
img_Spilt_and_Rotation('pos//', 820)

2.使用HOG与SVM进行目标检测

(1)

#训练
#1 参数 2 hog 3 svm 4 computer hog 5 label 6 train 7 pred 8draw
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 1.各个参数
posNum = 820 #正样本个数
negNum = 1931  #负样本个数
#winSize = (64,128) #win窗口 顶层单元
winSize = (64, 128) #win窗口 顶层单元

blockSize = (16, 16)  #一共有[((64-16)/8+1) + ((128-16)/8+1)] = 105个block
blockStride =(8, 8)  #block的步长,滑动,从上到下 从左到右
cellSize = (8, 8)   #cell: block内部最小单元,一共有16/8 * 16/8 = 4个 cell
nBin = 9 # 360度  每20度是一个bin 相同bin的角度是关于180对称

#2.hog
hog = cv2.HOGDescriptor(winSize, blockSize, blockStride, cellSize, nBin)
#3.computer hog featureNum = 105 * 4 * 9 = 3780
#featureNum = int(((128 - 16) / 8 + 1) * ((64 - 16) / 8 + 1) * 4 * 9)  # 3780

featureNum = int(((winSize[0] - blockSize[0]) / blockStride[0] + 1) * ((winSize[1] - blockSize[1])/blockStride[1] + 1) * 4 * nBin)
print(featureNum)
'''
featureArray和labelArray用于svm函数中
'''
#特征数组 维度:(posNum + NegNum) * featureNum 
featureArray = np.zeros(((posNum + negNum), featureNum), np.float32)
print(featureArray.shape)
#标签数组 维度:(PosNum + NegNum) * 1
labelArray = np.zeros(((posNum + negNum), 1), np.int32)
print(labelArray.shape)

print("label and feature OK")

(2)

# svm 监督学习 样本 标签 
#第一种 正样本准备 为1
for i in range(0, posNum):
    fileName = 'pos1//' + str(i + 1) + '.jpg'
    img = cv2.imread(fileName)
    print(fileName)
    hist = hog.compute(img, (8, 8))  # 3780
    for j in range(0, featureNum):
        featureArray[i, j] = hist[j]
    # featureArray hog [1,:] hog1 [2,:]hog2
    labelArray[i, 0] = 1
    # 正样本的训练数据进行学习 label 1


#第二种 负样本准备 为-1
for i in range(0, negNum):
    fileName = 'neg//' + str(i + 1) + '.jpg'
    print(fileName)
    img = cv2.imread(fileName)
    # hist 为一位 大小为featureNum
    hist = hog.compute(img, (8, 8)) # 第二参数为步长 
    for j in range(0, featureNum):
        featureArray[i + posNum, j] = hist[j]
    # featureArray hog[1, :]
    labelArray[i + posNum, 0] = -1 
print("dataset already OK!")
    
#svm创建以及属性设置
svm = cv2.ml.SVM_create()
svm.setType(cv2.ml.SVM_C_SVC)
svm.setKernel(cv2.ml.SVM_LINEAR) #线性内核
svm.setC(0.01) #目标函数的惩罚系数C,用来平衡分类间隔margin和错分样本

#6 train
ret = svm.train(featureArray, cv2.ml.ROW_SAMPLE, labelArray)
print("SVM train OK!")
# 7 myHog :《-myDetect
# myDetect-《resultArray  rho
# myHog-》detectMultiScale
# 7 检测  核心:create Hog -》 myDetect—》array-》
# resultArray-》resultArray = -1*alphaArray*supportVArray
# rho-》svm-〉svm.train
alpha = np.zeros((1), np.float32)
rho = svm.getDecisionFunction(0, alpha)
print(rho)
print(alpha)
alphaArray = np.zeros((1, 1), np.float32)
supportVArray = np.zeros((1, featureNum), np.float32)
resultArray = np.zeros((1, featureNum), np.float32)
alphaArray[0, 0] = alpha
resultArray = -1 * alphaArray * supportVArray
# detect
myDetect = np.zeros((3781), np.float32)
for i in range(0, 3780):
    myDetect[i] = resultArray[0, i]
myDetect[3780] = rho[0]
# rho svm (判决)
myHog = cv2.HOGDescriptor()
myHog.setSVMDetector(myDetect)
print("myhog ok")
# load
imageSrc = cv2.imread('test1.jpg', 1)
#64 * 128  90 * 160 
imgInfo = imageSrc.shape
height = imgInfo[0]
width = imgInfo[1]
mode = imgInfo[2]
#1 方法 缩小 2 等比例
#dstHeight = int(height * 0.2)
#dstWidth = int(width * 0.05)
dst = cv2.resize(imageSrc, (90, 160))
#cv2.imwrite("Test2.jpg", imageSrc)
# (8,8) win
objs = myHog.detectMultiScale(imageSrc, 0, (8, 8), (32, 32), 1.05, 2)
# xy wh 三维 最后一维
x = int(objs[0][0][0])
y = int(objs[0][0][1])
w = int(objs[0][0][2])
h = int(objs[0][0][3])
# 绘制展示
cv2.rectangle(imageSrc, (x, y), (x + w, y + h), (255, 0, 0), 2)

imageSrc = cv2.cvtColor(imageSrc, cv2.COLOR_BGR2RGB) #灰度处理图像

plt.rcParams['figure.figsize'] = (4.0, 4.0)
plt.imshow(imageSrc)
plt.title("res")
plt.savefig('res.png')

cv2.destroyAllWindows()


#cv2.imshow('dst', imageSrc)
#cv2.waitKey(0)

五.总结与反思

    这周的代码我参考了慕课网学习资料的代码,学习了HOG特征以及SVM支持向量机的基本原理以及代码实现的重点,并尝试自己设置正样本数据集,但自己并没有成功,因为自己通过预处理得到的图片并不难很好的在myHog.detectMultiScale方法中成功。我需要在日后的学习生活中继续改进, 不太能理解和作者的数据集都是64*128,为什么我会在detectMultiScale方法返回的objs报错:

objs = myHog.detectMultiScale(imageSrc, 0, (8, 8), (32, 32), 1.05, 2)
#print(obs) 这个是空的 出错
---> x = int(objs[0][0][0])
IndexError: tuple index out of range

很郁闷,应该是显示图片根本没有读进去,不知道是不是图片转换后太不清晰的原因,希望大佬赐教。

参考资料:
https://blog.csdn.net/qq_35992440/article/details/80987664
https://blog.csdn.net/Fredric18/article/details/85057050#commentBox
https://blog.csdn.net/qq_25352981/article/details/52605768
https://blog.csdn.net/zouxy09/article/details/7929348

猜你喜欢

转载自blog.csdn.net/weixin_43823854/article/details/102870310