本文主要內容來自於 OpenCV-Python 教程 的 OpenCV 中的圖像處理 部分,這個部分的主要內容如下:
-
學習在不同色彩空間之間改變圖像。另外學習跟蹤視頻中的彩色對象。
-
學習對圖像應用不同的幾何變換,比如旋轉、平移等。
-
學習使用全局閾值、自適應閾值、Otsu 的二值化等將圖像轉換爲二值圖像。
-
學習模糊圖像,使用自定義內核過濾圖像等。
-
瞭解形態學變換,如侵蝕、膨脹、開放、閉合等。
-
學習尋找圖像漸變、邊緣等。
-
學習通過 Canny 邊緣檢測尋找邊緣。
-
學習關於圖像金字塔的內容,以及如何使用它們進行圖像混合。
-
所有關於 OpenCV 中的輪廓的內容。
-
所有關於 OpenCV 中的直方圖的內容。
-
在 OpenCV 中遇到不同的圖像變換,如傅里葉變換、餘弦變換等。
-
學習使用模板匹配在圖像中搜索對象。
-
學習在一幅圖像中探測線。
-
學習在一幅圖像中探測圓。
-
學習使用分水嶺分割算法分割圖像。
-
學習使用 GrabCut 算法提取前景
這一部分無疑是 OpenCV 提供的核心能力的一部分了。
目標
- 在這份教程中,我們將學習如何將圖像從一個色彩空間轉換到另一個。比如 BGR -> Gray,BGR -> HSV,等等。
- 此外,我們將創建一個應用程序,從視頻中提取彩色的物品。
- 我們將學習如下的函數:cv.cvtColor(),cv.inRange() 等等。
改變色彩空間
在 OpenCV 中有超過 150 個色彩空間轉換方法可用。但我們只會深入瞭解其中的兩個,它們是最常用的:BGR -> Gray 和 BGR -> HSV。
對於色彩轉換,我們使用函數 cv.cvtColor(input_image, flag)
,其中的 flag 參數決定了轉換的類型。
對於 BGR -> Gray 轉換,我們使用 flag cv.COLOR_BGR2GRAY。類似地,對於 BGR -> HSV,我們使用 flag cv.COLOR_BGR2HSV。要獲得其它的 flags,可以執行如下這段代碼:
import cv2 as cv
def color_space():
flags = [name for name in dir(cv) if name.startswith("COLOR_")]
print("There is ", len(flags), " flags")
flags = "\n".join(flags)
print(flags)
OpenCV 4.5.4 版環境中執行這段代碼,輸出如下:
There is 346 flags
COLOR_BAYER_BG2BGR
COLOR_BAYER_BG2BGRA
COLOR_BAYER_BG2BGR_EA
. . . . . .
即可以執行的色彩空間轉換多達 346 種。
注意 對於 HSV,色調 (hue) 的範圍爲 [0,179],飽和度 (saturation) 的範圍爲 [0,255],明度 (value) 範圍爲 [0,255]。不同的軟件使用不同的尺度。因此,如果需要將 OpenCV 值與它們進行比較,則需要對這些範圍進行歸一化。
對象追蹤
現在我們知道如何將一幅 BGR 圖像轉換到 HSV 了,我們可以使用這種操作提取一個彩色的物體。在 HSV 中,表示彩色物體比在 BGR 色彩空間中更容易。在我們的應用程序中,我們將嘗試提取藍色物體。這是具體的方法:
- 獲取視頻的每一幀
- 將圖像從 BGR 轉換到 HSV 色彩空間
- 我們將 HSV 圖像閾值設置爲藍色範圍
- 現在單獨提取藍色物體,我們可以在該圖像上做任何我們想做的事情。
下面是詳細註釋的代碼:
def object_tracking():
cap = cv.VideoCapture(0)
while True:
# Take each frame
_, frame = cap.read()
# Convert BGR to HSV
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# define range of blue color in HSV
lower_blue = np.array([110, 50, 50])
upper_blue = np.array([130, 255, 255])
# Threshold the HSV image to get only blue colors
mask = cv.inRange(hsv, lower_blue, upper_blue)
# Bitwise-AND mask and original image
res = cv.bitwise_and(frame, frame, mask=mask)
mask = cv.merge((mask, mask, mask))
# print(frame.shape, mask.shape, res.shape)
dest = cv.hconcat([frame, mask, res])
cv.imshow('frame', dest)
k = cv.waitKey(5) & 0xFF
if k == 27:
break
cv.destroyAllWindows()
下圖展示了藍色物體的追蹤:
注意 圖像中有一些噪聲。我們將在後面的章節中瞭解如何移除它。 這是對象追蹤中最簡單的方法。一旦我們學習了輪廓相關的函數,我們就可以做很多事情,比如找到物體的質心並用它來跟蹤物體,只需在相機前移動我們的手指就可以繪製圖表,以及其它有趣的事情。
如何找到要追蹤的 HSV 值
這是 stackoverflow.com 中的一個常見問題。它非常簡單,我們可以使用相同的函數,cv.cvtColor()。不是傳入一幅圖像,而是僅僅傳入我們需要的 BGR 值。比如,要找到藍色、綠色和紅色的 HSV 值,可以通過如下這段代碼實現:
def bgr_color_in_hsv():
arr = np.zeros((1, 3, 3), np.uint8)
arr[0][0] = (255, 0, 0)
arr[0][1] = (0, 255, 0)
arr[0][2] = (0, 0, 255)
hsv = cv.cvtColor(arr, cv.COLOR_BGR2HSV)
print("Blue HSV: ", hsv[0][0])
print("Green HSV: ", hsv[0][1])
print("Red HSV: ", hsv[0][2])
上面這段代碼的輸出如下:
Blue HSV: [120 255 255]
Green HSV: [ 60 255 255]
Red HSV: [ 0 255 255]
要探測藍色之外的其它顏色的物體,只需要將上面那段代碼中,cv.inRange(hsv, lower_blue, upper_blue)
的參數修改爲目標顏色的範圍即可。比如將 [H-10, 100,100] 和 [H+10, 255, 255] 分別作爲下限和上限,可以追蹤紅色物體。除了這種方法,還可以使用任何圖像編輯工具,如 GIMP 或任何在線轉換器來查找這些值,但不要忘記調整 HSV 範圍。
其它資源
練習
- 嘗試找到一種方法來提取多個彩色對象,例如同時提取紅色、藍色和綠色對象。
這裏給出一個參考實現。這段代碼分別將各個顏色的物體提取出來,然後再將各個物體的圖像相加,得到最終需要的圖像:
def multi_objects_tracking():
cap = cv.VideoCapture(0)
while True:
# Take each frame
_, frame = cap.read()
# Convert BGR to HSV
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# define range of blue color in HSV
lower_blue = np.array([110, 50, 50])
upper_blue = np.array([130, 255, 255])
# Threshold the HSV image to get only blue colors
mask_blue = cv.inRange(hsv, lower_blue, upper_blue)
# Bitwise-AND mask and original image
blue_res = cv.bitwise_and(frame, frame, mask=mask_blue)
# define range of green color in HSV
lower_green = np.array([50, 50, 50])
upper_green = np.array([70, 255, 255])
# Threshold the HSV image to get only blue colors
mask_green = cv.inRange(hsv, lower_green, upper_green)
# Bitwise-AND mask and original image
green_res = cv.bitwise_and(frame, frame, mask=mask_green)
# define range of red color in HSV
lower_red = np.array([0, 50, 50])
upper_red = np.array([0, 255, 255])
# Threshold the HSV image to get only blue colors
mask_red = cv.inRange(hsv, lower_red, upper_red)
# Bitwise-AND mask and original image
red_res = cv.bitwise_and(frame, frame, mask=mask_red)
res = cv.add(blue_res, green_res)
res = cv.add(res, red_res)
dest = cv.hconcat([frame, res])
cv.imshow('frame', dest)
k = cv.waitKey(5) & 0xFF
if k == 27:
break
cv.destroyAllWindows()
結果將類似於下面這樣:
參考文檔
Done.