OpenCV+Python 指定物体识别

本文介绍一种基于HoG+Pyramids+Sliding Windows+SVM的物体识别方法

基本流程

(1)确定最小检测物体,对原图img缩放,缩放比例为(滑动窗大小/最小物体大小)。
(2)缩放后的图片,构建金字塔。
(3)对金字塔的每一层,通过滑动窗获取patch(类似ROI的概念),对patch归一化处理,之后给训练好的物体检测器识别,将识别成功的窗口位置和概率保存。(特征使用HoG,分类算法使用SVM)
(4)将物体窗口映射到原图img中的物体位置,概率不变。
(5)NMS处理重叠窗口。

HoG

论文
HoG原理简书1
HoG原理简书2
Histogram of Oriented Gradients 方向梯度直方图,是特征描述符算法大家庭的一份子,家庭成员还有SIFT,SURF,ORB。
统计图像局部区域的梯度方向信息来作为该局部图像区域的表征。

基本步骤
  1. 灰度化。如果使用彩色图像,每个像素的梯度幅值取三个通道最大的,梯度方向取梯度幅值最大通道的方向。
  2. 归一化。对图像进行Gamma矫正,一般对每个像素的灰度值开根号。
  3. 计算每个像素的梯度。梯度方向一般分为9的区间(bin)。20°/bin for 无符号方向(0~180°),40°/bin for 有符号方向(0~360°)
  4. 统计每个Cell的梯度方向直方图。一般一个Cell是8*8个像素。9个特征值,对应9个bin。
  5. 统计每个Block的梯度方向直方图图。一般一个Block是2*2个Cell。36个特征值的向量,同时做归一化。
  6. 计算HoG特征向量。根据Block的总个数,生成总的HoG向量(36*n维向量)。
HoG存在的问题
  1. 不知道物体在图像的哪个位置。
  2. 不知道图像的尺度。

Image Pyramids

解决图像的尺度问题
图像金字塔简书1
OpenCV中图像金字塔的基本步骤

  1. 根据系数改变图像的大小。
  2. 平滑图像。
def resize(img, scaleFactor):
    return cv2.resize(img, (int(img.shape[1] * (1 / scaleFactor)), int(img.shape[0] * (1 / scaleFactor))), interpolation=cv2.INTER_AREA)

def pyramid(image, scale=1.5, minSize=(200, 80)):
    yield image

    while True:
        image = resize(image, scale)
        if image.shape[0] < minSize[1] or image.shape[1] < minSize[0]:
            break

    yield image

Sliding Window

通过扫描图像的不同区域来解决图像中物体所在位置的问题。

def sliding_window(image, step, window_size):
    for y in range(0, image.shape[0], step):
        for x in range(0, image.shape[1], step):
            yield (x, y, image[y:y + window_size[1], x:x + window_size[0]])
None-Maximam Suppression

NMS简书1
NMS解决的是如何在重叠区域中保留置信度最高的区域。

import numpy as np


# Malisiewicz et al.
# Python port by Adrian Rosebrock
def non_max_suppression_fast(boxes, overlapThresh):
    # if there are no boxes, return an empty list
    if len(boxes) == 0:
        return []

    # if the bounding boxes integers, convert them to floats --
    # this is important since we'll be doing a bunch of divisions
    if boxes.dtype.kind == "i":
        boxes = boxes.astype("float")

    # initialize the list of picked indexes 
    pick = []

    # grab the coordinates of the bounding boxes
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]
    scores = boxes[:, 4]
    # compute the area of the bounding boxes and sort the bounding
    # boxes by the score/probability of the bounding box
    area = (x2 - x1 + 1) * (y2 - y1 + 1)
    idxs = np.argsort(scores)[::-1]

    # keep looping while some indexes still remain in the indexes
    # list
    while len(idxs) > 0:
        # grab the last index in the indexes list and add the
        # index value to the list of picked indexes
        last = len(idxs) - 1
        i = idxs[last]
        pick.append(i)

        # find the largest (x, y) coordinates for the start of
        # the bounding box and the smallest (x, y) coordinates
        # for the end of the bounding box
        xx1 = np.maximum(x1[i], x1[idxs[:last]])
        yy1 = np.maximum(y1[i], y1[idxs[:last]])
        xx2 = np.minimum(x2[i], x2[idxs[:last]])
        yy2 = np.minimum(y2[i], y2[idxs[:last]])

        # compute the width and height of the bounding box
        w = np.maximum(0, xx2 - xx1 + 1)
        h = np.maximum(0, yy2 - yy1 + 1)

        # compute the ratio of overlap
        overlap = (w * h) / area[idxs[:last]]

        # delete all indexes from the index list that have
        idxs = np.delete(idxs, np.concatenate(([last],
                                               np.where(overlap > overlapThresh)[0])))

    # return only the bounding boxes that were picked using the
    # integer data type
    return int(boxes[pick])

