深度学习:Opencv的blobFromImage是如何工作的

本文详细解析了OpenCV的blobFromImage方法在深度学习预处理中的作用,包括均值减法和缩放操作。通过示例展示了如何使用blobFromImage处理图像,并通过GoogLeNet进行物体分类,帮助理解blob的生成及其在神经网络中的应用。
摘要由CSDN通过智能技术生成

说明

本文翻译自作者** Adrian Rosebrock**的一篇文章。
原文链接:Deep learning: How OpenCV’s blobFromImage works

介绍

在这里插入图片描述
之前有PyImageSearch 的读者在前几期的深度学习课程评论里想了解Opencv的blobFromImage的工作机制,所以特意写了此篇博文。

如你所见,为了从深度神经网络获得(正确的)预测,首先需要预处理你的数据。

在深度学习和图像分类的背景下,这些预处理通常包括:
1.均值减法(Mean subtraction)
2.缩放(scaling)
(PS:专业名词劝退系列,不过后面有详细解释)
OpenCV的深度神经网络(dnn)模块包含了两个方法,可以用来预处理图像,也可以通过已训练的深度学习模型为物体分类做准备。

在今天的博文中,我们将剖析opencv的cv2.dnn.blobFromImagecv2.dnn.blobFromImages预处理函数,并了解它们是如何工作的。

