摘要:一般調高飽和度會降低中相對較低的數(shù)值,凸顯主要顏色的純度。對當前唯一的方法霍夫梯度法,它表示在檢測階段圓心的累加器閾值。第八個參數(shù),類型的有默認值,表示圓半徑的最小值。
目錄
?
?
?
為什么用HSV空間而不是RGB空間?
因為RGB通道并不能很好地反映出物體具體的顏色信息。
而HSV空間能夠非常直觀的表達色彩的明暗、色調、以及鮮艷程度,方便進行顏色之間的對比。
(RGB受光線影響很大,所以采取HSV)
這里用HSV的目的:得到合適的二值圖。
Hue(H):色調、色相(具體的顏色)
Saturation(S):飽和度、色彩純凈度
Value(V):明度
Hue范圍是[0,179],飽和范圍是[0,255],值范圍是[0,255]
(寫代碼的時候,犯了蠢錯誤:把3個都設成了179,發(fā)現(xiàn)死活調不對)
Hue:色相(具體的顏色)
明度:色彩的明亮程度,單通道亮度(并不等同于整體發(fā)光量)。
(明度越高越白,越低越黑,一般提高明度會同時提高R、G、B三通道的數(shù)值)
Saturation:飽和度、色彩純度(越低越灰,越高越純)??。
(一般調高飽和度會降低RGB中相對較低的數(shù)值,凸顯主要顏色的純度。 )
B站視頻講解:
短動畫慢語速1分鐘講清影視調色中色彩形成原理基礎——RGB與HSV
這次的初始化,除了電機、舵機、窗口等等的初始化,還有滑動條的初始設置。?
在創(chuàng)建滑動條之前:
1、需要設置窗口名稱,不然窗口都沒有,自然也就無法設置滑動條了。
2、創(chuàng)建回調函數(shù)。
def nothing(*arg): pass
def Trackbar_Init(): # 1 create windows cv2.namedWindow("h_binary") cv2.namedWindow("s_binary") cv2.namedWindow("v_binary")
# 2 Create Trackbar cv2.createTrackbar("hmin", "h_binary", 6, 179, nothing) cv2.createTrackbar("hmax", "h_binary", 26, 179, nothing) cv2.createTrackbar("smin", "s_binary", 110, 255, nothing) cv2.createTrackbar("smax", "s_binary", 255, 255, nothing) cv2.createTrackbar("vmin", "v_binary", 140, 255, nothing) cv2.createTrackbar("vmax", "v_binary", 255, 255, nothing) # 創(chuàng)建滑動條 滑動條值名稱 窗口名稱 滑動條值 滑動條閾值 回調函數(shù)
def Motor_Init(): global L_Motor, R_Motor L_Motor= GPIO.PWM(l_motor,100) R_Motor = GPIO.PWM(r_motor,100) L_Motor.start(0) R_Motor.start(0)def Direction_Init(): GPIO.setup(left_back,GPIO.OUT) GPIO.setup(left_front,GPIO.OUT) GPIO.setup(l_motor,GPIO.OUT) GPIO.setup(right_front,GPIO.OUT) GPIO.setup(right_back,GPIO.OUT) GPIO.setup(r_motor,GPIO.OUT)def Servo_Init(): global pwm_servo pwm_servo=Adafruit_PCA9685.PCA9685()def Trackbar_Init(): # 1 create windows cv2.namedWindow("h_binary") cv2.namedWindow("s_binary") cv2.namedWindow("v_binary") # 2 Create Trackbar cv2.createTrackbar("hmin", "h_binary", 6, 179, nothing) cv2.createTrackbar("hmax", "h_binary", 26, 179, nothing) cv2.createTrackbar("smin", "s_binary", 110, 255, nothing) cv2.createTrackbar("smax", "s_binary", 255, 255, nothing) cv2.createTrackbar("vmin", "v_binary", 140, 255, nothing) cv2.createTrackbar("vmax", "v_binary", 255, 255, nothing) # 創(chuàng)建滑動條 滑動條值名稱 窗口名稱 滑動條值 滑動條閾值 回調函數(shù)def Init(): GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) Direction_Init() Servo_Init() Motor_Init() Trackbar_Init()# 回調函數(shù)def nothing(*arg): pass
常規(guī)操作:向前、向后、向左、向右、停止。?
def Front(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,1) #left_front GPIO.output(left_back,0) #left_back R_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,1) #right_front GPIO.output(right_back,0) #right_back def Back(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,0) #left_front GPIO.output(left_back,1) #left_back R_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,0) #right_front GPIO.output(right_back,1) #right_backdef Left(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,0) #left_front GPIO.output(left_back,1) #left_back R_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,1) #right_front GPIO.output(right_back,0) #right_backdef Right(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,1) #left_front GPIO.output(left_back,0) #left_back R_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,0) #right_front GPIO.output(right_back,1) #right_backdef Stop(): L_Motor.ChangeDutyCycle(0) GPIO.output(left_front,0) #left_front GPIO.output(left_back,0) #left_back R_Motor.ChangeDutyCycle(0) GPIO.output(right_front,0) #right_front GPIO.output(right_back,0) #right_back
def set_servo_angle(channel,angle): angle=4096*((angle*11)+500)/20000 pwm_servo.set_pwm_freq(50) #frequency==50Hz (servo) pwm_servo.set_pwm(channel,0,int(angle))
set_servo_angle(4, 110) #top servo lengthwise #0:back 180:front set_servo_angle(5, 90) #bottom servo crosswise #0:left 180:right
# 1 get trackbar"s value hmin = cv2.getTrackbarPos("hmin", "h_binary") hmax = cv2.getTrackbarPos("hmax", "h_binary") smin = cv2.getTrackbarPos("smin", "s_binary") smax = cv2.getTrackbarPos("smax", "s_binary") vmin = cv2.getTrackbarPos("vmin", "v_binary") vmax = cv2.getTrackbarPos("vmax", "v_binary")
# 2 to HSV hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) cv2.imshow("hsv", hsv) h, s, v = cv2.split(hsv)
?
分別對H、S、V三個道閾值處理:
# 3 set threshold (binary image) # if value in (min, max):white; otherwise:black h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax)) s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax)) v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax))# 5 Show cv2.imshow("h_binary", h_binary) cv2.imshow("s_binary", s_binary) cv2.imshow("v_binary", v_binary)
對H、S、V三個通道與操作。(H&&S&&V)
# 4 get binary binary = cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary))
# 在HSV色彩空間下得到二值圖def Get_HSV(image): # 1 get trackbar"s value hmin = cv2.getTrackbarPos("hmin", "h_binary") hmax = cv2.getTrackbarPos("hmax", "h_binary") smin = cv2.getTrackbarPos("smin", "s_binary") smax = cv2.getTrackbarPos("smax", "s_binary") vmin = cv2.getTrackbarPos("vmin", "v_binary") vmax = cv2.getTrackbarPos("vmax", "v_binary") # 2 to HSV hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) cv2.imshow("hsv", hsv) h, s, v = cv2.split(hsv) # 3 set threshold (binary image) # if value in (min, max):white; otherwise:black h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax)) s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax)) v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax)) # 4 get binary(對H、S、V三個通道分別與操作) binary = cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary)) # 5 Show cv2.imshow("h_binary", h_binary) cv2.imshow("s_binary", s_binary) cv2.imshow("v_binary", v_binary) cv2.imshow("binary", binary) return binary
# 1 Capture the frames ret, frame = camera.read() image = frame cv2.imshow("frame", frame)
(Get_HSV(frame)其實就是步驟四的HSV處理)
# 2 get HSV binary = Get_HSV(frame)
# 3 Gausi blur blur = cv2.GaussianBlur(binary,(9,9),0)
# 4 Open kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9)) Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel) cv2.imshow("Open",Open)
# 5 Close Close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel) cv2.imshow("Close",Close)
?
霍夫變換圓檢測是基于圖像梯度實現(xiàn):
圓心檢測的原理︰圓心是圓周法線的交匯處,設置一個閾值,在某點的相交的直線的條數(shù)大于這個閾值就認為該交匯點為圓心。
圓半徑確定原理:圓心到圓周上的距離〔半徑)是相同的,設置一個閾值,只要相同距離的數(shù)量大于該閾值,就認為該距離是該圓心的半徑。
API
def HoughCircles(image: Any, method: Any, dp: Any, minDist: Any, circles: Any = None, param1: Any = None, param2: Any = None, minRadius: Any = None, maxRadius: Any = None) -> None
參數(shù):?
- 第二個參數(shù),int類型的method,即使用的檢測方法,目前OpenCV中就霍夫梯度法一種可以使用,它的標識符為CV_HOUGH_GRADIENT,在此參數(shù)處填這個標識符即可。
- 第三個參數(shù),double類型的dp,用來檢測圓心的累加器圖像的分辨率于輸入圖像之比的倒數(shù),且此參數(shù)允許創(chuàng)建一個比輸入圖像分辨率低的累加器。上述文字不好理解的話,來看例子吧。例如,如果dp= 1時,累加器和輸入圖像具有相同的分辨率。如果dp=2,累加器便有輸入圖像一半那么大的寬度和高度。
- 第四個參數(shù),double類型的minDist,為霍夫變換檢測到的圓的圓心之間的最小距離,即讓我們的算法能明顯區(qū)分的兩個不同圓之間的最小距離。這個參數(shù)如果太小的話,多個相鄰的圓可能被錯誤地檢測成了一個重合的圓。反之,這個參數(shù)設置太大的話,某些圓就不能被檢測出來了。
- 第五個參數(shù),InputArray類型的circles,經(jīng)過調用HoughCircles函數(shù)后此參數(shù)存儲了檢測到的圓的輸出矢量,每個矢量由包含了3個元素的浮點矢量(x, y, radius)表示。
- 第六個參數(shù),double類型的param1,有默認值100。它是第三個參數(shù)method設置的檢測方法的對應的參數(shù)。對當前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示傳遞給canny邊緣檢測算子的高閾值,而低閾值為高閾值的一半。
- 第七個參數(shù),double類型的param2,也有默認值100。它是第三個參數(shù)method設置的檢測方法的對應的參數(shù)。對當前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在檢測階段圓心的累加器閾值。它越小的話,就可以檢測到更多根本不存在的圓,而它越大的話,能通過檢測的圓就更加接近完美的圓形了。
- 第八個參數(shù),int類型的minRadius,有默認值0,表示圓半徑的最小值。
- 第九個參數(shù),int類型的maxRadius,也有默認值0,表示圓半徑的最大值。
# 6 Hough Circle detect circles = cv2.HoughCircles(Close,cv2.HOUGH_GRADIENT,2,120,param1=120,param2=20,minRadius=20,maxRadius=0) # param2:決定圓能否被檢測到(越少越容易檢測到圓,但相應的也更容易出錯)
# 1 獲取圓的圓心和半徑 x, y, r = int(circles[0][0][0]),int(circles[0][0][1]),int(circles[0][0][2]) print(x, y, r)
# 2 畫圓 cv2.circle(image, (x, y), r, (255,0,255),5) cv2.imshow("image", image)
# 圖像處理def Image_Processing(): global h, s, v # 1 Capture the frames ret, frame = camera.read() image = frame cv2.imshow("frame", frame) # 2 get HSV binary = Get_HSV(frame) # 3 Gausi blur blur = cv2.GaussianBlur(binary,(9,9),0) # 4 Open kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9)) Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel) cv2.imshow("Open",Open) # 5 Close Close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel) cv2.imshow("Close",Close) # 6 Hough Circle detect circles = cv2.HoughCircles(Close,cv2.HOUGH_GRADIENT,2,120,param1=120,param2=20,minRadius=20,maxRadius=0) # param2:決定圓能否被檢測到(越少越容易檢測到圓,但相應的也更容易出錯) # judge if circles is exist if circles is not None: # 1 獲取圓的圓心和半徑 x, y, r = int(circles[0][0][0]),int(circles[0][0][1]),int(circles[0][0][2]) print(x, y, r) # 2 畫圓 cv2.circle(image, (x, y), r, (255,0,255),5) cv2.imshow("image", image) else: (x,y),r = (0,0), 0 return (x,y), r
根據(jù)檢測到的圓,獲取到的坐標、半徑,進行運動控制。
這里可以做到跟蹤小球,前進和后退相配合,“敵進我退,敵退我進”。
# 運動控制(這里可以做到跟蹤小球,前景和后退相配合,“敵進我退,敵退我進”)def Move((x,y), r): low_xlimit = width/4 high_xlimit = 0.75 * width #low_ylimit = 3/4 * height ylimit = 0.75 * height print(high_xlimit, ylimit) # 沒檢測到,停止不動 if x==0: Stop() # 檢測到在圖片0.75以上的區(qū)域(距離正常) elif x>low_xlimit and xlow_xlimit and x=ylimit: Back(60) # 在左0.25區(qū)域,向左跟蹤 elif xhigh_xlimit: Right(60)
#Ball Tracking(HSV)import RPi.GPIO as GPIOimport timeimport Adafruit_PCA9685import numpy as npimport cv2#set capture windowwidth, height = 320, 240camera = cv2.VideoCapture(0)camera.set(3,width) camera.set(4,height) l_motor = 18left_front = 22left_back = 27r_motor = 23right_front = 25right_back = 24def Motor_Init(): global L_Motor, R_Motor L_Motor= GPIO.PWM(l_motor,100) R_Motor = GPIO.PWM(r_motor,100) L_Motor.start(0) R_Motor.start(0)def Direction_Init(): GPIO.setup(left_back,GPIO.OUT) GPIO.setup(left_front,GPIO.OUT) GPIO.setup(l_motor,GPIO.OUT) GPIO.setup(right_front,GPIO.OUT) GPIO.setup(right_back,GPIO.OUT) GPIO.setup(r_motor,GPIO.OUT)def Servo_Init(): global pwm_servo pwm_servo=Adafruit_PCA9685.PCA9685()def Trackbar_Init(): # 1 create windows cv2.namedWindow("h_binary") cv2.namedWindow("s_binary") cv2.namedWindow("v_binary") # 2 Create Trackbar cv2.createTrackbar("hmin", "h_binary", 6, 179, nothing) cv2.createTrackbar("hmax", "h_binary", 26, 179, nothing) cv2.createTrackbar("smin", "s_binary", 110, 255, nothing) cv2.createTrackbar("smax", "s_binary", 255, 255, nothing) cv2.createTrackbar("vmin", "v_binary", 140, 255, nothing) cv2.createTrackbar("vmax", "v_binary", 255, 255, nothing) # 創(chuàng)建滑動條 滑動條值名稱 窗口名稱 滑動條值 滑動條閾值 回調函數(shù)def Init(): GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) Direction_Init() Servo_Init() Motor_Init() Trackbar_Init()def Front(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,1) #left_front GPIO.output(left_back,0) #left_back R_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,1) #right_front GPIO.output(right_back,0) #right_back def Back(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,0) #left_front GPIO.output(left_back,1) #left_back R_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,0) #right_front GPIO.output(right_back,1) #right_backdef Left(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,0) #left_front GPIO.output(left_back,1) #left_back R_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,1) #right_front GPIO.output(right_back,0) #right_backdef Right(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,1) #left_front GPIO.output(left_back,0) #left_back R_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,0) #right_front GPIO.output(right_back,1) #right_backdef Stop(): L_Motor.ChangeDutyCycle(0) GPIO.output(left_front,0) #left_front GPIO.output(left_back,0) #left_back R_Motor.ChangeDutyCycle(0) GPIO.output(right_front,0) #right_front GPIO.output(right_back,0) #right_backdef set_servo_angle(channel,angle): angle=4096*((angle*11)+500)/20000 pwm_servo.set_pwm_freq(50) #frequency==50Hz (servo) pwm_servo.set_pwm(channel,0,int(angle))# 回調函數(shù)def nothing(*arg): pass# 在HSV色彩空間下得到二值圖def Get_HSV(image): # 1 get trackbar"s value hmin = cv2.getTrackbarPos("hmin", "h_binary") hmax = cv2.getTrackbarPos("hmax", "h_binary") smin = cv2.getTrackbarPos("smin", "s_binary") smax = cv2.getTrackbarPos("smax", "s_binary") vmin = cv2.getTrackbarPos("vmin", "v_binary") vmax = cv2.getTrackbarPos("vmax", "v_binary") # 2 to HSV hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) cv2.imshow("hsv", hsv) h, s, v = cv2.split(hsv) # 3 set threshold (binary image) # if value in (min, max):white; otherwise:black h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax)) s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax)) v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax)) # 4 get binary(對H、S、V三個通道分別與操作) binary = cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary)) # 5 Show cv2.imshow("h_binary", h_binary) cv2.imshow("s_binary", s_binary) cv2.imshow("v_binary", v_binary) cv2.imshow("binary", binary) return binary# 圖像處理def Image_Processing(): global h, s, v # 1 Capture the frames ret, frame = camera.read() image = frame cv2.imshow("frame", frame) # 2 get HSV binary = Get_HSV(frame) # 3 Gausi blur blur = cv2.GaussianBlur(binary,(9,9),0) # 4 Open kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9)) Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel) cv2.imshow("Open",Open) # 5 Close Close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel) cv2.imshow("Close",Close) # 6 Hough Circle detect circles = cv2.HoughCircles(Close,cv2.HOUGH_GRADIENT,2,120,param1=120,param2=20,minRadius=20,maxRadius=0) # param2:決定圓能否被檢測到(越少越容易檢測到圓,但相應的也更容易出錯) # judge if circles is exist if circles is not None: # 1 獲取圓的圓心和半徑 x, y, r = int(circles[0][0][0]),int(circles[0][0][1]),int(circles[0][0][2]) print(x, y, r) # 2 畫圓 cv2.circle(image, (x, y), r, (255,0,255),5) cv2.imshow("image", image) else: (x,y),r = (0,0), 0 return (x,y), r# 運動控制(這里可以做到跟蹤小球,前景和后退相配合,“敵進我退,敵退我進”)def Move((x,y), r): low_xlimit = width/4 high_xlimit = 0.75 * width #low_ylimit = 3/4 * height ylimit = 0.75 * height print(high_xlimit, ylimit) # 沒檢測到,停止不動 if x==0: Stop() # 檢測到在圖片0.75以上的區(qū)域(距離正常) elif x>low_xlimit and xlow_xlimit and x=ylimit: Back(60) # 在左0.25區(qū)域,向左跟蹤 elif xhigh_xlimit: Right(60) if __name__ == "__main__": Init() set_servo_angle(4, 110) #top servo lengthwise #0:back 180:front set_servo_angle(5, 90) #bottom servo crosswise #0:left 180:right while 1: # 1 Image Process (x,y), r = Image_Processing() # 2 Move Move((x,y), r) # must include this codes(otherwise you can"t open camera successfully) if cv2.waitKey(1) & 0xFF == ord("q"): Stop() GPIO.cleanup() break
這里的HSV是根據(jù)我自己當前的情況調節(jié)的,更改場景以后可能需要重新調節(jié)H、S、V三通道的閾值(max && min)
基礎的視覺檢測+運動,沒有太多的運動控制算法(PID等等都沒有涉及到)。有好的想法和建議歡迎交流討論,Thanks?(?ω?)?
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/119986.html
摘要:在我已經(jīng)制作完成一輛可以運行的遙控車時,公司發(fā)布了一個自駕車項目,來展示自動駕駛汽車的工作原理。需要注意的是,這里用的都是語言而非,其主要原因有兩個一方面,近來似乎已成為運用機器學習技術時實際使用的語言。 最近,Mapbox 的 Android 工程師 Antonio 使用計算機視覺和機器學習技術,為他的女兒 Violeta 重新制作了一臺遙控車。接下來我們看看 Antonio 是如何...
閱讀 3298·2023-04-26 02:40
閱讀 4650·2021-09-22 15:22
閱讀 1591·2021-09-22 10:02
閱讀 3485·2021-08-11 10:23
閱讀 1393·2019-08-30 15:55
閱讀 2495·2019-08-30 12:48
閱讀 589·2019-08-30 11:04
閱讀 704·2019-08-29 16:29