Opencv

Remake: HTML 계산기+ 시계 + OpenCV 웹캠 Flask 웹서버

coding art 2022. 6. 11. 15:05
728x90

참고: https://blog.daum.net/ejleep1/1001?category=585379 

 

Flask_webcam_claculator.py의 코드 구조를 살펴보자. 이미 작성된 calculator HTML/JavaScript 코드에 파이선으로 작성된 Opencv 웹캠 라이브 스트리밍 파트를 함께 넣어 보도록 한다. 윈도우즈 PC에 웹캠이 하나 설치되어 있어야 영상을 볼 수 있다.

전체 형태는 html 파일로 구성하지만 웹캠 영역은 Flask  파이선 코드에서 html 처리가 가능한 기능을 사용하여 끼워 넣도록 하자.

calculator HTML/JavaScript 코드 편집을 위해서는 별도의 HTML 무료 편집기인 Notepad++ 을 사용하도록 하자. 여기에서 사용하는 html 코드는 제목이 {{title}}로 되어 있는데 이 부분은 아래 코드의 templateData 의 'title' 에 대응한다.  사용자가 재목을 바꾸고 싶으면 이 부분을 수정 입력 후 그대로 calculatorwebcam.html 로 저장하자.

아나콘다 Spider에서 Flask 파이선 편집 작업을 위해서는 사용하는 가상공간 여기서는 base(root) 를 사용하였는데 필히 open terminal  기능을 사용하여 work frame을 지원하는 Flask 를 미리 install  해두어야 할 것이다.

 

웹상에 웹캠화면 설정하는 방법에 대해서는 별도로 살펴보기로 하자.

아울러 root 의 하부 폴더 위치에 해당하는 즉 ‘/video_feed’, 웹의 host 로컬 ip  port 설정 영역으로 구성된다. 

현재의 코드에서처럼 port 설정이 생략되어 있으면 Default 5000 이 된다.  port 넘버는 반드시 4자리라야 하며 예를 들어 2자리 수는 안 된다. 

한편 위에서 보듯이 host 로컬 ip  0.0.0.0 으로 설정할 경우 네트워킹 된 외부 컴퓨터의 웹에서 현재 Flask 코드를 실행 중인 컴퓨터의 로컬 ip + ‘:5000’ 을 웹 url에 입력하여 request를 보낼 수 있으나 로컬 ip 127.0.0.1 로 설정하는 경우에는 외부네트워크로부터 request를 받을 수 없음에 유의하자. 아울러 외부로부터 네트워킹을 위해서는 Flask 코드를 실행하는 컴퓨터의 웹을 반드시 꺼야 함에도 유의하자.

위 파이선 코드에서 '0.0.0.0:5000'으로 수정하여 실행할 경우 아래와 같이 http://192.168.0.12:5000/ 와 같이 자체생성한 로컬 ip:5000 을 제공하므로 이 url 을 함게네트워크 된 즉 동일한 무선공유기에서 유선이나 무선으로 네트워킹 된 경우 웹 url 박스에 입력하면 웹캠을 포함한 스크린 화면을 관찰할 수 있다.

 

라이브러리 영역에서 Flask 와 한 세트인 render_template, Response 와 함께 Opencv에 해당하는 cv2 import 하자.

Flask 코드의 특징 적인 구조는 Flask 선언과 함께 root  ‘/’에서 index.html을 사용하는 @app.route(‘/’)에서 calculator 및 시계 기능을 하는 HTML/JavaScript 코드 내용과 웹캠 스트리밍 부분을 통합하여 그 파일명을 calculatorwebcam.html 로 부여하고 현재의 Flask 파이선 코드가 저장되어 있는 폴더 내에 templates 폴더를 준비한 후 옮겨 넣도록 한다.

 

index.html에서 비데오 라이브 웹캠 영상 처리를 담당했던 코드를 살펴보자.

