OpenCV-Python实战(19)——OpenCV与深度学习的碰撞

OpenCV 中包含深度神经网络 (deep neural networks, DNN) 模块,可以使用深度神经网络实现前向计算(推理阶段),使用一些流行的深度学习框架进行预训练的网络(例如 Caffe、TensorFlow、Pytorch、Darknet等)就可以轻松用在 OpenCV 项目中了。在本文中,我们将通过实战学习将流行的深度学习模型架构应用于目标检测和图像分类中,构建 OpenCV 计算机视觉项目。

0. 前言

OpenCV中包含深度神经网络 (Deep Neural Networks, DNN) 模块,可以使用深度神经网络实现前向计算(推理阶段),使用一些流行的深度学习框架进行预训练的网络(例如Caffe、TensorFlow、Pytorch、Darknet等)就可以轻松用在OpenCV项目中了。
在介绍性示例的深度学习简介中,我们介绍了许多流行的深度学习网络架构。在本文中,我们将学习如何将这些架构应用于对象检测和图像分类。

1. cv2.dnn.blobFromImage() 函数详解

OpenCV中深度神经网络为了执行前向计算,其输入应该是一个blob,blob可以看作是经过预处理(包括缩放、裁剪、归一化、通道交换等)以馈送到网络的图像集合。
在OpenCV中,使用cv2.dnn.blobFromImage()构建blob:

# 图像加载
image = cv2.imread("example.jpg")
# 利用 image 创建 4 维 blob
blob = cv2.dnn.blobFromImage(image, 1.0, (300, 300), [104., 117., 123.], False, False)

上述代码意味着我们进行以下预处理:调整为 300 x 300 的 BGR 图像、分别对蓝色、绿色和红色通道执行 (104, 117, 123) 均值减法 。
接下来,我们将可以将blob设置为输入并获得检测结果如下:

# 将 blob 设置为输入并获取检测结果
net.setInput(blob)
detections = net.forward()

接下来,首先详细讲解cv2.dnn.blobFromImage()和cv2.dnn.blobFromImages()函数。理解这些函数后,对于后续项目的构建将很有帮助。
cv2.dnn.blobFromImage()函数的用法如下:

retval = cv2.dnn.blobFromImage(image[, scalefactor[, size[, mean[, swapRB[, crop[, ddepth]]]]]])

此函数从image创建一个四维blob,参数含义如下所示:

参数 解释
image 要预处理的输入图像
scalefactor 缩放因子,用于缩放像素值,默认值为 1.0,即不执行缩放
size 输出图像的尺寸
mean 将在图像中减去的标量,如果使用均值减法,在 swapRB =True 时,结果为 (mean-R, mean-G, mean-B)
swapRB 若此标志设置为 True,此标志可用于交换图像中的 R 和 B 通道
crop 此标志用于指示在调整大小后是否会裁剪图像
ddepth 输出 blob 的深度,可选值包括 CV_32F 或 CV_8U

如果crop=False,则在不裁剪的情况下执行图像的大小调整;否则,将首先调整大小,然后从中心裁剪图像
cv2.dnn.blobFromImage()函数的默认值如下:scalefactor=1.0, size = Size(), mean = Scalar(), swapRB = false, crop = false, ddepth = CV_32F。
cv2.dnn.blobFromImages()函数的用法如下:

retval = cv.dnn.blobFromImages(images[, scalefactor[, size[, mean[, swapRB[, crop[, ddepth]]]]]])

此函数可以从多个图像创建一个四维blob,通过这种方式,可以对整个网络执行一次前向计算获得多个图像的输出:

# 创建图像列表
images = []
for img in glob.glob('*.png'):
    images.append(cv2.imread(img))
blob_images = cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, False)
# 前向计算
net.setInput(blob_images)
detections = net.forward()

为了测试cv2.dnn.blobFromImage()函数,首先加载一个 BGR 图像,然后使用cv2.dnn.blobFromImage()函数创建一个四维blob。然后,我们编写get_image_from_blob()函数,该函数可用于执行逆预处理变换以再次获取输入图像,以更好地理解cv2.dnn.blobFromImage()函数的预处理:

