第7章 目标检测与识别
7.1 目标检测与识别技术
目标检测用来确定图像的某个区域是否包含要识别的对象
本章用到的技术:
梯度直方图
图像金字塔
滑动窗口
7.1.1 HOG描述符
HOG是一个特征描述符,与SIFT、SURF、ORB类型相同。
目标检测的内部机制都差不多:将图像划分为多个部分,计算各个部分的梯度。
HOG是基于梯度计算直方图的,HOG得到的特征描述符能为特征匹配和目标检测提供非常重要的信息。
下面是卡车图像和对应的HOG图
HOG提取的特征可以识别车辆的主要结构。
HOG将卡车图像分成小单元,每个单元包含了视角表示,视觉表示是按8个方向计算的颜色梯度。每个单元的8个值就是直方图。
将直方图外推成描述符是相当复杂的过程。首相计算每个单元的局部直方图,这些单元会合成较大的区域(块)。进行人类检测时,一个块包含2x2的单元时效果最好。仅仅比较两幅图像的单元是行不通的,除非两幅图像相同。
要解决两个方面的问题:
1.尺度问题(大小不同)
2.位置问题(要找到目标区域)
两个概念
(1)图像金字塔(pyramid)
图像金字塔式图像的多尺度表示,有助于解决不同尺度下的目标检测问题。
构建图像金字塔:
1.获取图像。
2.使用任意尺度的参数来调整图像大小
3.平滑图像(使用高斯模糊)
4.如果图像比最小尺寸还大,重复上述步骤。
(2)滑动窗口
包括图像中要移动部分的检测以及使用图像金字塔对各部分检测,这是为了在多尺度下检测对象。
滑动窗口通过扫描较大图像的较小区域来解决定位问题,进而在同一图像的不同尺度下重复扫描。
这种技术奖图像分解为多个部分,然后丢弃不太可能包含对象的部分,对剩余部分进行分类。
7.1.2 检测人
import cv2
import numpy as np
def is_inside(o,i):
ox,oy,ow,oh = o
ix,iy,iw,ih = i
return ox>ix and oy>iy and ox+ow < ix+iw and oy+oh<iy+ih
def draw_person(image,person):
x,y,w,h = person
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
img = cv2.imread("./images/people.jpg")
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
found,w = hog.detectMultiScale(img)
found_filtered = []
for ri,r in enumerate(found):
for qi,q in enumerate(found):
if ri != qi and is_inside(r,q):
break;
else:
found_filtered.append(r)
for person in found_filtered:
draw_person(img,person)
cv2.imshow("people detection",img)
cv2.waitKey()
cv2.destroyAllWindows()
7.1.3 创建和训练目标检测器
构建分类器
*概念
词袋(BOW)
BOW用来在文档中计算词出现的次数,然后用这些次数构成的向量来重新表示文档,。
2、计算机视觉中的BOW
BOW方法实现步骤:
1.取一个样板数据集
2.对数据集中的图像提取描述符
3.将描述符添加到BOW训练器中。
4.将描述符 聚类到k簇中
k-means聚类
对于给定的数据集,k表示要分割的数据集中的簇数,means是均值。
7.2 汽车检测
https://blog.csdn.net/sinat_38685910/article/details/95511133
import cv2
import numpy as np
from os.path import join
# 此数据集为UIUC Car Detection 百度云链接可下载
datapath = 'D:\\PY_TEST\\car\\CarData\\TrainImages\\'
def path(cls, i):
return "%s/%s%d.pgm" % (datapath, cls, i + 1)
pos, neg = "pos-", "neg-" # 数据集中图片命名方式
detect = cv2.xfeatures2d.SIFT_create() # 提取关键点
extract = cv2.xfeatures2d.SIFT_create() # 提取特征
# FLANN匹配器有两个参数:indexParams和searchParams,以字典的形式进行参数传参
flann_params = dict(algorithm=1, trees=5) # 1为FLANN_INDEX_KDTREE
matcher = cv2.FlannBasedMatcher(flann_params, {}) # 匹配特征
# 创建bow训练器,簇数为40
bow_kmeans_trainer = cv2.BOWKMeansTrainer(40)
# 初始化bow提取器
extract_bow = cv2.BOWImgDescriptorExtractor(extract, matcher)
def extract_sift(fn): # 参数为路径
im = cv2.imread(fn, 0)
return extract.compute(im, detect.detect(im))[1] # 返回描述符
# 读取8个正样本和8个负样本
for i in range(8):
bow_kmeans_trainer.add(extract_sift(path(pos, i)))
bow_kmeans_trainer.add(extract_sift(path(neg, i)))
# 利用训练器的cluster()函数,执行k-means分类并返回词汇
# k-means:属于聚类算法,所谓的聚类算法属于无监督学习,将样本x潜在所属类别Y找出来
voc = bow_kmeans_trainer.cluster()
extract_bow.setVocabulary(voc)
def bow_features(fn):
im = cv2.imread(fn, 0)
return extract_bow.compute(im, detect.detect(im))
# 两个数组,分别为训练数据和标签,并用bow提取器产生的描述符填充
traindata, trainlabels = [], []
for i in range(120):
traindata.extend(bow_features(path(pos, i)));
trainlabels.append(1) # 1为正匹配
traindata.extend(bow_features(path(neg, i)));
trainlabels.append(-1) # -1为负匹配
# 创建SVM实例,将训练数据和标签放到numpy数组中进行训练,有关SVM知识稍后写一篇补上
svm = cv2.ml.SVM_create()
svm.train(np.array(traindata), cv2.ml.ROW_SAMPLE, np.array(trainlabels))
def predict(fn):
f = bow_features(fn)
p = svm.predict(f)
print(fn, "\t", p[1][0][0])
return p
# 预测结果
car = 'car8.jpg'
car_img = cv2.imread(car)
car_predict = predict(car)
# 添加文字说明
font = cv2.FONT_HERSHEY_SIMPLEX
if (car_predict[1][0][0] == 1.0): # predict结果为1.0表示能检测到汽车
cv2.putText(car_img, 'Car Detected', (10, 30), font, 1, (0, 255, 0), 2, cv2.LINE_AA)
if (car_predict[1][0][0] == -1.0): # predict结果为-1.0表示不能检测到汽车
cv2.putText(car_img, 'Car Not Detected', (10, 30), font, 1, (0, 0, 255), 2, cv2.LINE_AA)
while (True):
cv2.imshow('BOW + SVM Success', car_img)
# 按q退出
if (cv2.waitKey(1000 // 12) & 0xff == ord("q")):
break
cv2.destroyAllWindows()
7.2.2 SVM和滑动窗口
滑动窗口:即选择一个矩形区域,每次往右滑动一步,直至最右,然后往下继续这个过程,直到全图结束。