Graduate

Update roll book

......@@ -166,7 +166,7 @@ def verify():
def tempAttendance():
attendance_db = pymysql.connect(read_default_file="./DB.cnf")
cursor = attendance_db.cursor(pymysql.cursors.DictCursor)
sql = "SELECT ls.student_id, s.student_name, sa.status FROM lecture_students AS ls LEFT JOIN student_attendance AS sa ON ls.student_id = sa.student_id LEFT JOIN student AS s ON ls.student_id = s.student_id;"
sql = "SELECT ls.student_id as student_id, s.student_name as student_name, ifnull(sa.status, 'absent') as status FROM lecture_students AS ls LEFT JOIN student_attendance AS sa ON ls.student_id = sa.student_id LEFT JOIN student AS s ON ls.student_id = s.student_id;"
mycursor.execute(sql)
data = mycursor.fetchall()
return render_template('attendance.html', output_data = data)
......
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Web Attendance System Register</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nanum+Gothic:400,700,800&amp;subset=korean">
<style>body,h1,h2,h3,h4,h5,p {font-family: "Nanum+Gothic", sans-serif}</style>
<style>
#container {
margin: 15px auto;
}
#videoInput {
background-color: #666;
}
#canvasOutput {
background-color: #666;
}
#student_id {
margin-top: 10px;
margin-bottom: 5px;
}
#student_name {
margin-bottom: 10px;
}
</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'>
var b64encoded = '';
var streaming = false;
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', () => {
var screenWidth = $(document).width();
var screenHeight = $(document).height();
var headerHeight = $('#header').height();
var inputformHeight = $('#inputForm').height();
var ratio = 1.0;
video.width = video.videoWidth;
video.height = video.videoHeight;
if (video.width > screenWidth || headerHeight + video.height + inputformHeight > screenHeight){
ratio = Math.min(screenWidth / (video.width * 1.0), screenHeight / ((headerHeight + video.height + inputformHeight) * 1.0));
}
container.style.width = Math.round(video.width * ratio) + 'px';
container.style.height = Math.round(video.height * ratio) + 'px';
canvasOutput.width = Math.round(video.width * ratio);
canvasOutput.height = Math.round(video.height * ratio);
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 msize = new cv.Size(video.width / 4, video.height / 4);
let dsize = new cv.Size(canvasOutput.width, canvasOutput.height);
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 this.arr[prop].init_time;
}
}
var ent = {x: x, y: y, width: width, height: height, time: now, init_time: now}
this.arr.push(ent)
return now;
}
};
var tracker = new Tracker();
classifier.load('haarcascade_frontalface_default.xml');
const FPS = 30;
function processVideo() {
try {
if (!streaming) {
// clean and stop.
src.delete();
dst.delete();
faces.delete();
classifier.delete();
return;
}
let begin = Date.now();
// start processing.
cap.read(src);
cv.flip(src, src, 1);
src.copyTo(dst);
// detect faces.
classifier.detectMultiScale(dst, faces, 1.1, 5, 0, msize);
// 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 margin_x = 0;
let margin_y = 0;
if (face.width > face.height)
{
margin_y = (face.width - face.height) / 2;
}
else
{
margin_x = (face.height - face.width) / 2;
}
Math.max(face.width, face.height)
Math.min(face.width, face.height)
let rect = new cv.Rect(Math.max(face.x-margin_x, 0), Math.max(face.y-margin_y, 0), Math.min(face.width+margin_x, src.cols), Math.min(face.height+margin_y, src.rows));
cropped = src.roi(rect);
let tempCanvas = document.createElement("canvas");
cv.imshow(tempCanvas,cropped);
if (Date.now() - tracker.register(face.x, face.y, face.width, face.height) > 1000){
// 1초동안 인식되면 사진 촬영 종료하고 등록 버튼 활성화
cv.rectangle(dst, point1, point2, [0, 255, 0, 255], 8);
b64encoded = tempCanvas.toDataURL("image/jpeg", 0.6);
toggle_streaming();
change_notice("촬영 완료! 정보를 등록해주세요<br>제대로 촬영되지 않은 경우 버튼을 눌러 다시 촬영할 수 있습니다.");
activate_sender();
}
}
// to do resize preview
cv.resize(dst, dst, dsize, 0, 0, cv.INTER_AREA);
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);
}
function activate_sender()
{
let sender = document.getElementById("sender");
sender.disabled = false;
}
function toggle_streaming()
{
let streamButton = document.getElementById("streamButton");
streaming = !streaming;
if (streaming){
streamButton.value = "촬영중지";
change_notice("얼굴이 인식되면 얼굴을 촬영합니다");
}
else{
streamButton.value = "촬영시작";
change_notice("촬영 시작 버튼을 누르면 얼굴을 촬영합니다");
}
main();
}
function change_notice(text)
{
let notice = document.getElementById("notice");
notice.innerHTML = text;
}
function submit()
{
let student_id = document.getElementById('student_id').value;
let student_name = document.getElementById('student_name').value;
if (b64encoded === '')
{
alert("얼굴을 먼저 촬영해주세요");
return;
}
if (!(student_id.length && student_name.length))
{
alert("학번과 이름을 입력해주세요");
return;
}
b64encoded = b64encoded.replace('data:image/jpeg;base64,', '')
$.ajax({
type: "POST",
url: "/register",
dataType: "json",
data: {'image':b64encoded, 'student_id':student_id, 'student_name':student_name},
success: function(data){
if (data.status == "success"){
alert("등록 성공");
}
else if (data.status == "already"){
alert("등록 실패: 이미 등록된 학번입니다. 학번을 변경해주세요.");
}
}
})
}
</script>
</head>
<body onload="cv['onRuntimeInitialized']=()=>{init();};" class="w3-light-grey">
<!-- w3-content defines a container for fixed size centered content,
and is wrapped around the whole page content, except for the footer in this example -->
<div class="w3-content">
<!-- Header -->
<header id="header" class="w3-container w3-center">
<h1><b>얼굴 등록</b></h1>
<p>Made by <span class="w3-tag">정해갑</span></p>
</header>
<div class="w3-row", style='text-align:center'>
<h2 id="notice">촬영 시작 버튼을 누르면 얼굴을 촬영합니다</h2>
<input id="streamButton" type="button" onclick="toggle_streaming()" value="활영시작">
<div id="container">
<video autoplay="true" id="videoInput" style="display: none; object-fit:cover;"></video>
<canvas id="canvasOutput">/canvas>
</div>
<div id="inputForm">
<strong>얼굴 이미지는 서버에 저장되지 않습니다</strong><br>
<strong>(복원 불가능한 512차원 벡터로 변환됩니다)</strong><br>
<strong>학번과 이름은 임의로 입력해주세요</strong><br>
<strong>예)1234/홍길동 등</strong><br>
학번: <input type="text" id="student_id"><br>
이름: <input type="text" id="student_name"><br>
<input id="sender" type="button" onclick="submit()" value="등록" disabled>
</div>
</div>
</div>
</body>
</html>
<title>Attendance</title>
<table>
<thead>
<tr>
<th>학번</th>
<th>이름</th>
<th>출결</th>
</tr>
</thead>
<tbody>
{% for row in output_data %}
<tr>
<td>{{row['student_id']}}</td>
<td>{{row['student_name']}}</td>
<td>{{row['status']}}</td>
</tr>
{% endfor %}
</tbody>
</table>
\ No newline at end of file
......