使用OpenCV确定图像中物体的颜色

1 前言

在这里插入图片描述

这是我们关于形状检测和分析的三部分系列的最后一篇文章。

以前,我们学习了如何:

今天,我们将对图像中的对象执行形状检测颜色标记

在这一点上,我们理解图像的区域可以通过颜色直方图基本颜色通道统计信息(例如均值和标准差)来表征。

但是,尽管我们可以计算这些统计数据,但它们无法为我们提供实际的标签,例如将区域标记为包含特定颜色的“红色”,“绿色”,“蓝色”或“黑色”。

在此博客文章中,我将详细介绍如何利用L*a*b*颜色空间以及欧几里德距离来使用Python和OpenCV标记,标注和确定图像中对象的颜色。

2 使用OpenCV确定对象颜色

在深入研究任何代码之前,让我们简要回顾一下我们的项目结构:

|--- pyimagesearch
|    |--- __init__.py
|    |--- colorlabeler.py
|    |--- shapedetector.py
|--- detect_color.py
|--- example_shapes.png

注意我们如何重用我们先前博客文章中的shapedetector.pyShapeDetector类。我们还将创建一个新文件colorlabeler.py,该文件将使用颜色的文本标签标记图像区域。

最后,将使用detect_color.py驱动程序脚本将所有片段粘合在一起。

在继续阅读本文之前,请确保已在系统上安装了imutils Python软件包

$ pip install imutils

在本课程的其余部分中,我们将在该库中使用各种功能。

2.1 标记图像中的颜色

该项目的第一步是创建一个Python类,该类可用于用其关联的颜色标记图像中的形状。

为此,我们在colorlabeler.py文件中定义一个名为ColorLabeler的类:

# import the necessary packages
from scipy.spatial import distance as dist
from collections import OrderedDict
import numpy as np
import cv2

class ColorLabeler:
	def __init__(self):
		# initialize the colors dictionary, containing the color
		# name as the key and the RGB tuple as the value
		colors = OrderedDict({
			"red": (255, 0, 0),
			"green": (0, 255, 0),
			"blue": (0, 0, 255)})

		# allocate memory for the L*a*b* image, then initialize
		# the color names list
		self.lab = np.zeros((len(colors), 1, 3), dtype="uint8")
		self.colorNames = []

		# loop over the colors dictionary
		for (i, (name, rgb)) in enumerate(colors.items()):
			# update the L*a*b* array and the color names list
			self.lab[i] = rgb
			self.colorNames.append(name)

		# convert the L*a*b* array from the RGB color space
		# to L*a*b*
		self.lab = cv2.cvtColor(self.lab, cv2.COLOR_RGB2LAB)

第2-5行导入我们所需的Python程序包,而第7行定义ColorLabeler类。

然后我们进入第8行的构造函数。首先,我们需要初始化一个colors字典(第11-14行),该字典指定颜色名称(字典的键)到RGB元组(字典的值)。

从那里,我们为NumPy数组分配内存以存储这些颜色,然后初始化颜色名称列表(第18和19行)。

下一步是遍历colors字典,然后分别更新NumPy数组和colorNames列表(第22-25行)。

最后,我们将NumPy“图像”从RGB颜色空间转换为L*a*b*颜色空间。

那么为什么我们使用L*a*b*颜色空间而不是RGB或HSV?

好吧,为了实际地标记和标记图像的区域包含某种颜色,我们将计算已知colors的数据集(即lab数组)与特定图像区域的平均值之间的欧几里得距离

使欧几里德距离最小的已知颜色将被选作颜色标识。

而且与HSV和RGB颜色空间不同,L*a*b*颜色之间的欧几里得距离具有实际的感知意义,因此在本文的其余部分中将使用它。

下一步是定义label方法:

	def label(self, image, c):
		# construct a mask for the contour, then compute the
		# average L*a*b* value for the masked region
		mask = np.zeros(image.shape[:2], dtype="uint8")
		cv2.drawContours(mask, [c], -1, 255, -1)
		mask = cv2.erode(mask, None, iterations=2)
		mean = cv2.mean(image, mask=mask)[:3]

		# initialize the minimum distance found thus far
		minDist = (np.inf, None)

		# loop over the known L*a*b* color values
		for (i, row) in enumerate(self.lab):
			# compute the distance between the current L*a*b*
			# color value and the mean of the image
			d = dist.euclidean(row[0], mean)

			# if the distance is smaller than the current distance,
			# then update the bookkeeping variable
			if d < minDist[0]:
				minDist = (d, i)

		# return the name of the color with the smallest distance
		return self.colorNames[minDist[1]]

label方法需要两个参数:L*a*b*图像,其中包含我们要为其计算颜色通道统计信息的形状,然后是c,我们感兴趣的图像的轮廓区域。

第34和35行为轮廓区域构造了一个mask,我们可以在下面看到一个示例:

在这里插入图片描述

注意如何将mask的前景区域设置为白色,而背景设置为黑色。我们只会在图像的mask(白色)区域内执行计算。

