프로그래밍/Developer Student Clubs

[Back-end] Flask-Darkflow-web-streaming ajax 구현

지누; 2020. 3. 5. 19:37

[3월 첫째주 구현 목표]

- 비동기 구현

  => 엔터키 누르면 영상에서 detect된 라벨 가져와서 div label 영역에 띄우기

  => 저번 포스팅에서는 setInterval 함수만 사용해 구현했는데, 현주가 ajax 사용해줘서 새로운 방법으로 다시 구현해 보았다.

  => 만족만족

 

https://turtlefromocean.tistory.com/11

 

[Flask-Darkflow-web-streaming 비동기 처리]

[목표 🎯] cv2, darkflow 로 분석한 prediction 결과를 페이지 위에 띄워주기 (cv2.putText() 로 영상 위에 띄우는 거는 우리가 만드는 서비스에 의미가 없기 때문) [생각 🤔] 1. 서버 - 클라이언트 간에 비동기..

turtlefromocean.tistory.com

[main.js]

$(document).ready(function()  {

	$(document).keyup(function(event){  // keyup 이벤트 처리 enter, backspace
	  var keycode = (event.keyCode ? event.keyCode : event.which);
	  if(keycode == '13') regist();  
	  if(keycode == '8') erase();
	});

	$("#add").click(function() {
	  regist();
	});

	$("#erase").click(function() {
	  erase();
	});


	function regist() {

	  $.ajax({
	    url: "/getlabel",
	    method: "GET"
	  }).done(function(r) {
	    $('#label').empty();
	    var result = r.split('2020HANDLANG');

	    result.reverse();

	    result = get_unique(result);

	    for (var i in result) {
	      $('#label').append( '<input type="checkbox" name="result_check" value="label'+i+'">' + result[i] + '</input><br>');
	    }

	    console.log("ajax-getLabel-success");

	  }).fail(function() {
	    console.log("ajax-getLabel-fail");
	  });

	}

	function erase()  {

	  $.ajax({
	    url: "/eraselabel",
	    method: "GET"
	  }).done(function(r) {
	    $('#label').empty();
	    $('#label').append( '<p> ===초기화=== </p>');
	    console.log("ajax-erase-success");
	  }).fail(function() {
	    console.log("ajax-erase-fail");
	  });
	}

	//중복되지 않게 나오도록 한번씩만 출력
	function get_unique(array) {
	  var unique_array = [];
	  $.each(array, function(index, element)  {     // 배열의 원소수만큼 반복
	    if($.inArray(element, unique_array) == -1)  {   // unique_array 에서 값을 찾는다. 값이 없을경우 (-1)
	      if(element != "") {
	        unique_array.push(element); // unique_array  배열에 값을 넣는다.
	      }
	      
	    }
	  });

	  return unique_array;
	}

});

 

* keypress 로 처음에 처리했는데 백스페이스가 안먹혀서 keyup으로 처리하니까 작동된다.

* app_2.py 에서 라벨이 추가될 때마다 split 변수로 '2020HANDLANG'을 같이 붙여줬고, ajax 로 받아오면 이 변수를 기준으로 나눌 수 있게 처리 (뭔가 더 제대로 된 방법이 있을 것 같은데,, json 자료형으로 받아오면 가능할 것 같다. 일단 패스)

* 최근 값일 수록 뒤에 붙으니까 split 되어 만들어진 result 배열을 reverse 시키고, 중복된 값 제거 (get_unique)

* 예측된 값들 중 정답(?)을 체크해서 받아올 수 있도록 checkbox 형태로 출력

 

[webcam_2.html (그냥 index.html)]

<html>
  <head>
    <meta charset="UTF-8">
    <title>Project Handlang</title>
    <script src="../static/js/jquery-3.4.1.js"></script>
    <script src="../static/js/main.js"></script>
    <link rel="stylesheet" href="../static/css/main.css" type="text/css"></link>
  </head>

  <body>

  <h1>Flask-darkflow-web-streaming</h1>

  <img id="bg" src="{{ url_for('video_feed') }}" alt = "webcam">
  <br>
  <button type="button" id="add"> add </button>
  <button type="button" id="erase">erase</button>

  <h3> 👉 Predict Result </h3>
  <div id="label"></div>

  </body>
</html>

 

[app_2.py]

from flask import Flask,url_for, render_template, Response
from darkflow.net.build import TFNet
import cv2
import tensorflow as tf

app=Flask(__name__)

options = {"model": "cfg/yolo.cfg", "load": "bin/yolo.weights", "threshold": 0.4}

tfnet = TFNet(options)

#실시간으로 detection된 label
current_label = ""

#최종 출력 결과 저장
final_result = ""

#split 이거 기준으로 jquery에서 split 
split = "2020HANDLANG"

def gen(camera):
    if not camera.isOpened():
        raise RuntimeError("Could not start camera")

    sess = tf.Session()

    with sess.as_default():

        while True:

            success, img = camera.read()

            if success:
                try:
                    results = tfnet.return_predict(img)

                    for result in results:
                        tl= (result['topleft']['x'],result['topleft']['y'])
                        br =(result['bottomright']['x'],result['bottomright']['y'])
                        label = result['label']
                        confidence = result['confidence']
                        
                        global current_label

                        if confidence > 0.4: # 신뢰도가 0.4 이상일 경우만
                            current_label += label
                            current_label += split

                        print(label)

                        cv2.rectangle(img,tl,br,(0,255,0),3)
                        cv2.putText(img,label,br,cv2.FONT_HERSHEY_COMPLEX, 1,(0,0,0),2)
                    
                    ret, jpeg = cv2.imencode('.jpg', img)
                    frame = jpeg.tobytes()

                    yield (b'--frame\r\n'
                           b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
                except:
                  print("An exception occurred")

            else:
                print("Status of camera.read()\n",success,"\n=======================")


#for ajax
@app.route('/getlabel')
def getLabel():
    global current_label
    global final_result
    final_result = current_label
    current_label = "" # 초기화
    return final_result


@app.route('/eraselabel')
def eraseLabel():
    global final_result
    global current_label
    current_label = "" # 초기화
    final_result = "" # 초기화
    return final_result

#video streaming
@app.route('/video_feed')
def video_feed():
    camera = cv2.VideoCapture(0)
    return Response(gen(camera), mimetype='multipart/x-mixed-replace; boundary=frame')

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

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

 

[구현영상]