樹莓派python OpenCV捕獲顏色塊並通過串列埠返回座標資訊
阿新 • • 發佈:2018-11-20
樹莓派python OpenCV捕獲顏色塊並通過串列埠返回座標資訊
樹莓派python OpenCV捕獲顏色塊並通過串列埠返回座標資訊
介紹
該程式碼起初是用在無人機尋找彩色物體定位上面,在無人機上面掛載樹莓派,藉助樹莓派的高運算能力和可拓展性來彌補飛控的不足。在攝像頭通過USB介面掛載到樹莓派上,藉助OpenCV來進行影象處理,並通過串列埠傳送彩色物體位置資訊給飛控,以實現無人機的定位。實則程式碼可以用在其他任何場景。
開發環境
在Windows下通過pycharm pro通過SSH連線樹莓派進行傳輸資料和除錯程式。這樣的好處是具有程式碼補全和提示功能,提高效率。
在pycharm pro工程裡的setting找到project Interpreter選擇add如圖:
然後選擇SSH Interpreter後輸入Host 和 Username 點選下一步按照提示即可連線上樹莓派。
在連線上樹莓派後,它會從樹莓派上下載python虛擬環境,需要一定的時間,下載完成後就可以像在樹莓派上編寫程式了。pyCharm很強大,在同步過後有很多實用 的功能值得去探索。包括程式碼同步,程式碼對比等等。
遠端除錯樹莓派(PyCharm實現)
思路
首先從攝像頭獲取影象>擇攝像頭中心為ROI>縮小影象降低處理時間>轉換為hsv顏色空間>提取出指定顏色(這裡為藍色)>灰度處理>二值化>膨脹消除內部干擾>找出重心>串列埠編碼>傳送座標。
程式碼
串列埠部分
# -*- coding:utf-8 -*-
import serial
import time
# 串列埠配置
def serialConfig(port, buat):
ser = serial.Serial(port, buat)
ser.flushInput()
ser. flushOutput()
if ser.isOpen() is False:
ser.open()
return True, ser
return True, ser
if __name__ == "__main__":
isOpen, Ser = serialConfig("/dev/ttyAMA0", 9600)
if isOpen is True:
try:
while True:
size = Ser.inWaiting() # 獲得緩衝區字元
if size != 0:
response = Ser.read(size) # 讀取內容並顯示
print response
Ser.write(bytes(response))
Ser.flushOutput()
Ser.flushInput() # 清空接收快取區
time.sleep(0.2) # 軟體延時
except KeyboardInterrupt:
Ser.close()
else:
print("port false")
影象處理部分
# -*- coding:utf-8 -*-
import cv2
import tkinter
import numpy as np
import time
import SerialPort as sp
win = tkinter.Tk(':0.0') # 指定輸出裝置,否則報錯 必須加這一句 不然找不到顯示裝置將報錯
cv2.setUseOptimized(True) # 優化opencv
# 藍色閾值
lower_blue = np.array([50, 50, 50])
upper_blue = np.array([130, 255, 255])
# 可以這樣知道想要查詢顏色對應的hsv空間
# green = np.uint8([[[0, 255, 0]]])
# hsv_green = cv2.cvtColor(green, cv2.COLOR_BGR2HSV)
lastCX = 0
lastCY = 0
cx = 0
cy = 0
def image_process(frame):
global cx, cy
global lastCY, lastCX
times = cv2.getTickCount()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_blue, upper_blue) # 得到藍色掩模
blue = cv2.bitwise_and(frame, frame, mask=mask) # 位與
gray = cv2.cvtColor(blue, cv2.COLOR_BGR2GRAY) # 灰度處理
# cv2.medianBlur(gray, 3) # 中值濾波
# bin_blue = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
temp, bin_blue = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY) # 簡單二值化
kernel = np.ones((10, 10), np.uint8) # 膨脹將要用到的核心
bin_blue = cv2.erode(bin_blue, kernel, iterations=1) # 膨脹以消除內部干擾
# 獲取影象輪廓
dst_image, contours, hierarchy = cv2.findContours(bin_blue, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
frame = cv2.drawContours(frame, contours, -1, (0, 255, 0), 2) # 原始影象上畫出輪廓
m = cv2.moments(dst_image) # 尋找輪廓的距
lastCX = cx
lastCY = cy
try: #防止除零發生異常
cx = int(m['m10'] / m['m00']) # 計算重心
cy = int(m['m01'] / m['m00'])
cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1) # 標記重心
cx = cx - 120
cy = cy - 120
except ZeroDivisionError:
cy = lastCY
cx = lastCX
cv2.circle(frame, (cx+120, cy+120), 5, (0, 0, 255), -1) # 標記重心
if cx > 120:
cx = 120
if cy >120:
cy = 120
if cx < -120:
cx = -120
if cy < -120:
cy = -120
timed = cv2.getTickCount() # 計算處理一幀的時間
time = (timed - times) / cv2.getTickFrequency()
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(frame, str(time)[: 5], (50, 50), font, 1, (255, 0, 0), 2)
# print time
return cx, cy, frame, dst_image
def serialSend(SerPort, data, flag = 0):
SerPort.write(chr(int( 0xaa ))) # 幀頭
for i in data:
SerPort.write(chr(int(i)))
SerPort.write(chr(int(flag)))
SerPort.write(chr(int( 0xab ))) # 幀尾
if __name__ == "__main__":
isOpened, ser = sp.serialConfig('/dev/ttyAMA0', 115200) # 配置串列埠
cap = cv2.VideoCapture(0)
while cap.isOpened():
ts = cv2.getTickCount()
ret, video = cap.read()
videoROI = video[ :480, 80:560] # 取影象中心為ROI
ROI = cv2.resize(videoROI, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_CUBIC) # 縮小影象以提高運算速率
x, y, image, dstImage = image_process(ROI) # 影象處理
finalX = x / 2 + 60# s縮小到<256 方便串列埠傳送
finalY = y / 2 + 60 # 由於串列埠不能傳送負數 所以這樣處理後資料範圍被限定在0-255 串口才能正常傳送 ,主要是python運用不是很熟練
data=[finalX,finalY]
serialSend(ser, data)
# print("imageX:{0} imageY:{1} finalX:{2} finalY:{3}".format(x, y, finalX, finalY))
#
# 放大以方便檢視
# image = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
# dstImage = cv2.resize(dstImage, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
#
# cv2.imshow('image', image)
# cv2.imshow('dst_image', dstImage)
#
# k = cv2.waitKey(1) & 0xff
# if k == 27:
# break
# if k == ord('s'):
# print(video.shape)
te = cv2.getTickCount()
tt = (te - ts) / cv2.getTickFrequency()
print(tt)
ser.close()
cap.release()
cv2.destroyAllWindows()
後續處理
飛機接收到座標資訊後就可以進行定位了。