이 부분을 다시 calculatorwebcam.html 에 수정해서 넣어야 한다. calculator 및 시계를 보여주는 HTML 웹 코드는 <table></table>을 사용하여 버튼 및 오브젝트 요소들을 배치하므로 웹캠 화면 역시 row  column 위치를 지정한 후 강제로 집어넣어야 한다. 아울러 width=50% 변수 설정을 사용하여 줄 바꿈이 일어나지 않도록 조정하여야 한다.

 

한편 실행코드인 Flask_webcam_calculator.py 가 위치한 폴더에 templates폴더가 함께 위치해야 하며 그 안에 index.html 대신에 이번에는 calculatorwebcam.html 이 있어야 한다.

 

이와같이 Flask  workframe 을 사용하면 웹화면에 필요한 정보들을 입력하고 아울러 실시간으로 봐야 할 동영상 공간을 확보하여 웹 화면을 구성할 수 있음을 알아 보았다. 필요하다면 html  파일을 손보아 login  화면을 추가할 수도 있을 것이다. 우리가 실생활에서 접하는 다양한 요소로 구성된 웹화면을 구성함에 Flask가 얼마나 유용한지 알 수 있을 것이다. Flask 는 작은 규모의 웹 구성에 적합하며 본격적인 웹 구성에서는 Django 가 전문적으로 사용되고 있음에 유의하자.

 

#Flask_webcam_calculator.py

# JavaScript Calculator WebCam Flask WebServer: 

#---------------------------------------------------------

from flask import Flask, render_template, Response

import cv2

import datetime

 

app = Flask(__name__)

 

camera = cv2.VideoCapture(0)  # use 0 for web camera

 

