Finding Objects in Images using OpenCV in Python

作者:禅与计算机程序设计艺术

1.简介

OpenCV(Open Source Computer Vision)是一个基于BSD许可的开源计算机视觉库,由Intel、英伟达、加州大学伯克利分校、开放电源、Sony等多个研究机构共同开发维护。OpenCV支持多种编程语言,包括C++、Python、Java和MATLAB等。OpenCV可以用于实时视频处理、图像分析与机器学习等领域。本文将阐述如何在Python中使用OpenCV对图片中的目标进行检测并进行识别。首先,需要导入相关模块:

import cv2 as cv
import numpy as np

2.基本概念术语说明

2.1.目标检测

目标检测(Object Detection)是计算机视觉领域的一个重要方向,其主要目的是从一张或多张图片中自动地检测出感兴趣的对象,并给予其分类与定位信息。简单的说,就是在一副图上找到一些看似相互独立的物体,并确定其位置,用矩形框标注出来。有两种典型的目标检测方法:第一种是基于模板匹配的方法;第二种是卷积神经网络(Convolutional Neural Networks, CNNs)。本文中,采用CNNs的方式进行目标检测。

2.2.基于模板匹配的目标检测

基于模板匹配的目标检测是最简单也最直接的方法。该方法的基本思路是:用一系列的固定大小的“模板”去扫描整幅图像,如果某个模板与图像某一区域匹配的概率高于某个预设的阈值,则认为这个区域可能包含一个目标。这种方法的缺点是容易受到光照、纹理、形状、颜色等因素的影响,且模板与目标的位置有偏差。

2.3.卷积神经网络(CNNs)

卷积神经网络(CNNs)是深度学习领域的基础模型之一,通过使用卷积层和池化层构建的特征提取器能够提取图像特征。不同于传统的线性激活函数的神经网络,CNNs通常采用步长为1的卷积核,从而实现全局的、端到端的特征提取过程,同时避免了像素间依赖的问题。因此,CNNs特别适合于图像或者视频数据集的分类任务。

2.4.目标检测的准确率评价指标

在目标检测过程中,需要计算模型的精度,衡量模型预测的结果与实际标签之间的一致程度。常用的准确率评价指标包括precision, recall, F1-score, mAP等。其中,precision表示检出的正例个数与检出个数的比率,即TP/(TP+FP),其中TP代表真阳性,FP代表假阳性;recall表示检出的正例个数与实际正例个数的比率,即TP/(TP+FN),其中FN代表假阴性。F1-score则是精确率和召回率的调和平均值。mAP(mean Average Precision)是针对每个类别的平均精度值的综合评估。

2.5.YOLO(You Only Look Once)算法

YOLO算法是目前应用最广泛的目标检测算法之一,由Darknet项目的开发者 和他的学生Alexey等提出,基于卷积神经网络实现。该算法在速度、准确率及易部署等方面都有非常突出的优势。YOLO算法将输入图像划分成S x S个网格(cell),每个网格负责预测B个边界框和C个对象类的概率,其中S=7、B=2、C=20。YOLO算法使用卷积神经网络对每个网格的输出做非极大值抑制(NMS),从而选择出置信度最大的目标边界框。

3.核心算法原理和具体操作步骤以及数学公式讲解

3.1.准备工作

载入OpenCV的imread()函数读取待检测的图片,然后转换为灰度图像,并通过cvtColor()函数变换颜色空间至HSV。然后创建原始图片的副本,用来显示绘制后的结果。

gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
result_image = image.copy()

3.2.设置阈值参数

YOLO算法使用的类别是COCO数据集,类别数为20,即从0到19。YOLO算法默认情况下,将一张图片的目标分割为7x7网格,每个网格预测两个边界框,以及每个边界框包含20个类别的概率。因此,每个网格输出的总数量是$(7\times 7 + 2)\times 20$=970。接下来,定义了各种超参数,如网络结构、IOU阈值、置信度等,这里不再赘述。

IMAGE_SIZE = (416, 416) # 设置输入图片尺寸
CLASS_NAMES = ["person", "bicycle", "car", "motorcycle",
               "airplane", "bus", "train", "truck", 
               "boat", "traffic light", "fire hydrant", 
               "stop sign", "parking meter", "bench", 
               "bird", "cat", "dog", "horse", "sheep", 
               "cow", "elephant", "bear", "zebra", 
               "giraffe"]   # 类别名称列表