# 加载图像
image = cv2.imread("example.png")
# 调用 cv2.dnn.blobFromImage() 函数
blob_image = cv2.dnn.blobFromImage(image, 1.0, (300, 300), [104., 117., 123.], False, False)
# blob_image 的尺寸为 (1, 3, 300, 300)
print(blob_image.shape)

def get_image_from_blob(blob_img, scalefactor, dim, mean, swap_rb, mean_added):
    images_from_blob = cv2.dnn.imagesFromBlob(blob_img)
    image_from_blob = np.reshape(images_from_blob[0], dim) / scalefactor
    image_from_blob_mean = np.uint8(image_from_blob)
    image_from_blob = image_from_blob_mean + np.uint8(mean)

    if mean_added is True:
        if swap_rb:
            image_from_blob = image_from_blob[:, :, ::-1]
        return image_from_blob
    else:
        if swap_rb:
            image_from_blob_mean = image_from_blob_mean[:, :, ::-1]
        return image_from_blob_mean

# 从 blob 中获取不同的图像
# img_from_blob 图像对应于调整为 (300,300) 的原始 BGR 图像,并且已经添加了通道均值
img_from_blob = get_image_from_blob(blob_image, 1.0, (300, 300, 3), [104., 117., 123.], False, True)
# img_from_blob_swap 图像对应于调整大小为 (300,300) 的原始 RGB 图像
# img_from_blob_swap 交换了蓝色和红色通道,并且已经添加了通道均值
img_from_blob_swap = get_image_from_blob(blob_image, 1.0, (300, 300, 3), [104., 117., 123.], True, True)
# img_from_blob_mean 图像对应于调整大小为 (300,300) 的原始 BGR 图像,其并未添加通道均值
img_from_blob_mean = get_image_from_blob(blob_image, 1.0, (300, 300, 3), [104., 117., 123.], False, False)
# img_from_blob_mean_swap 图像对应于调整为 (300,300) 的原始 RGB 图像
# img_from_blob_mean_swap 交换了蓝色和红色通道,并未添加通道均值
img_from_blob_mean_swap = get_image_from_blob(blob_image, 1.0, (300, 300, 3), [104., 117., 123.], True, False)
# 可视化
def show_img_with_matplotlib(color_img, title, pos):
    img_RGB = color_img[:, :, ::-1]

    ax = plt.subplot(1, 4, pos)
    plt.imshow(img_RGB)
    plt.title(title, fontsize=10)
    plt.axis('off')
show_img_with_matplotlib(img_from_blob, "img from blob " + str(img_from_blob.shape), 1)
show_img_with_matplotlib(img_from_blob_swap, "img from blob swap " + str(img_from_blob.shape), 2)
show_img_with_matplotlib(img_from_blob_mean, "img from blob mean " + str(img_from_blob.shape), 3)
show_img_with_matplotlib(img_from_blob_mean_swap, "img from blob mean swap " + str(img_from_blob.shape), 4)

程序的输出如下所示:

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞
接下来,我们首先加载目标文件夹中的所有图像,然后使用cv2.dnn.blobFromImages()函数创建一个四维blob,同样,我们编写get_images_from_blob()函数用于执行逆预处理变换以再次获取输入图像。
get_images_from_blob 函数的代码如下:

def get_images_from_blob(blob_imgs, scalefactor, dim, mean, swap_rb, mean_added):
    images_from_blob = cv2.dnn.imagesFromBlob(blob_imgs)
    imgs = []

    for image_blob in images_from_blob:
        image_from_blob = np.reshape(image_blob, dim) / scalefactor
        image_from_blob_mean = np.uint8(image_from_blob)
        image_from_blob = image_from_blob_mean + np.uint8(mean)
        if mean_added is True:
            if swap_rb:
                image_from_blob = image_from_blob[:, :, ::-1]
            imgs.append(image_from_blob)
        else:
            if swap_rb:
                image_from_blob_mean = image_from_blob_mean[:, :, ::-1]
            imgs.append(image_from_blob_mean)
    return imgs

如前所述,get_images_from_blob() 函数使用 OpenCV cv2.dnn.imagesFromBlob() 函数从 blob 返回图像。在脚本中,我们使用此函数从 blob 中获取不同的图像,如下所示:

# 加载图像并构造图像列表
images = []
for img in glob.glob('*.png'):
    images.append(cv2.imread(img))
