以及流量問題 ,我只希望能夠用網頁觀看,在多個不同的裝置(PC ,平板,手機)。當然
也會有些許的延遲(幾秒鐘),接下來也會繼續嘗試用其他的方式,讓影像不要Delay那麼嚴
重,在此先做個筆記。
一、環境說明
(一)Raspberry Pi B+
os 為 wheezy ,使用python 2.7 搭配OpenCV 2.4.10 。
網卡 Edimax EW-7811Un
(二)WebCam 為Logitech C170
(三)WebSocket Server 由 GlassFish 4.1 + Java 實作。
(四)觀看端由 Html + JavaScript 實作。
二、整體架構圖
三、架構說明:
(一) 輸入端由OpenCV開啟Webcam取得影像 ,以Jpeg作為壓縮格式後,轉成
Base64字串,由WebSocket client端發送(為了節省頻寬,目前品質壓到30%)。
(二) websocket 由python 的websocket-client package ,安裝方式,請參考
[Python] 用Python 做 WebSocket Client
(三)WebSocket Server在onMessage (收到圖片時)會傳給所有連線中的Client,
除了發送圖片的Raspberry Pi。
(四) 觀看端在網頁載入完成後,會連線到WebSocket Server,等待接收訊息。當觀看端的onMessage事件發生時,會解碼收到的圖片訊息,並且顯示在網頁上,並更新時間。
四、程式碼
(一) Raspberry Pi 上的發送圖片程式 Webcam.py
import sys import cv import cv2 import numpy import base64 from websocket import create_connection #connection to websocket server ws = create_connection('ws://192.168.0.101:8080/DemoEchoServer/echo') #JPEG encode_param encode_param=[int(cv2.IMWRITE_JPEG_QUALITY),30] #select first VideoCapture capture = cv2.VideoCapture(0) #set WebCame Frame Width capture.set(3,640) #set WebCam Frame Height capture.set(4,480) #defind function def repeat(): # Capture frame-by-frame ret, frame = capture.read() #encode to jpg if (type(ret)!=type(None)): result,imgencode=cv2.imencode('.jpg',frame,encode_param ) # to numpy array data = numpy.array(imgencode) # tostring stringData = data.tostring() #encode to base64 encoded = base64.b64encode(stringData) #send ws.send(encoded) #sleep 60millisecond cv2.waitKey(60) while True: repeat()(二)WebSocket Server EchoServer.java
import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.websocket.EndpointConfig; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/echo") public class EchoServer { //用來存放Session的集合 private static final Set(三)觀看端 display.htmlsessions = Collections .synchronizedSet(new HashSet ()); @OnOpen public void onOpen(Session session,EndpointConfig config) { session.setMaxBinaryMessageBufferSize(1024*1024); session.setMaxTextMessageBufferSize(1024*1024); System.out.println(session.getId()+" open"); //把session加入集合中 sessions.add(session); } @OnClose public void onClose(Session session) { System.out.println(session.getId()+" close"); //把session從集合中移除 sessions.remove(session); } @OnMessage public void onMessage(String message , Session session) throws IOException { try { //sent Image for(Session session2 : sessions) { if (!session2.getId().equals(session.getId())) { session2.getAsyncRemote().sendText(message); } //session2.getBasicRemote().sendText(message); } catch (Exception e) { e.printStackTrace();; } } @OnError public void onError(Throwable t) { t.printStackTrace(); } }
1: <html>
2: <head>
3: <title>TODO supply a title</title>
4: <meta charset="UTF-8">
5: <meta name="viewport" content="width=device-width, initial-scale=1.0">
6: <script language="javascript" type="text/javascript">
7: //echo server的網址
8: var wsUri = "ws://192.168.0.101:8080/DemoEchoServer/echo";
9: //定義元件
10: var outputElement;
11: //初始化function
12: function init()
13: {
14: outputElement = document.getElementById("output");
15: live();
16: }
17: //執行websocket
18: function live() {
19: //連線到echo server
20: websocket = new WebSocket(wsUri);
21: //連線成功要執行的function
22: websocket.onopen = function (evt) {
23: onOpen(evt)
24: };
25: //連結結速要執行的function
26: websocket.onclose = function (evt) {
27: onClose(evt)
28: };
29: //收到echo server傳來的訊息要做的function
30: websocket.onmessage = function (evt) {
31: onMessage(evt)
32: };
33: //當發生錯誤時要執行的function
34: websocket.onerror = function (evt) {
35: onError(evt)
36: };
37: }
38: //連線成功後,在output Element顯示時間
39: function onOpen(evt) {
40: var currentdate = new Date();
41: var datetime = "Last Sync: " + currentdate.getDate() + "/"
42: + (currentdate.getMonth()+1) + "/"
43: + currentdate.getFullYear() + " @ "
44: + currentdate.getHours() + ":"
45: + currentdate.getMinutes() + ":"
46: + currentdate.getSeconds();
47: writeToScreen(datetime+" onOpen")
48: }
49: //當中斷連線時輸出訊息在outputElement上
50: function onClose(evt) {
51: var currentdate = new Date();
52: var datetime = "Last Sync: " + currentdate.getDate() + "/"
53: + (currentdate.getMonth()+1) + "/"
54: + currentdate.getFullYear() + " @ "
55: + currentdate.getHours() + ":"
56: + currentdate.getMinutes() + ":"
57: + currentdate.getSeconds();
58: writeToScreen(datetime+" onClose")
59: }
60: //收到訊息時顯示在螢幕上, 並且解密base64圖片資料
61: function onMessage(evt) {
62: var currentdate = new Date();
63: var datetime = "Last Sync: " + currentdate.getDate() + "/"
64: + (currentdate.getMonth()+1) + "/"
65: + currentdate.getFullYear() + " @ "
66: + currentdate.getHours() + ":"
67: + currentdate.getMinutes() + ":"
68: + currentdate.getSeconds()+" "
69: + currentdate.getTime();
70: writeToScreen(datetime+" onMessage")
71: document.getElementById("ItemPreview").src = "data:image/png;base64," + evt.data;
72: }
73: //發生錯誤時顯示訊息在outputElement上
74: function onError(evt) {
75: var currentdate = new Date();
76: var datetime = "Last Sync: " + currentdate.getDate() + "/"
77: + (currentdate.getMonth()+1) + "/"
78: + currentdate.getFullYear() + " @ "
79: + currentdate.getHours() + ":"
80: + currentdate.getMinutes() + ":"
81: + currentdate.getSeconds();
82: writeToScreen(datetime+" onError")
83: }
84: //發送訊息
85: function doSend(message) {
86: writeToScreen("己傳送 " + message);
87: websocket.send(message);
88: }
89: //輸出訊息在outputElement上
90: function writeToScreen(message) {
91: var pre = document.getElementById("myp");
92: pre.style.wordWrap = "break-word";
93: pre.innerHTML = message;
94: output.appendChild(pre);
95: }
96: //加入事件,當載入完成後執行init()
97: window.addEventListener("load", init, false);
98: </script>
99: </head>
100: <body>
101: <div id="output"> <p id="myp"></div>
102: <div>
103: <img id="ItemPreview" src="" />
104: </div>
105: </body>
106: </html>
五、執行程式
(一)先開啟WebSocket Server。
(二)在Raspberry Pi上輸入 python Webcam.py
(三)開啟display.html觀看。
六、執行結果
七、待解決問題
(一) 畫面延遲問題, 以目前看來至少會慢個約5秒,慢的主要原因是資料量,每一次
都發送一張完整的圖片 (640 *480) ,樹梅派只有單核心, 要一直取影像再壓成JPEG傳送,
可能會有點吃力。
(二) 透過websocket Server 再做一次分派,好處是可以讓多個裝置同時間做觀看。雖然每一個之間還是會有Delay。把傳送影像用的session2.getBasicRemote().sendText(message); 改成用session2.getAsyncRemote().sendText(message); 效能看起來些許的提昇。
沒有留言:
張貼留言