Assalamualaikum, jumpa lagi di triyonos.com dengan tutorial-tutorial pemrograman yang menarik dan mudah untuk dipelajari. Alhamdulillah saya sudah bisa mulai menulis lagi di website kesayangan saya ini dalam rangka berbagi ilmu pemrograman, setelah sekian lama disibukkan dengan urusan pekerjaan sehari-hari. Pada tutorial kali ini, saya akan melanjutkan project aplikasi Face Recognition yang sudah saya buat sebelumnya pada tutorial "Pycharm Flask OpenCV Registrasi Face Recognition Menggunakan Database MySQL".
Sebelumnya ketika tombol Face Recognition pada halaman home diklik akan menampilkan tampilan webcam secara penuh.
Di tutorial ini saya akan memodifikasi halaman face recognition-nya dengan membagi halaman web menjadi 2 bagian yaitu tampilan web cam untuk face recognition dan tabel hasil scan face recognition. Selama proses scan wajah berlangsung ditandai dengan progress bar yang terus terisi sampai 100%. Dan jika sudah 100% data akan disimpan ke dalam database dan langsung ditampilkan ke dalam daftar Today Scan pada tabel sebelah kanan halaman web.
Bagi teman-teman yang ingin mempraktikkan tutorial ini, diwajibkan untuk mengikuti instruksi-instruksi pada tutorial "Pycharm Flask OpenCV Registrasi Face Recognition Menggunakan Database MySQL". Karena file-file project dan database-nya akan kita gunakan pada tutorial ini.
Baik, langsung saja kita lakukan modifikasi aplikasinya dengan tahapan sebagai berikut:
- Menambahkan table accs_hist di database flask_db
- Modifikasi file fr_page.html
- Modifikasi file app.py
1. Menambahkan table accs_hist di database flask_db
Anggap saja aplikasi ini sebagai aplikasi room access control, dimana setiap orang yang akan masuk ke dalam suatu restricted room, harus scan wajah terlebih dahulu. Data personil yang masuk ke restricted room tersebut akan terecord ke dalam database. Di sini saya menambahkan tabel baru dengan nama accs_hist (access history) sebagai tabel untuk menyimpan data personil yang masuk.
Copy script SQL di bawah ini
CREATE TABLE `accs_hist` (
`accs_id` int(11) NOT NULL AUTO_INCREMENT,
`accs_date` date NOT NULL,
`accs_prsn` varchar(3) NOT NULL,
`accs_added` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`accs_id`),
KEY `accs_date` (`accs_date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Buka phpMyAdmin, pilih database flask_db, paste script SQL ke jendela query di tab SQL phpMyAdmin, kemudian klik tombol Go.
Tabel accs_hist berhasil dibuat.
2. Modifikasi file fr_page.html
Layout halaman face recognition ini saya bagi menjadi 2 section, yaitu tampilan video web cam di sebelah kiri dan tabel today scan di sebelah kanan.
Dobel klik file fr_page.html, hapus semua script code, kemudian copy paste script code di bawah ini:
<!DOCTYPE html>
<html lang="en">
<head>
{% include "head.html" %}
</head>
<body >
{% include "navbar.html" %}
<div class="container-fluid">
<div class="row mt-5 mb-5">
<div class="col-md-8">
<img src="{{ url_for('video_feed') }}" width="100%" class="img-thumbnail">
</div>
<div class="col-md-4" style="height:500px; overflow-y:auto;">
<h3>Today Scan</h3>
<table class="table" >
<thead>
<tr>
<th>Person #</th>
<th>Name</th>
<th>Skill</th>
<th>Added</th>
</tr>
</thead>
<tbody id="scandata">
</tbody>
</table>
</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
let lastcnt = 0;
let cnt;
chkNewScan();
function chkNewScan() {
countTodayScan();
setTimeout(chkNewScan, 1000);
}
function countTodayScan() {
$.ajax({
url: '/countTodayScan',
type: 'GET',
dataType: 'json',
success: function(data) {
cnt = data.rowcount;
if (cnt > lastcnt) {
reloadTable();
}
lastcnt = cnt;
},
error: function(result){
console.log('no result!')
}
})
}
function reloadTable() {
$.ajax({
url: '/loadData',
type: 'GET',
dataType: 'json',
success: function(response){
var tr = $("#scandata");
tr.empty();
$.each(response, function(index, item) {
if (item.length > 0) {
for (let i = 0; i < item.length; i++) {
tr.append('<tr>'+
'<td>'+item[i][1]+'</td>'+
'<td>'+item[i][2]+'</td>'+
'<td>'+item[i][3]+'</td>'+
'<td>'+item[i][4]+'</td>'+
'</tr>');
}
}
});
},
error: function(result){
console.log('no result!')
}
});
}
});
</script>
</body>
</html>
3. Modifikasi file app.py
Dobel klik file app.py pada Pycharm IDE. Hapus semua script code yang ada, kemudian copy paste script python code berikut ini:
from flask import Flask, render_template, request, session, redirect, url_for, Response, jsonify
import mysql.connector
import cv2
from PIL import Image
import numpy as np
import os
import time
from datetime import date
app = Flask(__name__)
cnt = 0
pause_cnt = 0
justscanned = False
mydb = mysql.connector.connect(
host="localhost",
user="root",
passwd="",
database="flask_db"
)
mycursor = mydb.cursor()
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Generate dataset >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def generate_dataset(nbr):
face_classifier = cv2.CascadeClassifier("C:/Users/Erik/PycharmProjects/FlaskOpencv_FaceRecognition/resources/haarcascade_frontalface_default.xml")
def face_cropped(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_classifier.detectMultiScale(gray, 1.3, 5)
# scaling factor=1.3
# Minimum neighbor = 5
if faces is ():
return None
for (x, y, w, h) in faces:
cropped_face = img[y:y + h, x:x + w]
return cropped_face
cap = cv2.VideoCapture(0)
mycursor.execute("select ifnull(max(img_id), 0) from img_dataset")
row = mycursor.fetchone()
lastid = row[0]
img_id = lastid
max_imgid = img_id + 100
count_img = 0
while True:
ret, img = cap.read()
if face_cropped(img) is not None:
count_img += 1
img_id += 1
face = cv2.resize(face_cropped(img), (200, 200))
face = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)
file_name_path = "dataset/"+nbr+"."+ str(img_id) + ".jpg"
cv2.imwrite(file_name_path, face)
cv2.putText(face, str(count_img), (50, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
mycursor.execute("""INSERT INTO `img_dataset` (`img_id`, `img_person`) VALUES
('{}', '{}')""".format(img_id, nbr))
mydb.commit()
frame = cv2.imencode('.jpg', face)[1].tobytes()
yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
if cv2.waitKey(1) == 13 or int(img_id) == int(max_imgid):
break
cap.release()
cv2.destroyAllWindows()
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Train Classifier >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@app.route('/train_classifier/<nbr>')
def train_classifier(nbr):
dataset_dir = "C:/Users/Erik/PycharmProjects/FlaskOpencv_FaceRecognition/dataset"
path = [os.path.join(dataset_dir, f) for f in os.listdir(dataset_dir)]
faces = []
ids = []
for image in path:
img = Image.open(image).convert('L');
imageNp = np.array(img, 'uint8')
id = int(os.path.split(image)[1].split(".")[1])
faces.append(imageNp)
ids.append(id)
ids = np.array(ids)
# Train the classifier and save
clf = cv2.face.LBPHFaceRecognizer_create()
clf.train(faces, ids)
clf.write("classifier.xml")
return redirect('/')
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Face Recognition >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def face_recognition(): # generate frame by frame from camera
def draw_boundary(img, classifier, scaleFactor, minNeighbors, color, text, clf):
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
features = classifier.detectMultiScale(gray_image, scaleFactor, minNeighbors)
global justscanned
global pause_cnt
pause_cnt += 1
coords = []
for (x, y, w, h) in features:
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
id, pred = clf.predict(gray_image[y:y + h, x:x + w])
confidence = int(100 * (1 - pred / 300))
if confidence > 70 and not justscanned:
global cnt
cnt += 1
n = (100 / 30) * cnt
# w_filled = (n / 100) * w
w_filled = (cnt / 30) * w
cv2.putText(img, str(int(n))+' %', (x + 20, y + h + 28), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (153, 255, 255), 2, cv2.LINE_AA)
cv2.rectangle(img, (x, y + h + 40), (x + w, y + h + 50), color, 2)
cv2.rectangle(img, (x, y + h + 40), (x + int(w_filled), y + h + 50), (153, 255, 255), cv2.FILLED)
mycursor.execute("select a.img_person, b.prs_name, b.prs_skill "
" from img_dataset a "
" left join prs_mstr b on a.img_person = b.prs_nbr "
" where img_id = " + str(id))
row = mycursor.fetchone()
pnbr = row[0]
pname = row[1]
pskill = row[2]
if int(cnt) == 30:
cnt = 0
mycursor.execute("insert into accs_hist (accs_date, accs_prsn) values('"+str(date.today())+"', '" + pnbr + "')")
mydb.commit()
cv2.putText(img, pname + ' | ' + pskill, (x - 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (153, 255, 255), 2, cv2.LINE_AA)
time.sleep(1)
justscanned = True
pause_cnt = 0
else:
if not justscanned:
cv2.putText(img, 'UNKNOWN', (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2, cv2.LINE_AA)
else:
cv2.putText(img, ' ', (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2,cv2.LINE_AA)
if pause_cnt > 80:
justscanned = False
coords = [x, y, w, h]
return coords
def recognize(img, clf, faceCascade):
coords = draw_boundary(img, faceCascade, 1.1, 10, (255, 255, 0), "Face", clf)
return img
faceCascade = cv2.CascadeClassifier("C:/Users/Erik/PycharmProjects/FlaskOpencv_FaceRecognition/resources/haarcascade_frontalface_default.xml")
clf = cv2.face.LBPHFaceRecognizer_create()
clf.read("classifier.xml")
wCam, hCam = 400, 400
cap = cv2.VideoCapture(0)
cap.set(3, wCam)
cap.set(4, hCam)
while True:
ret, img = cap.read()
img = recognize(img, clf, faceCascade)
frame = cv2.imencode('.jpg', img)[1].tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
key = cv2.waitKey(1)
if key == 27:
break
@app.route('/')
def home():
mycursor.execute("select prs_nbr, prs_name, prs_skill, prs_active, prs_added from prs_mstr")
data = mycursor.fetchall()
return render_template('index.html', data=data)
@app.route('/addprsn')
def addprsn():
mycursor.execute("select ifnull(max(prs_nbr) + 1, 101) from prs_mstr")
row = mycursor.fetchone()
nbr = row[0]
# print(int(nbr))
return render_template('addprsn.html', newnbr=int(nbr))
@app.route('/addprsn_submit', methods=['POST'])
def addprsn_submit():
prsnbr = request.form.get('txtnbr')
prsname = request.form.get('txtname')
prsskill = request.form.get('optskill')
mycursor.execute("""INSERT INTO `prs_mstr` (`prs_nbr`, `prs_name`, `prs_skill`) VALUES
('{}', '{}', '{}')""".format(prsnbr, prsname, prsskill))
mydb.commit()
# return redirect(url_for('home'))
return redirect(url_for('vfdataset_page', prs=prsnbr))
@app.route('/vfdataset_page/<prs>')
def vfdataset_page(prs):
return render_template('gendataset.html', prs=prs)
@app.route('/vidfeed_dataset/<nbr>')
def vidfeed_dataset(nbr):
#Video streaming route. Put this in the src attribute of an img tag
return Response(generate_dataset(nbr), mimetype='multipart/x-mixed-replace; boundary=frame')
@app.route('/video_feed')
def video_feed():
# Video streaming route. Put this in the src attribute of an img tag
return Response(face_recognition(), mimetype='multipart/x-mixed-replace; boundary=frame')
@app.route('/fr_page')
def fr_page():
"""Video streaming home page."""
mycursor.execute("select a.accs_id, a.accs_prsn, b.prs_name, b.prs_skill, a.accs_added "
" from accs_hist a "
" left join prs_mstr b on a.accs_prsn = b.prs_nbr "
" where a.accs_date = curdate() "
" order by 1 desc")
data = mycursor.fetchall()
return render_template('fr_page.html', data=data)
@app.route('/countTodayScan')
def countTodayScan():
mydb = mysql.connector.connect(
host="localhost",
user="root",
passwd="",
database="flask_db"
)
mycursor = mydb.cursor()
mycursor.execute("select count(*) "
" from accs_hist "
" where accs_date = curdate() ")
row = mycursor.fetchone()
rowcount = row[0]
return jsonify({'rowcount': rowcount})
@app.route('/loadData', methods = ['GET', 'POST'])
def loadData():
mydb = mysql.connector.connect(
host="localhost",
user="root",
passwd="",
database="flask_db"
)
mycursor = mydb.cursor()
mycursor.execute("select a.accs_id, a.accs_prsn, b.prs_name, b.prs_skill, date_format(a.accs_added, '%H:%i:%s') "
" from accs_hist a "
" left join prs_mstr b on a.accs_prsn = b.prs_nbr "
" where a.accs_date = curdate() "
" order by 1 desc")
data = mycursor.fetchall()
return jsonify(response = data)
if __name__ == "__main__":
app.run(host='127.0.0.1', port=5000, debug=True)