OpenCV 追踪并标记颜色
这篇教学会介绍如何透过 OpenCV 追踪特定的颜色,并在追踪到颜色的时候,使用绘图的方式标记颜色区域 ( 会使用 inRange、dilate、boundingRect、findContours...等方法 )。
快速导览:
因为程序中的 OpenCV 会需要使用镜头或 GPU,所以请使用本机环境 ( 参考:使用 Python 虚拟环境 ) 或使用 Anaconda Jupyter 进行实作 ( 参考:使用 Anaconda ) ,并安装 OpenCV 函数库 ( 参考:OpenCV 函数库 )。
抓取特定颜色,移除颜色内的噪声
参考“抓取图像的特定颜色”文章范例,抓取图像中的特定颜色,但抓取到的颜色范围内,可能会有因为反光或阴影产生的噪声,这时可以参考“图像的侵蚀与膨胀”文章范例,让程序抓取图像中的特定颜色后,使用“膨胀”的方式移除颜色内的噪声,完成后再利用“侵蚀”缩回原本的大小。
import cv2
import numpy as np
lower = np.array([30,40,200]) # 轉換成 NumPy 陣列,範圍稍微變小 ( 55->30, 70->40, 252->200 )
upper = np.array([90,100,255]) # 轉換成 NumPy 陣列,範圍稍微加大 ( 70->90, 80->100, 252->255 )
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, img = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(img,(640,360)) # 縮小尺寸,加快處理速度
output = cv2.inRange(img, lower, upper) # 取得顏色範圍的顏色
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11)) # 設定膨脹與侵蝕的參數
output = cv2.dilate(output, kernel) # 膨脹影像,消除雜訊
output = cv2.erode(output, kernel) # 縮小影像,還原大小
cv2.imshow('oxxostudio', output)
if cv2.waitKey(1) == ord('q'):
break # 按下 q 鍵停止
cap.release()
cv2.destroyAllWindows()
取得颜色范围的轮廓座标
取得特定颜色后,使用 findContours 抓取颜色范围的轮廓座标,并透过 for 循环印出座标。
import cv2
import numpy as np
lower = np.array([30,40,200])
upper = np.array([90,100,255])
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, img = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(img,(640,360))
output = cv2.inRange(img, lower, upper)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11))
output = cv2.dilate(output, kernel)
output = cv2.erode(output, kernel)
# cv2.findContours 抓取顏色範圍的輪廓座標
# cv2.RETR_EXTERNAL 表示取得範圍的外輪廓座標串列,cv2.CHAIN_APPROX_SIMPLE 為取值的演算法
contours, hierarchy = cv2.findContours(output, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 使用 for 迴圈印出座標長相
for contour in contours:
print(contour)
cv2.imshow('oxxostudio', output)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
根据轮廓座标,绘制形状
取得轮廓的座标后,使用 contourArea 计算轮廓座标包覆的面积,如果面积大于 300 再进行绘图 ( 避免侦测到背景太小的区域 ),绘图使用 line 的方法,将每一个座标点连在一起。
import cv2
import numpy as np
lower = np.array([30,40,200])
upper = np.array([90,100,255])
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, img = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(img,(640,360))
output = cv2.inRange(img, lower, upper)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11))
output = cv2.dilate(output, kernel)
output = cv2.erode(output, kernel)
contours, hierarchy = cv2.findContours(output, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
area = cv2.contourArea(contour) # 取得範圍內的面積
color = (0,0,255) # 設定外框顏色
# 如果面積大於 300 再標記,避免標記到背景中太小的東西
if(area > 300):
for i in range(len(contour)):
if i>0 and i<len(contour)-1:
# 從第二個點開始畫線
img = cv2.line(img, (contour[i-1][0][0], contour[i-1][0][1]), (contour[i][0][0], contour[i][0][1]), color, 3)
elif i == len(contour)-1:
# 如果是最後一個點,與第一個點連成一線
img = cv2.line(img, (contour[i][0][0], contour[i][0][1]), (contour[0][0][0], contour[0][0][1]), color, 3)
cv2.imshow('oxxostudio', img)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
如果想用四边形标记特定颜色,可以使用 boundingRect 取得轮廓的 xy 座标和长宽尺寸,再透过 rectangle 绘制四边形。
import cv2
import numpy as np
lower = np.array([30,40,200]) # 轉換成 NumPy 陣列,範圍稍微變小 ( 55->30, 70->40, 252->200 )
upper = np.array([90,100,255]) # 轉換成 NumPy 陣列,範圍稍微加大 ( 70->90, 80->100, 252->255 )
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, img = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(img,(640,360))
output = cv2.inRange(img, lower, upper)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11))
output = cv2.dilate(output, kernel)
output = cv2.erode(output, kernel)
contours, hierarchy = cv2.findContours(output, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
area = cv2.contourArea(contour)
color = (0,0,255)
if(area > 300):
x, y, w, h = cv2.boundingRect(contour) # 取得座標與長寬尺寸
img = cv2.rectangle(img, (x, y), (x + w, y + h), color, 3) # 繪製四邊形
cv2.imshow('oxxostudio', img)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
同时追踪并标记多种颜色
运用同样的做法,只要知道颜色范围,就可以追踪各种不同的颜色,下方的程序码执行后,可以追踪两种不同的颜色。
import cv2
import numpy as np
lower = np.array([30,40,200])
upper = np.array([90,100,255])
blue_lower = np.array([90,100,0]) # 設定藍色最低值範圍
blue_upper = np.array([200,160,100]) # 設定藍色最高值範圍
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, img = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(img,(640,360))
output = cv2.inRange(img, lower, upper)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11))
output = cv2.dilate(output, kernel)
output = cv2.erode(output, kernel)
contours, hierarchy = cv2.findContours(output, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
area = cv2.contourArea(contour)
color = (0,0,255)
if(area > 300):
x, y, w, h = cv2.boundingRect(contour)
img = cv2.rectangle(img, (x, y), (x + w, y + h), color, 3)
# 設定選取藍色的程式
blue_output = cv2.inRange(img, blue_lower, blue_upper)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11))
blue_output = cv2.dilate(blue_output, kernel)
blue_output = cv2.erode(blue_output, kernel)
contours, hierarchy = cv2.findContours(blue_output, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
area = cv2.contourArea(contour)
color = (255,255,0)
if(area > 300):
x, y, w, h = cv2.boundingRect(contour)
img = cv2.rectangle(img, (x, y), (x + w, y + h), color, 3)
cv2.imshow('oxxostudio', img)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
微信扫码关注
抖音扫码关注