使用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計算輪廓中心。上週,我們學習瞭如何利用輪廓逼近來檢測圖像中的形狀。最後,今天,我們將形狀檢測算法與顏色標記器相結合,用於標記形狀的特定顏色名稱。

雖然此方法適用於半控制的照明條件下的較小顏色集,但可能不適用於控制較少的環境中的較大調色板。正如我在這篇文章的“限制”部分所暗示的那樣,實際上,我們有一種方法可以“學習”現實世界中“看起來”的顏色。我將在以後的博客文章中保留對這種方法的討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章