想要了解更多的opencv深度学习的图像预处理的内容,请继续阅读(链接

blobFromImage 的工作流程

前面提到过,opencv提供了两种方法进行图像的预处理:

cv2.dnn.blobFromImage
cv2.dnn.blobFromImages

这两个方法的作用是:
1.均值减法(Mean subtraction)
2.缩放(scaling)
3.可选的频道交换

接下来的部分,我们将:
1.探索均值减法和缩放
2.了解每个深度学习预处理函数的参数
3.学习这些方法的细节
4.最后,举例。

让我们开始吧

深度学习和均值减法

在这里插入图片描述
图片描述:均值减法直观点,就是左边图像的RGB减去中间的值后得到后边的值

在解释opencv的预处理功能之前,我们首先需要了解均值减法。在预处理的图片中,均值减法用来适应光照的变换(combat illumination changes)。因此,我们可以将均值减法视为一种协助卷积神经网络的技术。

在开始训练我们自己的深度神经网络之前,需要计算我们要训练的图片中RGB三个通道的平均像素(average pixel)。我们会得到三个变量:
在这里插入图片描述
每个变量是一个通道的平均值。总共三个通道也就是三个变量。

比如,ImageNet的均值为R=103.93, G=116.77,和 B=123.68(如果你使用过ImageNet,那你可能清楚这些值)

不过,在某些情况下,红色、绿色和蓝色的均值可能是按通道计算的,而不是按像素计算的,这会产生一个MxN矩阵。在这种情况下,处于训练或测试阶段的图片就减去每个通道值的MxN矩阵。

这两种方法(就是基于通道计算和像素计算)都是均值减法的形式,不过我们更看重基于像素的方式,尤其是数据多的情况下。

当我们准备好通过网络传递图片时(无论是为了训练还是测试),我们要将每个图片的每个通道的值减掉平均值。
在这里插入图片描述
我们还有一个缩放(scaling )的因子sigma,用来规范化。
在这里插入图片描述

sigma的值也许是训练过程中设置的均方差(也叫标准差)。不过,也可以手动设置sigma值将图片缩放到特定比例。
(PS:下面公式来自百度百科,标准差能反映一个数据集的离散程度。
例如,两组数的集合{0,5,9,14}和{5,6,8,9}其平均值都是7,但第二个集合具有较小的标准差。(百度百科))
在这里插入图片描述

重点要注意,并非所有的深度学习框架都有均值减法和缩放。所以在你预处理图片前,要了解你使用的深度神经网络。正如你在深度学习杂志上发现的那样,有些框架只有均值减法(将sigma设为了1),有些框架都有,有些都没有。

blobFromImage 和 blobFromImages

让我们首先参考opencv官方文档关于cv2.dnn.blobFromImage的说明。
(PS:可能需要的知识点blob概念

在这里插入图片描述

【blobFromImage】为图片创建了一个4维数组的blob。可以设置图片大小,并从图片中心进行剪切,减去均值,进行缩放,交换B和R通道

cv2.dnn.blobFromImagecv2.dnn.blobFromImages方法几乎相同。

让我们先看看cv2.dnn.blobFromImage方法的参数:

blob = cv2.dnn.blobFromImage(image, scalefactor=1.0, size, mean, swapRB=True)

每个参数的作用:
1.image:我们想要预处理的图片,之后会将图片放入深度神经网络来分类。
2.scalefactor:在均值减法处理后,我们可以用这个参数对图片进行缩放。这个参数默认为1。
3.size:设置卷积神经网络训练时输入的图片的大小。当前比较好的神经网络一般设置为224×224, 227×227, 299×299。
4.mean:这是均值减法中的均值,可以是3通道RGB的均值也可以是每个通道的均值,若是每个通道的均值需要用图片的每个通道减掉每个通道的均值。要确保通道顺序是RGB,尤其是swapRB=True时。
5.swapRB:opencv设定的图片是BGR通道顺序,然而,均值减法中要求我们使用RGB顺序。为了解决这个冲突,将该参数设为Ture,将R和B通道调换。该参数默认为Ture

cv2.dnn.blobFromImage方法在经过均值减法、标准化(PS:应该就是缩放)和通道交换后返回一个blob。

cv2.dnn.blobFromImages方法的参数是一样的。

blob = cv2.dnn.blobFromImages(images, scalefactor=1.0, size, mean, swapRB=True)

唯一的不同就是,我们可以传输多个图片,使我们能够批量处理图片。

如果你想预处理多个图片或帧,要使用cv2.dnn.blobFromImages,因为这会有更少的开销,批处理速度也更快。

使用blobFromImage 方法深度学习

现在我们已经学习了blobFromImageblobFromImages方法,让我们将它们应用到一些图片中,然后通过卷积神经网络进行分类。

在这之前,你的Opencv版本最少为3.3.0。Numpy也需要安装(pip install numpy)。安装imutils( pip install imutils)

假设你的图片预处理环境都已准备好了,让我们新建一个名为blob_from_images.py的文件,然后插入如下代码:
(PS:源码和所需的文件在源站已放出,需要翻墙,我将代码放在了最后,百度网盘)

# import the necessary packages
from imutils import paths
import numpy as np
import cv2

# load the class labels from disk
rows = open("synset_words.txt").read().strip().split("n")
classes = [r[r.find(" ") + 1:].split(",")[0] for r in rows]

# load our serialized model from disk
net = cv2.dnn.readNetFromCaffe("bvlc_googlenet.prototxt",
	"bvlc_googlenet.caffemodel")
	
# grab the paths to the input images
imagePaths = sorted(list(paths.list_images("images/")))

首先导入imutils , numpy , and cv2。(2-4行)
然后我们读取synset_words.txt (ImageNet 的物体分类标签文件),取出类和类标签(7-8行)

使用DNN方法cv2.dnn.readNetFromCaffe加载模块,
bvlc_googlenet.prototxt为模型参数,
bvlc_googlenet.caffemodel为模型框架(11-12行)

接下来我们要将加载图片和使用blobFromImage预处理。

# (1) load the first image from disk, (2) pre-process it by resizing
# it to 224x224 pixels, and (3) construct a blob that can be passed
# through the pre-trained network
image = cv2.imread(imagePaths[0])
resized = cv2.resize(image, (224, 224))
blob = cv2.dnn.blobFromImage(resized, 1, (224, 224), (104, 117, 123))
print("First Blob: {}".format(blob.shape))

在这一块中,我们首先加载图片(4行)并将它的大小设置为224x244(5行),这是GoogLeNet所需要的图片尺寸。

现在到了这篇博文的核心

在第6行中,我们使用了cv2.dnn.blobFromImage。如前一节所述,它会产生4维的数组blob用于我们的神经网络。

输出blob的尺寸,方便在终端对它进行分析。(7行)

接下来,我们将通过GoogLeNet输入blob:

# set the input to the pre-trained deep learning network and obtain
# the output predicted probabilities for each of the 1,000 ImageNet
# classes
net.setInput(blob)
preds = net.forward()

# sort the probabilities (in descending) order, grab the index of the
# top predicted label, and draw it on the input image
idx = np.argsort(preds[0])[::-1][0]
text = "Label: {}, {:.2f}%".format(classes[idx],
	preds[0][idx] * 100)
cv2.putText(image, text, (5, 25),  cv2.FONT_HERSHEY_SIMPLEX,
	0.7, (0, 0, 255), 2)
	
# show the output image
cv2.imshow("Image", image)
cv2.waitKey(0)

如果你熟悉我近期的深度学习博客,上面的几行代码看起来会很熟悉。我们通过网络输入了blob,并获取到了相似程度(原文为predictions)。(4-5行)

提取preds中相似程度最高的那一个下标(9行),生成一个标签展示在图片上。这个标签由物体分类标签和相似度百分比构成。(10-11行)

至此,我们在图片的顶部写了一个标签(12-13行),并等待我们按键以继续运行程序。(16-17行)。

现在是时候使用blobFromImage的复数形式了

除了我们创建和输入批量图片外,我们会做(几乎)同样的步骤。

# initialize the list of images we'll be passing through the network
images = []

# loop over the input images (excluding the first one since we
# already classified it), pre-process each image, and update the
# `images` list
for p in imagePaths[1:]:
	image = cv2.imread(p)
	image = cv2.resize(image, (224, 224))
	images.append(image)
	
# convert the images list into an OpenCV-compatible blob
blob = cv2.dnn.blobFromImages(images, 1, (224, 224), (104, 117, 123))
print("Second Blob: {}".format(blob.shape))

首先我们初始images列表类型(2行),然后读取imagePath里的图片,改变其大小,并将其添加进images中。(7-10行)

我们将images列表代入cv2.dnn.blobFromImages第一个参数中(13行)。其他参数和cv2.dnn.blobFromImage相同

接下来将通过GoogLeNet 传入blob,并在每个图片的顶部写入分类标签和相似度百分比。

# set the input to our pre-trained network and obtain the output
# class label predictions
net.setInput(blob)
preds = net.forward()

# loop over the input images
for (i, p) in enumerate(imagePaths[1:]):
	# load the image from disk
	image = cv2.imread(p)
	
	# find the top class label from the `preds` list and draw it on
	# the image
	idx = np.argsort(preds[i])[::-1][0]
	text = "Label: {}, {:.2f}%".format(classes[idx],
		preds[i][idx] * 100)
	cv2.putText(image, text, (5, 25),  cv2.FONT_HERSHEY_SIMPLEX,
		0.7, (0, 0, 255), 2)
		
	# display the output image
	cv2.imshow("Image", image)
	cv2.waitKey(0)

以上代码和之前差不多,只不过现在通过for循环进行了每个图片的处理。

就这样!让我们看看下一节的代码

结果展示

现在到了有趣的环节了。

首先打开终端运行程序

$ python blob_from_images.py

终端第一个输出结果是我们使用的cv2.dnn.blobFromImage方法

First Blob: (1, 3, 224, 224)

酒杯图片的结果展示如下:

在这里插入图片描述

图片描述:一杯诱人的啤酒被贴上了相应的标签,并被GoogLeNet识别为高识别度,由blobFromImage产生的blob尺寸输出在终端上

这啤酒我馋了,在我独自享用它之前,我来解释下为什么blob的shape为(1, 3, 224, 224)

这个元组的结构如下:

(num_images=1, num_channels=3, width=224, height=224)

因为我们只处理了一张图片,我们只有一个实体在blob中,所以第一个参数num_images=1。第二个是通道的数目,因为是BGR通道所以为3。最后两个是我们设置输入图片的长宽224x224。

接下来,让我们从其他图片生成一个blob。

第二个blob的shape为:

Second Blob: (4, 3, 224, 224)

因为这次blob包含了4个图片,故num_images=4,其他参数同上。

其他已识别的图片展示:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

在今天的课程中我们学习了opencv的blobFromImageblobFromImages深度学习方法。

在已训练的深度学习模块中,这些方法经常被用来输入图片的分类。

blobFromImageblobFromImages都包含均值减法和缩放。我们也可以根据需要交换R和B通道。几乎所有的主流深度学习模型都支持均值减法和缩放-----好处就是opencv使这些预处理变的非常简单。

(PS:后面就是作者推荐了自己写的书,不再翻译)
在这里插入图片描述
书的相关信息

源码和其他文件

1.在源站最下面有代码下载
2.百度网盘:链接 (提取码:3ful )

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值