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.

home page face recognition

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.

home page face recognition

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:

  1. Menambahkan table accs_hist di database flask_db
  2. Modifikasi file fr_page.html
  3. 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.

phpmyadmin create table

Tabel accs_hist berhasil dibuat.

accs_hist table

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)

			


Kalau ada pertanyaan silakan comment vidio ini di youtube ya, syaratnya harus subscribe terlebih dahulu 😃