SVM

算法重点:找到离分隔超平面最近的点,确保它们离分隔面的距离尽可能远。这里点到分隔面的距离被称为间隔margin,这个margin尽可能的大。支持向量support vector就是离分隔超平面最近的那些点,我们要最大化支持向量到分隔面的距离。

实例

行人检测

import cv2

def draw_person(image, persont):
    x, y, w, h = persont
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)

img = cv2.imread("people1.jpg")
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
rects, weights = hog.detectMultiScale(img)
for person in rects:
    draw_person(img, person)
cv2.imshow("people detection", img)

"""
        detectMultiScale(img[, hitThreshold[, winStride[, padding[, scale[, finalThreshold[, useMeanshiftGrouping]]]]]]) -> foundLocations, foundWeights
        .   @brief Detects objects of different sizes in the input image. The detected objects are returned as a list
        .   of rectangles.
        .   @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected.
        .   @param foundLocations Vector of rectangles where each rectangle contains the detected object.
        .   @param foundWeights Vector that will contain confidence values for each detected object.
        .   @param hitThreshold Threshold for the distance between features and SVM classifying plane.
        .   Usually it is 0 and should be specfied in the detector coefficients (as the last free coefficient).
        .   But if the free coefficient is omitted (which is allowed), you can specify it manually here.
        .   @param winStride Window stride. It must be a multiple of block stride. 步长
        .   @param padding Padding 填充
        .   @param scale Coefficient of the detection window increase.
        .   @param finalThreshold Final threshold
        .   @param useMeanshiftGrouping indicates grouping algorithm
"""
行人检测效果.png

检测一般物体

推荐博文

import cv2
import numpy as np

datapath = "TrainImages/"


def path(cls, i):
    return "%s/%s%d.pgm" % (datapath, cls, i + 1)


def extract_sift(fn):
    im = cv2.imread(fn, 0)
    return extract.compute(im, detect.detect(im))[1]


def bow_features(fn):
    im = cv2.imread(fn, 0)
    return extract_bow.compute(im, detect.detect(im))


def predict(fn):
    f = bow_features(fn)
    p = svm.predict(f)
    print(fn, "\t", p[1][0][0])
    return p


pos, neg = "pos-", "neg-"
# 创建sift特征提取
detect = cv2.xfeatures2d.SIFT_create()
extract = cv2.xfeatures2d.SIFT_create()

# 创建基于flann的匹配器
flann_params = dict(algorithm=1, trees=5)
matcher = cv2.FlannBasedMatcher(flann_params, {})
# 创建bow训练器
bow_kmeans_trainer = cv2.BOWKMeansTrainer(40)
# 创建词袋模型
extract_bow = cv2.BOWImgDescriptorExtractor(extract, matcher)
# 为模型输入正负样本
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分类,并且返回词汇。进一步制定extract_bow提取描述符
voc = bow_kmeans_trainer.cluster()
extract_bow.setVocabulary(voc)
# 创建2个数组,生成svm模型所需正负样本标签
traindata, trainlabels = [], []
for i in range(20):
    traindata.extend(bow_features(path(pos, i)));
    trainlabels.append(1)
    traindata.extend(bow_features(path(neg, i)));
    trainlabels.append(-1)
# 创建并训练一个svm模型
svm = cv2.ml.SVM_create()
svm.train(np.array(traindata), cv2.ml.ROW_SAMPLE, np.array(trainlabels))

# 测试两图
car, notcar = "car1", "car2"
car_img = cv2.imread(car)
notcar_img = cv2.imread(notcar)
car_predict = predict(car)
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)

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)

cv2.imshow('BOW + SVM Success', car_img)
cv2.imshow('BOW + SVM Failure', notcar_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容