A memo that detects and returns an image acquired from a webcam with Django's OpenCV

background

I want to detect faces from images (videos) taken with a webcam, with React and Django

image.png When you hold the camera over it, you can see your face It might be useful for something security

methodology

Last time The dropped part is replaced with the webcam Due to this, some security settings are required. image.png

Implementation

React

Last time is no different I added the process to access the camera with the constructor Get an image from the camera? GetCameraImage is cut out Slightly changed the receiving process of submitData

create-react-app user

index.js


import React from "react";
import ReactDOM from "react-dom";

const SERVER = "https://192.168.139.140:8000/image/"; //Server post

class App extends React.Component {
  /**
   * @param {json} props
   */
  constructor(props) {
    super(props);

    const canvas = document.createElement("canvas");
    const context = canvas.getContext("2d");
    this.state = {
      video: document.createElement("video"),
      canvas: canvas,
      context: context
    };
    if (navigator.mediaDevices){
      navigator.mediaDevices
        .getUserMedia({ video: true, audio: false })
        .then((stream) => {
          this.state.video.srcObject = stream;
          this.state.video.play();
          this.state.video.onloadedmetadata = (e) => {
            this.setState({width: this.state.video.videoWidth, height: this.state.video.videoHeight})
            this.state.canvas.width = this.state.width;
            this.state.canvas.height = this.state.height;
            this.submitData();
          };
        })
        .catch(function (error) {
          console.error(error);
          return;
        });
      }
  }

  /**
   *Get CSRF tokens from cookies
   */
  getCSRFtoken() {
    for (const c of document.cookie.split(";")) {
      const cArray = c.split("=");
      if (cArray[0] === "csrftoken") return cArray[1];
    }
  }

  /**
   *Get an image from the camera and convert it to a character string for sending and receiving
   * @return {str} base64
   */
  getCameraImage() {
    this.state.context.drawImage(
      this.state.video,
      0,
      0,
      this.state.width,
      this.state.height
    );
    return this.state.canvas.toDataURL();
  }

  /**
   *Send data to the server
   */
  submitData() {
    this.setState({ image: this.getCameraImage() });
    this.render();
    // console.log("Send", this.state);
    fetch(SERVER + "res/", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-CSRFToken": this.getCSRFtoken()
      },
      body: JSON.stringify({
        image: this.state.image //← Send image
      })
    })
      .then((res) => res.json())
      .then((res) => {
        console.log("Receive", res);
        this.setState({ image: res.image, message: res.message }); //← Set image
        this.render();
        setTimeout(() => this.submitData(), 1000); //Send after 1 second
      });
  }

  /**
   *Button and send / receive data rendering
   */
  render() {
    return (
      <div>
        <div className="image-element">
          {this.state.image ? <img src={this.state.image} alt="image" /> : ""}
        </div>
      </div>
    );
  }
}

ReactDOM.render(
  <React.StrictMode>
    <App message={"Image"} />
  </React.StrictMode>,
  document.getElementById("root")
);

The process of accessing the webcam

Copy and paste of JS that I wrote almost a long time ago

index.js


  constructor(props) {
    super(props);

    const canvas = document.createElement("canvas");
    const context = canvas.getContext("2d");
    this.state = { //Set canvas (for image drawing) and video (for video) elements
      video: document.createElement("video"),
      canvas: canvas,
      context: context
    };
    if (navigator.mediaDevices){
      navigator.mediaDevices
        .getUserMedia({ video: true, audio: false })
        .then((stream) => {
          this.state.video.srcObject = stream; //Get the video from the camera and put it in the video element
          this.state.video.play(); //Regeneration(?)
          this.state.video.onloadedmetadata = (e) => { //Once the metadata can be loaded
            this.setState({width: this.state.video.videoWidth, height: this.state.video.videoHeight}) //Size acquisition set
            this.state.canvas.width = this.state.width; //Specify the size of the canvas
            this.state.canvas.height = this.state.height;
            this.submitData(); //Send to server
          };
        })
        .catch(function (error) {
          console.error(error);
          return;
        });
      }
  }

If it's related to video, video.onloadedmetadata or a guy who smells like crap I was suffering to death when I was making a video viewing app VideoWidth is not set for video unless the metadata load is confirmed by this process. Surprisingly, I'm always confused about the size specification here video is video.videoWidth and canvas is canvas.width

There seems to be await these days

Function to get an image from the camera

This is also the same as the one I made a long time ago

index.js


  getCameraImage() {
    this.state.context.drawImage(this.state.video, 0, 0, this.state.width, this.state.height);
    return this.state.canvas.toDataURL();
  }

I feel that I was able to halve the horizontal size by doing this.state.width / 2 (remember) It didn't make sense to cut it out here

Send and receive

I wanted to check the response image, so I made the next transmission 1 second after drawing the face detection image.