ANCHORS = [[10, 13], [16, 30], [33, 23],
           [30, 61], [62, 45], [59, 119], 
           [116, 90], [156, 198], [373, 326]] # 锚框列表
CONFIDENCE_THRESHOLD = 0.5     # 置信度阈值
IOU_THRESHOLD = 0.45            # IOU阈值

3.3.转变图像大小并缩放

将图片缩放至指定尺寸后,并根据输入图片尺寸,按比例缩放边界框和置信度,以符合最终输出尺寸。

def resize_with_pad(img):
    h, w = img.shape[:2]
    size = IMAGE_SIZE[0] if max(w, h) == size else min(IMAGE_SIZE)

    fx = fy = float(size)/max(h, w)
    new_h, new_w = int(h*fx), int(w*fy)

    resized = cv.resize(img, (new_w, new_h))

    canvas = np.full((IMAGE_SIZE[0], IMAGE_SIZE[1], 3), 128, dtype="uint8")

    dx = (canvas.shape[1]-resized.shape[1])//2
    dy = (canvas.shape[0]-resized.shape[0])//2

    canvas[dy:dy+resized.shape[0],dx:dx+resized.shape[1],:] = resized

    return canvas, (new_w/w, new_h/h)

3.4.生成锚框

锚框即用来检测目标的最小矩形框。由于不同尺寸的物体大小不一样,因此需要计算不同大小的锚框,然后使用滑动窗口方式生成不同比例的锚框。下面的代码首先遍历所有锚框,然后计算其在原图上的坐标范围,然后使用滑动窗口生成不同比例的锚框。

def generate_anchors():
    anchors = []
    for anchor in ANCHORS:
        base_size = max(anchor)//2

        num_x = math.floor(math.sqrt(anchor[0]/base_size)*NUM_ANCHOR_SCALES[0])
        num_y = math.floor(math.sqrt(anchor[1]/base_size)*NUM_ANCHOR_SCALES[1])

        cx, cy = [], []
        ws, hs = [], []
        step_x = 1./num_x 
        step_y = 1./num_y 

        for j in range(num_y):
            for i in range(num_x):
                cx.append(step_x/2.+i*step_x)
                cy.append(step_y/2.+j*step_y)

                ws.append(float(anchor[0])/IMAGE_SIZE[0]*base_size)
                hs.append(float(anchor[1])/IMAGE_SIZE[1]*base_size)

        anchors += [(cx[a], cy[a], ws[a], hs[a]) for a in range(len(ws))]

    return anchors

3.5.训练YOLO模型

YOLO模型是基于VGG16网络构建的,先经过卷积神经网络提取特征,然后利用YOLO算法在特征图上计算得出目标的边界框和置信度。最后,将边界框和置信度合并,计算mAP指标。

model = yolo_v3(input_shape=(IMAGE_SIZE[0], IMAGE_SIZE[1], 3))
optimizer = keras.optimizers.Adam(lr=LEARNING_RATE)
loss = YOLOLoss(anchors, NUM_CLASSES)

model.compile(optimizer=optimizer, loss=loss)

history = model.fit(X_train, y_train, epochs=EPOCHS, 
                    batch_size=BATCH_SIZE, validation_data=(X_val, y_val),
                    callbacks=[ModelCheckpoint("best_model.h5", save_weights_only=True)])

scores = model.evaluate(X_test, Y_test, verbose=0)
print("Accuracy: {:.2f}%".format(scores[1]*100))

3.6.运行检测

检测流程如下:首先,把原始图片进行预处理,然后送入模型进行预测。然后,在预测得到的边界框的基础上,进一步缩放、扭曲、裁剪,最终获得与原始图片相同大小的检测结果。最后,显示检测结果。

def detect_objects(image):
    original_size = image.shape[:-1][::-1]    # 获取原始图片的尺寸

    input_img, scale = resize_with_pad(image)      # 按照指定尺寸缩放图片

    input_img /= 255.                             # 将图片归一化至0~1之间

    detections = model.predict(np.expand_dims(input_img, axis=0))[0]  # 使用模型进行预测

    results = postprocess(detections, SCALES, ANCHORS, NUM_CLASSES, confidence_threshold=CONFIDENCE_THRESHOLD)  # 对检测结果进行后处理

    draw_boxes(original_size, result_image, results)               # 在原始图片上绘制检测结果

    display_image(result_image)                                  # 展示检测结果

猜你喜欢

转载自blog.csdn.net/universsky2015/article/details/133504602