def gen_frames():  # generate frame by frame from camera

    while True:

        # Capture frame-by-frame

        success, frame = camera.read()  # read the camera frame

       

        if (not success):

            break

        else:

            ret, buffer = cv2.imencode('.jpg', frame)

            frame = buffer.tobytes()

            yield (b'--frame\r\n'

                   b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

                   # concat frame one by one and show result

 

 

@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')

 

 

@app.route('/')

 

def hello():

 

    now = datetime.datetime.now()

    timeString = now.strftime("%Y-%m-%d %H:%M")

    templateData = { 'title' : 'Raspberry Pi Calculator WebCam Server',

                     'time' : timeString }

    

    return render_template('calculatorwebcam.html', **templateData)

 

 

if (__name__ == '__main__'):

    app.run(host='0.0.0.0', port=5000)

 

 

---calculatorwebcam.html: 이하의 html 코드에서 Notepad++ 을 사용하여 확장자 html로 저장 후 templates 폴더에 넣는다---

 

<!-----webcam calculator-->

 

<!DOCTYPE HTML>

 

<html>

<body>

<center><h1>{{title}}</h1>

</body>

<head>

<style>

body{

background-color:tan;}

.box{background-color:#3d4543;height:490px;width:510px; border-style:solid; border-color:lightblue;

border-radius:10px;position:relative;top:50px;left:1%;}

.display{background-color:#222;width:440px; position:relative;

left:2px;top:20px;height:35px;}

.display input{width:415px;position:relative;left:2px;top:2px;height:30px;

color:black;background-color:#bccd95;font-size:21px;text-align:right;}

.keys{position:relative;top:15px;}

.button{width:40px;height:30px;border-radius:8px; border-style:solid; border-color:green;

margin-left:12px;cursor:pointer;border-top:2px solid transparent;}

.button.gray{color:white;background-color:#6f6f6f;

border-bottom:black 2px solid;border-top:2px #6f6f6f solid;}

.button.pink{color:black;background-color:#ff4561;

border-bottom:black 2px solid;}

.button.black{color:white;background-color:#303030;

border-bottom:black 2px solid;border-top:2px #303030 solid;}

.button.orange{color:black;background-color:FF9933;

border-bottom:black 2px solid;border-top:2px FF9933 solid;}

.gray:active{border-top:black 2px solid;

border-bottom:2px #6f6f6f solid;}

.pink:active{border-top:black 2px solid;

border-bottom:#ff4561 2px solid;}

.black:active{border-top:black 2px solid;

border-bottom:#303030 2px solid;}

.orange:active{border-top:black 2px solid;

border-bottom:FF9933 2px solid;}

.buttondigiclock{width:125px;height:40px;border:none;border-radius:8px;margin-left:15px;

cursor:pointer;border-top:2px solid transparent;}

.buttondigiclock.black{color:white;background-color:;

border-bottom:lightblue 2px solid;border-top:2px 303030 solid;}

p{line-height:10px;}

 

</style>

</head>

<body>

<div class="box">

<div class="display">

<input type="text" readonly size="18" id="d">

</div>

<div class="displayx">

<input type="text" readonly size="18" id="dx">

</div>

<div class="keys">

<table>

<tbody>

<tr>

<td><input type="button" class="button gray" value="Pi" onclick='v("3.14159")'></td>

<td><input type="button" class="button gray" value="(" onclick='v("(")'></td>

<td><input type="button" class="button gray" value=")" onclick='v(")")'></td>

<td><input type="button" class="button black" value="7" onclick='v("7")'></td>

<td><input type="button" class="button black" value="8" onclick='v("8")'></td>

<td><input type="button" class="button black" value="9" onclick='v("9")'></td>

<td><input type="button" class="button pink" value="/" onclick='v("/")'></td>

<td><input type="button" class="button pink" value="*" onclick='v("*")'></td>

</tr>

</tbody>

<tr>

<td><input type="button" class="button black" value="4" onclick='v("4")'></td>

<td><input type="button" class="button black" value="5" onclick='v("5")'></td>

<td><input type="button" class="button black" value="6" onclick='v("6")'></td>

<td><input type="button" class="button black"value="1" onclick='v("1")'></td>

<td><input type="button" class="button black" value="2" onclick='v("2")'></td>

<td><input type="button" class="button black" value="3" onclick='v("3")'></td>

<td><input type="button" class="button pink" value="-" onclick='v("-")'></td>

<td><input type="button" class="button pink" value="+" onclick='v("+")'></td>

</tr>

<tr>

<td><input type="button" class="button black" value="0" onclick='v("0")'></td>

<td><input type="button" class="button black" value="." onclick='v(".")'></td>

<td><input type="button" class="button black" value="sin" onclick='v("Math.sin")'></td>

<td><input type="button" class="button black" value="cos" onclick='v("Math.cos")'></td>

<td><input type="button" class="button black" value="tan" onclick='v("Math.tan")'></td>

<td><input type="button" class="button black" value="log" onclick='v("Math.log")'></td>

<td><input type="button" class="button black" value="C" onclick='c("")'></td>

<td><input type="button" class="button orange" value="=" onclick='e()'></td>

</tr>

</tbody>

</table>

<table>

<tbody>

<tr>

<td>

<body onload="updateTime()">

<!-- viewBox is coordinate system, width and height are on-screen size -->

<svg id="clock" viewBox="0 0 100 100" width="120" height="120">

<circle id="face" cx="50" cy="50" r="45"/> <!-- the clock face -->

<circle id="smallface" cx="50" cy="32" r="10"/> <!-- the small clock face -->

<g id="ticks">

<!-- 12 hour tick marks -->

<line x1='50' y1='5.000' x2='50.00' y2='10.00'/>

<line x1='72.50' y1='11.03' x2='70.00' y2='15.36'/>

<line x1='88.97' y1='27.50' x2='84.64' y2='30.00'/>

<line x1='95.00' y1='50.00' x2='90.00' y2='50.00'/>

<line x1='88.97' y1='72.50' x2='84.64' y2='70.00'/>

<line x1='72.50' y1='88.97' x2='70.00' y2='84.64'/>

<line x1='50.00' y1='95.00' x2='50.00' y2='90.00'/>

<line x1='27.50' y1='88.97' x2='30.00' y2='84.64'/>

<line x1='11.03' y1='72.50' x2='15.36' y2='70.00'/>

<line x1='5.000' y1='50.00' x2='10.00' y2='50.00'/>

<line x1='11.03' y1='27.50' x2='15.36' y2='30.00'/>

<line x1='27.50' y1='11.03' x2='30.00' y2='15.36'/>

</g>

<g id="smallticks">

<!-- 12 hour tick marks -->

<line x1='50' y1='24' x2='50' y2='22'/>

<line x1='58' y1='32' x2='60' y2='32'/>

<line x1='50' y1='40' x2='50' y2='42'/>

<line x1='42' y1='32' x2='40' y2='32'/>

</g>

<g id="numbers">

<!-- Number the cardinal directions-->

<text x="50" y="18">12</text><text x="85" y="53">3</text>

<text x="50" y="88">6</text><text x="15" y="53">9</text>

</g>

<!-- Draw hands pointing straight up. We rotate them in the code. -->

<g id="hands"> <!-- Add shadows to the hands -->

<line id="hourhand" x1="50" y1="50" x2="50" y2="24"/>

<line id="minutehand" x1="50" y1="50" x2="50" y2="20"/>

<line id="secondhand" x1="50" y1="32" x2="50" y2="24"/>

</g>

</svg>

</body>

</td>

<td>

<form name="theClock">

<input type=text class="button buttondigiclock black" name="theDate" style="font-size:20px;text-align: center;">

</td>

<td>

<input type=text class="button buttondigiclock black" name="theTime" style="font-size:20px;text-align: center;">

</form>

</td>

</tr>

</tbody>

</table>

<table><tbody><tr>

 

            <td><h3 style="color:#FFFFFF;">Live WebCam</h3></td>

            <td><img src="{{ url_for('video_feed') }}" width="50%"></td>

 

</tr></tbody></table>

</div>

</div>

<script type='text/javascript'>

function c(val){

document.getElementById("d").value=val;}

function v(val){

document.getElementById("d").value+=val;}

function e() {

try {

c(eval(document.getElementById("d").value)) }

catch(e) {

c('Error') } }

</script>

</body>

<head>

<script>

function updateTime() { // Update the SVG clock

var now = new Date();

var sec = now.getSeconds();

var min = now.getMinutes();

var hour = (now.getHours() % 12) + min/60;

var secangle = sec*6;

var minangle = min*6;

var hourangle = hour*30;

var actualMonth = now.getMonth() + 1;

//Prepare time output!

document.theClock.theTime.value = ""

+ now.getHours() + ":"

+ now.getMinutes() + ":"

+ now.getSeconds();

document.theClock.theDate.value = ""

+ now.getFullYear() + ":"

+ actualMonth + ":"

+ now.getDate();

// Get SVG elements for the hands of the clock

var sechand = document.getElementById("secondhand");

var minhand = document.getElementById("minutehand");

var hourhand = document.getElementById("hourhand");

// Set an SVG attribute on them to move them around the clock face

sechand.setAttribute("transform", "rotate(" + secangle + ",50,32)");

minhand.setAttribute("transform", "rotate(" + minangle + ",50,50)");

hourhand.setAttribute("transform", "rotate(" + hourangle + ",50,50)");

setTimeout(updateTime, 1000);

}

</script>

<style>

/* These CSS styles all apply to the SVG elements defined below */

#clock {

/* styles for everything in the clock */

stroke: black;

/* black lines */

stroke-linecap: round;

/* with rounded ends */

fill: #eef;

/* on a light blue gray background */

}

#face { stroke-width: 3px;}

/* clock face outline */

#smallface { stroke:green; stroke-width: 1px;}

/* clock face outline */

#ticks { stroke-width: 2; }

/* lines that mark each hour */

#smallticks { stroke:green; stroke-width: 1; }

#hourhand {stroke-width: 5px;}

/* wide hour hand */

#minutehand {stroke: deepskyblue; stroke-width: 3px;} /* narrow minute hand */

#secondhand {stroke:red; stroke-width: 1px;}

#numbers {

/* how to draw the numbers */

font-family: sans-serif; font-size: 7pt; font-weight: bold;

text-anchor: middle; stroke: none; fill: blue;

}

</style>

</head>

 

<table>

 

</table>

</html>​