# 调用 cv2.dnn.blobFromImages() 函数
blob_images = cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, False)
# 打印形状
print(blob_images.shape)
# 从 blob 中获取不同的图像
# imgs_from_blob 图像对应于调整大小为 (300,300) 的原始 BGR 图像,并且已经添加了通道均值
imgs_from_blob = get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], False, True)
# img_from_blob_swap 图像对应于调整大小为 (300,300) 的原始 RGB 图像
# img_from_blob_swap 交换了蓝色和红色通道,并且已经添加了通道均值
imgs_from_blob_swap = get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], True, True
# img_from_blob_mean 图像对应于调整大小为 (300,300) 的原始 BGR 图像,其并未添加通道均值
imgs_from_blob_mean = get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], False, False)
# img_from_blob_mean_swap 图像对应于调整为 (300,300) 的原始 RGB 图像
# img_from_blob_mean_swap 交换了蓝色和红色通道,并未添加通道均值
imgs_from_blob_mean_swap = get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], True, False)
# 可视化,show_img_with_matplotlib() 函数与上例相同
for i in range(len(images)):
    show_img_with_matplotlib(imgs_from_blob[i], "img from blob " + str(imgs_from_blob[i].shape), i * 4 + 1)
    show_img_with_matplotlib(imgs_from_blob_swap[i], "img from blob swap " + str(imgs_from_blob_swap[i].shape), i * 4 + 2)
    show_img_with_matplotlib(imgs_from_blob_mean[i], "img from blob mean " + str(imgs_from_blob_mean[i].shape), i * 4 + 3)
    show_img_with_matplotlib(imgs_from_blob_mean_swap[i], "img from blob mean swap " + str(imgs_from_blob_mean_swap[i].shape), i * 4 + 4)

程序输出如下所示:

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞
cv2.dnn.blobFromImage()和cv2.dnn.blobFromImages()的最后一个重要的参数是crop参数,它指示图像是否需要裁剪,在crop = True的情况下,图像从中心进行裁剪。为了更好的理解OpenCV在cv2.dnn.blobFromImage()和cv2.dnn.blobFromImages()函数中执行的裁剪,我们编写get_cropped_img()函数进行复刻:

def get_cropped_img(img):
    img_copy = img.copy()
    size = min(img_copy.shape[1], img_copy.shape[0])
    x1 = int(0.5 * (img_copy.shape[1] - size))
    y1 = int(0.5 * (img_copy.shape[0] - size))
    return img_copy[y1:(y1 + size), x1:(x1 + size)]

如上图,裁剪后的图片大小以原图最小边为准。

images = []
for img in glob.glob('*.png'):
    images.append(cv2.imread(img))
# 使用 get_cropped_img() 函数进行裁剪
cropped_img = get_cropped_img(images[0])
# crop = False 不进行裁剪
blob_images = cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, False)
print(blob_images)
# crop = True 进行裁剪
blob_blob_images_cropped = cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, True)
imgs_from_blob = get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], False, True)
imgs_from_blob_cropped = get_images_from_blob(blob_blob_images_cropped, 1.0, (300, 300, 3), [104., 117., 123.], False, True)
# 可视化,show_img_with_matplotlib() 函数与上例相同
for i in range(len(images)):
    show_img_with_matplotlib(imgs_from_blob[i], "img {} from blob ".format(i) + str(imgs_from_blob[i].shape), i + 1)
    show_img_with_matplotlib(imgs_from_blob_cropped[i], "img {} from blob cropped ".format(i) + str(imgs_from_blob[i].shape), i + 5)

程序的输出如下图所示,可以看到裁剪保持了图像的纵横比:

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞

2. OpenCV DNN 人脸检测器

接下来,将多个图像馈送到网络进行前向计算输出人脸检测结果,以更好的理解cv2.dnn.blobFromImages()函数。
首先查看当cv2.dnn.blobFromImages()函数中crop=True时的检测效果:

net = cv2.dnn.readNetFromCaffe("deploy.prototxt", "res10_300x300_ssd_iter_140000_fp16.caffemodel")
# 加载图片,构建 blob
img_1 = cv2.imread('example_1.png')
img_2 = cv2.imread('example_2.png')
images = [img_1.copy(), img_2.copy()]
blob_images = cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, False)
# 前向计算
net.setInput(blob_images)
detections = net.forward()

