Showing
2 changed files
with
146 additions
and
81 deletions
... | @@ -58,16 +58,16 @@ function load_cascade() | ... | @@ -58,16 +58,16 @@ function load_cascade() |
58 | 58 | ||
59 | function main() | 59 | function main() |
60 | { | 60 | { |
61 | -let video = document.getElementById("videoInput"); | 61 | + let video = document.getElementById("videoInput"); |
62 | -let canvasOutput = document.getElementById("canvasOutput"); | 62 | + let canvasOutput = document.getElementById("canvasOutput"); |
63 | -let canvasContext = canvasOutput.getContext('2d'); | 63 | + let canvasContext = canvasOutput.getContext('2d'); |
64 | -let src = new cv.Mat(video.height, video.width, cv.CV_8UC4); | 64 | + let src = new cv.Mat(video.height, video.width, cv.CV_8UC4); |
65 | -let dst = new cv.Mat(video.height, video.width, cv.CV_8UC4); | 65 | + let dst = new cv.Mat(video.height, video.width, cv.CV_8UC4); |
66 | -// let gray = new cv.Mat(); | 66 | + // let gray = new cv.Mat(); |
67 | -let cap = new cv.VideoCapture(video); | 67 | + let cap = new cv.VideoCapture(video); |
68 | -let faces = new cv.RectVector(); | 68 | + let faces = new cv.RectVector(); |
69 | -let classifier = new cv.CascadeClassifier(); | 69 | + let classifier = new cv.CascadeClassifier(); |
70 | -class Tracker{ | 70 | + class Tracker{ |
71 | constructor(){ | 71 | constructor(){ |
72 | this.arr = new Array(); | 72 | this.arr = new Array(); |
73 | } | 73 | } |
... | @@ -92,14 +92,14 @@ class Tracker{ | ... | @@ -92,14 +92,14 @@ class Tracker{ |
92 | this.arr.push(ent) | 92 | this.arr.push(ent) |
93 | return true; | 93 | return true; |
94 | } | 94 | } |
95 | -}; | 95 | + }; |
96 | -var tracker = new Tracker(); | 96 | + var tracker = new Tracker(); |
97 | -var streaming = true; | 97 | + var streaming = true; |
98 | 98 | ||
99 | -classifier.load('haarcascade_frontalface_default.xml'); | 99 | + classifier.load('haarcascade_frontalface_default.xml'); |
100 | 100 | ||
101 | -const FPS = 30; | 101 | + const FPS = 30; |
102 | -function processVideo() { | 102 | + function processVideo() { |
103 | try { | 103 | try { |
104 | if (!streaming) { | 104 | if (!streaming) { |
105 | // clean and stop. | 105 | // clean and stop. |
... | @@ -132,10 +132,9 @@ function processVideo() { | ... | @@ -132,10 +132,9 @@ function processVideo() { |
132 | let tempCanvas = document.createElement("canvas"); | 132 | let tempCanvas = document.createElement("canvas"); |
133 | cv.imshow(tempCanvas,cropped); | 133 | cv.imshow(tempCanvas,cropped); |
134 | console.log('b64encode'); | 134 | console.log('b64encode'); |
135 | - if (tracker.register(face.x, face.y, face.width, face.height, Date.now())){ | 135 | + if (tracker.register(face.x, face.y, face.width, face.height)){ |
136 | let b64encoded = tempCanvas.toDataURL("image/jpeg", 1.0); | 136 | let b64encoded = tempCanvas.toDataURL("image/jpeg", 1.0); |
137 | - b64encoded = b64encoded.replace('data:image/jpeg;base64,', '') | 137 | + b64encoded = b64encoded.replace('data:image/jpeg;base64,', ''); |
138 | - console.log('ajax post'); | ||
139 | $.ajax({ | 138 | $.ajax({ |
140 | type: "POST", | 139 | type: "POST", |
141 | url: "/verify", | 140 | url: "/verify", |
... | @@ -172,15 +171,15 @@ function processVideo() { | ... | @@ -172,15 +171,15 @@ function processVideo() { |
172 | } catch (err) { | 171 | } catch (err) { |
173 | console.log(err); | 172 | console.log(err); |
174 | } | 173 | } |
175 | -} | 174 | + } |
176 | -setTimeout(processVideo, 0); | 175 | + setTimeout(processVideo, 0); |
177 | } | 176 | } |
178 | </script> | 177 | </script> |
179 | </head> | 178 | </head> |
180 | <body onload="cv['onRuntimeInitialized']=()=>{ init(); };"> | 179 | <body onload="cv['onRuntimeInitialized']=()=>{ init(); };"> |
181 | -<div id="container"> | 180 | + <div id="container"> |
182 | -<video autoplay="true" id="videoInput" style="display: none; object-fit: cover;"></video> | 181 | + <video autoplay="true" id="videoInput" style="display: none; object-fit: cover;"></video> |
183 | -<canvas id="canvasOutput"></canvas> | 182 | + <canvas id="canvasOutput"></canvas> |
184 | -</div> | 183 | + </div> |
185 | </body> | 184 | </body> |
186 | </html> | 185 | </html> | ... | ... |
... | @@ -11,8 +11,32 @@ | ... | @@ -11,8 +11,32 @@ |
11 | <script type='text/javascript' src="{{url_for('static', filename='js/utils.js')}}"></script> | 11 | <script type='text/javascript' src="{{url_for('static', filename='js/utils.js')}}"></script> |
12 | <script type='text/javascript' src="https://code.jquery.com/jquery-1.12.4.min.js"></script> | 12 | <script type='text/javascript' src="https://code.jquery.com/jquery-1.12.4.min.js"></script> |
13 | <script type='text/javascript'> | 13 | <script type='text/javascript'> |
14 | -var tempImage = new Image(); | 14 | +var b64encoded = ''; |
15 | -var tempCanvas = document.createElement("canvas"); | 15 | +var streaming = true; |
16 | +function init() | ||
17 | +{ | ||
18 | + let video = document.getElementById('videoInput'); | ||
19 | + let container = document.getElementById('container'); | ||
20 | + let canvasOutput = document.getElementById("canvasOutput"); | ||
21 | + if (navigator.mediaDevices.getUserMedia){ | ||
22 | + navigator.mediaDevices.getUserMedia({ video: true }) | ||
23 | + .then(function (stream) { | ||
24 | + video.srcObject = stream; | ||
25 | + video.addEventListener('canplay', () => { | ||
26 | + video.width = video.videoWidth; | ||
27 | + video.height = video.videoHeight; | ||
28 | + container.style.width = video.videoWidth + 'px'; | ||
29 | + container.style.height = video.videoHeight + 'px'; | ||
30 | + canvasOutput.width = video.videoWidth; | ||
31 | + canvasOutput.height = video.videoHeight; | ||
32 | + load_cascade(); | ||
33 | + }); | ||
34 | + }).catch(function (err0r) { | ||
35 | + console.log("Something went wrong!"); | ||
36 | + streaming = false; | ||
37 | + }); | ||
38 | + } | ||
39 | +} | ||
16 | 40 | ||
17 | function load_cascade() | 41 | function load_cascade() |
18 | { | 42 | { |
... | @@ -20,69 +44,121 @@ function load_cascade() | ... | @@ -20,69 +44,121 @@ function load_cascade() |
20 | let faceCascadeURL = 'static/js/haarcascade_frontalface_default.xml' | 44 | let faceCascadeURL = 'static/js/haarcascade_frontalface_default.xml' |
21 | let utils = new Utils('errorMessage'); | 45 | let utils = new Utils('errorMessage'); |
22 | utils.createFileFromUrl(faceCascadeFile, faceCascadeURL, () => { | 46 | utils.createFileFromUrl(faceCascadeFile, faceCascadeURL, () => { |
23 | - activate(); | 47 | + main(); |
24 | }); | 48 | }); |
25 | } | 49 | } |
26 | -function activate() | ||
27 | -{ | ||
28 | - let fileloader = document.getElementById("fileloader"); | ||
29 | - fileloader.disabled = false; | ||
30 | -} | ||
31 | 50 | ||
32 | -function detect_face() | 51 | +function main() |
33 | { | 52 | { |
34 | - let canvas = document.createElement('canvas'); | 53 | + let video = document.getElementById("videoInput"); |
35 | - canvas.width = tempImage.width; | 54 | + let canvasOutput = document.getElementById("canvasOutput"); |
36 | - canvas.height = tempImage.height; | 55 | + let canvasContext = canvasOutput.getContext('2d'); |
37 | - let ctx = canvas.getContext('2d') | 56 | + let src = new cv.Mat(video.height, video.width, cv.CV_8UC4); |
38 | - ctx.drawImage(tempImage, 0, 0); | 57 | + let dst = new cv.Mat(video.height, video.width, cv.CV_8UC4); |
39 | - let src = cv.imread(canvas); | 58 | + let cap = new cv.VideoCapture(video); |
40 | - let dst = new cv.Mat(src.cols, src.rows, cv.CV_8UC4); | ||
41 | - // let gray = new cv.Mat(); | ||
42 | let faces = new cv.RectVector(); | 59 | let faces = new cv.RectVector(); |
43 | let classifier = new cv.CascadeClassifier(); | 60 | let classifier = new cv.CascadeClassifier(); |
61 | + | ||
62 | + class Tracker{ | ||
63 | + constructor(){ | ||
64 | + this.arr = new Array(); | ||
65 | + } | ||
66 | + register = function(x, y, width, height) { | ||
67 | + var x_center = (x + width) / 2; | ||
68 | + var y_center = (y + height) / 2; | ||
69 | + var now = Date.now() | ||
70 | + this.arr = this.arr.filter(ent => now - ent.time < 300); | ||
71 | + for (const prop in this.arr){ | ||
72 | + var prop_x_center = (this.arr[prop].x + this.arr[prop].width) / 2; | ||
73 | + var prop_y_center = (this.arr[prop].y + this.arr[prop].height) / 2; | ||
74 | + if (Math.abs(x_center - prop_x_center) < 10 && Math.abs(y_center - prop_y_center) < 10){ | ||
75 | + this.arr[prop].x = x; | ||
76 | + this.arr[prop].y = y; | ||
77 | + this.arr[prop].width = width; | ||
78 | + this.arr[prop].height = height; | ||
79 | + this.arr[prop].time = now; | ||
80 | + return this.arr[prop].init_time; | ||
81 | + } | ||
82 | + } | ||
83 | + var ent = {x: x, y: y, width: width, height: height, time: now, init_time: now} | ||
84 | + this.arr.push(ent) | ||
85 | + return now; | ||
86 | + } | ||
87 | + }; | ||
88 | + | ||
89 | + var tracker = new Tracker(); | ||
44 | classifier.load('haarcascade_frontalface_default.xml'); | 90 | classifier.load('haarcascade_frontalface_default.xml'); |
91 | + const FPS = 30; | ||
92 | + function processVideo() { | ||
93 | + try { | ||
94 | + if (!streaming) { | ||
95 | + // clean and stop. | ||
96 | + src.delete(); | ||
97 | + dst.delete(); | ||
98 | + gray.delete(); | ||
99 | + faces.delete(); | ||
100 | + classifier.delete(); | ||
101 | + return; | ||
102 | + } | ||
103 | + let begin = Date.now(); | ||
104 | + // start processing. | ||
105 | + cap.read(src); | ||
106 | + cv.flip(src, src, 1); | ||
45 | src.copyTo(dst); | 107 | src.copyTo(dst); |
46 | // cv.cvtColor(dst, gray, cv.COLOR_RGBA2GRAY, 0); | 108 | // cv.cvtColor(dst, gray, cv.COLOR_RGBA2GRAY, 0); |
47 | - let msize = new cv.Size(tempImage.width / 4, tempImage.height / 4); | ||
48 | // detect faces. | 109 | // detect faces. |
110 | + let msize = new cv.Size(video.width / 4, video.height / 4); | ||
49 | classifier.detectMultiScale(dst, faces, 1.1, 3, 0, msize); | 111 | classifier.detectMultiScale(dst, faces, 1.1, 3, 0, msize); |
50 | - if (faces.size() == 0) | ||
51 | - { | ||
52 | - alert('얼굴이 인식되지 않았습니다. 얼굴 이미지가 작지 않은지 확인해주세요.'); | ||
53 | - } | ||
54 | - else if (faces.size() > 1) | ||
55 | - { | ||
56 | - alert('하나의 얼굴만 등록해주세요.') | ||
57 | - } | ||
58 | // draw faces. | 112 | // draw faces. |
113 | + console.log('draw faces'); | ||
59 | for (let i = 0; i < faces.size(); ++i) { | 114 | for (let i = 0; i < faces.size(); ++i) { |
60 | let face = faces.get(i); | 115 | let face = faces.get(i); |
61 | let point1 = new cv.Point(face.x, face.y); | 116 | let point1 = new cv.Point(face.x, face.y); |
62 | let point2 = new cv.Point(face.x + face.width, face.y + face.height); | 117 | let point2 = new cv.Point(face.x + face.width, face.y + face.height); |
63 | cv.rectangle(dst, point1, point2, [255, 0, 0, 255], 8); | 118 | cv.rectangle(dst, point1, point2, [255, 0, 0, 255], 8); |
119 | + let cropped = new cv.Mat(); | ||
64 | let rect = new cv.Rect(Math.max(face.x-22, 0), Math.max(face.y-44, 0), Math.min(face.width+44, src.cols), Math.min(face.height+66, src.rows)); | 120 | let rect = new cv.Rect(Math.max(face.x-22, 0), Math.max(face.y-44, 0), Math.min(face.width+44, src.cols), Math.min(face.height+66, src.rows)); |
65 | - let cropped = src.roi(rect); | 121 | + cropped = src.roi(rect); |
122 | + let tempCanvas = document.createElement("canvas"); | ||
66 | cv.imshow(tempCanvas,cropped); | 123 | cv.imshow(tempCanvas,cropped); |
124 | + if (Date.now() - tracker.register(face.x, face.y, face.width, face.height) > 1000){ | ||
125 | + // 1초동안 인식되면 사진 촬영 종료하고 등록 버튼 활성화 | ||
126 | + b64encoded = tempCanvas.toDataURL("image/jpeg", 1.0); | ||
127 | + toggle_streaming(); | ||
128 | + activate_sender(); | ||
67 | } | 129 | } |
68 | - if (faces.size() == 1) | ||
69 | - { | ||
70 | - let sender = document.getElementById("sender"); | ||
71 | - sender.disabled = false; | ||
72 | } | 130 | } |
73 | - if (dst.cols > $(window).width() || dst.rows > $(window).height()) | 131 | + // to do resize preview |
74 | - { | 132 | + cv.imshow('canvasOutput', dst); |
75 | - let ratio = Math.min($(window).width() / parseFloat(dst.cols), $(window).height() / parseFloat(dst.rows)); | 133 | + // schedule the next one. |
76 | - let dsize = new cv.Size(dst.cols * ratio, dst.rows * ratio); | 134 | + let delay = 1000/FPS - (Date.now() - begin); |
77 | - cv.resize(dst, dst, dsize, 0, 0, cv.INTER_AREA); | 135 | + setTimeout(processVideo, delay); |
136 | + } catch (err) { | ||
137 | + console.log(err); | ||
138 | + } | ||
78 | } | 139 | } |
79 | - let preview = document.getElementById('preview'); | 140 | + setTimeout(processVideo, 0); |
80 | - cv.imshow(preview, dst); | 141 | +} |
142 | + | ||
143 | +function activate_sender() | ||
144 | +{ | ||
145 | + let sender = document.getElementById("sender"); | ||
146 | + fileloader.disabled = false; | ||
147 | +} | ||
148 | + | ||
149 | +function toggle_streaming() | ||
150 | +{ | ||
151 | + streamButton = document.getElementById("streamButton"); | ||
152 | + streaming = !streaming; | ||
153 | + if (streaming) | ||
154 | + streamButton.value = "촬영중지"; | ||
155 | + else | ||
156 | + streamButton.value = "촬영시작"; | ||
157 | + main(); | ||
81 | } | 158 | } |
82 | 159 | ||
83 | function submit() | 160 | function submit() |
84 | { | 161 | { |
85 | - let b64encoded = tempCanvas.toDataURL('image/jpeg', 1.0); | ||
86 | let student_id = document.getElementById('student_id').value; | 162 | let student_id = document.getElementById('student_id').value; |
87 | let student_name = document.getElementById('student_name').value; | 163 | let student_name = document.getElementById('student_name').value; |
88 | b64encoded = b64encoded.replace('data:image/jpeg;base64,', '') | 164 | b64encoded = b64encoded.replace('data:image/jpeg;base64,', '') |
... | @@ -102,20 +178,9 @@ function submit() | ... | @@ -102,20 +178,9 @@ function submit() |
102 | }) | 178 | }) |
103 | } | 179 | } |
104 | 180 | ||
105 | -var loadFile = function(event) { | ||
106 | - var reader = new FileReader(); | ||
107 | - reader.readAsDataURL(event.target.files[0]); | ||
108 | - reader.onload = function() { | ||
109 | - tempImage.src = reader.result; | ||
110 | - tempImage.onload = function() | ||
111 | - { | ||
112 | - detect_face(); | ||
113 | - } | ||
114 | - }; | ||
115 | - }; | ||
116 | </script> | 181 | </script> |
117 | </head> | 182 | </head> |
118 | -<body onload="cv['onRuntimeInitialized']=()=>{load_cascade();};" class="w3-light-grey"> | 183 | +<body onload="cv['onRuntimeInitialized']=()=>{init();};" class="w3-light-grey"> |
119 | <!-- w3-content defines a container for fixed size centered content, | 184 | <!-- w3-content defines a container for fixed size centered content, |
120 | and is wrapped around the whole page content, except for the footer in this example --> | 185 | and is wrapped around the whole page content, except for the footer in this example --> |
121 | <div class="w3-content" style="max-width:1400px"> | 186 | <div class="w3-content" style="max-width:1400px"> |
... | @@ -126,14 +191,15 @@ and is wrapped around the whole page content, except for the footer in this exam | ... | @@ -126,14 +191,15 @@ and is wrapped around the whole page content, except for the footer in this exam |
126 | </header> | 191 | </header> |
127 | 192 | ||
128 | <div class="w3-row", style='text-align:center'> | 193 | <div class="w3-row", style='text-align:center'> |
129 | - <h2><b>얼굴 파일을 등록해주세요 (jpeg only)</b></h2> | 194 | + <h2><b>얼굴을 등록해주세요 (jpeg only)</b></h2> |
195 | + <div id="container"> | ||
196 | + <video autoplay="true" id="videoInput" style="display: none; object-fit: cover;"></video> | ||
197 | + <canvas id="canvasOutput"></canvas> | ||
198 | + </div> | ||
130 | <div> | 199 | <div> |
131 | 학번: <input type="text" id="student_id"><br> | 200 | 학번: <input type="text" id="student_id"><br> |
132 | 이름: <input type="text" id="student_name"><br><br> | 201 | 이름: <input type="text" id="student_name"><br><br> |
133 | - <input type="file" id="fileloader" name="file" onchange="loadFile(event)" autocomplete="off" accept="image/jpeg" required disabled> | 202 | + <input id="streamButton" type="button" onclick="toggle_streaming()" value="활영중지" disabled> |
134 | - <div> | ||
135 | - <canvas id="preview"></canvas> | ||
136 | - </div> | ||
137 | <input id="sender" type="button" onclick="submit()" value="등록" disabled> | 203 | <input id="sender" type="button" onclick="submit()" value="등록" disabled> |
138 | </div> | 204 | </div> |
139 | </div> | 205 | </div> | ... | ... |
-
Please register or login to post a comment