index.js


  submitData() {
    this.setState({ image: this.getCameraImage() });
    this.render();
    // console.log("Send", this.state);
    fetch(SERVER + "res/", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-CSRFToken": this.getCSRFtoken()
      },
      body: JSON.stringify({
        image: this.state.image //← Send image
      })
    })
      .then((res) => res.json())
      .then((res) => {
        console.log("Receive", res);
        this.setState({ image: res.image, message: res.message }); //← Set image
        this.render();
        setTimeout(() => this.submitData(), 1000); //Send after 1 second
      });
  }

I thought about sending by pressing a button, but I just render the button and post the event.

Build

Build this and as usual

{% csrf_token %}
{% load static %}

At the beginning of `ʻindex.htmland replace" / with "{% static'image /'%} / When I checked later, there was "/ `` in the js process in index.html, so I want to avoid that

When I thrust this into the server of previous, navigator.mediaDevices.getUserMedia is complaining. Today's browsers cannot access the camera unless index.html is provided with encrypted communication for security reasons. So it is necessary to install SSL on the server side

Django Introduce development SSL to the server

Preparation

Added pip django-sslserver to previous

python -m venv test
cd test
source bin/activate
pip install -U pip
pip install django opencv-python django-sslserver

django-admin startproject server
cd server
python manage.py startapp image
python manage.py migrate

openssl genrsa -out foobar.key 2048
openssl req -new -key foobar.key -out foobar.csr
openssl x509 -req -days 365 -in foobar.csr -signkey foobar.key -out foobar.crt

Almost the same as References Change the path of the last foobar.csr and foobar.key because it was generated in the same place as manage.py Also, at the development stage, all input after `ʻopenssl genrsa -out foobar.key 2048`` could be done with Enter.

server/setting.py


ALLOWED_HOSTS = ["127.0.0.1"]

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'image.apps.ImageConfig',
    'sslserver', #Add SSL server
]
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = False
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Others are the same as last time I want to complete this one page

server/urls.py


from django.contrib import admin
from django.urls import path, include

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

image/urls.py


from django.urls import path

from . import views

app_name = 'urls'
urlpatterns = [
    path('', views.Index, name='index'),
    path('res/', views.Res, name='res'),
]

Put you in `ʻimage / static`` haarcascade_frontalface_default.xml

views.py


from django.shortcuts import render
from django.http.response import JsonResponse
from django.http import HttpResponse

import json, base64
import numpy as np
import cv2

def Index(request):
    return render(request, 'image/index.html')

def Res(request):
    data = request.body.decode('utf-8')
    jsondata = json.loads(data)

    image_base64 = jsondata["image"]
    encoded_data = image_base64.split(',')[1]
    nparr = np.fromstring(base64.b64decode(encoded_data), np.uint8)
    image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)

    face_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    face_cascade = cv2.CascadeClassifier("image/static/haarcascade_frontalface_default.xml")
    faces = face_cascade.detectMultiScale(face_gray)
    for x, y, w, h in faces: #Write a rectangle in the face area
        cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2)

    result, dst_data = cv2.imencode('.png', image)
    dst_base64 = base64.b64encode(dst_data).decode()
    return JsonResponse({"image": "data:image/png;base64,"+dst_base64, "message": "Django"})

result

image.png I was able to detect my moody face

Conclusion

Communication becomes troublesome at once when the server surroundings enter

It will take time to prepare the real server, so start playing with Heroku I can't let React do it, so next time I want to make something like a bulletin board

References

@Syoitu, “Enable Django https in a few lines,” November 07, 2019

Recommended Posts

A memo that detects and returns an image acquired from a webcam with Django's OpenCV
Get an image from a web page and resize it
A memo that reads data from dashDB with Python & Spark
A server that returns the number of people in front of the camera with bottle.py and OpenCV
Create an API that returns data from a model using turicreate
I made a Line bot that guesses the gender and age of a person from an image
Try to extract a character string from an image with Python3
Face detection from multiple image files with openCV, cut out and save
A python program that resizes a video and turns it into an image
A memo with Python2.7 and Python3 on CentOS
Video acquisition / image shooting from a webcam
I tried to extract a line art from an image with Deep Learning
A memo that made a graph animated with plotly
Develop a web API that returns data stored in DB with Django and SQLite
Convert images from FlyCapture SDK to a form that can be used with openCV
Create a High Dynamic Range Image (HDR) with OpenCV and Python (Mertens, Robertson, Debevec)
A memo that uses an interactive display mode like Jupyter notebook with VSCode + Python
Image capture / OpenCV speed comparison with and without GPU
A memo that I touched the Datastore with python
"A book that understands Flask from scratch" Reading memo
How to take a captured image from a video (OpenCV)
[Python] Webcam frame size and FPS settings with OpenCV
A learning roadmap that allows you to develop and publish services from scratch with Python
Try to generate a cyclic peptide from an amino acid sequence with Python and RDKit
An example of a mechanism that returns a prediction by HTTP from the result of machine learning
Steps to create a Job that pulls a Docker image and tests it with Github Actions