51
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FlaskとOpenCVで live streaming

Last updated at Posted at 2019-04-27

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>

51
40
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?