Help us understand the problem. What is going on with this article?

FlaskとOpenCVで live streaming

More than 1 year has passed since last update.

FlaskとOpenCVを使ったVideo streamingのサンプルがありますが、コードについて自分の理解の範囲で解説を加えました。

参考サンプルコード
Python Live Video Streaming Example
Video Streaming with Flask
Webcam Live Streaming using Flask

Flaskアプリでは、3つのものを作る必要があります。
(1)ブラウザーで表現するもの(HTML+テンプレートjinja2+必要があればCSS)
(2)リクエストとレスポンスを振り合分けるルーティング(Python flask)
(3)リクエストを受けて処理をする関数(python)

Flaskでは以下のホルダー構成を作る必要があります。
・staticフォルダ:画像ファイル等の静的なリソースファイルの置き場として認識
・templatesフォルダ:テンプレート置き場として認識
※デフォルトで、これらのフォルダーを参照するようになっていますが、変更も可能です。(例えば、/static/xxxというリクエストは、ルーティング処理を書かずともFlaskが自動的に処理)

[Python] 軽量WebフレームワークのFlaskに入門(準備、起動、HTML、静的ファイル、GET、POSTなど)

PythonのFlaskでのrender_templateで画像ファイルの表示方法

─── static
|   └── js(必要があれば)
|   |   └──main.js
|   └── main.cs
├── templates
│   └── index.html
├──  camera.py (処理する関数)
└──  main.py (メインプログラム)

OpenCVを使ったカメラの設定とキャプチャー

# camera.py

import cv2

class VideoCamera(object):
    def __init__(self):
        self.video = cv2.VideoCapture(0)

        # Opencvのカメラをセットします。(0)はノートパソコンならば組み込まれているカメラ

    def __del__(self):
        self.video.release()

    def get_frame(self):
        success, image = self.video.read()
        ret, jpeg = cv2.imencode('.jpg', image)
        return jpeg.tobytes()

        # read()は、二つの値を返すので、success, imageの2つ変数で受けています。
        # OpencVはデフォルトでは raw imagesなので JPEGに変換
        # ファイルに保存する場合はimwriteを使用、メモリ上に格納したい時はimencodeを使用
        # cv2.imencode() は numpy.ndarray() を返すので .tobytes() で bytes 型に変換


ルーティングのプログラム(flask)

# main.py

from flask import Flask, render_template, Response

from camera import VideoCamera

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

    # "/" を呼び出したときには、indexが表示される。

def gen(camera):
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')

# returnではなくジェネレーターのyieldで逐次出力。
# Generatorとして働くためにgenとの関数名にしている
# Content-Type(送り返すファイルの種類として)multipart/x-mixed-replace を利用。
# HTTP応答によりサーバーが任意のタイミングで複数の文書を返し、紙芝居的にレンダリングを切り替えさせるもの。
#(※以下に解説参照あり)

@app.route('/video_feed')
def video_feed():
    return Response(gen(VideoCamera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

# 0.0.0.0はすべてのアクセスを受け付けます。    
# webブラウザーには、「localhost:5000」と入力

※Content-Type(送り返すファイルの種類として)multipart/x-mixed-replaceについて
multipart/x-mixed-replace (MIME型)
Content-Typeの書き方として(上記参照のサイトより)
区切り(boundary)の文字列を決める:例では、boundary=ThisRandomString
区切り文字列に指定された文字列の前に “--” を付けてパートを区切る
最後の区切りには後にも “--” を入れる:例では、--ThisRandomString--
Multipart 自身もコンテント・ヘッダとコンテント・ボディ、各パートもそれぞれコンテント・ヘッダとコンテント・ボディから構成される

Content-type: multipart/x-mixed-replace;boundary=ThisRandomString

 --ThisRandomString
 Content-type: text/plain

 Data for the first object.

 --ThisRandomString
 Content-type: text/plain

 Data for the second and last object.

 --ThisRandomString--

この書式に合わせてyieldの内容を記載。
区切り文字列 "frame",(\r\n)は改行コード。b''はByte型の意味。

b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n'

templateホルダー内にindex.htmlファイルを作成。

<html>
  <head>
    <title>Video Streaming Demonstration</title>
  </head>
  <body>
    <h1>Video Streaming Demonstration</h1>
    <img src="{{ url_for('video_feed') }}">
    <!-- jinja2のテンプレートの書き方です。/video_feedを呼び出しています。 -->
  </body>
</html>

Gyutan
材料評価の装置や解析手法の開発を行っています。数年前まではLabVIEWで計測装置のプログラムを書いていました。最近はPythonばかりです。Microbitは、地元の子供たちにプログラミングを教えるために、使う予定です。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away