目标检测与识别
目标检测是一个程序,它用来确定图像的某个区域是否含有要要识别的对象,对象识别是程序识别对象的能力。识别通常只处理已检测到对象的区域,例如人们总是会在有人脸图像的区域去识别人脸。
在计算机视觉中有很多目标检测和识别的技术
- 梯度直方图(Histogram of Oriented Gradient)
- 图像金字塔(image pyramid)
- 滑动窗口(sliding window)
HOG描述符
HOG不是基于颜色而是基于梯度来计算直方图的。将直方图外推(extrapolation)成描述符的过程:首先计算每个单元的局部直方图,这些单元会合成较大的区域,称为块(block);按块来构成特征向量为了便于归一化,同时考虑光照和阴影(shadowing)的变化(一个单元的区域太小,不能检测这样的变化)。这样减少了图像与块之间光照和阴影 的差异,从而提高了检测精度。
尺度问题
如比较两幅图像(大小不同),找不到一组相同的梯度,检测失败
位置问题
检测的目标可能位于图像的任何地方,需要扫描得到感兴趣的区域,在这些区域尝试检测目标。要解决这个问题,先引入两个概念:
图像金字塔
图像金字塔(pyramid)是图像的多尺度表示,有助于解决不同尺度下的目标检测问题。
可通过以下过程来构建图像金字塔:
- 获取图像
- 使用任意尺度的参数来调整(缩小)图像的大小
- 平滑图像(使用高斯模糊)
- 如果图像比最小尺寸还大,重复步骤1
detectMultiScale() 函数的scaleFactor参数表示一个比率,即在每层金字塔中获得的图像与上一层的比率。参数越小,金字塔层数越多,计算更慢,计算量变大,精度某种程度上会更高。
滑动窗口
滑动窗口是用于计算机视觉的一种技术,包括图像中要移动部分(滑动窗口)的检查以及使用图像金字塔对各部分进行检测(多尺度检测对象)。
滑动窗口通过扫描较大图像的较小区域来解决定位问题,进而在同一图像的不同尺度下重复扫描。使用此方法需要将每幅图像分解,丢弃不太可能包含对象的部分,对剩下部分分类,引发区域重叠问题(overlapping region)
区域重叠:在对图像执行人脸检测时使用滑动窗口。每次窗口都会丢掉几个像素,这意味着一个滑动窗口可以对同一人脸的四个不同位置进行正匹配,(只需一个匹配结果,对良好评分区域不感兴趣,对最高评分区域感兴趣),这样会引发另一个问题:非最大抑制。
非最大(或非极大)抑制
非最大(或非极大)抑制是指定一组重叠区域,可以用最大评分来抑制所有未分类区域。这是一种与图像同一区域相关的所有结果进行抑制的技术,这些区域没有最大评分(同样排放(colocate)的窗口往往具有最高的评分,并且重叠区域会变得明显,这里只关心结果最好的窗口,并丢弃评分较低的重叠窗口)。
实现非最大抑制算法的过程:
- 一旦建立了图像金字塔,为了检测目标,可采用滑动窗口来搜索图像;
- 收集当前所有含有目标的窗口(超出一定任意阈值),并得到有最高响应的窗口W;
- 消除所有与W有明显重叠的窗口;
- 移动到下一个有最高相应的窗口,在当前尺度下重复上述过程;
在这个过程完成后,移动图像金字塔的下一个尺度,并重复前面的过程。如何确定窗口的评分呢?需要一个分类系统来确定某一特征是否存在,并且对这个分类有一个置信度评分,采用支持向量机(SVM)来分类。
汽车检测
文件的 tree / 树状图:
.
├── detect_hog_svm.py
└── images
├── CarData ── TrainImages
└── pic
├── car.jpg
└── bb.jpg
代码
# -*- coding: utf-8 -*-
"""
Created on Fri Jan 8 19:23:22 2021
@author: gkm0120
"""
# detect_hog_svm.py
import cv2
import numpy as np
from os.path import join
datapath = "D:/Images/CarData/TrainImages" # 声明训练的基础路径
def path(cls,i):
return "%s/%s%d.pgm" % (datapath,cls,i+1)
pos, neg = "pos-", "neg-" #数据集图像以pos-x.pgm和neg-x.pgm命名,其中x为数字
detect = cv2.xfeatures2d.SIFT_create() #检测关键点
extract = cv2.xfeatures2d.SIFT_create() # 提取特征
flann_params = dict(algorithm = 1, trees = 5) #数字1为算法床底的参数,表示使用FLANN_INDEX_KDTREE算法
matcher = cv2.FlannBasedMatcher(flann_params, {})
bow_kmeans_trainer = cv2.BOWKMeansTrainer(40) #创建BOW训练器,指定簇数为40
extract_bow = cv2.BOWImgDescriptorExtractor(extract, matcher) # 初始化BOW提取器,视觉词汇作为BOW类的输入,在测试图像中会检测这些视觉词汇
# 提取SIFT特征,获取图像路径,并以灰度格式读取图像,然后返回描述符
def extract_sift(fn):
im = cv2.imread(fn,0)
return extract.compute(im, detect.detect(im))[1]
# 每个类都从数据集中读取8张图像(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)))
voc = bow_kmeans_trainer.cluster() #创建视觉单词词汇需调用的cluster函数,执行k-means分类并返回词汇
extract_bow.setVocabulary(voc) #指定返回的词汇,从测试图像中提取描述符
# 声明函数,参数是一幅图像的路径,并且返回基于BOW的描述符提取器计算得到的描述符
def bow_features(fn):
im = cv2.imread(fn,0)
return extract_bow.compute(im, detect.detect(im))
# 创建两个数组,分别对应数据和标签,并用BOWImgDescriptorExtractor产生的描述符填充它们
traindata, trainlabels = [],[]
for i in range(20):
traindata.extend(bow_features(path(pos, i))); trainlabels.append(1) # 1表示正匹配
traindata.extend(bow_features(path(neg, i))); trainlabels.append(-1) #-1表示负匹配
svm = cv2.ml.SVM_create() #创建SVM实例
svm.train(np.array(traindata), cv2.ml.ROW_SAMPLE, np.array(trainlabels)) #通过将训练数据和标签放到NumPy数组中进行训练
# 定义一个函数显示predict方法的结果,并返回这些结果
def predict(fn):
f = bow_features(fn);
p = svm.predict(f)
print (fn, "\t", p[1][0][0])
return p
# 定义两个样本图像的路径,并将路径中图像文件读取放到NumPy数组中
car, notcar = "D:/Images/pic/car.jpg","D:/Images/pic/bb.jpg"
car_img = cv2.imread(car)
notcar_img = cv2.imread(notcar)
car_predict = predict(car) #传递给训练好的SVM,并预测结果
not_car_predict = predict(notcar)
font = cv2.FONT_HERSHEY_SIMPLEX
if (car_predict[1][0][0] == 1.0):
cv2.putText(car_img,'Car Detected',(10,30), font, 1,(0,255,0),2,cv2.LINE_AA) #含有汽车的图像能检测到汽车,预测值为1.0
if (not_car_predict[1][0][0] == -1.0):
cv2.putText(notcar_img,'Car Not Detected',(10,30), font, 1,(0,0, 255),2,cv2.LINE_AA) #不含汽车的图像不检测到汽车,预测值为-1.0
cv2.imshow('BOW + SVM Success', car_img)
cv2.imshow('BOW + SVM Failure', notcar_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
输入
|
|
图例
|
|