添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

建立 MJPEG 即時影像串流

這篇教學會介紹 False 與 mjpeg-streamer 兩種建立 MJPEG 即時影像串流的方法,讓電腦攝影機變成遠端攝影機,使用類似 CCTV 監視器影像的方式傳送 MJPEG 影像串流。

延伸參考: 爬取交通部 CCTV 即時影像

快速導覽:

  • 認識 MJPEG 影像串流
  • 使用 Flask
  • 使用 mjpeg-streamer
  • 認識 MJPEG 影像串流

    MJPEG 或 Motion JPEG ( Motion Joint Photographic Experts Group ) 是一種影像壓縮格式, 可以將串流影片的每一幀壓縮為 JPEG 圖片進行傳送,這種傳輸模式不僅大幅減少了資料傳輸量,更提高了解壓縮的效率 ,這種影像串流的技術常用於物聯網設備的攝影機、監視器、微控制器等應用情境,由於是傳輸 JPEG,因此傳輸過程中「無法傳輸聲音」並會產生「失真壓縮」。

    MJPEG 的傳輸屬於「多部分回應 Multipart Responses」,過程中每個影格都被獨立壓縮成 JPEG 格並以一個獨立部分 ( part ) 的形式存在,Multipart Responses 使用 Content-Type 來傳輸資料,在 MJPEG 的情況下,Content-Type 通常是「 multipart/x-mixed-replace 」,表示每個部分之間的分隔符號,用來區分不同的影格。

    MJPEG 影像串流的結構如下:Content-Type 標頭為「 multipart/x-mixed-replace 」並定義邊界字串 boundary 為「 frame 」,接著每個部分以「 --frame 」為前綴,並具有自己的 Content-Type 標頭。

    HTTP/1.1 200 OK
    Content-Type: multipart/x-mixed-replace; boundary=frame
    --frame
    Content-Type: image/jpeg
    <jpeg data here>
    --frame
    Content-Type: image/jpeg
    <jpeg data here>
    

    用 Flask

    了解 MJPEG 影像串流的結構之後,就能透過 Flask 搭配 OpenCV,建立可以即時傳輸串流影像的簡單網頁,首先安裝 Flask 和 OpenCV。

    pip install flask, opencv-python
    

    使用下方的程式碼,使用 Flask 建立一個簡單的網頁,當網頁開啟後,裡面會放入一張路徑為「/mjpeg」的圖片,讀取圖片時,會發送另外一個 http request 執行 mjpeg 函式,函式內容根據上述的「多部分回應 Multipart Responses」結構,發送 MJPEG 的內容。

    from flask import Flask, Response
    import cv2, time
    app = Flask(__name__)
    def gen_frames():
        cap = cv2.VideoCapture(0)               # 開啟鏡頭
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)  # 設定影像寬度 320
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240) # 設定影像高度 240
        while True:
            ret, frame = cap.read()  # 讀取影像
            if not ret:
                break
            else:
                jpg = cv2.imencode('.jpg', frame)[1]  # 轉換成 jpg 陣列
                mjpeg = jpg.tobytes()                  # 轉換成傳輸 bytes
                # 使用 generator 的方式,每次傳輸特定每個部分
                yield (b'--frame\r\n'
                      b'Content-Type: image/jpeg\r\n\r\n' + mjpeg + b'\r\n')
    @app.route('/mjpeg')
    def mjpeg():
        # 回傳 Multipart Responses
        return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
    @app.route('/')
    def index():
        return '''<h1>OXXO.STUDIO 影像串流</h1>
        <img src="/mjpeg">'''
    if __name__ == '__main__':
        app.run("127.0.0.1", port=5000, threaded=True)
    

    因此當網頁開啟後,網頁中所載入的就會自動根據 MJPEG 發送對應的串流,不斷顯示每個部分的影像。

    在網頁上點擊右鍵查看網頁原始碼,可以看出顯示串流的 HTML 為一個 img 標籤。

    由於使用 Flask 是產生「內網」的網址,如果要透過外部連接查看影像串流,必須使用對外的 IP 或固定網址,也可以串接 nrgok 產生對外網址,詳細參考:使用 ngrok 服務

    使用 mjpeg-streamer

    除了自己使用 Flask 架設網頁伺服器,也可以使用 mjpeg-streamer 套件架設 MJPEG 的伺服器 ( 常見於樹莓派或一些單晶片裝置使用,詳細參考「mjpeg-streamer,首先輸入指令安裝 opencv-python mjpeg-streamer 套件。

    pip install opencv-python mjpeg-streamer --prefer-binary
    

    執行下方程式碼,就會在本機環境建立一個 MJPEG 專屬的伺服器,接著就能使用「http://xxx.xxx.xxx.xxx/mjpeg」開啟串流圖片,但這個做法只能產生串流影像,如果需要使用自己的網頁樣式呈現串流,則必須使用 Flask 的做法。

    import cv2
    from mjpeg_streamer import MjpegServer, Stream
    cap = cv2.VideoCapture(0)
    # 設定串流資訊,mjpeg 為圖片網址 size 尺寸 quality 品質 fps 幀率 ( 每秒幾格 )
    stream = Stream("mjpeg", size=(640, 480), quality=50, fps=30)
    server = MjpegServer("127.0.0.1", 5000) # 設定伺服器資訊
    server.add_stream(stream)  # 添加串流
    server.start()             # 啟動伺服器
    while True:
        ret, frame = cap.read()  # 讀取影像
        if not ret:
            break
        else:
            stream.set_frame(frame) # 設定串流內容