第37行仅针对mask区域计算图像的L*,a*和b *通道中每个通道的平均值。

最后,第43-51行处理遍历lab矩阵的每一行,计算每种已知颜色与平均颜色之间的欧几里得距离,然后返回具有最小欧几里得距离的颜色的名称。

2.2 定义颜色标签和形状检测过程

现在我们已经定义了ColorLabeler,让我们创建detect_color.py驱动程序脚本。在此脚本中,我们将结合上周ShapeDetector类和今天的帖子中的ColorLabeler

让我们开始吧:

# import the necessary packages
from pyimagesearch.shapedetector import ShapeDetector
from pyimagesearch.colorlabeler import ColorLabeler
import argparse
import imutils
import cv2

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
	help="path to the input image")
args = vars(ap.parse_args())

第2-6行导入所需的Python软件包,请注意我们如何导入ShapeDetectorColorLabeler

然后,第9-12行解析我们的命令行参数。像本系列中的其他两篇文章一样,我们只需要一个参数即可:--image,即我们要处理的图像在硬盘上路径。

接下来,我们可以加载图像并进行处理:

# load the image and resize it to a smaller factor so that
# the shapes can be approximated better
image = cv2.imread(args["image"])
resized = imutils.resize(image, width=300)
ratio = image.shape[0] / float(resized.shape[0])

# blur the resized image slightly, then convert it to both
# grayscale and the L*a*b* color spaces
blurred = cv2.GaussianBlur(resized, (5, 5), 0)
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
lab = cv2.cvtColor(blurred, cv2.COLOR_BGR2LAB)
thresh = cv2.threshold(gray, 60, 255, cv2.THRESH_BINARY)[1]

# find contours in the thresholded image
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
	cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)

# initialize the shape detector and color labeler
sd = ShapeDetector()
cl = ColorLabeler()

第16-18行从磁盘加载图像,然后创建其调整大小的版本resized,并跟踪原始高度与调整后的高度的比率ratio。我们调整图像大小,以便轮廓近似更精确地用于形状识别。此外,图像越小,要处理的数据越少,因此我们的代码将执行得更快。

第22-25行将高斯平滑应用于我们调整大小后的图像,转换为灰度和L*a*b*,最后进行阈值处理以显示图像中的形状:

在第29-30行,我们找到形状的轮廓,并根据我们的OpenCV版本获取适当的cnts元组值。

现在我们准备检测图像中每个对象的形状和颜色:

# loop over the contours
for c in cnts:
	# compute the center of the contour
	M = cv2.moments(c)
	cX = int((M["m10"] / M["m00"]) * ratio)
	cY = int((M["m01"] / M["m00"]) * ratio)

	# detect the shape of the contour and label the color
	shape = sd.detect(c)
	color = cl.label(lab, c)

	# multiply the contour (x, y)-coordinates by the resize ratio,
	# then draw the contours and the name of the shape and labeled
	# color on the image
	c = c.astype("float")
	c *= ratio
	c = c.astype("int")
	text = "{} {}".format(color, shape)
	cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
	cv2.putText(image, text, (cX, cY),
		cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)

	# show the output image
	cv2.imshow("Image", image)
	cv2.waitKey(0)

我们开始在第38行上遍历每个轮廓,而第40-42行计算形状的中心

使用轮廓,我们可以检测物体的形状,然后在第45和46行确定其颜色。

最后,第51-57行处理当前形状的轮廓,然后在输出图像上绘制颜色和文本标签。

第60和61行将结果显示到我们的屏幕上。

2.3 颜色标签结果

执行以下命令:

$ python detect_color.py --image example_shapes.png

在这里插入图片描述

从上面的GIF中可以看出,每个对象在形状和颜色方面都已正确识别。

3 局限性

使用本文中介绍的方法标记颜色的主要缺点之一是,由于光照条件以及各种色调和饱和度,颜色很少看起来像纯红色,绿色,蓝色等。

您通常可以使用L*a*b*颜色空间和欧几里得距离来识别少量颜色,但是对于较大的调色板,此方法可能会返回错误的结果,具体取决于图像的复杂性。

因此,话虽如此,我们如何才能更可靠地标记图像中的颜色?

也许有一种方法可以“学习”现实世界中颜色的“外观”。

确实有。

这正是我将在以后的博客文章中讨论的内容。

4 总结

今天是我们关于形状检测和分析的三部分系列的最后一篇文章。

我们首先学习如何使用OpenCV计算轮廓中心。上周,我们学习了如何利用轮廓逼近来检测图像中的形状。最后,今天,我们将形状检测算法与颜色标记器相结合,用于标记形状的特定颜色名称。

虽然此方法适用于半控制的照明条件下的较小颜色集,但可能不适用于控制较少的环境中的较大调色板。正如我在这篇文章的“限制”部分所暗示的那样,实际上,我们有一种方法可以“学习”现实世界中“看起来”的颜色。我将在以后的博客文章中保留对这种方法的讨论。

  • 14
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值