for i in range(0, detections.shape[2]):
    # 首先,获得检测结果所属的图像
    img_id = int(detections[0, 0, i, 0])
    # 获取预测的置信度
    confidence = detections[0, 0, i, 2]
    # 过滤置信度较低的预测
    if confidence > 0.25:
        # 获取当前图像尺寸
        (h, w) = images[img_id].shape[:2]
        # 获取检测的 (x, y) 坐标
        box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
        (startX, startY, endX, endY) = box.astype("int")
        # 绘制边界框和概率
        text = "{:.2f}%".format(confidence * 100)
        y = startY - 10 if startY - 10 > 10 else startY + 10
        cv2.rectangle(images[img_id], (startX, startY), (endX, endY), (0, 0, 255), 2)
        cv2.putText(images[img_id], text, (startX, y), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 2)
# 可视化
show_img_with_matplotlib(img_1, "input img 1", 1)
show_img_with_matplotlib(img_2, "input img 2", 2)
show_img_with_matplotlib(images[0], "output img 1", 3)
show_img_with_matplotlib(images[1], "output img 2", 4)

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞

接下来使用裁剪后的检测结果保持纵横比,可以看到在保持纵横比的情况下检测的置信度更高:

# 只需修改 cv2.dnn.blobFromImages() 中的 crop 参数为 True
blob_images = cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, True)

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞

3. OpenCV 图像分类

接下来将介绍使用不同的预训练深度学习模型执行图像分类,为了对比不同模型的运行效率,可以使用net.getPerfProfile()方法获取推理阶段所用时间:

# 前向计算获取预测结果
net.setInput(blob)
preds = net.forward()
# 获取推理时间
t, _ = net.getPerfProfile()
print('Inference time: %.2f ms' % (t * 1000.0 / cv2.getTickFrequency()))

如上所示,在执行推理后调用net.getPerfProfile()方法获取推理时间, 通过这种方式,可以比较使用不同深度学习架构的推理时间。

3.1 使用 AlexNet 进行图像分类

使用Caffe预训练的AlexNet模型进行图像分类可以分为以下步骤:

  1. 加载类别名称
  2. 加载的Caffe模型
  3. 加载输入图像,并对输入图像进行预处理获取blob
  4. 将输入的blob馈送到网络,进行推理,并得到输出
  5. 得到概率最高的 10 个预测类别(降序排列)
  6. 在图像上绘制最自信的类别和概率

类别名、模型架构和模型权重参数均可在Gitbub进行下载。

# 1. 加载类的名称
rows = open('synset_words.txt').read().strip().split('\n')
classes = [r[r.find(' ') + 1:].split(',')[0] for r in rows]
# 2. 加载的 Caffe 模型
net = cv2.dnn.readNetFromCaffe("bvlc_alexnet.prototxt", "bvlc_alexnet.caffemodel")
# 3. 加载输入图像,并对输入图像进行预处理获取 blob
image = cv2.imread('pandas.jpeg')
blob = cv2.dnn.blobFromImage(image, 1, (227, 227), (104, 117, 123))
print(blob.shape)
# 4. 将输入的 `blob` 馈送到网络,进行推理,并得到输出
net.setInput(blob)
preds = net.forward()
# 获取推理时间
t, _ = net.getPerfProfile()
print('Inference time: %.2f ms' % (t * 1000.0 / cv2.getTickFrequency()))
# 5. 得到概率最高的 10 个预测类别(降序排列)
indexes = np.argsort(preds[0])[::-1][:10]
# 6. 在图像上绘制置信度最高的类别和概率
text = "label: {}\nprobability: {:.2f}%".format(classes[indexes[0]], preds[0][indexes[0]] * 100)
y0, dy = 30, 30
for i, line in enumerate(text.split('\n')):
    y = y0 + i * dy
    cv2.putText(image, line, (5, y), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2)

# 打印置信度排名前十的类别
for (index, idx) in enumerate(indexes):
    print("{}. label: {}, probability: {:.10}".format(index + 1, classes[idx], preds[0][idx]))

show_img_with_matplotlib(image, "AlexNet and caffe pre-trained models", 1)
plt.show()

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞

如上图所示,图片以 0.9948046803 的置信度被分类为大熊猫,置信度前 10 的预测结果如下:

