index.html 6.31 KB
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Web Attendance System</title>
<style>
#container {
	margin: 0px auto;
	border: 10px #333 solid;
}
#videoInput {
	background-color: #666;
}
#canvasOutput {
	background-color: #666;
}
</style>
<script type='text/javascript' src="{{url_for('static', filename='js/opencv.js')}}"></script>
<script type='text/javascript' src="{{url_for('static', filename='js/utils.js')}}"></script>
<script type='text/javascript' src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type='text/javascript'>

function init()
{
    let video = document.getElementById('videoInput');
    let container = document.getElementById('container');
    let canvasOutput = document.getElementById("canvasOutput");
    if (navigator.mediaDevices.getUserMedia){
        navigator.mediaDevices.getUserMedia({ video: true })
	    .then(function (stream) {
                video.srcObject = stream;
	        video.addEventListener('canplay', () => {
                    video.width = video.videoWidth;
                    video.height = video.videoHeight;
                    container.style.width = video.videoWidth + 'px';
                    container.style.height = video.videoHeight + 'px';
                    canvasOutput.width = video.videoWidth;
                    canvasOutput.height = video.videoHeight;
                    load_cascade();
	        });
	    }).catch(function (err0r) {
                console.log("Something went wrong!");
                streaming = false;
            });
    }
}

function load_cascade()
{
    let faceCascadeFile = 'haarcascade_frontalface_default.xml'
    let faceCascadeURL = 'static/js/haarcascade_frontalface_default.xml'
    let utils = new Utils('errorMessage');	
    utils.createFileFromUrl(faceCascadeFile, faceCascadeURL, () => {
        main()
    });
}


function main()
{
let video = document.getElementById("videoInput");
let canvasOutput = document.getElementById("canvasOutput");
let canvasContext = canvasOutput.getContext('2d');
let src = new cv.Mat(video.height, video.width, cv.CV_8UC4);
let dst = new cv.Mat(video.height, video.width, cv.CV_8UC4);
// let gray = new cv.Mat();
let cap = new cv.VideoCapture(video);
let faces = new cv.RectVector();
let classifier = new cv.CascadeClassifier();
class Tracker{
    constructor(){
        this.arr = new Array();
    }
    register = function(x, y, width, height) {
	var x_center = (x + width) / 2;
	var y_center = (y + height) / 2;
	var now = Date.now()
	this.arr = this.arr.filter(ent => now - ent.time < 300);
        for (const prop in this.arr){
	    var prop_x_center = (this.arr[prop].x + this.arr[prop].width) / 2;
	    var prop_y_center = (this.arr[prop].y + this.arr[prop].height) / 2;
            if (Math.abs(x_center - prop_x_center) < 10 && Math.abs(y_center - prop_y_center) < 10){
	        this.arr[prop].x = x;
	        this.arr[prop].y = y;
	        this.arr[prop].width = width;
	        this.arr[prop].height = height;
	        this.arr[prop].time = now;
	        return false;
	    }
	}
	var ent = {x: x, y: y, width: width, height: height, time: now}
	this.arr.push(ent)
	return true;
    }
};
var tracker = new Tracker();
var streaming = true;

classifier.load('haarcascade_frontalface_default.xml');

const FPS = 30;
function processVideo() {
    try {
        if (!streaming) {
            // clean and stop.
            src.delete();
            dst.delete();
            gray.delete();
            faces.delete();
            classifier.delete();
            return;
        }
        let begin = Date.now();
        // start processing.
        cap.read(src);
        cv.flip(src, src, 1);
        src.copyTo(dst);
        // cv.cvtColor(dst, gray, cv.COLOR_RGBA2GRAY, 0);
        // detect faces.
        let msize = new cv.Size(video.width / 4, video.height / 4);
        classifier.detectMultiScale(dst, faces, 1.1, 3, 0, msize);
        // draw faces.
	console.log('draw faces');
        for (let i = 0; i < faces.size(); ++i) {
            let face = faces.get(i);
            let point1 = new cv.Point(face.x, face.y);
            let point2 = new cv.Point(face.x + face.width, face.y + face.height);
            cv.rectangle(dst, point1, point2, [255, 0, 0, 255], 8);
            let cropped = new cv.Mat();
            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));
            cropped = src.roi(rect);
            let tempCanvas = document.createElement("canvas");
            cv.imshow(tempCanvas,cropped);
	    console.log('b64encode');
			if (tracker.register(face.x, face.y, face.width, face.height, Date.now())){
				let b64encoded = tempCanvas.toDataURL("image/jpeg", 1.0);
				b64encoded = b64encoded.replace('data:image/jpeg;base64,', '')
			       console.log('ajax post');
				$.ajax({
					type: "POST",
					url: "/verify",
					dataType: "json",
					data: {'image':b64encoded},
					success: function(data){
					   if (data.status == "attend"){
						   var newDiv = document.createElement("div");
						   var newContent = document.createTextNode('[' + data.student_id + '/' + data.student_name + ']' + "출석");
						   newDiv.appendChild(newContent);
						   document.body.appendChild(newDiv); 
					   }
					   else if (data.status == "already"){
						   var newDiv = document.createElement("div");
						   var newContent = document.createTextNode('[' + data.student_id + '/' + data.student_name + ']' + "이미 출석처리 되었습니다.");
						   newDiv.appendChild(newContent);
				                   document.body.appendChild(newDiv); 
					   }
					   else{
						   var newDiv = document.createElement("div");
						   var newContent = document.createTextNode("실패");
						   newDiv.appendChild(newContent);
					           document.body.appendChild(newDiv);
					   }
					}
				});
			}
        }
        // to do resize preview
        cv.imshow('canvasOutput', dst);
        // schedule the next one.
        let delay = 1000/FPS - (Date.now() - begin);
        setTimeout(processVideo, delay);
    } catch (err) {
        console.log(err);
    }
}
setTimeout(processVideo, 0);
}
</script>
</head>
<body onload="cv['onRuntimeInitialized']=()=>{ init(); };">
<div id="container">
<video autoplay="true" id="videoInput" style="display: none; object-fit: cover;"></video>
<canvas id="canvasOutput"></canvas>
</div>
</body>
</html>