1. 程式人生 > >Raspberry Zero 上實現平滑視訊圖傳

Raspberry Zero 上實現平滑視訊圖傳

在某些應用場合我們可能需要通過一個裝置通過WIFI將影象傳到其它的機器進行顯示或者圖形分析,那怎麼可以低成本地實現呢?其實很簡單,我們只需要一塊 Raspberry Zero W 和一個RPI 攝像頭就行了,兩個加起來成本也只不過150左右。 這個組合不單單只是實現一個圖傳,最重要的是Raspberry Zero上執行的是Linux,它幾乎可以執行我們各種各樣的程式碼。將它作為一個小型的程式設計平臺也未嘗不可。 由於硬體部分太簡單了沒有必要浪費篇幅過多地講述,那就直接進入軟體部分。實現圖傳必然有兩端:傳送端與接收端。 傳送端 - 運行於Raspberry Zero通過OpenCV直接讀取視訊流,然後將資料寫入到Socket中傳送出去。如果實時傳遞的話可能會由於網路通訊等的各種原因導致丟包,或者說由於失去有效的網路連線而引發程式的異常,為了防止這種情況出現我使用了pyzmq這個包,傳送端也是訊息的釋出方,將Socket的處理放到訊息佇列中,當訂閱方從訊息佇列中讀取資訊時就從Socket中拿出排隊的資料,這樣處理起來就平滑多了。 接收端 - 可運行於所有能執行python環境的平臺,它只負責從Socket中讀取流資料然後通過OpenCV顯示到視窗中,也是訊息的訂閱方。 > Python 中的Socket使用可以說是在眾多語言中最簡單的,關於Socket的知識在此不多講,不懂的朋友可以先去找些資料先學習一下。 釋出方與訂閱方都需要安裝pyzmq: ## 安裝 pyzmq ``` $ pip install pyzmq ``` > 如果在樹莓上安裝pyzmq會非常慢可能要等個10來20分鐘的,不要以為你的樹莓掛了只是Raspberry Zero效能實在太低要進行本機編譯實在是一件非常痛苦之事。 ## 釋出方 - Streamer Raspberry Zero 端的釋出方的程式碼如下: ```python import base64 import cv2 import zmq context = zmq.Context() footage_socket = context.socket(zmq.PUB) footage_socket.connect('tcp://*:5555') camera = cv2.VideoCapture(0) while True: try: success, frame = camera.read() if not success: break; frame = cv2.resize(frame, (640, 480)) # 將每一幀的畫面大小設定為640x480 encoded, buffer = cv2.imencode('.jpg', frame) jpg_as_text = base64.b64encode(buffer) footage_socket.send(jpg_as_text) except KeyboardInterrupt: camera.release() cv2.destroyAllWindows() break ``` 原理非常簡單就是將每一幀的畫面先轉成base64的編碼格式以字元流的方式寫入到socket中傳出去。 執行程式碼: ``` pi $ python streamer.py ``` ## 訂閱方 Viewer ```python import cv2 import zmq import base64 import numpy as np context = zmq.Context() footage_socket = context.socket(zmq.SUB) footage_socket.bind('tcp://10.0.0.25:5555') # 這裡需要指定Steamer的發地址 footage_socket.setsockopt_string(zmq.SUBSCRIBE, np.unicode('')) while True: try: source = footage_socket.recv_string() img = base64.b64decode(source) npimg = np.fromstring(img, dtype=np.uint8) frame = cv2.imdecode(npimg, 1) frame = cv2.flip(frame, flipCode=-1) cv2.imshow("Stream", frame) cv2.waitKey(1) except KeyboardInterrupt: cv2.destroyAllWindows() break ```