1. label: giant panda, probability: 0.9948046803
2. label: lesser panda, probability: 0.002839741996
3. label: Arctic fox, probability: 0.001207351917
4. label: teddy, probability: 0.0001851956185
5. label: Samoyed, probability: 0.0001801071776
6. label: Old English sheepdog, probability: 0.0001742217282
7. label: Border collie, probability: 8.865840209e-05
8. label: badger, probability: 6.369451148e-05
9. label: indri, probability: 5.878169759e-05
10. label: dalmatian, probability: 4.750319204e-05

3.2 使用 GoogLeNet 进行图像分类

使用GoogLeNet模型进行图像分类的步骤与使用Caffe预训练的AlexNet模型进行图像分类步骤相同,唯一的区别在于其加载的模型为Caffe预训练的GoogLeNet模型(类别名、GoogLeNet模型架构和模型权重参数均可在Gitbub进行下载):

net = cv2.dnn.readNetFromCaffe("bvlc_googlenet.prototxt", "bvlc_googlenet.caffemodel")

程序的输出如下:

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞
如上图所示,图片以 0.997341454 的置信度被分类为大熊猫,置信度前 10 的预测结果如下:

1. label: giant panda, probability: 0.997341454
2. label: lesser panda, probability: 0.00163484423
3. label: Arctic fox, probability: 0.0001777200523
4. label: Madagascar cat, probability: 0.0001774487464
5. label: indri, probability: 0.000139524127
6. label: teddy, probability: 8.606248593e-05
7. label: langur, probability: 4.349003575e-05
8. label: soccer ball, probability: 4.122937389e-05
9. label: dalmatian, probability: 3.438878412e-05
10. label: capuchin, probability: 2.523723379e-05

3.3 使用 ResNet 进行图像分类

使用ResNet模型进行图像分类的步骤与使用AlexNet模型进行图像分类步骤相同,唯一的区别在于其加载的模型为Caffe预训练的ResNet模型(这里对类别名、训练后ResNet模型架构和模型权重参数文件进行压缩供大家进行下载,也可以自己构建模型训练获得ResNet模型参数):

net = cv2.dnn.readNetFromCaffe("ResNet-50-deploy.prototxt", "ResNet-50-model.caffemodel")

程序的输出如下:

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞
如上图所示,图片以 0.9906288981 的置信度被分类为大熊猫,置信度前 10 的预测结果如下:

1. label: giant panda, probability: 0.9906288981
2. label: badger, probability: 0.002265424468
3. label: lesser panda, probability: 0.00156865106
4. label: dalmatian, probability: 0.0008186288178
5. label: skunk, probability: 0.0004198730458
6. label: indri, probability: 0.0004003143404
7. label: ram, probability: 0.0002219640737
8. label: Madagascar cat, probability: 0.0001818448509
9. label: ice bear, probability: 0.000126767889
10. label: Newfoundland, probability: 0.0001197585734

3.4 使用 SqueezeNet 进行图像分类

接下来使用SqueezeNet神经网络架构执行图像分类,参数量相比AlexNet网络架构减少了 50 倍,对以上程序进行修改,将其加载的模型修改为Caffe预训练的SqueezeNet模型(类别名、SqueezeNet模型架构和模型权重参数均可在Gitbub进行下载):

net = cv2.dnn.readNetFromCaffe('squeezenet_v1.1_deploy.prototxt', "squeezenet_v1.1.caffemodel")
# ...
# 使用 SqueezeNet 时需要对预测结果 preds 进行整形
preds = preds.reshape((1, len(classes)))
indexes = np.argsort(preds[0])[::-1][:10]

程序的输出如下:

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞

如上图所示,图片以 0.9963214397 的置信度被分类为大熊猫,置信度前 10 的预测结果如下:

1. label: giant panda, probability: 0.9963214397
2. label: lesser panda, probability: 0.003480769461
3. label: teddy, probability: 4.718080891e-05
4. label: Arctic fox, probability: 4.203413118e-05
5. label: Samoyed, probability: 2.355617653e-05
6. label: soccer ball, probability: 1.932817031e-05
7. label: langur, probability: 1.073173735e-05
8. label: gibbon, probability: 7.374388133e-06
9. label: weasel, probability: 4.493716915e-06
10. label: Eskimo dog, probability: 3.58794864e-06

