Sử dụng Django/Flask và Opencv để stream video từ webcam!

Giới thiệu

Nhân có một số người bạn hỏi về sử dụng opencv để stream và hiển thị nó trên web mình sẽ viết bài chia sẻ cơ bản nhất để làm việc đó. Bài chia sẻ sẽ sử dụng 2 web framework phổ biến nhất của Python là Flask và Django

Cài đặt

Trên hệ điều hành Ubuntu 16.04 mình cài các library trên virtualenv. Cụ thể thông tin các gói:

(venv) % python --version                      
Python 3.6.1

(venv) % pip freeze                            
Django==2.0.2
Flask==0.12.2
itsdangerous==0.24
Jinja2==2.10
MarkupSafe==1.0
numpy==1.14.1
opencv-python==3.4.0.12
pytz==2018.3
Werkzeug==0.14.1

Thực hiện

Django

Tạo một project django

Sử dụng CLI của django tạo một project tên là stream và app là webcam:

(venv) % django-admin startproject stream # Tạo project lấy tên là "stream"
(venv) % cd stream && django-admin startapp webcam # Tạo app lấy lên là "webcam"

Chạy thử nào:

(venv) % python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).

February 26, 2018 - 03:45:01
Django version 2.0.2, using settings 'stream.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Xử lý stream

Mở browser vào địa chỉ: http://127.0.0.1:8000/ và đây là kết quả: Giao diện của Django 2.0 có vẻ nhìn sáng sủa hơn các phiên bản cũ 😄

Tiếp tục, mình sẽ tạo một app in ra "Hello Word!":

Setup app webcam vào trong settings.py của project stream:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'webcam'
]

Trong thư mục app webcam, mình tạo một thư mục templates. Sau đó, tạo một file tên là index.html Nội dung của file html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Stream</title>
</head>
<body>
<h1>Hello Word!</h1>
</body>
</html>

Back lại, trong thư mục app webcam, trong file views.py. Bạn định nghĩa view:

from django.http import HttpResponse

def index(request):
    template = loader.get_template('index.html')
    return HttpResponse(template.render({}, request))

Cuối cùng, định nghĩa URL trong stream/urls.py:

from django.contrib import admin
from django.urls import path
from webcam.views import index

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', index),
]

Chạy lại web django, sau đó vào địa chỉ: http://127.0.0.1:8000/index/. Và đây là kết quả: Tiếp theo mình sẽ cài đặt stream Trở lại file html mình thay đổi lại một chút:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Stream</title>
</head>
<body>
<h1>Video Streaming Django</h1>
<img src="{% url 'video-feed' %}" alt="">
</body>
</html>

Tiếp theo, mình thêm function xử lý stream ở view.

def stream():
    cap = cv2.VideoCapture(0) 

    while True:
        ret, frame = cap.read()

        if not ret:
            print("Error: failed to capture image")
            break

        cv2.imwrite('demo.jpg', frame)
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + open('demo.jpg', 'rb').read() + b'\r\n')

def video_feed(request):
    return StreamingHttpResponse(stream(), content_type='multipart/x-mixed-replace; boundary=frame')

Nhìn code bạn cũng đã thấy luồng xử lý stream rồi. Rất đơn giản:

  • Đọc capture từ webcam sử dụng opencv
  • Từ capture đó, get ra các frame.
  • Từ các frame đó bạn có thể làm rất nhiều điều: ví dụ như nhận dạng khuôn mặt, đoán tuổi, giới tính, blabla..
  • Cuối cùng là ghi cái frame đó ra file và yield cái frame đó hiển thị trên html

Vậy đó!! Cuối cùng, ta cũng định nghĩa lại URL stream:

from django.contrib import admin
from django.urls import path
from webcam.views import index, video_feed

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', index),
    path('video_feed/', video_feed, name="video-feed")
]

Reload lại URL: http://127.0.0.1:8000/index/ xem kết quả . Thứ lỗi vì mình hơi xấu trai 😄

Flask

Với Flask, mình cũng làm tương tự:

Tạo một project với cấu trúc:

Trong file index.html

<html>
  <head>
    <title>Video Streaming Demonstration</title>
  </head>
  <body>
    <h1>Video Streaming Demonstration</h1>
    <img src="{{ url_for('video_feed') }}">
  </body>
</html>

Flask sử dụng url_for trong template tương tự như url của Django vậy.

Trong file server app.py

from flask import Flask, render_template, Response
import cv2

app = Flask(__name__)


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


def gen():
    cap = cv2.VideoCapture(0)

    while True:
        ret, frame = cap.read()

        if not ret:
            print("Error: failed to capture image")
            break

        cv2.imwrite('demo.jpg', frame)
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + open('demo.jpg', 'rb').read() + b'\r\n')


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


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

Luồng xử lý stream của Flask và Django đều giống nhau nên mình cũng không giải thích thêm. Chạy thử code:

(venv) % python app.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 720-880-471

Mở browser lên vào địa chỉ: http://127.0.0.1:5000/ ta sẽ được kết quả y hệt như khi sử dụng Django

Nếu bạn còn vướng mắc về Django và Flask bạn có thể contact với mình qua Skype hoangminh.dev

Thanks for reading!