Opencv

Flask Opencv 웹캠에 의한 웹서버상에서 안면인식

coding art 2020. 5. 2. 18:50
728x90

 

아나콘다나 라즈베리 파이에서 웹캠에 의한 안면 인식 어신 러닝이 가능하다면 이 웹캠 화면을 웹서버 방식으로 사용하기 위해서 Flask 에 의한 코드를 작성해 보기로 한다. 물론 라즈베리 파이와 같은 작은 보드에서 구현 가능한 웹서버를 완성하기 위해서는 실행 속도 차이가 7배에 달하므로 윈도우즈 10에서 코드를 작성 시험해 본 후에 라즈베리 파이에 옮기기로 하자.

 

단 윈도우즈 PC 의 아나콘다에서 face_detect.pyFlask 화하여 코딩하여 실행할 경우 웹에서 0.0.0.0:5000 이 아니고 local:5000 또는 127.0.0.1:5000 임에 유의하자. 아무래도 Flask 시스템에서 face_detect.py 실행 시간이 조금 증가할 수 있는데 80~100 밀리 초 수준이며 라즈베리 파이에서도 당연히 실행이 가능하지만 600~900 밀리 초 수준으로 느려진다. 따라서 이 Flask 시스템 코드 작성 개발은 반드시 데스크톱에서 해야 할 필요가 있다.

아래 그림을 참조하면 윈도우즈 PC 의 아나콘다에서 안면 인식에 소요되는 시간이 녹색 80 밀리 초임을 알 수 있다. 특히 ‘title’로 출력된 timeString 시간은 웹서버 시작 초기에 한번 출력되며 한편 gen_frame() 루틴 내에서 cv2.VideoCapture(0) 화면에 더하여 loop 과정에서 체크하는 안면 인식 결과 원을 cv2.circle 명령에 의해 작도함과 아울러 안면인식 소요 시간 및 timeStringcv2.putText 명령을 사용하여 출력한 결과를 추가한 최종 이미지 즉 image를 웹 출력한 것이다.

 

Flask 구조에 따른 웹서버 알고리듬 코드 구조를 살펴보자. haarcacade 라이브러리는 import 과정에 해당하므로 헤더 영역에 그냥 두면 된다. Flask(__name__)를 선언하고 root 영역 즉 @app.route(‘/’)에서 함수 def index(): def gen_frames():를 배치하면 순서대로 실행이 이루어진다. cv2.VidepCapure() cv2.imshow()에 이르는 과정은 웹캠 Opencv 파이선 코드와 동일하며 이머서 “q”를 입력하면 break 하여 루프를 중지하는 과정까지 그대로 사용한다. 여기까지 단계의 image cv2.VideoCapture(0)에서 읽은 이미지에 안면 인식 결과 원을 작도한 이미지이다. 주의해야 할 점은 필요한 모든 정보를 화면상에 출력한 후 최종적으로 imencode를 실행하할 필요가 있다. 이 부분부터는 imageFlask 웹에 올릴 수 있도록 imencode 명령 처리를 하여 바이트로 변환이 필요하다.

아래의 코드에서 ∙∙∙ 부분이 이미지에 정보를 추가하기 위한 루틴을 넣을 수 있는 공간이다.

 

 

 

∙∙∙에 해당하는 부분에 Opencv 명령을 사용하여 안면 인식 소요 시간을 계산하고 인식된 안면에 원을 작도한 후 안면인식 소요시간과 timeString 값을 추력하여 웹에 올릴 최종 이미지를 완성한다.

 

image encoding imencode 명령은 Opencv에서 초당 frame rate 속도로 처리되는 각각 이미지 frame을 하나의‘jpg’ 파일로 간주하고 엔코딩 작업 후 바이트 단위로 변환하여 Flask 웹에서 사용할 frame 이미지를 얻게 된다. 이과정이 함수 gen_frame()이 하게 되는 역할이며 @app.route(‘/video_feed’) root 아래에 위치한 폴더 ‘/video_feed’에서 video_feed() 함수의 리턴 항목에 gen_frames()를 일종의 callback 으로 사용하게 된다. 이러한 Flask 코드의 구조는 거의 정해져 있으므로 그대로 사용하면 된다. 사용자가 해야 할 코드 작업은 Flask 구조를 제외한 효율적인 머신 러닝 코드를 수정하는 작업에 국한 될 것이다.

 

 

 

한편 index.html 는 그대로 사용하되 frame 의 크기를 조절하는 기능이 있으므로 width 값을 조정하면 된다. 100% 이상의 크기도 가능하다. 성공적으로 실행이 되면 라즈베리 파이에서도 상당히 느리겠지만 실행이 가능하므로 감시용 시스템 제작이 가능할 것이다.

 

#face_detect_webserver.py


from flask import Flask, render_template, Response
import cv2
import numpy as np
import time
import datetime
import sys

faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
num = 3
app = Flask(__name__)

@app.route('/')
def index():
    """Video streaming home page."""
    now = datetime.datetime.now()
    timeString = now.strftime("%Y-%m-%d %H:%M")
    templateData = {
            'title':'Image Streaming',
            'time': timeString
            }
    return render_template('index.html', **templateData)

def gen_frames():
    camera = cv2.VideoCapture(0)
    time.sleep(0.2)
    lastTime = time.time()*1000.0

    while True:
        ret, image = camera.read()
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
       
        faces = faceCascade.detectMultiScale(gray,scaleFactor=1.1,minNeighbors=6)
        delt = time.time()*1000.0-lastTime
        s = str(int(delt))
        #print (delt," Found {0} faces!".format(len(faces)) )
        lastTime = time.time()*1000.0
        # Draw a rectangle around the faces
        for (x, y, w, h) in faces:
            cv2.circle(image, (int(x+w/2), int(y+h/2)), int((w+h)/3), (255, 255, 255), 3)
        cv2.putText(image, s, (10, 25),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
        now = datetime.datetime.now()
        timeString = now.strftime("%Y-%m-%d %H:%M")
        cv2.putText(image, timeString, (10, 45),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
        cv2.imshow("Frame", image)
        key = cv2.waitKey(1) & 0xFF
     # if the `q` key was pressed, break from the loop
        if key == ord("q"):
            break
   
        ret, buffer = cv2.imencode('.jpg', image)
        frame = buffer.tobytes()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
       
 
@app.route('/video_feed')

def video_feed():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(gen_frames(),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

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

 

#index.html

<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
          integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

    <title>{{title}}</title>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-lg-8  offset-lg-2">
            <h3 class="mt-5">{{time}}</h3>
            <img src="{{ url_for('video_feed') }}" width="100%">
        </div>
    </div>
</div>
</body>
</html>