4. OpenCV 目标检测

接下来,我们将介绍使用不同的预训练模型来进行目标检测。对象检测的任务是检测图像或视频中预定义类(例如猫、汽车和人类)的实例。

4.1 使用 MobileNet-SSD 进行目标检测

MobileNets是用于移动视觉应用的高效卷积神经网络,MobileNet-SSD在COCO数据集上进行了训练,达到了72.27% mAP,可以用于检测到 20 种对象类别(这里对训练后MobileNet-SSD模型架构和模型权重参数文件进行压缩供大家进行下载,也可以自己构建模型训练获得MobileNet-SSD模型参数):

Person(人): Person(人)
Animal(动物): Bird(鸟), cat(猫), cow(牛), dog(狗), horse(马), sheep(羊)
Vehicle(交通工具): Aeroplane(飞机), bicycle(自行车), boat(船), bus(公共汽车), car(小轿车), motorbike(摩托车), train(火车)
Indoor(室内): Bottle(水瓶), chair(椅子), dining table(餐桌), potted plant(盆栽), sofa(沙发), TV/monitor(电视/显示器)

通过使用MobileNet-SSD和Caffe预训练模型,使用OpenCV DNN模块执行对象检测:

# 加载模型及参数
net = cv2.dnn.readNetFromCaffe('MobileNetSSD_deploy.prototxt', 'MobileNetSSD_deploy.caffemodel')
# 图片读取
image = cv2.imread('test_img.jpg')
# 定义类别名
class_names = {0: 'background', 1: 'aeroplane', 2: 'bicycle', 3: 'bird', 4: 'boat', 5: 'bottle', 6: 'bus', 7: 'car',8: 'cat', 9: 'chair', 10: 'cow', 11: 'diningtable', 12: 'dog', 13: 'horse', 14: 'motorbike', 15: 'person', 16: 'pottedplant', 17: 'sheep', 18: 'sofa', 19: 'train', 20: 'tvmonitor'}
# 预处理
blob = cv2.dnn.blobFromImage(image, 0.007843, (300, 300), (127.5, 127.5, 127.5))
print(blob.shape)
# 前向计算
net.setInput(blob)
detections = net.forward()

t, _ = net.getPerfProfile()
print('Inference time: %.2f ms' % (t * 1000.0 / cv2.getTickFrequency()))
# 输入图像尺寸
dim = 300

# 处理检测结果
for i in range(detections.shape[2]):
    # 获得预测的置信度
    confidence = detections[0, 0, i, 2]

    # 去除置信度较低的预测
    if confidence > 0.2:
        # 获取类别标签
        class_id = int(detections[0, 0, i, 1])
        # 获取检测到目标对象框的坐标
        xLeftBottom = int(detections[0, 0, i, 3] * dim)
        yLeftBottom = int(detections[0, 0, i, 4] * dim)
        xRightTop = int(detections[0, 0, i, 5] * dim)
        yRightTop = int(detections[0, 0, i, 6] * dim)
        # 缩放比例系数
        heightFactor = image.shape[0] / dim
        widthFactor = image.shape[1] / dim
        # 根据缩放比例系数计算检测结果最终坐标
        xLeftBottom = int(widthFactor * xLeftBottom)
        yLeftBottom = int(heightFactor * yLeftBottom)
        xRightTop = int(widthFactor * xRightTop)
        yRightTop = int(heightFactor * yRightTop)
        # 绘制矩形框
        cv2.rectangle(image, (xLeftBottom, yLeftBottom), (xRightTop, yRightTop), (0, 255, 0), 2)
        # 绘制置信度和类别
        if class_id in class_names:
            label = class_names[class_id] + ": " + str(confidence)
            labelSize, baseLine = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 1, 2)
            yLeftBottom = max(yLeftBottom, labelSize[1])
            cv2.rectangle(image, (xLeftBottom, yLeftBottom - labelSize[1]),
                          (xLeftBottom + labelSize[0], yLeftBottom + 0), (0, 255, 0), cv2.FILLED)
            cv2.putText(image, label, (xLeftBottom, yLeftBottom), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)

程序输出如下图所示,几乎所有的物体都被正确且准确地检测到:

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞

4.1 使用 YOLO V3 进行目标检测

YOLO v3使用一些技巧来改进训练和提高性能,包括多尺度预测和更好的主干分类器等(这里同样对训练后YOLO V3模型架构和模型权重参数文件进行整理压缩供大家进行下载,也可以自己构建模型训练获得YOLO V3模型参数,或者在以下地址下载yolov3.weights文件):

# 加载类别名
class_names = open('coco.names').read().strip().split('\n')
# 加载网络及参数
net = cv2.dnn.readNetFromDarknet('yolov3.cfg', 'yolov3.weights')
# 加载测试图像
image = cv2.imread('test_img.jpg')
(H, W) = image.shape[:2]
# 获取网络输出
layer_names = net.getLayerNames()
layer_names = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
# 预处理
blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416), swapRB=True, crop=False)
print(blob.shape)
# 前向计算
net.setInput(blob)
layerOutputs = net.forward(layer_names)
t, _ = net.getPerfProfile()
print('Inference time: %.2f ms' % (t * 1000.0 / cv2.getTickFrequency()))
# 构建结果数组
boxes = []
confidences = []
class_ids = []
# 循环输出结果
for output in layerOutputs:
    # 循环检测结果
    for detection in output:
        # 获取类别 id 和置信度
        scores = detection[5:]
        class_id = np.argmax(scores)
        confidence = scores[class_id]
        # 过滤低置信度目标
        if confidence > 0.25:
            # 使用原始图像的尺寸缩放边界框坐标
            box = detection[0:4] * np.array([W, H, W, H])
            (centerX, centerY, width, height) = box.astype("int")
            # 计算边界框左上角坐标
            x = int(centerX - (width / 2))
            y = int(centerY - (height / 2))
            # 将结果添加到结果数组中
            boxes.append([x, y, int(width), int(height)])
            confidences.append(float(confidence))
            class_ids.append(class_id)
# 应用非极大值抑制
indices = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.3)
# 绘制结果
if len(indices) > 0:
    for i in indices.flatten():
        (x, y) = (boxes[i][0], boxes[i][1])
        (w, h) = (boxes[i][2], boxes[i][3])
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        label = "{}: {:.4f}".format(class_names[class_ids[i]], confidences[i])
        labelSize, baseLine = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 1, 2)
        y = max(y, labelSize[1])
        cv2.rectangle(image, (x, y - labelSize[1]), (x + labelSize[0], y + 0), (0, 255, 0), cv2.FILLED)
        cv2.putText(image, label, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)

程序输出如下所示:

OpenCV-Python实战(19)——OpenCV与深度学习的碰撞

概括

在本文中,我们首先通过cv2.dnn.blobFromImage()和cv2.dnn.blobFromImages()函数了解了如何在OpenCV中构建网络输入blob,然后通过实战学习将流行的深度学习模型架构应用于目标检测和图像分类中,构建OpenCV计算机视觉项目。

系列链接

OpenCV-Python实战(1)——OpenCV简介与图像处理基础
OpenCV-Python实战(2)——图像与视频文件的处理
OpenCV-Python实战(3)——OpenCV中绘制图形与文本
OpenCV-Python实战(4)——OpenCV常见图像处理技术
OpenCV-Python实战(5)——OpenCV图像运算
OpenCV-Python实战(6)——OpenCV中的色彩空间和色彩映射
OpenCV-Python实战(7)——直方图详解
OpenCV-Python实战(8)——直方图均衡化
OpenCV-Python实战(9)——OpenCV用于图像分割的阈值技术
OpenCV-Python实战(10)——OpenCV轮廓检测
OpenCV-Python实战(11)——OpenCV轮廓检测相关应用
OpenCV-Python实战(12)——一文详解AR增强现实
OpenCV-Python实战(13)——OpenCV与机器学习的碰撞
OpenCV-Python实战(14)——人脸检测详解
OpenCV-Python实战(15)——面部特征点检测详解
OpenCV-Python实战(16)——人脸追踪详解
OpenCV-Python实战(17)——人脸识别详解
OpenCV-Python实战(18)——深度学习简介与入门示例

版权声明:本文为博主盼小辉丶原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/LOVEmy134611/article/details/122946665

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2022年2月16日 下午4:20
下一篇 2022年2月16日 下午5:01

相关推荐