Spaces:
Sleeping
Sleeping
face rec api with remote mysql 10mb server
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitignore +3 -0
- Dockerfile +29 -0
- README.md +1 -0
- app/__init__.py +56 -0
- app/admin/__init__.py +6 -0
- app/admin/routes.py +136 -0
- app/api/__init__.py +5 -0
- app/api/routes.py +208 -0
- app/demo/__init__.py +6 -0
- app/demo/routes.py +207 -0
- app/face_detection/Models/v1/anchors.txt +4 -0
- app/face_detection/Models/v1/model.h5 +3 -0
- app/face_detection/config.py +15 -0
- app/face_detection/create_load_model.py +254 -0
- app/face_detection/decode_yolo_v2.py +108 -0
- app/face_detection/helper.py +24 -0
- app/face_detection/inference.py +281 -0
- app/face_recognition/Models/v1/config.py +3 -0
- app/face_recognition/Models/v1/model.h5 +3 -0
- app/face_recognition/aligner.py +80 -0
- app/face_recognition/config.py +4 -0
- app/face_recognition/helper.py +115 -0
- app/face_recognition/inference.py +211 -0
- app/face_recognition/setup_db_features.py +28 -0
- app/face_recognition/split_model.py +12 -0
- app/helper.py +140 -0
- app/main/__init__.py +5 -0
- app/main/routes.py +6 -0
- app/static/admin/dashboard.css +32 -0
- app/static/admin/dashboard.js +119 -0
- app/static/admin/login.css +49 -0
- app/static/admin/registeration.js +56 -0
- app/static/demo/index/dark.css +354 -0
- app/static/demo/index/icon.jpg +0 -0
- app/static/demo/index/script.js +498 -0
- app/static/demo/index/style.css +235 -0
- app/static/user/dashboard.css +305 -0
- app/static/user/dashboard.js +483 -0
- app/static/user/login.css +49 -0
- app/static/user/registeration.css +44 -0
- app/static/user/registeration.js +74 -0
- app/templates/admin/dashboard.html +28 -0
- app/templates/admin/login.html +25 -0
- app/templates/base.html +13 -0
- app/templates/demo.html +1 -0
- app/templates/demo/index.html +130 -0
- app/templates/user/dashboard.html +157 -0
- app/templates/user/login.html +25 -0
- app/templates/user/registeration.html +34 -0
- app/user/__init__.py +8 -0
.gitignore
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# *.h5
|
| 2 |
+
myvenv/
|
| 3 |
+
__pycache__/
|
Dockerfile
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM ubuntu
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
RUN apt-get update && \
|
| 5 |
+
apt-get -y upgrade && \
|
| 6 |
+
apt-get install -y \
|
| 7 |
+
sudo
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install python3 pip vim mc wget curl
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
COPY . /app
|
| 14 |
+
WORKDIR /app
|
| 15 |
+
RUN pip install -r requirements.txt
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
CMD python3 -c "print('Docker is more simple Deployment Tool')"
|
| 20 |
+
|
| 21 |
+
RUN pwd
|
| 22 |
+
RUN ls -l
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
EXPOSE 7860
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
CMD "flask run --host=0.0.0.0 --port=7860"
|
README.md
CHANGED
|
@@ -5,6 +5,7 @@ colorFrom: blue
|
|
| 5 |
colorTo: blue
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 5 |
colorTo: blue
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
app_port: 7860
|
| 9 |
---
|
| 10 |
|
| 11 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app/__init__.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask,session
|
| 2 |
+
from datetime import timedelta
|
| 3 |
+
|
| 4 |
+
from app.face_detection import inference as fd
|
| 5 |
+
from app.face_detection.helper import get_crops as fd_get_crops
|
| 6 |
+
from app.face_recognition import inference as fr
|
| 7 |
+
from app.face_recognition.aligner import aligner
|
| 8 |
+
from app.face_recognition import helper as fr_helper
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
face_detector=fd.face_detection("app/face_detection/Models/v1")
|
| 13 |
+
face_detector.square_preprocessing=fd.square_pad()
|
| 14 |
+
# face_recognizer=fr.face_recognition("app/face_recognition/feature_extractor.h5")
|
| 15 |
+
face_recognizer=fr.face_recognition("app/face_recognition/Models/v1")
|
| 16 |
+
aligner_obj=aligner(min_aligner_confidence=0.6)
|
| 17 |
+
|
| 18 |
+
image_size=544
|
| 19 |
+
p_thres=0.7
|
| 20 |
+
nms_thres=0.3
|
| 21 |
+
batch_size=1
|
| 22 |
+
face_detector.set_mode(p_thres,nms_thres,mode="sized",image_size=image_size,batch_size=batch_size)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# def create_app(config_class=Config):
|
| 26 |
+
def create_app():
|
| 27 |
+
app=Flask(__name__)
|
| 28 |
+
# app.config.from_object(config_class)
|
| 29 |
+
# app.permanent_session_lifetime = timedelta(seconds=5)
|
| 30 |
+
|
| 31 |
+
@app.before_request
|
| 32 |
+
def make_session_permanent():
|
| 33 |
+
session.permanent = True
|
| 34 |
+
|
| 35 |
+
from app.main import bp as main_bp
|
| 36 |
+
app.register_blueprint(main_bp)
|
| 37 |
+
|
| 38 |
+
from app.demo import bp as demo_bp
|
| 39 |
+
app.register_blueprint(demo_bp,url_prefix="/demo")
|
| 40 |
+
|
| 41 |
+
from app.user import bp as user_bp
|
| 42 |
+
app.register_blueprint(user_bp,url_prefix='/user')
|
| 43 |
+
|
| 44 |
+
from app.admin import bp as admin_bp
|
| 45 |
+
app.register_blueprint(admin_bp,url_prefix='/admin')
|
| 46 |
+
|
| 47 |
+
from app.api import bp as api_bp
|
| 48 |
+
app.register_blueprint(api_bp,url_prefix='/api')
|
| 49 |
+
|
| 50 |
+
app.secret_key='asdasr34r'
|
| 51 |
+
|
| 52 |
+
@app.route("/test/")
|
| 53 |
+
def test_page():
|
| 54 |
+
return "<h1>This is a test page</h1>"
|
| 55 |
+
|
| 56 |
+
return app
|
app/admin/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Blueprint
|
| 2 |
+
|
| 3 |
+
bp=Blueprint("admin",__name__)
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
from app.admin import routes
|
app/admin/routes.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import render_template,request,jsonify,redirect,url_for,session
|
| 2 |
+
import mysql.connector
|
| 3 |
+
from werkzeug.security import generate_password_hash,check_password_hash
|
| 4 |
+
from app.admin import bp
|
| 5 |
+
from app.helper import generate_random_id,access_database_as_admin,create_user_table,drop_user_table
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@bp.route("/login/")
|
| 9 |
+
def login_page():
|
| 10 |
+
return render_template("admin/login.html")
|
| 11 |
+
|
| 12 |
+
@bp.route("/login/<message>")
|
| 13 |
+
def login_page_message(message):
|
| 14 |
+
return render_template("admin/login.html",message_class='active',message=message)
|
| 15 |
+
|
| 16 |
+
def get_random_unique_id():
|
| 17 |
+
dataBase = access_database_as_admin()
|
| 18 |
+
cursor=dataBase.cursor()
|
| 19 |
+
|
| 20 |
+
while(True):
|
| 21 |
+
random_id=generate_random_id()
|
| 22 |
+
cursor.execute("select username from admins where session_token=%s",[random_id])
|
| 23 |
+
if cursor.fetchone() is None: break
|
| 24 |
+
dataBase.close()
|
| 25 |
+
return random_id
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@bp.route("/authenticate/",methods=["POST"])
|
| 30 |
+
def authenticate():
|
| 31 |
+
dataBase = access_database_as_admin()
|
| 32 |
+
cursor=dataBase.cursor()
|
| 33 |
+
cursor.execute("select password from admins where username=%s",[request.form['username']])
|
| 34 |
+
db_password_hash=cursor.fetchone()
|
| 35 |
+
dataBase.close()
|
| 36 |
+
|
| 37 |
+
# print(db_password_hash)
|
| 38 |
+
if None==db_password_hash:
|
| 39 |
+
# username doesn't exists
|
| 40 |
+
return redirect(url_for('admin.login_page_message', message = "username doesn't exists"))
|
| 41 |
+
|
| 42 |
+
elif(check_password_hash(db_password_hash[0],request.form['password'])):
|
| 43 |
+
# set session and login
|
| 44 |
+
session.permanent = True
|
| 45 |
+
|
| 46 |
+
session['admin_token']=get_random_unique_id()
|
| 47 |
+
|
| 48 |
+
dataBase = access_database_as_admin()
|
| 49 |
+
cursor=dataBase.cursor()
|
| 50 |
+
cursor.execute("update admins set session_token=%s where username=%s",(session['admin_token'],request.form['username']))
|
| 51 |
+
dataBase.commit()
|
| 52 |
+
dataBase.close()
|
| 53 |
+
|
| 54 |
+
return redirect("/admin/")
|
| 55 |
+
else:
|
| 56 |
+
# incorrect password
|
| 57 |
+
return redirect(url_for('admin.login_page_message', message = "Incorrect password"))
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def is_auth(func):
|
| 63 |
+
def wrapper_func(*args,**kwargs):
|
| 64 |
+
if "admin_token" not in session:
|
| 65 |
+
return redirect(url_for('admin.login_page_message', message = "login in first"))
|
| 66 |
+
else:
|
| 67 |
+
dataBase = access_database_as_admin()
|
| 68 |
+
cursor=dataBase.cursor()
|
| 69 |
+
cursor.execute("select username from admins where session_token=%s",[session['admin_token']])
|
| 70 |
+
if cursor.fetchone() is None:
|
| 71 |
+
# no such session in db records
|
| 72 |
+
dataBase.close()
|
| 73 |
+
return redirect(url_for('admin.login_page_message', message = "no such session in db"))
|
| 74 |
+
else:
|
| 75 |
+
dataBase.close()
|
| 76 |
+
return func(*args,**kwargs)
|
| 77 |
+
# Renaming the function name:
|
| 78 |
+
wrapper_func.__name__ = func.__name__
|
| 79 |
+
return wrapper_func
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
@bp.route("/")
|
| 83 |
+
@is_auth
|
| 84 |
+
def user_dashboard():
|
| 85 |
+
return render_template("admin/dashboard.html")
|
| 86 |
+
|
| 87 |
+
@bp.route("/get_all_requests/", methods=["GET"])
|
| 88 |
+
@is_auth
|
| 89 |
+
def get_all_requests():
|
| 90 |
+
dataBase = access_database_as_admin()
|
| 91 |
+
cursor=dataBase.cursor()
|
| 92 |
+
cursor.execute("select username,request_message,access_key from users where access_key is null or access_key!='rejected';")
|
| 93 |
+
data=cursor.fetchall()
|
| 94 |
+
dataBase.close()
|
| 95 |
+
print(data)
|
| 96 |
+
data_dict=dict()
|
| 97 |
+
for one_row in data:
|
| 98 |
+
for i,column_name in enumerate(cursor.column_names):
|
| 99 |
+
data_dict[column_name]=[one_row[i]] if column_name not in data_dict else data_dict[column_name]+[one_row[i]]
|
| 100 |
+
|
| 101 |
+
print(data_dict)
|
| 102 |
+
return jsonify(data_dict)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def get_random_unique_access_key():
|
| 106 |
+
dataBase = access_database_as_admin()
|
| 107 |
+
cursor=dataBase.cursor()
|
| 108 |
+
|
| 109 |
+
while(True):
|
| 110 |
+
random_access_key=generate_random_id()
|
| 111 |
+
cursor.execute("select username from users where access_key=%s",[random_access_key])
|
| 112 |
+
if cursor.fetchone() is None: break
|
| 113 |
+
dataBase.close()
|
| 114 |
+
return random_access_key
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
@bp.route("/update_requests/",methods=["POST"])
|
| 118 |
+
@is_auth
|
| 119 |
+
def update_requests():
|
| 120 |
+
print(request.form)
|
| 121 |
+
dataBase = access_database_as_admin()
|
| 122 |
+
cursor=dataBase.cursor()
|
| 123 |
+
if request.form['mode']=="accept":
|
| 124 |
+
cursor.execute("update users set access_key=%s where username=%s",[get_random_unique_access_key(),request.form['username']])
|
| 125 |
+
create_user_table(request.form['username']) # also add a table for this user
|
| 126 |
+
|
| 127 |
+
elif request.form['mode']=="reject":
|
| 128 |
+
cursor.execute("update users set access_key=%s where username=%s",["rejected",request.form['username']])
|
| 129 |
+
drop_user_table(request.form['username']) # Drop table for this user
|
| 130 |
+
|
| 131 |
+
elif request.form['mode']=="revoke":
|
| 132 |
+
cursor.execute("update users set access_key=NULL where username=%s",[request.form['username']])
|
| 133 |
+
|
| 134 |
+
dataBase.commit()
|
| 135 |
+
dataBase.close()
|
| 136 |
+
return jsonify({"message":"success"})
|
app/api/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Blueprint
|
| 2 |
+
|
| 3 |
+
bp=Blueprint("api",__name__)
|
| 4 |
+
|
| 5 |
+
from app.api import routes
|
app/api/routes.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import request,jsonify,g
|
| 2 |
+
from app.api import bp
|
| 3 |
+
from app.helper import generate_random_id,access_database_as_admin,image_to_base64,base64_to_image,add_row_user_table,read_row_user_table,read_user_table,remove_person_from_user_table
|
| 4 |
+
from PIL import ImageOps,Image
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
from app import face_detector,face_recognizer,aligner_obj,fd_get_crops,fr_helper
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
#################################################################################################################################################
|
| 12 |
+
|
| 13 |
+
def set_image_size(settings,mode):
|
| 14 |
+
if mode=='small':
|
| 15 |
+
face_detector.image_size=[settings['small_size']]
|
| 16 |
+
elif mode=='large':
|
| 17 |
+
face_detector.image_size=[settings['large_size']]
|
| 18 |
+
elif mode=='both':
|
| 19 |
+
face_detector.image_size=[settings['small_size'],settings['large_size']]
|
| 20 |
+
else:
|
| 21 |
+
raise("Error")
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def load_settings(username):
|
| 27 |
+
|
| 28 |
+
dataBase = access_database_as_admin()
|
| 29 |
+
cursor=dataBase.cursor()
|
| 30 |
+
cursor.execute("select * from user_settings where username=%s",[username])
|
| 31 |
+
settings=cursor.fetchone()
|
| 32 |
+
columns=cursor.column_names
|
| 33 |
+
|
| 34 |
+
if settings is None:
|
| 35 |
+
# get default settings and insert a row in user_settings
|
| 36 |
+
cursor.execute("select p_thres,nms_thres,small_size,large_size,d_thres,a_thres,db_mode,fr_mode from default_settings where page='user'")
|
| 37 |
+
settings=cursor.fetchone()
|
| 38 |
+
columns=cursor.column_names
|
| 39 |
+
cursor.execute(f"insert into user_settings(username,{','.join(columns)}) values(%s,{','.join(map(lambda x:'%s',columns))})",(session['user']['username'],)+settings)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
settings= dict(zip(columns, settings))
|
| 43 |
+
# Disconnecting from the server
|
| 44 |
+
dataBase.commit()
|
| 45 |
+
dataBase.close()
|
| 46 |
+
|
| 47 |
+
# set face detector settings
|
| 48 |
+
face_detector.p_thres=settings['p_thres']
|
| 49 |
+
face_detector.nms_thres=settings['nms_thres']
|
| 50 |
+
# we will set image_size inside routes
|
| 51 |
+
|
| 52 |
+
# set face aligner settings
|
| 53 |
+
aligner_obj.face_mesh_images.min_detection_confidence=settings['a_thres']
|
| 54 |
+
|
| 55 |
+
# set face recognizer settings
|
| 56 |
+
face_recognizer.thres=settings['d_thres']
|
| 57 |
+
|
| 58 |
+
return settings
|
| 59 |
+
|
| 60 |
+
#################################################################################################################################################
|
| 61 |
+
|
| 62 |
+
def is_auth(func):
|
| 63 |
+
def wrapper_func(*args,**kwargs):
|
| 64 |
+
if "access_key" not in request.form: return jsonify({"message":"send access key too"})
|
| 65 |
+
else:
|
| 66 |
+
dataBase = access_database_as_admin()
|
| 67 |
+
cursor=dataBase.cursor()
|
| 68 |
+
cursor.execute("select username from users where access_key=%s",[request.form["access_key"]])
|
| 69 |
+
data=cursor.fetchone()
|
| 70 |
+
if data is None:
|
| 71 |
+
dataBase.close()
|
| 72 |
+
return jsonify({"message":"no such access key in database"})
|
| 73 |
+
else:
|
| 74 |
+
dataBase.close()
|
| 75 |
+
return func(data[0],*args,**kwargs)
|
| 76 |
+
# Renaming the function name:
|
| 77 |
+
wrapper_func.__name__ = func.__name__
|
| 78 |
+
return wrapper_func
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
@bp.route("/get_crops/",methods=["POST"])
|
| 82 |
+
@is_auth
|
| 83 |
+
def get_crops(username):
|
| 84 |
+
|
| 85 |
+
settings=load_settings(username)
|
| 86 |
+
set_image_size(settings,settings["db_mode"])
|
| 87 |
+
if "image_size" in request.form: face_detector.image_size=list(map(lambda x:int(x),request.form["image_size"].split(",")))
|
| 88 |
+
if "thres" in request.form: face_recognizer.thres=request.form["thres"]
|
| 89 |
+
print(face_detector.image_size)
|
| 90 |
+
|
| 91 |
+
file = request.files['image']
|
| 92 |
+
|
| 93 |
+
image=Image.open(file.stream).convert("RGB")
|
| 94 |
+
image = ImageOps.exif_transpose(image)
|
| 95 |
+
image=np.array(image)
|
| 96 |
+
print(image.shape)
|
| 97 |
+
|
| 98 |
+
image,objs_found=face_detector.predict(image)
|
| 99 |
+
print(objs_found)
|
| 100 |
+
|
| 101 |
+
all_aligned_crops=fd_get_crops(image,objs_found,aligner_obj,resize=(face_recognizer.model_config.input_size,face_recognizer.model_config.input_size))
|
| 102 |
+
all_aligned_crops_base64=[]
|
| 103 |
+
|
| 104 |
+
for i,aligned_crop in enumerate(all_aligned_crops):
|
| 105 |
+
all_aligned_crops_base64.append(image_to_base64(aligned_crop))
|
| 106 |
+
|
| 107 |
+
return jsonify({"message":"success","crops":all_aligned_crops_base64})
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
@bp.route("/add_person/",methods=["POST"])
|
| 113 |
+
@is_auth
|
| 114 |
+
def add_person(username):
|
| 115 |
+
|
| 116 |
+
# print(request.form)
|
| 117 |
+
json_data=request.get_json()
|
| 118 |
+
person_name=json_data['person_name']
|
| 119 |
+
remarks=json_data['remarks']
|
| 120 |
+
group_id=json_data["group_id"] if "group_id" in json_data else None
|
| 121 |
+
print(person_name)
|
| 122 |
+
all_remarks=[]
|
| 123 |
+
all_remarks_features=[]
|
| 124 |
+
for remark in remarks.keys():
|
| 125 |
+
all_img_features=[]
|
| 126 |
+
for img_base64 in remarks[remark]:
|
| 127 |
+
img=base64_to_image(img_base64)
|
| 128 |
+
# print(remark,img.shape)
|
| 129 |
+
|
| 130 |
+
all_img_features.append(face_recognizer.feature_extractor.predict(img[None,:,:,::-1],verbose=0)[0])
|
| 131 |
+
all_img_features=np.array(all_img_features)
|
| 132 |
+
all_remarks_features.append(all_img_features.mean(axis=0))
|
| 133 |
+
all_remarks.append(remark)
|
| 134 |
+
|
| 135 |
+
all_remarks_features=np.array(all_remarks_features)
|
| 136 |
+
|
| 137 |
+
print(all_remarks_features.shape)
|
| 138 |
+
print(all_remarks)
|
| 139 |
+
print(username)
|
| 140 |
+
|
| 141 |
+
add_row_user_table(username=username,person_id=person_name,face_vectors=all_remarks_features.astype("float64"),remarks=",".join(all_remarks),group_id=group_id)
|
| 142 |
+
read_row_user_table(username)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
return jsonify({"message":"success"})
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
@bp.route("/remove_person/",methods=["POST"])
|
| 150 |
+
@is_auth
|
| 151 |
+
def remove_person(username):
|
| 152 |
+
|
| 153 |
+
print(username)
|
| 154 |
+
remove_person_from_user_table(username,request.get_json()["person_id"])
|
| 155 |
+
|
| 156 |
+
return jsonify({"message":"success"})
|
| 157 |
+
# return jsonify({"message":"success",'image':pred_img})
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
@bp.route("/face_recognize/",methods=["POST"])
|
| 161 |
+
@is_auth
|
| 162 |
+
def face_recognition(username):
|
| 163 |
+
|
| 164 |
+
settings=load_settings(username)
|
| 165 |
+
set_image_size(settings,settings["fr_mode"])
|
| 166 |
+
if "image_size" in request.form: face_detector.image_size=list(map(lambda x:int(x),request.form["image_size"].split(",")))
|
| 167 |
+
if "d_thres" in request.form: face_recognizer.thres=request.form["d_thres"]
|
| 168 |
+
print(face_detector.image_size)
|
| 169 |
+
|
| 170 |
+
# print(request.form)
|
| 171 |
+
file = request.files['image']
|
| 172 |
+
|
| 173 |
+
image=Image.open(file.stream).convert("RGB")
|
| 174 |
+
image = ImageOps.exif_transpose(image)
|
| 175 |
+
image=np.array(image)
|
| 176 |
+
print(image.shape)
|
| 177 |
+
|
| 178 |
+
print(username)
|
| 179 |
+
data=read_user_table(username) if "group_id" not in request.form else read_user_table(username,request.form["group_id"])
|
| 180 |
+
faces=data['person_id']
|
| 181 |
+
db_faces_features=data['face_vectors']
|
| 182 |
+
|
| 183 |
+
for i in range(len(faces)):
|
| 184 |
+
print(faces[i],":",db_faces_features[i].shape)
|
| 185 |
+
|
| 186 |
+
# face_recognizer.set_face_db_and_mode(faces=faces,db_faces_features=db_faces_features,distance_mode="avg",recognition_mode="repeat")
|
| 187 |
+
face_recognizer.set_face_db_and_mode(faces=faces,db_faces_features=db_faces_features,distance_mode="best",recognition_mode="repeat")
|
| 188 |
+
img,objs_found=face_detector.predict(image)
|
| 189 |
+
h,w=img.shape[:2]
|
| 190 |
+
tree=fr_helper.objs_found_to_xml("test.jpg",w,h,objs_found)
|
| 191 |
+
tree=face_recognizer.predict(img,tree)
|
| 192 |
+
pred_img=fr_helper.show_pred_image(tree,img)
|
| 193 |
+
pred_img=image_to_base64(pred_img)
|
| 194 |
+
objs_found=fr_helper.xml_to_objs_found(tree) # everything is okay till here
|
| 195 |
+
# print(objs_found[0])
|
| 196 |
+
|
| 197 |
+
objs_found=face_detector.square_preprocessing.rescale(objs_found) #rescale coordinates to original image's resolution
|
| 198 |
+
# print(objs_found[0])
|
| 199 |
+
|
| 200 |
+
all_crops=fd_get_crops(image,objs_found)
|
| 201 |
+
all_crops_base64=[]
|
| 202 |
+
|
| 203 |
+
for i,aligned_crop in enumerate(all_crops):
|
| 204 |
+
all_crops_base64.append(image_to_base64(aligned_crop))
|
| 205 |
+
|
| 206 |
+
person_ids=[obj_found['class'] for obj_found in objs_found]
|
| 207 |
+
|
| 208 |
+
return jsonify({"message":"success","pred_image":pred_img,"person_ids":person_ids,"crops":all_crops_base64,"objs_found":objs_found})
|
app/demo/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Blueprint
|
| 2 |
+
|
| 3 |
+
bp=Blueprint("demo",__name__)
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
from app.demo import routes
|
app/demo/routes.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from werkzeug.utils import secure_filename
|
| 2 |
+
import cv2
|
| 3 |
+
import numpy as np
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import os
|
| 6 |
+
from glob import glob
|
| 7 |
+
import shutil
|
| 8 |
+
import io
|
| 9 |
+
import base64
|
| 10 |
+
import uuid
|
| 11 |
+
from flask import render_template,request,send_from_directory,session,jsonify,json
|
| 12 |
+
from app import helper
|
| 13 |
+
from PIL import ImageOps
|
| 14 |
+
from app.helper import generate_random_id,access_database_as_admin,image_to_base64,base64_to_image,add_row_user_table,read_row_user_table,read_user_table,remove_person_from_user_table
|
| 15 |
+
from app.demo import bp
|
| 16 |
+
import copy
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
from app import face_detector,face_recognizer,aligner_obj,fd_get_crops,fr_helper,fd,fr
|
| 23 |
+
|
| 24 |
+
# @bp.before_app_first_request
|
| 25 |
+
# def create_temp_path():
|
| 26 |
+
# if not os.path.exists(temp_path):os.mkdir(temp_path)
|
| 27 |
+
|
| 28 |
+
# @bp.route(f"/{temp_path}/<subfolder>/<filename>")
|
| 29 |
+
# def upload(filename,subfolder):
|
| 30 |
+
# return send_from_directory(temp_path+f"/{subfolder}/", filename)
|
| 31 |
+
|
| 32 |
+
@bp.route("/")
|
| 33 |
+
def index():
|
| 34 |
+
return render_template('demo/index.html')
|
| 35 |
+
|
| 36 |
+
def load_settings(func):
|
| 37 |
+
def wrapper_func(*args,**kwargs):
|
| 38 |
+
# set face detector settings
|
| 39 |
+
face_detector.p_thres=session["demo"]['settings']['p_thres']
|
| 40 |
+
face_detector.nms_thres=session["demo"]['settings']['nms_thres']
|
| 41 |
+
# we will set image_size inside routes
|
| 42 |
+
|
| 43 |
+
# set face aligner settings
|
| 44 |
+
aligner_obj.face_mesh_images.min_detection_confidence=session["demo"]['settings']['a_thres']
|
| 45 |
+
|
| 46 |
+
# set face recognizer settings
|
| 47 |
+
face_recognizer.thres=session["demo"]['settings']['d_thres']
|
| 48 |
+
|
| 49 |
+
return func(*args,**kwargs)
|
| 50 |
+
# Renaming the function name:
|
| 51 |
+
wrapper_func.__name__ = func.__name__
|
| 52 |
+
return wrapper_func
|
| 53 |
+
|
| 54 |
+
def get_image_size(mode):
|
| 55 |
+
if mode=='small':
|
| 56 |
+
return [session["demo"]['settings']['small_size']]
|
| 57 |
+
elif mode=='large':
|
| 58 |
+
return [session["demo"]['settings']['large_size']]
|
| 59 |
+
elif mode=='both':
|
| 60 |
+
return [session["demo"]['settings']['small_size'],session["demo"]['settings']['large_size']]
|
| 61 |
+
else:
|
| 62 |
+
raise("Error")
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@bp.route("/add_crops/",methods=["POST"])
|
| 66 |
+
@load_settings
|
| 67 |
+
def set_crops():
|
| 68 |
+
# set session
|
| 69 |
+
if not "demo_token" in session:
|
| 70 |
+
session["demo_token"]=helper.generate_random_id()
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
# exit();
|
| 74 |
+
# cursor.execute("insert into demo_sessions",[request.form['username']])
|
| 75 |
+
|
| 76 |
+
print(request.form)
|
| 77 |
+
file = request.files['image']
|
| 78 |
+
print(file)
|
| 79 |
+
fname = secure_filename(file.filename)
|
| 80 |
+
|
| 81 |
+
image=Image.open(file.stream).convert("RGB")
|
| 82 |
+
image = ImageOps.exif_transpose(image)
|
| 83 |
+
image=np.array(image)
|
| 84 |
+
|
| 85 |
+
# do your deep learning work
|
| 86 |
+
face_detector.image_size=get_image_size(session["demo"]['settings']['db_mode'])
|
| 87 |
+
print(face_detector.image_size)
|
| 88 |
+
|
| 89 |
+
image,objs_found=face_detector.predict(image)
|
| 90 |
+
print(face_detector.image_size)
|
| 91 |
+
|
| 92 |
+
all_aligned_crops=fd_get_crops(image,objs_found,aligner_obj,resize=(face_recognizer.model_config.input_size,face_recognizer.model_config.input_size))
|
| 93 |
+
all_aligned_crops_base64=[]
|
| 94 |
+
all_aligned_crops_names=[]
|
| 95 |
+
|
| 96 |
+
for i,aligned_crop in enumerate(all_aligned_crops):
|
| 97 |
+
all_aligned_crops_base64.append(helper.image_to_base64(aligned_crop))
|
| 98 |
+
|
| 99 |
+
if (len(all_aligned_crops_base64)!=0):
|
| 100 |
+
image=fd.pred_image(image,objs_found)
|
| 101 |
+
|
| 102 |
+
img_base64=helper.image_to_base64(image)
|
| 103 |
+
print(img_base64[:10])
|
| 104 |
+
|
| 105 |
+
return jsonify({"message":"successful","image":img_base64,"image_name":fname,"crops":all_aligned_crops_base64})
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
@bp.route("/update_crops_labels/",methods=["POST"])
|
| 109 |
+
@load_settings
|
| 110 |
+
def update_crops_labels():
|
| 111 |
+
print(request.form.keys())
|
| 112 |
+
|
| 113 |
+
imgs_base64=request.form['faces'].split(",")
|
| 114 |
+
print(len(imgs_base64))
|
| 115 |
+
all_img_features=[]
|
| 116 |
+
for img_base64 in imgs_base64:
|
| 117 |
+
img=cv2.resize(base64_to_image(img_base64),[face_recognizer.model_config.input_size,face_recognizer.model_config.input_size])
|
| 118 |
+
all_img_features.append(face_recognizer.feature_extractor.predict(img[None,...],verbose=0)[0])
|
| 119 |
+
all_img_features[-1]=all_img_features[-1].astype("float32").tobytes().decode("latin-1")
|
| 120 |
+
|
| 121 |
+
print(all_img_features.__len__())
|
| 122 |
+
# for decoding numpy array
|
| 123 |
+
# print(all_img_features[0])
|
| 124 |
+
# print(np.frombuffer(all_img_features[0].encode("latin-1"),dtype="float32"))
|
| 125 |
+
|
| 126 |
+
return jsonify({"message":"success","features":all_img_features})
|
| 127 |
+
|
| 128 |
+
@bp.route("/face_recognition/",methods=["POST"])
|
| 129 |
+
@load_settings
|
| 130 |
+
def face_recognition():
|
| 131 |
+
|
| 132 |
+
file = request.files['image']
|
| 133 |
+
# print(file)
|
| 134 |
+
|
| 135 |
+
image=Image.open(file.stream).convert("RGB")
|
| 136 |
+
image = ImageOps.exif_transpose(image)
|
| 137 |
+
image=np.array(image)
|
| 138 |
+
db_images=json.loads(request.form['db_images'])
|
| 139 |
+
|
| 140 |
+
db_faces_features=[]
|
| 141 |
+
names=[]
|
| 142 |
+
for name in db_images:
|
| 143 |
+
names.append(name)
|
| 144 |
+
person_features=[]
|
| 145 |
+
for decoded_features in db_images[name]:
|
| 146 |
+
person_features.append(np.frombuffer(decoded_features.encode("latin-1"),dtype="float32"))
|
| 147 |
+
person_features=np.array(person_features)
|
| 148 |
+
db_faces_features.append(person_features)
|
| 149 |
+
|
| 150 |
+
for i in range(len(names)):
|
| 151 |
+
print(names[i],":",db_faces_features[i].shape)
|
| 152 |
+
|
| 153 |
+
# face_recognizer.set_face_db_and_mode(faces=faces,db_faces_features=db_faces_features,distance_mode="avg",recognition_mode="repeat")
|
| 154 |
+
face_recognizer.set_face_db_and_mode(faces=names,db_faces_features=db_faces_features,distance_mode="best",recognition_mode="repeat")
|
| 155 |
+
|
| 156 |
+
face_detector.image_size=get_image_size(session["demo"]['settings']['fr_mode'])
|
| 157 |
+
|
| 158 |
+
img,objs_found=face_detector.predict(image)
|
| 159 |
+
h,w=img.shape[:2]
|
| 160 |
+
tree=fr_helper.objs_found_to_xml("test.jpg",w,h,objs_found)
|
| 161 |
+
tree=face_recognizer.predict(img,tree)
|
| 162 |
+
pred_img=fr_helper.show_pred_image(tree,img)
|
| 163 |
+
|
| 164 |
+
pred_img=helper.image_to_base64(pred_img)
|
| 165 |
+
|
| 166 |
+
return jsonify({"message":"success",'image':pred_img})
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
@bp.route("/get_settings/",methods=['GET'])
|
| 170 |
+
def get_settings():
|
| 171 |
+
session.permanent=True
|
| 172 |
+
if "demo" not in session:
|
| 173 |
+
dataBase = access_database_as_admin()
|
| 174 |
+
cursor=dataBase.cursor()
|
| 175 |
+
cursor.execute("select * from default_settings where page='demo'")
|
| 176 |
+
|
| 177 |
+
session["demo"]={'settings':dict()}
|
| 178 |
+
session["demo"]['settings']= dict(zip(cursor.column_names, cursor.fetchone()))
|
| 179 |
+
# Disconnecting from the server
|
| 180 |
+
dataBase.commit()
|
| 181 |
+
dataBase.close()
|
| 182 |
+
|
| 183 |
+
return session["demo"]['settings']
|
| 184 |
+
|
| 185 |
+
@bp.route("/reset_settings/",methods=['GET'])
|
| 186 |
+
def reset_settings():
|
| 187 |
+
del session["demo"]
|
| 188 |
+
|
| 189 |
+
return {"message":"success"}
|
| 190 |
+
|
| 191 |
+
@bp.route("/update_settings/",methods=['POST'])
|
| 192 |
+
def update_settings():
|
| 193 |
+
json_data=request.get_json()
|
| 194 |
+
|
| 195 |
+
if "demo" not in session:
|
| 196 |
+
session["demo"]={'settings':dict()}
|
| 197 |
+
# print(json_data)
|
| 198 |
+
session["demo"]['settings']['p_thres']=float(json_data['p_thres'])
|
| 199 |
+
session["demo"]['settings']['nms_thres']=float(json_data['nms_thres'])
|
| 200 |
+
session["demo"]['settings']['large_size']=int(json_data['large_size'])
|
| 201 |
+
session["demo"]['settings']['small_size']=int(json_data['small_size'])
|
| 202 |
+
session["demo"]['settings']['d_thres']=float(json_data['d_thres'])
|
| 203 |
+
session["demo"]['settings']['a_thres']=float(json_data['a_thres'])
|
| 204 |
+
session["demo"]['settings']['db_mode']=json_data['db_mode']
|
| 205 |
+
session["demo"]['settings']['fr_mode']=json_data['fr_mode']
|
| 206 |
+
|
| 207 |
+
return {"message":"success"}
|
app/face_detection/Models/v1/anchors.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
1.577280900000000097e+00 1.936586900000000000e+00
|
| 2 |
+
8.381339999999999790e+00 1.007399999999999984e+01
|
| 3 |
+
4.881808999999999621e+00 5.764687000000000339e+00
|
| 4 |
+
2.773467000000000127e+00 3.284511999999999876e+00
|
app/face_detection/Models/v1/model.h5
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:90dd2a59c7acc25d967c9914a7a53362ed2229c56e3105f538e04388fce70390
|
| 3 |
+
size 607092424
|
app/face_detection/config.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import tensorflow.keras.backend as K
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
cell_size=32
|
| 7 |
+
class_names=['face']
|
| 8 |
+
class_colors={class_name:np.random.rand(3) for class_name in class_names}
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class_to_idx={class_name:i for i,class_name in enumerate(class_names)}
|
| 13 |
+
idx_to_class={i:class_name for i,class_name in enumerate(class_names)}
|
| 14 |
+
|
| 15 |
+
|
app/face_detection/create_load_model.py
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import tensorflow as tf
|
| 3 |
+
from tensorflow.keras import Model,layers
|
| 4 |
+
from app.face_detection.config import class_names
|
| 5 |
+
|
| 6 |
+
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
|
| 7 |
+
# custom layer for reshaping last layer
|
| 8 |
+
|
| 9 |
+
# custom layer for reshaping last layer
|
| 10 |
+
|
| 11 |
+
class yolo_reshape(tf.keras.layers.Layer):
|
| 12 |
+
global num_anchors
|
| 13 |
+
def __init__(self, **kwargs):
|
| 14 |
+
super(yolo_reshape,self).__init__()
|
| 15 |
+
self.last_item=(5+len(class_names))
|
| 16 |
+
def call(self,output_layer):
|
| 17 |
+
shape = [tf.shape(output_layer)[k] for k in range(4)]
|
| 18 |
+
# print(shape)
|
| 19 |
+
# tf.print(shape)
|
| 20 |
+
return tf.reshape(output_layer,[shape[0],shape[1],shape[2],num_anchors,self.last_item])
|
| 21 |
+
|
| 22 |
+
def create_model():
|
| 23 |
+
global num_anchors
|
| 24 |
+
def space_to_depth_x2(x):
|
| 25 |
+
return tf.nn.space_to_depth(x,block_size=2)
|
| 26 |
+
|
| 27 |
+
x_input=layers.Input(shape=(None,None,3))
|
| 28 |
+
x=layers.Lambda(lambda x:x/255.)(x_input)
|
| 29 |
+
x=layers.Conv2D(32,(3,3),strides=(1,1),padding='same',name='conv_1',use_bias=False)(x)
|
| 30 |
+
x=layers.BatchNormalization(name='norm_1')(x)
|
| 31 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 32 |
+
x=layers.MaxPooling2D(pool_size=(2,2))(x)
|
| 33 |
+
|
| 34 |
+
x=layers.Conv2D(64,(3,3),strides=(1,1),padding='same',name='conv_2',use_bias=False)(x)
|
| 35 |
+
x=layers.BatchNormalization(name='norm_2')(x)
|
| 36 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 37 |
+
x=layers.MaxPooling2D(pool_size=(2,2))(x)
|
| 38 |
+
|
| 39 |
+
x=layers.Conv2D(128,(3,3),strides=(1,1),padding='same',name='conv_3',use_bias=False)(x)
|
| 40 |
+
x=layers.BatchNormalization(name='norm_3')(x)
|
| 41 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 42 |
+
|
| 43 |
+
x=layers.Conv2D(64,(1,1),strides=(1,1),padding='same',name='conv_4',use_bias=False)(x)
|
| 44 |
+
x=layers.BatchNormalization(name='norm_4')(x)
|
| 45 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 46 |
+
|
| 47 |
+
x=layers.Conv2D(128,(3,3),strides=(1,1),padding='same',name='conv_5',use_bias=False)(x)
|
| 48 |
+
x=layers.BatchNormalization(name='norm_5')(x)
|
| 49 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 50 |
+
x=layers.MaxPooling2D(pool_size=(2,2))(x)
|
| 51 |
+
|
| 52 |
+
x=layers.Conv2D(256,(3,3),strides=(1,1),padding='same',name='conv_6',use_bias=False)(x)
|
| 53 |
+
x=layers.BatchNormalization(name='norm_6')(x)
|
| 54 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 55 |
+
|
| 56 |
+
x=layers.Conv2D(128,(1,1),strides=(1,1),padding='same',name='conv_7',use_bias=False)(x)
|
| 57 |
+
x=layers.BatchNormalization(name='norm_7')(x)
|
| 58 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 59 |
+
|
| 60 |
+
x=layers.Conv2D(256,(3,3),strides=(1,1),padding='same',name='conv_8',use_bias=False)(x)
|
| 61 |
+
x=layers.BatchNormalization(name='norm_8')(x)
|
| 62 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 63 |
+
x=layers.MaxPooling2D(pool_size=(2,2))(x)
|
| 64 |
+
|
| 65 |
+
x=layers.Conv2D(512,(3,3),strides=(1,1),padding='same',name='conv_9',use_bias=False)(x)
|
| 66 |
+
x=layers.BatchNormalization(name='norm_9')(x)
|
| 67 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 68 |
+
|
| 69 |
+
x=layers.Conv2D(256,(1,1),strides=(1,1),padding='same',name='conv_10',use_bias=False)(x)
|
| 70 |
+
x=layers.BatchNormalization(name='norm_10')(x)
|
| 71 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 72 |
+
|
| 73 |
+
x=layers.Conv2D(512,(3,3),strides=(1,1),padding='same',name='conv_11',use_bias=False)(x)
|
| 74 |
+
x=layers.BatchNormalization(name='norm_11')(x)
|
| 75 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 76 |
+
|
| 77 |
+
x=layers.Conv2D(256,(1,1),strides=(1,1),padding='same',name='conv_12',use_bias=False)(x)
|
| 78 |
+
x=layers.BatchNormalization(name='norm_12')(x)
|
| 79 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 80 |
+
|
| 81 |
+
x=layers.Conv2D(512,(3,3),strides=(1,1),padding='same',name='conv_13',use_bias=False)(x)
|
| 82 |
+
x=layers.BatchNormalization(name='norm_13')(x)
|
| 83 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 84 |
+
|
| 85 |
+
skip_connection = x
|
| 86 |
+
|
| 87 |
+
x=layers.MaxPooling2D(pool_size=(2,2))(x)
|
| 88 |
+
|
| 89 |
+
x=layers.Conv2D(1024,(3,3),strides=(1,1),padding='same',name='conv_14',use_bias=False)(x)
|
| 90 |
+
x=layers.BatchNormalization(name='norm_14')(x)
|
| 91 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 92 |
+
|
| 93 |
+
x=layers.Conv2D(512,(1,1),strides=(1,1),padding='same',name='conv_15',use_bias=False)(x)
|
| 94 |
+
x=layers.BatchNormalization(name='norm_15')(x)
|
| 95 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 96 |
+
|
| 97 |
+
x=layers.Conv2D(1024,(3,3),strides=(1,1),padding='same',name='conv_16',use_bias=False)(x)
|
| 98 |
+
x=layers.BatchNormalization(name='norm_16')(x)
|
| 99 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 100 |
+
|
| 101 |
+
x=layers.Conv2D(512,(1,1),strides=(1,1),padding='same',name='conv_17',use_bias=False)(x)
|
| 102 |
+
x=layers.BatchNormalization(name='norm_17')(x)
|
| 103 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 104 |
+
|
| 105 |
+
x=layers.Conv2D(1024,(3,3),strides=(1,1),padding='same',name='conv_18',use_bias=False)(x)
|
| 106 |
+
x=layers.BatchNormalization(name='norm_18')(x)
|
| 107 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 108 |
+
|
| 109 |
+
x=layers.Conv2D(1024,(3,3),strides=(1,1),padding='same',name='conv_19',use_bias=False)(x)
|
| 110 |
+
x=layers.BatchNormalization(name='norm_19')(x)
|
| 111 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 112 |
+
|
| 113 |
+
x=layers.Conv2D(1024,(3,3),strides=(1,1),padding='same',name='conv_20',use_bias=False)(x)
|
| 114 |
+
x=layers.BatchNormalization(name='norm_20')(x)
|
| 115 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 116 |
+
|
| 117 |
+
skip_connection=layers.Conv2D(64,(1,1),strides=(1,1),padding='same',name='conv_21',use_bias=False)(skip_connection)
|
| 118 |
+
skip_connection=layers.BatchNormalization(name='norm_21')(skip_connection)
|
| 119 |
+
skip_connection=layers.LeakyReLU(alpha=0.1)(skip_connection)
|
| 120 |
+
skip_connection=layers.Lambda(space_to_depth_x2)(skip_connection) # halfs the resolution and add more depth
|
| 121 |
+
|
| 122 |
+
x=layers.concatenate([skip_connection,x])
|
| 123 |
+
x=layers.Conv2D(1024,(3,3),strides=(1,1),padding='same',name='conv_22',use_bias=False)(x)
|
| 124 |
+
x=layers.BatchNormalization(name='norm_22')(x)
|
| 125 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 126 |
+
|
| 127 |
+
x=layers.Conv2D((num_anchors*(5+len(class_names))),(1,1),strides=(1,1),padding='same',name='conv_23')(x)
|
| 128 |
+
out=yolo_reshape()(x)
|
| 129 |
+
|
| 130 |
+
model=Model(x_input,out,name='yolo_v2_model')
|
| 131 |
+
# model.summary()
|
| 132 |
+
return model
|
| 133 |
+
|
| 134 |
+
def create_tiny_model():
|
| 135 |
+
global num_anchors
|
| 136 |
+
|
| 137 |
+
x_input=layers.Input(shape=(416,416,3))
|
| 138 |
+
x=layers.Lambda(lambda x:x/255.)(x_input)
|
| 139 |
+
x=layers.Conv2D(16,(3,3),strides=(1,1),padding='same',name='conv_1',use_bias=False)(x)
|
| 140 |
+
x=layers.BatchNormalization(name='norm_1')(x)
|
| 141 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 142 |
+
x=layers.MaxPooling2D(pool_size=(2,2),strides=(2,2))(x)
|
| 143 |
+
|
| 144 |
+
x=layers.Conv2D(32,(3,3),strides=(1,1),padding='same',name='conv_2',use_bias=False)(x)
|
| 145 |
+
x=layers.BatchNormalization(name='norm_2')(x)
|
| 146 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 147 |
+
x=layers.MaxPooling2D(pool_size=(2,2),strides=(2,2))(x)
|
| 148 |
+
|
| 149 |
+
x=layers.Conv2D(64,(3,3),strides=(1,1),padding='same',name='conv_3',use_bias=False)(x)
|
| 150 |
+
x=layers.BatchNormalization(name='norm_3')(x)
|
| 151 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 152 |
+
x=layers.MaxPooling2D(pool_size=(2,2),strides=(2,2))(x)
|
| 153 |
+
|
| 154 |
+
x=layers.Conv2D(128,(3,3),strides=(1,1),padding='same',name='conv_4',use_bias=False)(x)
|
| 155 |
+
x=layers.BatchNormalization(name='norm_4')(x)
|
| 156 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 157 |
+
x=layers.MaxPooling2D(pool_size=(2,2),strides=(2,2))(x)
|
| 158 |
+
|
| 159 |
+
x=layers.Conv2D(256,(3,3),strides=(1,1),padding='same',name='conv_5',use_bias=False)(x)
|
| 160 |
+
x=layers.BatchNormalization(name='norm_5')(x)
|
| 161 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 162 |
+
x=layers.MaxPooling2D(pool_size=(2,2),strides=(2,2))(x)
|
| 163 |
+
|
| 164 |
+
x=layers.Conv2D(512,(3,3),strides=(1,1),padding='same',name='conv_6',use_bias=False)(x)
|
| 165 |
+
x=layers.BatchNormalization(name='norm_6')(x)
|
| 166 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 167 |
+
# x=layers.MaxPooling2D(pool_size=(2,2),strides=(1,1))(x)
|
| 168 |
+
|
| 169 |
+
x=layers.Conv2D(1024,(3,3),strides=(1,1),padding='same',name='conv_7',use_bias=False)(x)
|
| 170 |
+
x=layers.BatchNormalization(name='norm_7')(x)
|
| 171 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
x=layers.Conv2D(1024,(3,3),strides=(1,1),padding='same',name='conv_8',use_bias=False)(x)
|
| 175 |
+
x=layers.BatchNormalization(name='norm_8')(x)
|
| 176 |
+
x=layers.LeakyReLU(alpha=0.1)(x)
|
| 177 |
+
|
| 178 |
+
x=layers.Conv2D((num_anchors*(5+len(class_names))),(1,1),strides=(1,1),padding='same',name='conv_9')(x)
|
| 179 |
+
out=yolo_reshape()(x)
|
| 180 |
+
|
| 181 |
+
model=Model(x_input,out,name='yolo_v2_tiny_model')
|
| 182 |
+
# model.summary()
|
| 183 |
+
return model
|
| 184 |
+
|
| 185 |
+
def load_model(path):
|
| 186 |
+
# model=tf.keras.models.load_model(path,custom_objects={'yolo_dynamic_reshape':yolo_dynamic_reshape},compile=False)
|
| 187 |
+
model=create_model()
|
| 188 |
+
model.load_weights(path)
|
| 189 |
+
# model=tf.keras.models.load_model(path)
|
| 190 |
+
return model
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
import struct
|
| 194 |
+
|
| 195 |
+
def get_original_weights(model,path_to_weight = "./yolov2-voc.weights"):
|
| 196 |
+
global num_anchors
|
| 197 |
+
# path_to_weight = "./yolov2.weights"(trained on coco dataset (80 classes))
|
| 198 |
+
if "yolov2-voc.weights" in path_to_weight: offset=5;nb_conv = 23;
|
| 199 |
+
if "darknet19_448.conv.23" in path_to_weight: offset=4;nb_conv = 18;
|
| 200 |
+
if "yolov2-tiny-voc.weights" in path_to_weight: offset=4;nb_conv = 9;
|
| 201 |
+
print(offset,nb_conv)
|
| 202 |
+
|
| 203 |
+
class WeightReader:
|
| 204 |
+
def __init__(self, weight_file):
|
| 205 |
+
self.offset = offset # an offset of 5 as first 5 values are non weight values(they are weight header)(for yolov2-voc.weights)
|
| 206 |
+
# self.all_weights = np.fromfile(weight_file, dtype='float32')
|
| 207 |
+
self.all_weights = open(weight_file,'rb')
|
| 208 |
+
weight_header=struct.unpack(f'{offset}i', self.all_weights.read(offset*4))
|
| 209 |
+
# print("weight Header(major, minor, revision, seen):",weight_header)
|
| 210 |
+
|
| 211 |
+
def read_bytes(self, size):
|
| 212 |
+
weights = struct.unpack('%df' % size, self.all_weights.read(size*4))
|
| 213 |
+
# print(weights)
|
| 214 |
+
# input("wait now forever")
|
| 215 |
+
return np.array(weights)
|
| 216 |
+
|
| 217 |
+
weight_reader = WeightReader(path_to_weight)
|
| 218 |
+
# print("all_weights = {}".format(np.fromfile(path_to_weight, dtype='float32').shape[0]-weight_reader.offset))
|
| 219 |
+
|
| 220 |
+
for i in range(1, nb_conv+1):
|
| 221 |
+
conv_layer = model.get_layer('conv_' + str(i))
|
| 222 |
+
|
| 223 |
+
if i < nb_conv:
|
| 224 |
+
norm_layer = model.get_layer('norm_' + str(i))
|
| 225 |
+
|
| 226 |
+
size = np.prod(norm_layer.get_weights()[0].shape)
|
| 227 |
+
|
| 228 |
+
beta = weight_reader.read_bytes(size)
|
| 229 |
+
gamma = weight_reader.read_bytes(size)
|
| 230 |
+
mean = weight_reader.read_bytes(size)
|
| 231 |
+
var = weight_reader.read_bytes(size)
|
| 232 |
+
|
| 233 |
+
weights = norm_layer.set_weights([gamma, beta, mean, var])
|
| 234 |
+
|
| 235 |
+
if len(conv_layer.get_weights()) > 1:
|
| 236 |
+
bias = weight_reader.read_bytes(np.prod(conv_layer.get_weights()[1].shape))
|
| 237 |
+
kernel = weight_reader.read_bytes(np.prod(conv_layer.get_weights()[0].shape))
|
| 238 |
+
kernel = kernel.reshape(list(reversed(conv_layer.get_weights()[0].shape)))
|
| 239 |
+
kernel = kernel.transpose([2,3,1,0])
|
| 240 |
+
# print(kernel.shape)
|
| 241 |
+
kernel=kernel.reshape([*kernel.shape[:-1],num_anchors,-1]) # reshape to this format so we change change position of p idx
|
| 242 |
+
idx=4 # in darknet each object was encoded as [x,y,w,h,p,c] but we use [p,x,y,w,h,c]
|
| 243 |
+
kernel=np.concatenate([kernel[...,idx:idx+1],kernel[...,:idx],kernel[...,idx+1:]],axis=-1) # setting p to idx 0
|
| 244 |
+
# print(kernel.shape)
|
| 245 |
+
kernel=kernel.reshape([*kernel.shape[:-2],-1])
|
| 246 |
+
# print(kernel.shape)
|
| 247 |
+
conv_layer.set_weights([kernel, bias])
|
| 248 |
+
else:
|
| 249 |
+
kernel = weight_reader.read_bytes(np.prod(conv_layer.get_weights()[0].shape))
|
| 250 |
+
kernel = kernel.reshape(list(reversed(conv_layer.get_weights()[0].shape)))
|
| 251 |
+
kernel = kernel.transpose([2,3,1,0])
|
| 252 |
+
conv_layer.set_weights([kernel])
|
| 253 |
+
|
| 254 |
+
return model
|
app/face_detection/decode_yolo_v2.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import matplotlib.pyplot as plt
|
| 3 |
+
from matplotlib.patches import Rectangle
|
| 4 |
+
import copy
|
| 5 |
+
import tensorflow.keras.backend as K
|
| 6 |
+
import cv2
|
| 7 |
+
from app.face_detection.config import cell_size,idx_to_class,class_to_idx,class_colors
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def get_objects(y_pred,p=0.5,decode_preds=True,idx=None):
|
| 12 |
+
global tf_anchors
|
| 13 |
+
output_size=y_pred.shape[1]
|
| 14 |
+
image_size=cell_size*output_size
|
| 15 |
+
|
| 16 |
+
y_pred=copy.deepcopy(y_pred)
|
| 17 |
+
if decode_preds:
|
| 18 |
+
y_pred[...,0]=K.sigmoid(y_pred[...,0])
|
| 19 |
+
y_pred[...,3:5]=np.clip((K.exp(y_pred[...,3:5])*tf_anchors).numpy(),0,output_size)
|
| 20 |
+
# y_pred[...,3:5]=np.clip(y_pred[...,3:5],0,output_size)
|
| 21 |
+
objs_found=[]
|
| 22 |
+
idxs=np.where(y_pred[...,0]>=p)
|
| 23 |
+
if np.size(idxs):
|
| 24 |
+
for i,obj in enumerate(y_pred[idxs[0],idxs[1],idxs[2],:]):
|
| 25 |
+
# obj (p,x,y,w,h,c_1,c_2,c_3,c_4,c_5.......c_n)
|
| 26 |
+
if decode_preds:
|
| 27 |
+
obj[1:3]=K.sigmoid(obj[1:3]) # x,y
|
| 28 |
+
|
| 29 |
+
prob=obj[0]
|
| 30 |
+
obj=obj[1:]
|
| 31 |
+
|
| 32 |
+
obj[4]=np.argmax(obj[4:])
|
| 33 |
+
obj=obj[:5]
|
| 34 |
+
|
| 35 |
+
obj[0]=idxs[1][i]+obj[0] # center x
|
| 36 |
+
obj[1]=idxs[0][i]+obj[1] # center y
|
| 37 |
+
|
| 38 |
+
obj[0]=np.clip(obj[0]-(obj[2]/2),0,output_size) # xmin
|
| 39 |
+
obj[1]=np.clip(obj[1]-(obj[3]/2),0,output_size) # ymin
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
obj_name=idx_to_class[obj[4]]
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
obj_details={'p':prob,'xywh':list(obj[:-1]/output_size),'class_idx':int(obj[4]),'class':obj_name} # xywh are scaled 0 to 1
|
| 46 |
+
if idx is not None:obj_details['idx']=idx
|
| 47 |
+
objs_found.append(obj_details)
|
| 48 |
+
objs_found=sorted(objs_found,key=lambda x:x['p'],reverse=True)
|
| 49 |
+
return objs_found
|
| 50 |
+
|
| 51 |
+
def list_get_iou(bboxes1, bboxes2):
|
| 52 |
+
|
| 53 |
+
bboxes1 = [bboxes1[0],bboxes1[1],bboxes1[0]+bboxes1[2],bboxes1[1]+bboxes1[3]]
|
| 54 |
+
bboxes2 = [bboxes2[0],bboxes2[1],bboxes2[0]+bboxes2[2],bboxes2[1]+bboxes2[3]]
|
| 55 |
+
|
| 56 |
+
xA = max(bboxes1[0], bboxes2[0])
|
| 57 |
+
yA = max(bboxes1[1], bboxes2[1])
|
| 58 |
+
xB = min(bboxes1[2], bboxes2[2])
|
| 59 |
+
yB = min(bboxes1[3], bboxes2[3])
|
| 60 |
+
|
| 61 |
+
intersection_area = max(0, xB - xA ) * max(0, yB - yA )
|
| 62 |
+
|
| 63 |
+
box1_area = (bboxes1[2] - bboxes1[0] ) * (bboxes1[3] - bboxes1[1] )
|
| 64 |
+
box2_area = (bboxes2[2] - bboxes2[0] ) * (bboxes2[3] - bboxes2[1] )
|
| 65 |
+
|
| 66 |
+
iou = intersection_area / float(box1_area + box2_area - intersection_area+1e-6)
|
| 67 |
+
|
| 68 |
+
return iou
|
| 69 |
+
|
| 70 |
+
def nms(objs_found,iou_threshold=0.2):
|
| 71 |
+
objs_found=np.array(copy.deepcopy(objs_found))
|
| 72 |
+
best_boxes=[]
|
| 73 |
+
while len(objs_found)>0:
|
| 74 |
+
obj=objs_found[0]
|
| 75 |
+
objs_found=objs_found[1:]
|
| 76 |
+
|
| 77 |
+
delete_idx=[]
|
| 78 |
+
for b_idx,b in enumerate(objs_found):
|
| 79 |
+
|
| 80 |
+
if obj['class_idx']==b['class_idx']:
|
| 81 |
+
iou=list_get_iou(obj['xywh'],b['xywh'])
|
| 82 |
+
if iou>= iou_threshold:
|
| 83 |
+
delete_idx.append(b_idx)
|
| 84 |
+
objs_found=np.delete(objs_found,delete_idx)
|
| 85 |
+
best_boxes.append(obj)
|
| 86 |
+
return best_boxes
|
| 87 |
+
|
| 88 |
+
def show_objects(img,objs_found,return_img=False):
|
| 89 |
+
plt.imshow(img)
|
| 90 |
+
for i in range(len(objs_found)):
|
| 91 |
+
p=objs_found[i]['p']
|
| 92 |
+
obj=objs_found[i]['xywh']
|
| 93 |
+
obj_name=objs_found[i]['class']
|
| 94 |
+
plt.gca().add_patch(Rectangle((obj[0],obj[1]),(obj[2]),(obj[3]),linewidth=4,edgecolor=class_colors[obj_name],facecolor='none'))
|
| 95 |
+
plt.text(obj[0],obj[1],obj_name)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def pred_image(img,objs_found,font_scale=2,thickness=4):
|
| 100 |
+
for i in range(len(objs_found)):
|
| 101 |
+
p=objs_found[i]['p']
|
| 102 |
+
obj=np.array(objs_found[i]['xywh'])*img.shape[0]
|
| 103 |
+
obj_name=objs_found[i]['class']
|
| 104 |
+
|
| 105 |
+
img=cv2.rectangle(img,(int(obj[0]),int(obj[1])),(int(obj[0]+obj[2]),int(obj[1]+obj[3])),(class_colors[obj_name]*255),thickness)
|
| 106 |
+
img=cv2.putText(img,obj_name,(int(obj[0]),int(obj[1])),cv2.FONT_HERSHEY_SIMPLEX,font_scale, (0,0,0), thickness, lineType=cv2.LINE_AA)
|
| 107 |
+
# draw_text(img, "world", font_scale=4, pos=(10, 20 + h), text_color_bg=(255, 0, 0))
|
| 108 |
+
return img
|
app/face_detection/helper.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def get_crops(img,objs_found,aligner=None,resize:tuple=None):
|
| 6 |
+
img_h,img_w,_=img.shape
|
| 7 |
+
all_crops=[]
|
| 8 |
+
for obj_found in objs_found:
|
| 9 |
+
xmin,ymin=obj_found['xywh'][0],obj_found['xywh'][1]
|
| 10 |
+
xmax,ymax=xmin+obj_found['xywh'][2],ymin+obj_found['xywh'][3]
|
| 11 |
+
# rescale them
|
| 12 |
+
xmin,ymin=int(xmin*img_w),int(ymin*img_h)
|
| 13 |
+
xmax,ymax=int(xmax*img_w),int(ymax*img_h)
|
| 14 |
+
|
| 15 |
+
crop=img[ymin:ymax,xmin:xmax]
|
| 16 |
+
if aligner is not None:
|
| 17 |
+
crop=aligner.align_image(crop)
|
| 18 |
+
if crop is None: continue
|
| 19 |
+
if resize is not None:
|
| 20 |
+
crop=cv2.resize(crop,resize)
|
| 21 |
+
all_crops.append(crop)
|
| 22 |
+
|
| 23 |
+
return all_crops
|
| 24 |
+
|
app/face_detection/inference.py
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import numpy as np
|
| 3 |
+
import matplotlib.pyplot as plt
|
| 4 |
+
import math
|
| 5 |
+
import copy
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def tiler(img,image_size,tiles=2,pad=0,):
|
| 11 |
+
# pad is 0 to 0.9%
|
| 12 |
+
h,w=img.shape[:2]
|
| 13 |
+
|
| 14 |
+
tile_h=(h//tiles)
|
| 15 |
+
tile_w=(w//tiles)
|
| 16 |
+
|
| 17 |
+
pad=int(tile_h*pad)
|
| 18 |
+
|
| 19 |
+
crops=[]
|
| 20 |
+
coordinates=[]
|
| 21 |
+
for i in range(tiles):
|
| 22 |
+
for j in range(tiles):
|
| 23 |
+
ymin=tile_h*i
|
| 24 |
+
xmin=tile_w*j
|
| 25 |
+
|
| 26 |
+
if i!=0:ymin=ymin-pad
|
| 27 |
+
if j!=0:xmin=xmin-pad
|
| 28 |
+
|
| 29 |
+
ymax=min(ymin+tile_h+pad,h)
|
| 30 |
+
xmax=min(xmin+tile_w+pad,w)
|
| 31 |
+
|
| 32 |
+
# crops.append(img[ymin:ymax,xmin:xmax])
|
| 33 |
+
crops.append(cv2.resize(img[ymin:ymax,xmin:xmax],[image_size,image_size]))
|
| 34 |
+
coordinates.append((xmin,ymin,xmax,ymax))
|
| 35 |
+
# print(crops[-1].shape)
|
| 36 |
+
return coordinates,np.array(crops)
|
| 37 |
+
|
| 38 |
+
class square_crop:
|
| 39 |
+
def __call__(self,img):
|
| 40 |
+
h,w=img.shape[:2]
|
| 41 |
+
self.w_removed,self.h_removed=0,0
|
| 42 |
+
if w>h:
|
| 43 |
+
self.w_removed=(w-h)//2
|
| 44 |
+
img=img[:,self.w_removed:w-self.w_removed]
|
| 45 |
+
elif h>w:
|
| 46 |
+
self.h_removed=(h-w)//2
|
| 47 |
+
img=img[self.h_removed:h-self.h_removed,:]
|
| 48 |
+
|
| 49 |
+
h,w=img.shape[:2]
|
| 50 |
+
self.w_removed,self.h_removed=self.w_removed/w,self.h_removed/h
|
| 51 |
+
return img
|
| 52 |
+
def rescale(self,objs_found):
|
| 53 |
+
raise NotImplementedError
|
| 54 |
+
|
| 55 |
+
class square_pad:
|
| 56 |
+
def __call__(self,img,color=(0,0,0)):
|
| 57 |
+
h,w=img.shape[:2]
|
| 58 |
+
self.w_added,self.h_added=0,0
|
| 59 |
+
if h>w:
|
| 60 |
+
self.w_added=int((h-w)/2)
|
| 61 |
+
padding=(np.ones([h,self.w_added,3])*np.array(color)[None,None,:]).astype("uint8")
|
| 62 |
+
img=np.concatenate([padding,img,padding],axis=1)
|
| 63 |
+
elif w>h:
|
| 64 |
+
self.h_added=int((w-h)/2)
|
| 65 |
+
padding=(np.ones([self.h_added,w,3])*np.array(color)[None,None,:]).astype("uint8")
|
| 66 |
+
img=np.concatenate([padding,img,padding],axis=0)
|
| 67 |
+
h,w=img.shape[:2]
|
| 68 |
+
|
| 69 |
+
self.w_added,self.h_added=self.w_added/w,self.h_added/h
|
| 70 |
+
|
| 71 |
+
return img
|
| 72 |
+
def rescale(self,objs_found):
|
| 73 |
+
|
| 74 |
+
for i in range(len(objs_found)):
|
| 75 |
+
objs_found[i]['xywh'][0]=(objs_found[i]['xywh'][0]-self.w_added)/(1-2*self.w_added)
|
| 76 |
+
objs_found[i]['xywh'][1]=(objs_found[i]['xywh'][1]-self.h_added)/(1-2*self.h_added)
|
| 77 |
+
objs_found[i]['xywh'][2]=(objs_found[i]['xywh'][2])/(1-2*self.w_added)
|
| 78 |
+
objs_found[i]['xywh'][3]=(objs_found[i]['xywh'][3])/(1-2*self.h_added)
|
| 79 |
+
return objs_found
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class face_detection:
|
| 83 |
+
def __init__(self,model_path):
|
| 84 |
+
################### set num_anchors & tf_anchors ###################
|
| 85 |
+
anchor_boxes=np.loadtxt(model_path+"/anchors.txt")
|
| 86 |
+
num_anchors=anchor_boxes.shape[0]
|
| 87 |
+
tf_anchors=K.reshape(K.variable(anchor_boxes),[1, 1, 1, num_anchors, 2])
|
| 88 |
+
load_model_lib.num_anchors=num_anchors
|
| 89 |
+
decode_model_lib.tf_anchors=tf_anchors
|
| 90 |
+
####################################################################
|
| 91 |
+
|
| 92 |
+
self.model=load_model(model_path+"/model.h5")
|
| 93 |
+
|
| 94 |
+
self.modes_available=["tiled","sized"]
|
| 95 |
+
self.square_preprocessing=square_crop
|
| 96 |
+
|
| 97 |
+
def invoke_model(self,img,p=0.2,iou_threshold=0.3,batch_size=4):
|
| 98 |
+
all_objs_found=[]
|
| 99 |
+
|
| 100 |
+
for i in range(math.ceil(img.shape[0]/batch_size)):
|
| 101 |
+
|
| 102 |
+
y_pred=self.model.predict(img[int(i*batch_size):int((i+1)*batch_size)].astype('float32'),verbose=0)
|
| 103 |
+
# print(y_pred.shape)
|
| 104 |
+
|
| 105 |
+
for i in range(y_pred.shape[0]):
|
| 106 |
+
objs_found=get_objects(y_pred[i],p=p)
|
| 107 |
+
objs_found=nms(objs_found,iou_threshold=iou_threshold)
|
| 108 |
+
all_objs_found.append(objs_found)
|
| 109 |
+
|
| 110 |
+
return all_objs_found
|
| 111 |
+
def get_tiled_output(self,img,p_thres,nms_thres,tiles,pad,image_size,save_tiles=None,batch_size=4):
|
| 112 |
+
# pad is 0 to 0.5%
|
| 113 |
+
# pad=0.05
|
| 114 |
+
img=cv2.resize(img,[image_size*tiles,image_size*tiles])
|
| 115 |
+
|
| 116 |
+
coordinates,crops=tiler(img,tiles=tiles,pad=pad,image_size=image_size)
|
| 117 |
+
|
| 118 |
+
all_objs_found=self.invoke_model(crops,p_thres,nms_thres,batch_size)
|
| 119 |
+
|
| 120 |
+
if save_tiles:
|
| 121 |
+
fig=plt.figure(figsize=(3*tiles,3*tiles))
|
| 122 |
+
plt.axis("off")
|
| 123 |
+
plt.title(f"pad:{pad}")
|
| 124 |
+
|
| 125 |
+
for i in range(int(tiles*tiles)):
|
| 126 |
+
fig.add_subplot(tiles,tiles,i+1)
|
| 127 |
+
plt.axis("off")
|
| 128 |
+
plt.imshow(pred_image(crops[i],all_objs_found[i]))
|
| 129 |
+
# plt.show(block=False)
|
| 130 |
+
plt.savefig(save_tiles+f"_{tiles}.jpg")
|
| 131 |
+
plt.close()
|
| 132 |
+
|
| 133 |
+
all_objs_found_joined=[]
|
| 134 |
+
for i in range(int(tiles*tiles)):
|
| 135 |
+
xmin,ymin,xmax,ymax=coordinates[i]
|
| 136 |
+
w,h=xmax-xmin,ymax-ymin
|
| 137 |
+
for j in range(all_objs_found[i].__len__()):
|
| 138 |
+
# all_objs_found[i][j]['xywh']=np.array(all_objs_found[i][j]['xywh'])/image_size
|
| 139 |
+
all_objs_found[i][j]['xywh']=np.array(all_objs_found[i][j]['xywh'])*w
|
| 140 |
+
all_objs_found[i][j]['xywh'][0]+=xmin
|
| 141 |
+
all_objs_found[i][j]['xywh'][1]+=ymin
|
| 142 |
+
all_objs_found[i][j]['xywh']=(all_objs_found[i][j]['xywh']/(image_size*tiles)).tolist()
|
| 143 |
+
all_objs_found_joined.extend(all_objs_found[i])
|
| 144 |
+
|
| 145 |
+
all_objs_found_joined=sorted(all_objs_found_joined,reverse=True,key=lambda x:x["p"]) # This was very important
|
| 146 |
+
all_objs_found_joined=nms(all_objs_found_joined,nms_thres)
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
return all_objs_found_joined
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def set_mode(self,p_thres,nms_thres,batch_size=4,mode="tiled",**kwargs):
|
| 153 |
+
# mode : tiled or sized
|
| 154 |
+
|
| 155 |
+
self.p_thres=p_thres
|
| 156 |
+
self.nms_thres=nms_thres
|
| 157 |
+
self.batch_size=batch_size
|
| 158 |
+
|
| 159 |
+
if mode=="tiled":
|
| 160 |
+
try:
|
| 161 |
+
self.image_size=kwargs['image_size']
|
| 162 |
+
if "tiles" not in kwargs:
|
| 163 |
+
self.tiles=[1]
|
| 164 |
+
self.pad=0
|
| 165 |
+
else:
|
| 166 |
+
self.tiles=kwargs['tiles'] if(type(kwargs['tiles'])==type(list([1])) or type(kwargs['tiles'])==type(np.zeros([]))) else [kwargs['tiles']]
|
| 167 |
+
self.pad=kwargs['pad']
|
| 168 |
+
except:
|
| 169 |
+
|
| 170 |
+
raise ValueError(f"Not all tiled mode parameters passed.")
|
| 171 |
+
|
| 172 |
+
self.save_tiles=kwargs["save_tiles"] if ("save_tiles" in kwargs) else None
|
| 173 |
+
|
| 174 |
+
elif mode=="sized":
|
| 175 |
+
try:
|
| 176 |
+
# self.image_size=kwargs['image_size']
|
| 177 |
+
self.image_size=kwargs['image_size'] if(type(kwargs['image_size'])==type(list([1])) or type(kwargs['image_size'])==type(np.zeros([]))) else [kwargs['image_size']]
|
| 178 |
+
self.batch_size=1
|
| 179 |
+
except:
|
| 180 |
+
raise ValueError(f"Not all Sized mode parameters passed.")
|
| 181 |
+
else:
|
| 182 |
+
raise ValueError(f"Unavailable mode={mode} \nmode can only be one of:{self.modes_available}")
|
| 183 |
+
|
| 184 |
+
self.mode=mode
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def predict_once(self,img):
|
| 188 |
+
|
| 189 |
+
if (type(img)==str):
|
| 190 |
+
img=cv2.cvtColor(cv2.imread(img),cv2.COLOR_BGR2RGB)
|
| 191 |
+
elif (type(img)!=type(np.zeros([]))):
|
| 192 |
+
raise TypeError(f"Inappropriate type of image={type(img)}")
|
| 193 |
+
|
| 194 |
+
# if img.shape[0]!=img.shape[1]: raise ValueError(f"The image should be squared")
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
if not hasattr(self,'mode'): raise ValueError(f"First call set_mode function to set mode using one of the following mode :{self.modes_available}")
|
| 198 |
+
|
| 199 |
+
if self.mode=='tiled':
|
| 200 |
+
if type(self.tiles)==type(list([1])) or type(self.tiles)==type(np.zeros([])): raise TypeError("use advanced_predict function for inference on multiple tiles")
|
| 201 |
+
objs_found=self.get_tiled_output(img,p_thres=self.p_thres,nms_thres=self.nms_thres,tiles=self.tiles,pad=self.pad,image_size=self.image_size,save_tiles=self.save_tiles)
|
| 202 |
+
elif self.mode=='sized':
|
| 203 |
+
if type(self.image_size)==type(list([1])) or type(self.image_size)==type(np.zeros([])): raise TypeError("use advanced_predict function for inference on multiple sizes")
|
| 204 |
+
resized_img=cv2.resize(img,[self.image_size,self.image_size])
|
| 205 |
+
objs_found=self.invoke_model(resized_img[None,:,:,:],self.p_thres,self.nms_thres,batch_size=1)[0]
|
| 206 |
+
|
| 207 |
+
return img,objs_found
|
| 208 |
+
|
| 209 |
+
def predict(self,img):
|
| 210 |
+
|
| 211 |
+
if (type(img)==str):
|
| 212 |
+
img=cv2.cvtColor(cv2.imread(img),cv2.COLOR_BGR2RGB)
|
| 213 |
+
elif (type(img)!=type(np.zeros([]))):
|
| 214 |
+
raise TypeError(f"Inappropriate type of image={type(img)}")
|
| 215 |
+
|
| 216 |
+
if img.shape[0]!=img.shape[1]: img=self.square_preprocessing(img)
|
| 217 |
+
|
| 218 |
+
if not hasattr(self,'mode'): raise ValueError(f"First call set_mode function to set mode using one of the following mode :{self.modes_available}")
|
| 219 |
+
|
| 220 |
+
if self.mode=="tiled":
|
| 221 |
+
all_objs_found=[]
|
| 222 |
+
all_tiles=copy.deepcopy(self.tiles)
|
| 223 |
+
for tiles in all_tiles:
|
| 224 |
+
self.tiles=tiles
|
| 225 |
+
_,objs_found=self.predict_once(img)
|
| 226 |
+
all_objs_found.extend(objs_found)
|
| 227 |
+
self.tiles=all_tiles
|
| 228 |
+
|
| 229 |
+
elif self.mode=="sized":
|
| 230 |
+
all_objs_found=[]
|
| 231 |
+
all_image_size=copy.deepcopy(self.image_size)
|
| 232 |
+
for image_size in all_image_size:
|
| 233 |
+
self.image_size=image_size
|
| 234 |
+
_,objs_found=self.predict_once(img)
|
| 235 |
+
all_objs_found.extend(objs_found)
|
| 236 |
+
self.image_size=all_image_size
|
| 237 |
+
all_objs_found=sorted(all_objs_found,reverse=True,key=lambda x:x["p"]) # This was very important
|
| 238 |
+
all_objs_found=nms(all_objs_found,self.nms_thres)
|
| 239 |
+
|
| 240 |
+
return img,all_objs_found
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
if __name__=="__main__":
|
| 247 |
+
import create_load_model as load_model_lib
|
| 248 |
+
import decode_yolo_v2 as decode_model_lib
|
| 249 |
+
from create_load_model import *
|
| 250 |
+
from decode_yolo_v2 import *
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
face_detector=face_detection("face_detection/Models/v1/")
|
| 254 |
+
|
| 255 |
+
# test_img="C:\\Users\\Home\\Downloads\\WhatsApp Image 2023-01-04 at 6.58.40 PM.jpeg"
|
| 256 |
+
test_img="C:\\Users\\Home\\Downloads\\family-52.jpg"
|
| 257 |
+
|
| 258 |
+
p_thres=0.5
|
| 259 |
+
nms_thres=0.3
|
| 260 |
+
tiles=3
|
| 261 |
+
pad=0
|
| 262 |
+
|
| 263 |
+
# face_detector.set_mode(p_thres,nms_thres,mode="tiled",tiles=[1,2],pad=pad,image_size=416,save_tiles="tile")
|
| 264 |
+
# face_detector.set_mode(p_thres=p_thres,nms_thres=nms_thres,image_size=608)
|
| 265 |
+
# face_detector.set_mode(p_thres,nms_thres,mode="sized",image_size=256)
|
| 266 |
+
# face_detector.set_mode(p_thres,nms_thres,mode="sized",image_size=352)
|
| 267 |
+
# face_detector.set_mode(p_thres,nms_thres,mode="sized",image_size=608)
|
| 268 |
+
face_detector.set_mode(p_thres,nms_thres,mode="sized",image_size=1024)
|
| 269 |
+
# face_detector.set_mode(p_thres,nms_thres,mode="sized",image_size=[608,1024])
|
| 270 |
+
img,objs_found=face_detector.predict(test_img)
|
| 271 |
+
# img,objs_found=face_detector.advanced_predict(test_img)
|
| 272 |
+
pred_img=pred_image(img,objs_found)
|
| 273 |
+
plt.figure()
|
| 274 |
+
plt.imshow(pred_img)
|
| 275 |
+
plt.show()
|
| 276 |
+
# cv2.imwrite("test_output.jpg",pred_img[:,:,::-1])
|
| 277 |
+
else:
|
| 278 |
+
import app.face_detection.create_load_model as load_model_lib
|
| 279 |
+
import app.face_detection.decode_yolo_v2 as decode_model_lib
|
| 280 |
+
from app.face_detection.create_load_model import *
|
| 281 |
+
from app.face_detection.decode_yolo_v2 import *
|
app/face_recognition/Models/v1/config.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
input_size=224
|
| 2 |
+
thres=0.5
|
| 3 |
+
large_distance=10
|
app/face_recognition/Models/v1/model.h5
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ab0f8c73a7493b4125b24055aaee62bbc3ab99e43c8fdb1c31fef473469270a8
|
| 3 |
+
size 539190512
|
app/face_recognition/aligner.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import itertools
|
| 3 |
+
import numpy as np
|
| 4 |
+
import mediapipe as mp
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
+
import PIL
|
| 7 |
+
import os
|
| 8 |
+
import shutil
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class aligner:
|
| 13 |
+
def __init__(self,min_aligner_confidence):
|
| 14 |
+
mp_face_mesh = mp.solutions.face_mesh
|
| 15 |
+
|
| 16 |
+
self.face_mesh_images = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1,
|
| 17 |
+
min_detection_confidence=min_aligner_confidence)
|
| 18 |
+
|
| 19 |
+
mp_drawing = mp.solutions.drawing_utils
|
| 20 |
+
mp_drawing_styles = mp.solutions.drawing_styles
|
| 21 |
+
LEFT_EYE_INDEXES = list(set(itertools.chain(*mp_face_mesh.FACEMESH_LEFT_EYE)))
|
| 22 |
+
RIGHT_EYE_INDEXES = list(set(itertools.chain(*mp_face_mesh.FACEMESH_RIGHT_EYE)))
|
| 23 |
+
|
| 24 |
+
self.LEFT_EYE_INDEX=LEFT_EYE_INDEXES[7] # eye point index
|
| 25 |
+
self.RIGHT_EYE_INDEX=RIGHT_EYE_INDEXES[4] # eye point index
|
| 26 |
+
|
| 27 |
+
def align_image(self,img):
|
| 28 |
+
|
| 29 |
+
# start work
|
| 30 |
+
face_mesh_results = self.face_mesh_images.process(img)
|
| 31 |
+
if face_mesh_results.multi_face_landmarks!=None:
|
| 32 |
+
face_landmarks=face_mesh_results.multi_face_landmarks[0]
|
| 33 |
+
|
| 34 |
+
h,w,_=img.shape
|
| 35 |
+
|
| 36 |
+
points=[]
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
x_coord=int(np.clip(face_landmarks.landmark[self.LEFT_EYE_INDEX].x*w,0,w))
|
| 40 |
+
y_coord=int(np.clip(face_landmarks.landmark[self.LEFT_EYE_INDEX].y*h,0,h))
|
| 41 |
+
points.append((x_coord,y_coord))
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
x_coord=int(np.clip(face_landmarks.landmark[self.RIGHT_EYE_INDEX].x*w,0,w))
|
| 45 |
+
y_coord=int(np.clip(face_landmarks.landmark[self.RIGHT_EYE_INDEX].y*h,0,h))
|
| 46 |
+
points.append((x_coord,y_coord))
|
| 47 |
+
|
| 48 |
+
p0=np.array(points[0],dtype='float64')
|
| 49 |
+
p1=np.array(points[1],dtype='float64')
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
h=abs(p0[1]-p1[1])
|
| 53 |
+
w=abs(p0[0]-p1[0])
|
| 54 |
+
|
| 55 |
+
theta=np.arctan(h/w)
|
| 56 |
+
|
| 57 |
+
angle=(theta * 180) / np.pi
|
| 58 |
+
|
| 59 |
+
def get_direction(p0,p1):
|
| 60 |
+
if p0[0]<p1[0]:
|
| 61 |
+
if p0[1]<p1[1]:
|
| 62 |
+
direction=1
|
| 63 |
+
else:
|
| 64 |
+
direction=-1
|
| 65 |
+
else:
|
| 66 |
+
if p1[1]<p0[1]:
|
| 67 |
+
direction=1
|
| 68 |
+
else:
|
| 69 |
+
direction=-1
|
| 70 |
+
return direction
|
| 71 |
+
|
| 72 |
+
direction=get_direction(p0,p1)
|
| 73 |
+
angle=direction*angle
|
| 74 |
+
# print("rotated anticlockwise by :",angle,"angle")
|
| 75 |
+
new_img = PIL.Image.fromarray(img)
|
| 76 |
+
new_img = new_img.rotate(angle)
|
| 77 |
+
|
| 78 |
+
return np.array(new_img)
|
| 79 |
+
else:
|
| 80 |
+
return None
|
app/face_recognition/config.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
min_aligner_confidence=0.6
|
| 2 |
+
db_dir="aligned_crops"
|
| 3 |
+
img_dir="images"
|
| 4 |
+
save_dir="results"
|
app/face_recognition/helper.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import xml.etree.ElementTree as ET
|
| 2 |
+
import numpy as np
|
| 3 |
+
import cv2
|
| 4 |
+
|
| 5 |
+
def show_pred_image(tree,img):
|
| 6 |
+
root=tree.getroot()
|
| 7 |
+
|
| 8 |
+
size=root.find('size')
|
| 9 |
+
w,h=int(size.find("width").text),int(size.find("height").text)
|
| 10 |
+
default_color=[220,255,0]
|
| 11 |
+
random_color=np.random.randn(3)*255
|
| 12 |
+
|
| 13 |
+
for obj in root.findall("object"):
|
| 14 |
+
bndbox=obj.find("bndbox")
|
| 15 |
+
classname=obj.find('name').text
|
| 16 |
+
|
| 17 |
+
xmin,ymin , xmax,ymax=int(bndbox.find('xmin').text),int(bndbox.find('ymin').text),int(bndbox.find('xmax').text),int(bndbox.find('ymax').text)
|
| 18 |
+
|
| 19 |
+
if classname=="face":
|
| 20 |
+
color=default_color
|
| 21 |
+
else:
|
| 22 |
+
color=random_color
|
| 23 |
+
distance=obj.find("distance").text
|
| 24 |
+
classname+=f"({distance})"
|
| 25 |
+
|
| 26 |
+
img=cv2.rectangle(img,(xmin,ymin),(xmax,ymax),color,7)
|
| 27 |
+
img=cv2.putText(img,classname,(xmin,ymin),cv2.FONT_HERSHEY_SIMPLEX,2,color,thickness=5)
|
| 28 |
+
return img
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def xml_to_objs_found(tree):
|
| 32 |
+
root=tree.getroot()
|
| 33 |
+
|
| 34 |
+
size=root.find('size')
|
| 35 |
+
image_w,image_h=int(size.find("width").text),int(size.find("height").text)
|
| 36 |
+
|
| 37 |
+
objs_found=[]
|
| 38 |
+
for obj in root.findall("object"):
|
| 39 |
+
obj_found=dict()
|
| 40 |
+
bndbox=obj.find("bndbox")
|
| 41 |
+
classname=obj.find('name').text
|
| 42 |
+
distance=float(obj.find('distance').text) if classname!="face" else None
|
| 43 |
+
|
| 44 |
+
xmin,ymin , xmax,ymax=int(bndbox.find('xmin').text),int(bndbox.find('ymin').text),int(bndbox.find('xmax').text),int(bndbox.find('ymax').text)
|
| 45 |
+
|
| 46 |
+
x,y = xmin/image_w , ymin/image_h
|
| 47 |
+
w,h = (xmax-xmin)/image_w , (ymax-ymin)/image_h
|
| 48 |
+
|
| 49 |
+
obj_details={'xywh':[x,y,w,h],'class':classname,'distance':distance}
|
| 50 |
+
objs_found.append(obj_details)
|
| 51 |
+
|
| 52 |
+
return objs_found
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def objs_found_to_xml(test_img,w,h,objs_found):
|
| 56 |
+
root=ET.Element("annotation")
|
| 57 |
+
|
| 58 |
+
filename_tag=ET.Element("filename")
|
| 59 |
+
filename_tag.text=test_img
|
| 60 |
+
root.append(filename_tag)
|
| 61 |
+
path_tag=ET.Element("path")
|
| 62 |
+
path_tag.text="./"+test_img
|
| 63 |
+
root.append(path_tag)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
size_tag=ET.Element("size")
|
| 67 |
+
# w,h defined above
|
| 68 |
+
# print(w,h)
|
| 69 |
+
width_tag=ET.Element("width")
|
| 70 |
+
width_tag.text=str(w)
|
| 71 |
+
height_tag=ET.Element("height")
|
| 72 |
+
height_tag.text=str(h)
|
| 73 |
+
depth_tag=ET.Element("depth")
|
| 74 |
+
depth_tag.text="3"
|
| 75 |
+
|
| 76 |
+
size_tag.append(width_tag)
|
| 77 |
+
size_tag.append(height_tag)
|
| 78 |
+
size_tag.append(depth_tag)
|
| 79 |
+
root.append(size_tag)
|
| 80 |
+
|
| 81 |
+
# add all objects
|
| 82 |
+
for obj_found in objs_found:
|
| 83 |
+
obj_found['xywh']=np.array(obj_found['xywh'])*w
|
| 84 |
+
|
| 85 |
+
obj_tag=ET.Element("object")
|
| 86 |
+
name_tag=ET.Element("name")
|
| 87 |
+
name_tag.text=obj_found['class']
|
| 88 |
+
obj_tag.append(name_tag)
|
| 89 |
+
|
| 90 |
+
bndbox_tag=ET.Element("bndbox")
|
| 91 |
+
xmin_tag=ET.Element("xmin")
|
| 92 |
+
xmin_tag.text=str(int(obj_found['xywh'][0]))
|
| 93 |
+
ymin_tag=ET.Element("ymin")
|
| 94 |
+
ymin_tag.text=str(int(obj_found['xywh'][1]))
|
| 95 |
+
xmax_tag=ET.Element("xmax")
|
| 96 |
+
xmax_tag.text=str(int(obj_found['xywh'][0]+obj_found['xywh'][2]))
|
| 97 |
+
ymax_tag=ET.Element("ymax")
|
| 98 |
+
ymax_tag.text=str(int(obj_found['xywh'][1]+obj_found['xywh'][3]))
|
| 99 |
+
|
| 100 |
+
obj_found['xywh']=np.array(obj_found['xywh'])/w
|
| 101 |
+
|
| 102 |
+
bndbox_tag.append(xmin_tag)
|
| 103 |
+
bndbox_tag.append(ymin_tag)
|
| 104 |
+
bndbox_tag.append(xmax_tag)
|
| 105 |
+
bndbox_tag.append(ymax_tag)
|
| 106 |
+
|
| 107 |
+
obj_tag.append(bndbox_tag)
|
| 108 |
+
root.append(obj_tag)
|
| 109 |
+
|
| 110 |
+
xml=ET.ElementTree(root)
|
| 111 |
+
|
| 112 |
+
# with open(xml_file_path,"wb") as f:
|
| 113 |
+
# xml.write(f)
|
| 114 |
+
|
| 115 |
+
return xml
|
app/face_recognition/inference.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import PIL
|
| 2 |
+
from PIL import ImageOps
|
| 3 |
+
import xml.etree.ElementTree as ET
|
| 4 |
+
import shutil
|
| 5 |
+
import os
|
| 6 |
+
import numpy as np
|
| 7 |
+
from glob import glob
|
| 8 |
+
import tensorflow as tf
|
| 9 |
+
from tensorflow.keras import backend as K
|
| 10 |
+
import cv2
|
| 11 |
+
import matplotlib.pyplot as plt
|
| 12 |
+
import importlib
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
from app.face_recognition import config
|
| 17 |
+
from app.face_recognition.aligner import aligner
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class face_recognition:
|
| 22 |
+
def __init__(self,model_path,thres=None,min_aligner_confidence=None):
|
| 23 |
+
config_file_path='.'.join(model_path.split("/"))+".config"
|
| 24 |
+
# print(config_file_path)
|
| 25 |
+
self.model_config= importlib.import_module(config_file_path)
|
| 26 |
+
# print(self.model_config)
|
| 27 |
+
self.thres=thres if thres is not None else self.model_config.thres
|
| 28 |
+
self.aligner=aligner(min_aligner_confidence) if min_aligner_confidence is not None else aligner(config.min_aligner_confidence)
|
| 29 |
+
self.feature_extractor=tf.keras.models.load_model(model_path+"/model.h5",compile=False)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def euclidean_distance(self,vectors):
|
| 34 |
+
squared_sum=np.sum(np.square(vectors[0]-vectors[1]),axis=-1,keepdims=True)
|
| 35 |
+
return np.sqrt(np.maximum(squared_sum,1e-7))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def calculate_distance(self,crop_img,db_faces_features,mode='avg'):
|
| 40 |
+
"""
|
| 41 |
+
mode= 'avg' or 'best'
|
| 42 |
+
"""
|
| 43 |
+
if mode not in ['avg','best']: raise ValueError(f"Unknown mode:{mode} \nMode should be one of these:{['avg','best']}")
|
| 44 |
+
crop_img_features=self.feature_extractor.predict(crop_img[None,:,:,:],verbose=0)
|
| 45 |
+
all_distances=[] # distance of this particular crop with all faces in database
|
| 46 |
+
for face_idx in range(len(self.faces)):
|
| 47 |
+
if mode=='avg':
|
| 48 |
+
db_face_features=db_faces_features[face_idx].mean(axis=0,keepdims=True) # avg method
|
| 49 |
+
new_crop_img_features=crop_img_features.copy()
|
| 50 |
+
else:
|
| 51 |
+
db_face_features=db_faces_features[face_idx] # best method
|
| 52 |
+
new_crop_img_features=np.tile(crop_img_features,[db_face_features.shape[0],1])
|
| 53 |
+
try:
|
| 54 |
+
assert(db_face_features.shape==new_crop_img_features.shape)
|
| 55 |
+
except:
|
| 56 |
+
raise AssertionError(f"db_face_features shape{db_face_features.shape} does not match crop_img_features shape{new_crop_img_features.shape}")
|
| 57 |
+
|
| 58 |
+
distance=np.min(self.euclidean_distance([db_face_features,new_crop_img_features]),axis=0)[0]
|
| 59 |
+
|
| 60 |
+
if distance<=self.thres:
|
| 61 |
+
all_distances.append(distance) # obj distance wrt to all faces in database
|
| 62 |
+
else:
|
| 63 |
+
all_distances.append(self.model_config.large_distance) # not the person garruntied
|
| 64 |
+
return all_distances
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def repeat_allowed_face_recognition(self,distance_dict):
|
| 69 |
+
faceidx_to_obj_dict=dict()
|
| 70 |
+
for obj in distance_dict.keys():
|
| 71 |
+
distances=np.array(distance_dict[obj])
|
| 72 |
+
min_distance,min_distance_idx = distances.min(),distances.argmin()
|
| 73 |
+
if min_distance<=self.thres:
|
| 74 |
+
obj.find('name').text = self.faces[min_distance_idx]
|
| 75 |
+
distance_tag=ET.Element("distance")
|
| 76 |
+
distance_tag.text="{:.2f}".format(min_distance)
|
| 77 |
+
obj.append(distance_tag)
|
| 78 |
+
return faceidx_to_obj_dict
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def assign_face_label(self,obj):
|
| 82 |
+
|
| 83 |
+
# find min and argmin
|
| 84 |
+
min_distance,min_distance_idx = self.distance_dict[obj].min(),self.distance_dict[obj].argmin()
|
| 85 |
+
# base condition
|
| 86 |
+
if min_distance>=self.thres:
|
| 87 |
+
# print("end");
|
| 88 |
+
return;
|
| 89 |
+
if min_distance_idx not in self.faceidx_to_obj_dict:
|
| 90 |
+
self.faceidx_to_obj_dict[min_distance_idx]=(obj,min_distance) # stores obj and distance
|
| 91 |
+
else:
|
| 92 |
+
|
| 93 |
+
if(min_distance>self.faceidx_to_obj_dict[min_distance_idx][1]):
|
| 94 |
+
self.distance_dict[obj][min_distance_idx]=self.model_config.large_distance
|
| 95 |
+
self.assign_face_label(obj)
|
| 96 |
+
else:
|
| 97 |
+
temp_obj,temp_min_distance=self.faceidx_to_obj_dict[min_distance_idx]
|
| 98 |
+
self.faceidx_to_obj_dict[min_distance_idx]=(obj,min_distance) # stores obj and distance
|
| 99 |
+
self.distance_dict[temp_obj][min_distance_idx]=self.model_config.large_distance
|
| 100 |
+
self.assign_face_label(temp_obj)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def no_repeat_allowed_face_recognition(self,distance_dict):
|
| 104 |
+
|
| 105 |
+
self.faceidx_to_obj_dict=dict()
|
| 106 |
+
for obj in distance_dict.keys():
|
| 107 |
+
self.assign_face_label(obj)
|
| 108 |
+
|
| 109 |
+
for idx,(obj,distance) in self.faceidx_to_obj_dict.items():
|
| 110 |
+
obj.find('name').text = self.faces[idx]
|
| 111 |
+
distance_tag=ET.Element("distance")
|
| 112 |
+
distance_tag.text="{:.2f}".format(distance)
|
| 113 |
+
obj.append(distance_tag)
|
| 114 |
+
# print(obj.find("distance").text)
|
| 115 |
+
|
| 116 |
+
return self.faceidx_to_obj_dict
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def forward_pass(self,img,tree,mode="repeat"):
|
| 120 |
+
'''mode : "repeat" or "no-repeat" '''
|
| 121 |
+
root=tree.getroot()
|
| 122 |
+
self.distance_dict=dict()
|
| 123 |
+
|
| 124 |
+
size=root.find('size')
|
| 125 |
+
w,h=int(size.find("width").text),int(size.find("height").text)
|
| 126 |
+
|
| 127 |
+
for i,obj in enumerate(root.findall("object")):
|
| 128 |
+
bndbox=obj.find("bndbox")
|
| 129 |
+
|
| 130 |
+
xmin,ymin , xmax,ymax=int(bndbox.find('xmin').text),int(bndbox.find('ymin').text),int(bndbox.find('xmax').text),int(bndbox.find('ymax').text)
|
| 131 |
+
|
| 132 |
+
crop_img=img[ymin:ymax,xmin:xmax]
|
| 133 |
+
crop_img=cv2.resize(crop_img,[self.model_config.input_size,self.model_config.input_size])
|
| 134 |
+
crop_img=self.aligner.align_image(crop_img)
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
if crop_img is not None:
|
| 138 |
+
self.distance_dict[obj]=np.array(self.calculate_distance(crop_img,self.db_faces_features,mode=self.distance_mode))
|
| 139 |
+
|
| 140 |
+
# print(distance_dict)
|
| 141 |
+
|
| 142 |
+
if mode=="repeat":
|
| 143 |
+
faceidx_to_obj_dict=self.repeat_allowed_face_recognition(self.distance_dict)
|
| 144 |
+
elif mode=="no-repeat":
|
| 145 |
+
faceidx_to_obj_dict=self.no_repeat_allowed_face_recognition(self.distance_dict)
|
| 146 |
+
return tree
|
| 147 |
+
|
| 148 |
+
def predict(self,img,tree):
|
| 149 |
+
if (not hasattr(self,"distance_mode")) or (not hasattr(self,"recognition_mode")): raise ValueError(f"Call set_face_db_and_mode method first!")
|
| 150 |
+
tree=self.forward_pass(img,tree,mode=self.recognition_mode)
|
| 151 |
+
return tree
|
| 152 |
+
|
| 153 |
+
def set_face_db_and_mode(self,faces,db_faces_features,distance_mode="avg",recognition_mode="repeat"):
|
| 154 |
+
|
| 155 |
+
if distance_mode not in ['avg','best']: raise ValueError(f"Unknown mode:{distance_mode} \nMode should be one of these:{['avg','best']}")
|
| 156 |
+
if recognition_mode not in ['repeat','no-repeat']: raise ValueError(f"Unknown mode:{recognition_mode} \nMode should be one of these:{['repeat','no-repeat']}")
|
| 157 |
+
self.distance_mode=distance_mode
|
| 158 |
+
self.recognition_mode=recognition_mode
|
| 159 |
+
|
| 160 |
+
self.faces=faces
|
| 161 |
+
self.db_faces_features=db_faces_features
|
| 162 |
+
|
| 163 |
+
# print(face_features)
|
| 164 |
+
# for xml_file in ["/content/images - Copy/IMG20221124131734.xml"]:
|
| 165 |
+
|
| 166 |
+
if __name__=="__main__":
|
| 167 |
+
|
| 168 |
+
from helper import *
|
| 169 |
+
|
| 170 |
+
img_dir=config.img_dir
|
| 171 |
+
save_dir=config.save_dir
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
_,faces,_=next(os.walk(config.db_dir))
|
| 176 |
+
db_faces_features=[np.loadtxt(f"{config.db_dir}/{face_dir}/features.npy",ndmin=2) for face_dir in faces]
|
| 177 |
+
|
| 178 |
+
for i in range(len(faces)):
|
| 179 |
+
print(faces[i],":",db_faces_features[i].shape)
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
if os.path.exists(save_dir):shutil.rmtree(save_dir)
|
| 183 |
+
os.mkdir(save_dir)
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
fr=face_recognition("face_recognition/Models/v1")
|
| 187 |
+
# fr=face_recognition(thres=0.3)
|
| 188 |
+
# fr.set_face_db_and_mode(faces,db_faces_features,distance_mode="best",recognition_mode="repeat")
|
| 189 |
+
fr.set_face_db_and_mode(faces,db_faces_features,distance_mode="best",recognition_mode="no-repeat")
|
| 190 |
+
|
| 191 |
+
for xml_file in glob(f"{img_dir}/*.xml"):
|
| 192 |
+
tree=ET.parse(xml_file)
|
| 193 |
+
root=tree.getroot()
|
| 194 |
+
img_name=img_dir+'/'+root.find("filename").text
|
| 195 |
+
img=PIL.Image.open(img_name).convert("RGB")
|
| 196 |
+
img = ImageOps.exif_transpose(img)
|
| 197 |
+
img=np.array(img)
|
| 198 |
+
|
| 199 |
+
tree=fr.predict(img,tree)
|
| 200 |
+
|
| 201 |
+
img=show_pred_image(tree,img)
|
| 202 |
+
# plot examples
|
| 203 |
+
# plt.figure(figsize=(10,10))
|
| 204 |
+
# plt.axis("off")
|
| 205 |
+
# plt.title("Labeled images")
|
| 206 |
+
# plt.imshow(img)
|
| 207 |
+
# plt.show()
|
| 208 |
+
print(xml_to_objs_found(tree))
|
| 209 |
+
|
| 210 |
+
cv2.imwrite(save_dir+"/"+root.find("filename").text,cv2.cvtColor(img,cv2.COLOR_RGB2BGR))
|
| 211 |
+
|
app/face_recognition/setup_db_features.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import tensorflow as tf
|
| 2 |
+
from face_recognition import config
|
| 3 |
+
import cv2
|
| 4 |
+
from glob import glob
|
| 5 |
+
import os
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
feature_extractor=tf.keras.models.load_model("face_recognition/feature_extractor.h5",compile=False)
|
| 9 |
+
# feature_extractor.summary()
|
| 10 |
+
|
| 11 |
+
extensions=['.jpg','.jpeg','.png','.svg','.webp']
|
| 12 |
+
|
| 13 |
+
db_dir=config.db_dir
|
| 14 |
+
_,sub_folders,_=next(os.walk(db_dir))
|
| 15 |
+
print(sub_folders)
|
| 16 |
+
for sub_folder in sub_folders:
|
| 17 |
+
image_paths=[]
|
| 18 |
+
[image_paths.extend(glob(db_dir+"\\"+sub_folder+"\\*"+extension)) for extension in extensions]
|
| 19 |
+
|
| 20 |
+
all_img_features=[]
|
| 21 |
+
for image_path in image_paths:
|
| 22 |
+
print(image_path)
|
| 23 |
+
img=cv2.resize(cv2.imread(image_path),[config.input_size,config.input_size])
|
| 24 |
+
all_img_features.append(feature_extractor.predict(img[None,:,:,::-1],verbose=0)[0])
|
| 25 |
+
|
| 26 |
+
np.savetxt(db_dir+"\\"+sub_folder+"\\features.npy",all_img_features)
|
| 27 |
+
|
| 28 |
+
# "aligned_all"
|
app/face_recognition/split_model.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import tensorflow as tf
|
| 2 |
+
from tensorflow.keras import backend as K
|
| 3 |
+
from tensorflow.keras import Model,layers
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
model=tf.keras.models.load_model("face_recognization_model.h5",compile=False)
|
| 7 |
+
# model.summary()
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
feature_extractor=Model(model.layers[0].input,model.layers[2](model.layers[0].input),name='feature_extractor')
|
| 11 |
+
feature_extractor.summary()
|
| 12 |
+
feature_extractor.save("feature_extractor.h5")
|
app/helper.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import io
|
| 2 |
+
import base64
|
| 3 |
+
import uuid
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import mysql.connector
|
| 6 |
+
|
| 7 |
+
def generate_random_id():
|
| 8 |
+
return str(uuid.uuid1())
|
| 9 |
+
|
| 10 |
+
def image_to_base64(img):
|
| 11 |
+
img=Image.fromarray(img[:,:,:3])
|
| 12 |
+
rawBytes=io.BytesIO()
|
| 13 |
+
img.save(rawBytes,"JPEG")
|
| 14 |
+
rawBytes.seek(0)
|
| 15 |
+
img_base64=str(base64.b64encode(rawBytes.read()))
|
| 16 |
+
return img_base64
|
| 17 |
+
|
| 18 |
+
def base64_to_image(img_base64):
|
| 19 |
+
# Assuming base64_str is the string value without 'data:image/jpeg;base64,'
|
| 20 |
+
img = Image.open(io.BytesIO(base64.decodebytes(bytes(img_base64, "utf-8"))))
|
| 21 |
+
return np.array(img)
|
| 22 |
+
|
| 23 |
+
def access_database_as_admin():
|
| 24 |
+
|
| 25 |
+
return mysql.connector.connect(host="bnadwttldj2i5cq9aymp-mysql.services.clever-cloud.com",
|
| 26 |
+
user="uvfqvcypihhznd2u",
|
| 27 |
+
port=3306,
|
| 28 |
+
password="CX6TBadRQYFqprozqDTo",
|
| 29 |
+
database="bnadwttldj2i5cq9aymp")
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def create_user_table(username):
|
| 33 |
+
dataBase=access_database_as_admin()
|
| 34 |
+
cursor=dataBase.cursor()
|
| 35 |
+
query=f"""create table if not exists user_{username}(
|
| 36 |
+
person_id varchar(30) unique not null,
|
| 37 |
+
face_vectors blob not null,
|
| 38 |
+
remarks text not null,
|
| 39 |
+
group_id char(30)
|
| 40 |
+
);"""
|
| 41 |
+
cursor.execute(query)
|
| 42 |
+
dataBase.commit()
|
| 43 |
+
dataBase.close()
|
| 44 |
+
|
| 45 |
+
def drop_user_table(username):
|
| 46 |
+
dataBase=access_database_as_admin()
|
| 47 |
+
cursor=dataBase.cursor()
|
| 48 |
+
query=f"drop table if exists user_{username};"
|
| 49 |
+
cursor.execute(query)
|
| 50 |
+
dataBase.commit()
|
| 51 |
+
dataBase.close()
|
| 52 |
+
|
| 53 |
+
def add_row_user_table(username,person_id,face_vectors,remarks,group_id=None):
|
| 54 |
+
"""
|
| 55 |
+
person_id:str
|
| 56 |
+
face_vectors:np.array shape:(n,128)
|
| 57 |
+
remarks:np.array shape:(n,)
|
| 58 |
+
group_id:str
|
| 59 |
+
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
# print(face_vectors.tobytes())
|
| 63 |
+
# print(remarks.tobytes())
|
| 64 |
+
# print(np.frombuffer(remarks.tobytes(), dtype="float64"))
|
| 65 |
+
# pass
|
| 66 |
+
dataBase=access_database_as_admin()
|
| 67 |
+
cursor=dataBase.cursor()
|
| 68 |
+
cursor.execute(f"select person_id from user_{username} where person_id=%s;",[person_id])
|
| 69 |
+
if cursor.fetchone() is None:
|
| 70 |
+
if group_id is not None:
|
| 71 |
+
query=f"insert into user_{username}(person_id,face_vectors,remarks,group_id) values(%s,%s,%s,%s);"
|
| 72 |
+
cursor.execute(query,[person_id,face_vectors.tobytes(),remarks,group_id])
|
| 73 |
+
else:
|
| 74 |
+
query=f"insert into user_{username}(person_id,face_vectors,remarks) values(%s,%s,%s);"
|
| 75 |
+
cursor.execute(query,[person_id,face_vectors.tobytes(),remarks])
|
| 76 |
+
else:
|
| 77 |
+
if group_id is not None:
|
| 78 |
+
query=f"update user_{username} SET face_vectors=%s,remarks=%s,group_id=%s where person_id=%s;"
|
| 79 |
+
cursor.execute(query,[face_vectors.tobytes(),remarks,group_id,person_id])
|
| 80 |
+
else:
|
| 81 |
+
query=f"update user_{username} SET face_vectors=%s,remarks=%s where person_id=%s;"
|
| 82 |
+
cursor.execute(query,[face_vectors.tobytes(),remarks,person_id])
|
| 83 |
+
dataBase.commit()
|
| 84 |
+
dataBase.close()
|
| 85 |
+
|
| 86 |
+
def read_row_user_table(username):
|
| 87 |
+
dataBase=access_database_as_admin()
|
| 88 |
+
cursor=dataBase.cursor()
|
| 89 |
+
query=f"select person_id,face_vectors,remarks,group_id from user_{username};"
|
| 90 |
+
cursor.execute(query)
|
| 91 |
+
data=list(cursor.fetchone())
|
| 92 |
+
data[1]=np.frombuffer(data[1],dtype="float64")
|
| 93 |
+
|
| 94 |
+
data[2]=data[2].split(",")
|
| 95 |
+
data[1]=data[1].reshape(len(data[2]),-1)
|
| 96 |
+
print(data)
|
| 97 |
+
# dataBase.commit()
|
| 98 |
+
dataBase.close()
|
| 99 |
+
|
| 100 |
+
def read_user_table(username,split_remarks=True,group_id=None):
|
| 101 |
+
dataBase=access_database_as_admin()
|
| 102 |
+
cursor=dataBase.cursor()
|
| 103 |
+
if group_id is None:
|
| 104 |
+
cursor.execute(f"select person_id,face_vectors,remarks,group_id from user_{username};")
|
| 105 |
+
else:
|
| 106 |
+
cursor.execute(f"select person_id,face_vectors,remarks,group_id from user_{username} where group_id=%s;",[group_id])
|
| 107 |
+
data={column_name:[] for column_name in cursor.column_names}
|
| 108 |
+
for row in cursor.fetchall():
|
| 109 |
+
row=list(row)
|
| 110 |
+
row[1]=np.frombuffer(row[1],dtype="float64")
|
| 111 |
+
row[2]=row[2].split(",")
|
| 112 |
+
|
| 113 |
+
data['person_id'].append(row[0])
|
| 114 |
+
data['face_vectors'].append(row[1].reshape(len(row[2]),-1))
|
| 115 |
+
if not split_remarks:
|
| 116 |
+
row[2]=",".join(row[2])
|
| 117 |
+
data['remarks'].append(row[2])
|
| 118 |
+
data['group_id'].append(row[3])
|
| 119 |
+
|
| 120 |
+
dataBase.close()
|
| 121 |
+
# print(data['person_id'])
|
| 122 |
+
# print(data['face_vectors'])
|
| 123 |
+
return data
|
| 124 |
+
|
| 125 |
+
def remove_person_from_user_table(username,person_id):
|
| 126 |
+
dataBase=access_database_as_admin()
|
| 127 |
+
cursor=dataBase.cursor()
|
| 128 |
+
query=f"delete from user_{username} where person_id=%s;"
|
| 129 |
+
cursor.execute(query,[person_id])
|
| 130 |
+
dataBase.commit()
|
| 131 |
+
dataBase.close()
|
| 132 |
+
|
| 133 |
+
import numpy as np
|
| 134 |
+
|
| 135 |
+
# add_row_user_table("abc","1",np.array([[2,3],[1,4]],dtype="float64"),"front face,left face,right face",group_id="1")
|
| 136 |
+
# read_user_table("abc")
|
| 137 |
+
# print(np.fromstring("a,b,c",dtype='S3'))
|
| 138 |
+
|
| 139 |
+
# drop_user_table("abc")
|
| 140 |
+
# create_user_table("abc")
|
app/main/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Blueprint
|
| 2 |
+
|
| 3 |
+
bp=Blueprint("main",__name__)
|
| 4 |
+
|
| 5 |
+
from app.main import routes
|
app/main/routes.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import redirect
|
| 2 |
+
from app.main import bp
|
| 3 |
+
|
| 4 |
+
@bp.route("/")
|
| 5 |
+
def index():
|
| 6 |
+
return redirect("/demo/")
|
app/static/admin/dashboard.css
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*{
|
| 2 |
+
padding: 3px 9px;
|
| 3 |
+
}
|
| 4 |
+
#access_requests_table{
|
| 5 |
+
/* border:2px dashed black; */
|
| 6 |
+
|
| 7 |
+
/* position: absolute;
|
| 8 |
+
top:50%;
|
| 9 |
+
left:50%;
|
| 10 |
+
transform: translate(-50%,-50%); */
|
| 11 |
+
width:fit-content;
|
| 12 |
+
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
table, th, td {
|
| 16 |
+
border:2px solid black;
|
| 17 |
+
border-collapse: collapse;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
button{
|
| 21 |
+
cursor: pointer;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
.accept{
|
| 25 |
+
background-color: greenyellow;
|
| 26 |
+
}
|
| 27 |
+
.reject{
|
| 28 |
+
background-color: red;
|
| 29 |
+
}
|
| 30 |
+
.revoke{
|
| 31 |
+
background-color: blueviolet;
|
| 32 |
+
}
|
app/static/admin/dashboard.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function add_request_row(username,request_message)
|
| 2 |
+
{
|
| 3 |
+
//#access_requests_table
|
| 4 |
+
// <tr>
|
| 5 |
+
// <td>abc</td>
|
| 6 |
+
// <td>Please give access :(</td>
|
| 7 |
+
//
|
| 8 |
+
// <td>
|
| 9 |
+
// <button class="accept"><i class="fa-solid fa-check"></i></button>
|
| 10 |
+
// <button class="reject"><i class="fa-solid fa-close"></i></button>
|
| 11 |
+
// </td>
|
| 12 |
+
// </tr>
|
| 13 |
+
var table_row=document.createElement("tr");
|
| 14 |
+
var username_tag=document.createElement("td");
|
| 15 |
+
username_tag.innerText=username;
|
| 16 |
+
var request_message_tag=document.createElement("td");
|
| 17 |
+
request_message_tag.innerText=request_message;
|
| 18 |
+
|
| 19 |
+
var review_tag=document.createElement("td");
|
| 20 |
+
review_tag.innerHTML='<button class="accept" onclick="perform_action(this);"><i class="fa-solid fa-check"></i></button>\
|
| 21 |
+
<button class="reject" onclick="perform_action(this);"><i class="fa-solid fa-close"></i></button>';
|
| 22 |
+
table_row.appendChild(username_tag);
|
| 23 |
+
table_row.appendChild(request_message_tag);
|
| 24 |
+
table_row.appendChild(review_tag);
|
| 25 |
+
document.querySelector("#access_requests_table").appendChild(table_row);
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
fetch("get_all_requests/").then(function(response){
|
| 33 |
+
return response.json();
|
| 34 |
+
}).then(function(response){
|
| 35 |
+
console.log(response);
|
| 36 |
+
if ('username' in response)
|
| 37 |
+
{
|
| 38 |
+
for(var i=0;i<response['username'].length;i++)
|
| 39 |
+
{
|
| 40 |
+
if (response['access_key'][i]==null)
|
| 41 |
+
add_request_row(response['username'][i],response['request_message'][i])
|
| 42 |
+
else
|
| 43 |
+
add_access_row(response['username'][i],response['request_message'][i])
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
})
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
function perform_action(elem){
|
| 53 |
+
// console.log(elem);
|
| 54 |
+
console.log(elem.getAttribute("class"));
|
| 55 |
+
var row=elem.parentNode.parentNode;
|
| 56 |
+
// username:row.firstChild.innerText
|
| 57 |
+
|
| 58 |
+
if (elem.getAttribute("class")=="reject")
|
| 59 |
+
{
|
| 60 |
+
const response = confirm("Are you sure you want to do that?");
|
| 61 |
+
if (!response)
|
| 62 |
+
{
|
| 63 |
+
return;
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
var formdata=new FormData();
|
| 68 |
+
formdata.append("username",row.firstChild.innerText);
|
| 69 |
+
formdata.append("mode",elem.getAttribute("class"));
|
| 70 |
+
fetch("update_requests/",{
|
| 71 |
+
method:"POST",
|
| 72 |
+
body:formdata
|
| 73 |
+
}).then(function(response){
|
| 74 |
+
return response.json();
|
| 75 |
+
}).then(function(response){
|
| 76 |
+
console.log("response:",response);
|
| 77 |
+
if (elem.getAttribute("class")=="accept")
|
| 78 |
+
{
|
| 79 |
+
add_access_row(row.firstChild.innerText,row.querySelector("td:nth-child(2)").innerText);
|
| 80 |
+
}
|
| 81 |
+
else if (elem.getAttribute("class")=="revoke")
|
| 82 |
+
{
|
| 83 |
+
add_request_row(row.firstChild.innerText,row.firstChild.dataset.request_message);
|
| 84 |
+
}
|
| 85 |
+
row.remove();
|
| 86 |
+
})
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
function add_access_row(username,request_message)
|
| 97 |
+
{
|
| 98 |
+
//#access_requests_table
|
| 99 |
+
// <tr>
|
| 100 |
+
// <td>username</td>
|
| 101 |
+
// <td>
|
| 102 |
+
// <button class="reject"><i class="fa-solid fa-close"></i></button>
|
| 103 |
+
// </td>
|
| 104 |
+
// </tr>
|
| 105 |
+
var table_row=document.createElement("tr");
|
| 106 |
+
var username_tag=document.createElement("td");
|
| 107 |
+
username_tag.innerText=username;
|
| 108 |
+
username_tag.dataset.request_message=request_message;
|
| 109 |
+
var remove_access_tag=document.createElement("td");
|
| 110 |
+
remove_access_tag.innerHTML='<button class="revoke" onclick="perform_action(this);"><i class="fa-solid fa-close"></i></button>';
|
| 111 |
+
table_row.appendChild(username_tag);
|
| 112 |
+
table_row.appendChild(remove_access_tag);
|
| 113 |
+
document.querySelector("#access_granted_table").appendChild(table_row);
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
// add_access_row('username')
|
| 118 |
+
// add_access_row('anuj')
|
| 119 |
+
// add_access_row('abc')
|
app/static/admin/login.css
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#container{
|
| 2 |
+
position: absolute;
|
| 3 |
+
top:50%;
|
| 4 |
+
left:50%;
|
| 5 |
+
transform:translate(-50%,-50%);
|
| 6 |
+
display: flex;
|
| 7 |
+
row-gap: 20px;
|
| 8 |
+
flex-direction: column;
|
| 9 |
+
align-items: center;
|
| 10 |
+
border:2px solid black;
|
| 11 |
+
/* box-sizing: border-box; */
|
| 12 |
+
|
| 13 |
+
padding: 20px;
|
| 14 |
+
}
|
| 15 |
+
#container>h1{
|
| 16 |
+
text-align: center;
|
| 17 |
+
margin: 0px;
|
| 18 |
+
}
|
| 19 |
+
#container>p{
|
| 20 |
+
text-align: center;
|
| 21 |
+
margin: 0px;
|
| 22 |
+
margin-bottom: 5px;
|
| 23 |
+
visibility: hidden;
|
| 24 |
+
color:red;
|
| 25 |
+
}
|
| 26 |
+
.active{
|
| 27 |
+
visibility: visible !important;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
#container>div{
|
| 31 |
+
display: flex;
|
| 32 |
+
width:314px;
|
| 33 |
+
justify-content: space-between;
|
| 34 |
+
}
|
| 35 |
+
#container>div>p{
|
| 36 |
+
margin: 0;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
#container>div>input{
|
| 40 |
+
border:none;
|
| 41 |
+
outline:none;
|
| 42 |
+
border-bottom:1px solid black;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
#container>input{
|
| 46 |
+
width:30%;
|
| 47 |
+
margin: 10px;
|
| 48 |
+
}
|
| 49 |
+
|
app/static/admin/registeration.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function check_username(elem){
|
| 2 |
+
if(elem.value!=''){
|
| 3 |
+
fetch(`/user/is_username_available/?username=${elem.value}`,{
|
| 4 |
+
method:"GET",
|
| 5 |
+
|
| 6 |
+
}).then(function(response){
|
| 7 |
+
return response.json();
|
| 8 |
+
}).then(function(response){
|
| 9 |
+
// show a indicator that this username is available
|
| 10 |
+
if(response['available'])
|
| 11 |
+
{
|
| 12 |
+
// show green indicator
|
| 13 |
+
console.log("Username available");
|
| 14 |
+
elem.dataset.valid=true;
|
| 15 |
+
elem.style.borderColor='green';
|
| 16 |
+
elem.style.borderWidth=4;
|
| 17 |
+
}
|
| 18 |
+
else
|
| 19 |
+
{
|
| 20 |
+
// show red indicator
|
| 21 |
+
console.log("Username unavailable");
|
| 22 |
+
elem.dataset.valid=false;
|
| 23 |
+
elem.style.borderColor='red';
|
| 24 |
+
elem.style.borderWidth=4;
|
| 25 |
+
}
|
| 26 |
+
});
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
function check_password_is_matching(confirm_password){
|
| 31 |
+
var password=document.querySelector("#password");
|
| 32 |
+
if(confirm_password.value!=password.value){
|
| 33 |
+
confirm_password.style.borderColor='red';
|
| 34 |
+
confirm_password.style.borderWidth=4;
|
| 35 |
+
}
|
| 36 |
+
else{
|
| 37 |
+
confirm_password.style.borderColor='green';
|
| 38 |
+
confirm_password.style.borderWidth=4;
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
function validate_form(form){
|
| 43 |
+
if(form.username.dataset.valid=="false"){
|
| 44 |
+
document.querySelector("#container>p").style.visibility='visible';
|
| 45 |
+
document.querySelector("#container>p").innerText="username not available";
|
| 46 |
+
return false;
|
| 47 |
+
}
|
| 48 |
+
else if(form.password.value!=form.confirm_password.value){
|
| 49 |
+
document.querySelector("#container>p").style.visibility='visible';
|
| 50 |
+
document.querySelector("#container>p").innerText="password not matching";
|
| 51 |
+
return false;
|
| 52 |
+
}
|
| 53 |
+
else{
|
| 54 |
+
return true;
|
| 55 |
+
}
|
| 56 |
+
}
|
app/static/demo/index/dark.css
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root{
|
| 2 |
+
/* --color-primary:#7380ec; */
|
| 3 |
+
/* --color-primary:rgb(205, 18, 234); */
|
| 4 |
+
--color-primary:rgb(163 18 234);
|
| 5 |
+
--color-secondary:rgb(173, 54, 232);
|
| 6 |
+
|
| 7 |
+
--color-name-border:white;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
*{
|
| 11 |
+
box-sizing: border-box;
|
| 12 |
+
/* background-color: rgb(22, 22, 22); */
|
| 13 |
+
background-color: rgb(12, 12, 12);
|
| 14 |
+
color:white;
|
| 15 |
+
font-weight: bold;
|
| 16 |
+
font-family: 'Poppins';
|
| 17 |
+
/* pointer-events: none; */
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
/* width */
|
| 23 |
+
::-webkit-scrollbar {
|
| 24 |
+
width: 10px;
|
| 25 |
+
height:10px;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
/* Track */
|
| 29 |
+
/* ::-webkit-scrollbar-track {
|
| 30 |
+
background: #B2EBF2;
|
| 31 |
+
} */
|
| 32 |
+
|
| 33 |
+
/* Handle */
|
| 34 |
+
::-webkit-scrollbar-thumb {
|
| 35 |
+
/* background: #5E35B1; */
|
| 36 |
+
background: var(--color-primary);
|
| 37 |
+
border-radius: 10px;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
/* Handle on hover */
|
| 41 |
+
::-webkit-scrollbar-thumb:hover {
|
| 42 |
+
background: var(--color-secondary);
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
#db_images_bar
|
| 47 |
+
{
|
| 48 |
+
display: flex;
|
| 49 |
+
height:100px;
|
| 50 |
+
/* border:2px solid rgb(39, 86, 255); */
|
| 51 |
+
width: 100%;
|
| 52 |
+
padding-bottom: 10px;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
#db_images_bar>.add_button{
|
| 56 |
+
height: 60px;
|
| 57 |
+
width: 20%;
|
| 58 |
+
padding: 2px;
|
| 59 |
+
margin: auto 0;
|
| 60 |
+
margin-right: 5px;
|
| 61 |
+
display: flex;
|
| 62 |
+
align-items: center;
|
| 63 |
+
justify-content: center;
|
| 64 |
+
flex-direction: column;
|
| 65 |
+
z-index: 2;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
#db_images_bar>.add_button>i{
|
| 70 |
+
color:white;
|
| 71 |
+
background-color: inherit;
|
| 72 |
+
font-size: 3em;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
#db_images
|
| 78 |
+
{
|
| 79 |
+
display: flex;
|
| 80 |
+
height: 100%;
|
| 81 |
+
width: 80%;
|
| 82 |
+
overflow: scroll hidden;
|
| 83 |
+
grid-row-start: 1;
|
| 84 |
+
margin: 0px 10px;
|
| 85 |
+
}
|
| 86 |
+
.db_image{
|
| 87 |
+
height:inherit;
|
| 88 |
+
filter: blur(0);
|
| 89 |
+
transition: 0.3s;
|
| 90 |
+
border-radius: inherit;
|
| 91 |
+
}
|
| 92 |
+
.db_image_container{
|
| 93 |
+
height: inherit;
|
| 94 |
+
background: black;
|
| 95 |
+
margin-right:10px ;
|
| 96 |
+
position: relative;
|
| 97 |
+
display: inline-block;
|
| 98 |
+
|
| 99 |
+
border-radius: 20%;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
.db_image_container:hover{
|
| 103 |
+
cursor: pointer;
|
| 104 |
+
}
|
| 105 |
+
.db_image_container:hover .db_image{
|
| 106 |
+
opacity: 0.5;
|
| 107 |
+
filter: blur(3px);
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
.db_image_container>.close_text{
|
| 111 |
+
opacity: 0;
|
| 112 |
+
font-size: xxx-large;
|
| 113 |
+
color:white;
|
| 114 |
+
position: absolute;
|
| 115 |
+
top:50%;
|
| 116 |
+
left:50%;
|
| 117 |
+
transform: translate(-50%,-50%);
|
| 118 |
+
padding: 0px;
|
| 119 |
+
margin: 0px;
|
| 120 |
+
transition: 0.3s;
|
| 121 |
+
background-color: transparent;
|
| 122 |
+
}
|
| 123 |
+
.db_image_container:hover .close_text{
|
| 124 |
+
opacity: 1;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
/*new main container*/
|
| 134 |
+
#container{
|
| 135 |
+
height:452px;
|
| 136 |
+
width:1000px;
|
| 137 |
+
/* border:2px dotted black; */
|
| 138 |
+
margin: 0 auto;
|
| 139 |
+
display: flex;
|
| 140 |
+
position: absolute;
|
| 141 |
+
top:50%;
|
| 142 |
+
left:50%;
|
| 143 |
+
transform: translate(-50%,-50%);
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
#container>.left-section{
|
| 147 |
+
height: inherit;
|
| 148 |
+
width:50%;
|
| 149 |
+
border-right:4px solid rgb(123, 123, 123);
|
| 150 |
+
display: flex;
|
| 151 |
+
align-items: center;
|
| 152 |
+
flex-direction: column;
|
| 153 |
+
padding: 10px;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
#face_rec_image{
|
| 157 |
+
height: 65%;
|
| 158 |
+
/* border: 1px solid turquoise; */
|
| 159 |
+
border:0;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
img[src=""],img:not([src]) {
|
| 165 |
+
opacity: 0;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
img[src="*"]{
|
| 169 |
+
opacity: 1;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
.buttons{
|
| 174 |
+
display: flex;
|
| 175 |
+
align-items: center;
|
| 176 |
+
justify-content: center;
|
| 177 |
+
/* border:2px solid red; */
|
| 178 |
+
height:50px;
|
| 179 |
+
width:100%;
|
| 180 |
+
}
|
| 181 |
+
.buttons>div>input{
|
| 182 |
+
padding:2px 5px;
|
| 183 |
+
margin:0px 5px;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
#container>.right-section{
|
| 187 |
+
height: inherit;
|
| 188 |
+
width:50%;
|
| 189 |
+
display: flex;
|
| 190 |
+
align-items: center;
|
| 191 |
+
flex-direction: column;
|
| 192 |
+
/* justify-content: center; */
|
| 193 |
+
padding: 10px;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
#unassigned_faces{
|
| 197 |
+
width:100%;
|
| 198 |
+
height:100px;
|
| 199 |
+
/* border:1px solid rgb(255, 66, 255); */
|
| 200 |
+
padding:10px;
|
| 201 |
+
display: flex;
|
| 202 |
+
flex-direction: row;
|
| 203 |
+
align-items: center;
|
| 204 |
+
overflow: scroll hidden;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
.crop_img{
|
| 208 |
+
height: 50px;
|
| 209 |
+
margin: 2px;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
#name_list{
|
| 213 |
+
width:100%;
|
| 214 |
+
height:55%;
|
| 215 |
+
/* border:2px solid rgb(66, 113, 255); */
|
| 216 |
+
overflow: hidden scroll;
|
| 217 |
+
padding:15px;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
#name_list>.person{
|
| 221 |
+
width:100%;
|
| 222 |
+
height: 80px;
|
| 223 |
+
border-bottom:2px solid var(--color-name-border);
|
| 224 |
+
position: relative;
|
| 225 |
+
padding: 4px 1px;
|
| 226 |
+
display: flex;
|
| 227 |
+
align-items: end;
|
| 228 |
+
}
|
| 229 |
+
#name_list>.person>input{
|
| 230 |
+
position: absolute;
|
| 231 |
+
left:5px;
|
| 232 |
+
top:5px;
|
| 233 |
+
/* padding: inherit; */
|
| 234 |
+
background-color: transparent;
|
| 235 |
+
border:none;
|
| 236 |
+
outline: none;
|
| 237 |
+
}
|
| 238 |
+
#name_list>.person>input:focus{
|
| 239 |
+
border-bottom: 1px solid var(--color-name-border);
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
#name_list>.person>i{
|
| 243 |
+
color:red;
|
| 244 |
+
position: absolute;
|
| 245 |
+
top:50%;
|
| 246 |
+
transform: translate(0,-50%);
|
| 247 |
+
right:20px;
|
| 248 |
+
|
| 249 |
+
}
|
| 250 |
+
#name_list>.person>i:hover{
|
| 251 |
+
cursor:pointer;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
#name_list>.person>.faces{
|
| 255 |
+
height:fit-content;
|
| 256 |
+
width: 100%;
|
| 257 |
+
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
#add_name_btn{
|
| 262 |
+
margin: 40px;
|
| 263 |
+
width:inherit;
|
| 264 |
+
height:inherit;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
.btn{
|
| 268 |
+
background-color: var(--color-primary);
|
| 269 |
+
box-shadow: 0px 0px 43px -4px var(--color-primary);
|
| 270 |
+
border-radius: 5px;
|
| 271 |
+
border:none;
|
| 272 |
+
}
|
| 273 |
+
.btn:hover{
|
| 274 |
+
cursor: pointer;
|
| 275 |
+
background-color: var(--color-secondary);
|
| 276 |
+
/* box-shadow: 0px 0px 43px -4px var(--color-secondary); */
|
| 277 |
+
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
.settings_btn{
|
| 281 |
+
position: absolute;
|
| 282 |
+
top:40px;
|
| 283 |
+
right:40px;
|
| 284 |
+
font-size: larger;
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
#settings_menu{
|
| 288 |
+
background-color: black;
|
| 289 |
+
height: fit-content;
|
| 290 |
+
width:40%;
|
| 291 |
+
z-index: 10000;
|
| 292 |
+
position: absolute;
|
| 293 |
+
top:50%;
|
| 294 |
+
left:50%;
|
| 295 |
+
transform: translate(-50%,-50%);
|
| 296 |
+
border-radius: 10px;
|
| 297 |
+
box-shadow: 0px 0px 43px -4px var(--color-primary);
|
| 298 |
+
padding: 2px 20px;
|
| 299 |
+
display: none;
|
| 300 |
+
}
|
| 301 |
+
#settings_menu>h1{
|
| 302 |
+
background-color: inherit;
|
| 303 |
+
}
|
| 304 |
+
#settings_menu>.settings{
|
| 305 |
+
background-color: inherit;
|
| 306 |
+
height: 280px;
|
| 307 |
+
overflow: hidden scroll;
|
| 308 |
+
position: relative;
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
#settings_menu>.close_btn{
|
| 312 |
+
color:red;
|
| 313 |
+
position: absolute;
|
| 314 |
+
top: 20px;
|
| 315 |
+
right: 20px;
|
| 316 |
+
font-size: larger;
|
| 317 |
+
background-color: transparent;
|
| 318 |
+
}
|
| 319 |
+
#settings_menu>.close_btn:hover{
|
| 320 |
+
cursor: pointer;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
.reset_btn_container{
|
| 324 |
+
position: absolute;
|
| 325 |
+
top: 20px;
|
| 326 |
+
right: 30px;
|
| 327 |
+
}
|
| 328 |
+
.reset_btn_container>.reset_btn{
|
| 329 |
+
color:white;
|
| 330 |
+
position: fixed;
|
| 331 |
+
font-size: larger;
|
| 332 |
+
background-color: transparent;
|
| 333 |
+
}
|
| 334 |
+
.reset_btn_container>.reset_btn:hover{
|
| 335 |
+
cursor: pointer;
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
#container.blur{
|
| 339 |
+
filter: blur(5px);
|
| 340 |
+
pointer-events: none;
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
#save_btn_container{
|
| 344 |
+
display: flex;
|
| 345 |
+
justify-content: center;
|
| 346 |
+
margin: 10px 0px;
|
| 347 |
+
font-size: large;
|
| 348 |
+
}
|
| 349 |
+
#save_settings_btn{
|
| 350 |
+
background-color: rgb(76, 76, 232);
|
| 351 |
+
outline: none;
|
| 352 |
+
border: none;
|
| 353 |
+
border-radius: 6px;
|
| 354 |
+
}
|
app/static/demo/index/icon.jpg
ADDED
|
|
app/static/demo/index/script.js
ADDED
|
@@ -0,0 +1,498 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
var dragged_elem;
|
| 2 |
+
var touch_start_time=null;
|
| 3 |
+
var elapsedTime=null;
|
| 4 |
+
var maxAllowedTime=500;
|
| 5 |
+
|
| 6 |
+
function drag(ev){
|
| 7 |
+
// ev.preventDefault();
|
| 8 |
+
console.log(ev.target.id);
|
| 9 |
+
dragged_elem=ev.target;
|
| 10 |
+
// ev.dataTransfer.setData("text",ev.target.id);
|
| 11 |
+
}
|
| 12 |
+
function dragTouchstart(e,elem) {
|
| 13 |
+
elapsedTime=null;
|
| 14 |
+
touch_start_time = new Date().getTime();
|
| 15 |
+
}
|
| 16 |
+
function dragTouchmove(e,elem) {
|
| 17 |
+
|
| 18 |
+
let touchX = e.touches[0].pageX;
|
| 19 |
+
let touchY = e.touches[0].pageY;
|
| 20 |
+
|
| 21 |
+
if(elapsedTime==null)
|
| 22 |
+
{
|
| 23 |
+
elapsedTime = new Date().getTime() - touch_start_time;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
// console.log(elapsedTime);
|
| 27 |
+
|
| 28 |
+
if (elapsedTime<maxAllowedTime) return;
|
| 29 |
+
|
| 30 |
+
// console.log("no-scroll")
|
| 31 |
+
e.preventDefault()
|
| 32 |
+
|
| 33 |
+
if (dragged_elem==null)
|
| 34 |
+
{
|
| 35 |
+
dragged_elem=elem;
|
| 36 |
+
console.log(dragged_elem);
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
function allowDrop(ev){
|
| 42 |
+
ev.preventDefault();
|
| 43 |
+
console.log("ready to be dropped");
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
function drop(ev,element){
|
| 47 |
+
ev.preventDefault();
|
| 48 |
+
console.log("dropped!");
|
| 49 |
+
element.appendChild(dragged_elem);
|
| 50 |
+
dragged_elem=null;
|
| 51 |
+
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
function dragTouchend(e,elem) {
|
| 55 |
+
|
| 56 |
+
if(dragged_elem==null) return;
|
| 57 |
+
|
| 58 |
+
var droppable_element=document.elementFromPoint(e.changedTouches[0].clientX, e.changedTouches[0].clientY).closest(".droppable");
|
| 59 |
+
if(droppable_element==null) return;
|
| 60 |
+
|
| 61 |
+
if(droppable_element.hasAttribute("data-dropto")) droppable_element=droppable_element.querySelector(droppable_element.dataset.dropto)
|
| 62 |
+
|
| 63 |
+
droppable_element.appendChild(dragged_elem);
|
| 64 |
+
|
| 65 |
+
console.log(droppable_element)
|
| 66 |
+
dragged_elem=null;
|
| 67 |
+
// e.target.style.transform="scale(0.8)";
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
// function click_based_drop(elem)
|
| 71 |
+
// {
|
| 72 |
+
// if(dragged_elem!=null){
|
| 73 |
+
|
| 74 |
+
// console.log("asd");
|
| 75 |
+
// if(elem.getAttribute("class")=="person")
|
| 76 |
+
// {
|
| 77 |
+
// console.log();
|
| 78 |
+
// elem.querySelector(".faces").appendChild(dragged_elem);
|
| 79 |
+
// dragged_elem=null;
|
| 80 |
+
// features_updated=false;
|
| 81 |
+
// }
|
| 82 |
+
// else if (elem.getAttribute("id")=="unassigned_faces")
|
| 83 |
+
// {
|
| 84 |
+
// document.querySelector("#unassigned_faces").appendChild(dragged_elem);
|
| 85 |
+
// dragged_elem=null;
|
| 86 |
+
// features_updated=false;
|
| 87 |
+
// }
|
| 88 |
+
// }
|
| 89 |
+
|
| 90 |
+
// }
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
var database_input=document.querySelector("#db_images_bar>.add_button>input")
|
| 94 |
+
database_input.addEventListener("change", function(e){
|
| 95 |
+
if(e.target.files[0]){
|
| 96 |
+
console.log(e.target.files[0].name);
|
| 97 |
+
|
| 98 |
+
const formdata = new FormData();
|
| 99 |
+
formdata.append("a",23);
|
| 100 |
+
formdata.append("image",e.target.files[0]);
|
| 101 |
+
|
| 102 |
+
fetch("/demo/add_crops/",
|
| 103 |
+
{
|
| 104 |
+
method:'POST',
|
| 105 |
+
body:formdata,
|
| 106 |
+
}).then(
|
| 107 |
+
function(response)
|
| 108 |
+
{
|
| 109 |
+
return response.json();
|
| 110 |
+
}
|
| 111 |
+
).then(
|
| 112 |
+
function(response)
|
| 113 |
+
{
|
| 114 |
+
console.log(response);
|
| 115 |
+
if (response['message']=='successful')
|
| 116 |
+
{
|
| 117 |
+
|
| 118 |
+
var images_div=document.querySelector("#db_images_bar>#db_images");
|
| 119 |
+
var img_container_tag=document.createElement("div");
|
| 120 |
+
var img_remove_tag=document.createElement("p");
|
| 121 |
+
var img_tag=document.createElement("img");
|
| 122 |
+
img_tag.src="data:image/jpeg;base64,"+response['image'].split('\'')[1];
|
| 123 |
+
img_tag.setAttribute("class","db_image");
|
| 124 |
+
img_remove_tag.setAttribute("class","close_text");
|
| 125 |
+
img_remove_tag.innerHTML="✖";
|
| 126 |
+
img_container_tag.setAttribute("class","db_image_container");
|
| 127 |
+
img_container_tag.setAttribute("onclick","remove_image(this);");
|
| 128 |
+
img_container_tag.dataset.image_name=response['image_name'];
|
| 129 |
+
img_container_tag.appendChild(img_tag);
|
| 130 |
+
img_container_tag.appendChild(img_remove_tag);
|
| 131 |
+
|
| 132 |
+
img_container_tag.addEventListener("mouseenter",function(e){
|
| 133 |
+
|
| 134 |
+
var image=document.querySelector("#face_rec_image");
|
| 135 |
+
if (image.hasAttribute('src'))
|
| 136 |
+
{
|
| 137 |
+
image.dataset.old_src=image.src;
|
| 138 |
+
}
|
| 139 |
+
image.src=e.target.querySelector("img").src;
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
})
|
| 143 |
+
img_container_tag.addEventListener("mouseleave",function(){
|
| 144 |
+
|
| 145 |
+
var image=document.querySelector("#face_rec_image");
|
| 146 |
+
if (image.hasAttribute("data-old_src")){
|
| 147 |
+
console.log("has_old_src")
|
| 148 |
+
image.src=image.dataset.old_src;
|
| 149 |
+
delete image.dataset.old_src;
|
| 150 |
+
}
|
| 151 |
+
else{
|
| 152 |
+
image.removeAttribute('src');
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
})
|
| 157 |
+
|
| 158 |
+
images_div.appendChild(img_container_tag);
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
// add crops to the #unassigned_faces
|
| 164 |
+
for(var i=0;i<response["crops"].length;i++)
|
| 165 |
+
{
|
| 166 |
+
var crop_container=document.querySelector("#unassigned_faces");
|
| 167 |
+
var crop_img=document.createElement("img");
|
| 168 |
+
crop_img.src="data:image/jpeg;base64,"+response["crops"][i].split('\'')[1];
|
| 169 |
+
crop_img.dataset.image_name=response['image_name'];
|
| 170 |
+
|
| 171 |
+
crop_img.setAttribute("class","crop_img");
|
| 172 |
+
|
| 173 |
+
crop_img.setAttribute("draggable","true");
|
| 174 |
+
crop_img.setAttribute("ondragstart","drag(event)");
|
| 175 |
+
crop_container.appendChild(crop_img)
|
| 176 |
+
|
| 177 |
+
// disable right click
|
| 178 |
+
crop_img.addEventListener("contextmenu",function(ev){
|
| 179 |
+
ev.preventDefault();
|
| 180 |
+
// console.log("right click");
|
| 181 |
+
// ev.target.style.transform="scale(1.2)";
|
| 182 |
+
// ev.target.focus();
|
| 183 |
+
// dragged_elem=e.target;
|
| 184 |
+
});
|
| 185 |
+
|
| 186 |
+
// crop_img.addEventListener("blur",function(ev){
|
| 187 |
+
// console.log("blur");
|
| 188 |
+
// ev.target.style.transform="scale(1)";
|
| 189 |
+
// });
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
// add touch events
|
| 193 |
+
// crop_img.addEventListener("touchstart",function(e){
|
| 194 |
+
// console.log("touch-started");
|
| 195 |
+
// });
|
| 196 |
+
crop_img.setAttribute("ontouchstart","dragTouchstart(event,this);");
|
| 197 |
+
crop_img.setAttribute("ontouchmove","dragTouchmove(event,this);");
|
| 198 |
+
crop_img.setAttribute("ontouchend","dragTouchend(event,this);");
|
| 199 |
+
// crop_img.addEventListener("touchmove",function(e){
|
| 200 |
+
|
| 201 |
+
// dragTouchmove(e);
|
| 202 |
+
// });
|
| 203 |
+
// crop_img.addEventListener("touchend",function(e){
|
| 204 |
+
// console.log("touch-end");
|
| 205 |
+
// dragTouchend(e);
|
| 206 |
+
// });
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
// response['images'].forEach(img_name => {
|
| 210 |
+
// img_tag=document.createElement("img");
|
| 211 |
+
// img_tag.src=Flask.url_for('upload',{subfolder:'images',filename:img_name} );
|
| 212 |
+
// images_div.appendChild(img_tag);
|
| 213 |
+
// });
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
}
|
| 218 |
+
);
|
| 219 |
+
}
|
| 220 |
+
});
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
function remove_image(e)
|
| 226 |
+
{
|
| 227 |
+
// reset old image in image-viewer start ----------------------------------------
|
| 228 |
+
var image=document.querySelector("#face_rec_image");
|
| 229 |
+
if (image.hasAttribute("data-old_src")){
|
| 230 |
+
console.log("has_old_src")
|
| 231 |
+
image.src=image.dataset.old_src;
|
| 232 |
+
delete image.dataset.old_src;
|
| 233 |
+
}
|
| 234 |
+
else{
|
| 235 |
+
image.removeAttribute('src');
|
| 236 |
+
}
|
| 237 |
+
// reset old image in image-viewer end ------------------------------------------
|
| 238 |
+
|
| 239 |
+
// console.log(e)
|
| 240 |
+
|
| 241 |
+
crops=document.querySelectorAll(`.crop_img[data-image_name='${e.dataset.image_name}']`)
|
| 242 |
+
// console.log(crops)
|
| 243 |
+
for(var i=0;i<crops.length;i++)
|
| 244 |
+
crops[i].remove();
|
| 245 |
+
|
| 246 |
+
e.remove();
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
function add_person(){
|
| 253 |
+
// make such a structure:
|
| 254 |
+
// <div class="person" ontouchend="dragTouchend(event)" ondragover="allowDrop(event)" ondrop="drop(event,this)">
|
| 255 |
+
// <input type="text" value="Anuj" ondrop="return false;" onkeypress="deselect(event,this);">
|
| 256 |
+
// <div class="faces"></div>
|
| 257 |
+
// <i class="fa-solid fa-xmark" onclick="this.parentElement.remove();"></i>
|
| 258 |
+
// </div>
|
| 259 |
+
var person=document.createElement("div");
|
| 260 |
+
person.setAttribute("class","person droppable");
|
| 261 |
+
person.dataset.dropto=".faces";
|
| 262 |
+
person.setAttribute("ondragover","allowDrop(event)");
|
| 263 |
+
person.setAttribute("ondrop","drop(event,this.querySelector('.faces'))");
|
| 264 |
+
// person.setAttribute("ontouchend","dragTouchend(event)");
|
| 265 |
+
var name=document.createElement("input");
|
| 266 |
+
name.setAttribute("type","text");
|
| 267 |
+
name.setAttribute("ondrop","return false;");
|
| 268 |
+
name.setAttribute("onkeyup","deselect(event,this);");
|
| 269 |
+
var faces=document.createElement("div");
|
| 270 |
+
faces.setAttribute("class","faces");
|
| 271 |
+
var close_icon=document.createElement("i");
|
| 272 |
+
close_icon.setAttribute("class","fa-solid fa-xmark");
|
| 273 |
+
close_icon.setAttribute("onclick","remove_person(this.parentElement);");
|
| 274 |
+
person.appendChild(name);
|
| 275 |
+
person.appendChild(faces);
|
| 276 |
+
person.appendChild(close_icon);
|
| 277 |
+
document.querySelector("#name_list").appendChild(person);
|
| 278 |
+
name.focus();
|
| 279 |
+
// name.select();
|
| 280 |
+
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
function load_image_preview(elem){
|
| 284 |
+
if(elem.files[0])
|
| 285 |
+
{
|
| 286 |
+
console.log(elem.files[0]);
|
| 287 |
+
if (FileReader && elem.files[0]) {
|
| 288 |
+
var fr = new FileReader();
|
| 289 |
+
fr.onload = function () {
|
| 290 |
+
document.querySelector("#face_rec_image").src = fr.result;
|
| 291 |
+
document.querySelector("#face_rec_image").style.width="unset";
|
| 292 |
+
}
|
| 293 |
+
fr.readAsDataURL(elem.files[0]);
|
| 294 |
+
}
|
| 295 |
+
}
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
function deselect(e,elem){
|
| 299 |
+
// document.body.focus();
|
| 300 |
+
|
| 301 |
+
if(e.key=="Enter")
|
| 302 |
+
elem.blur();
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
function update_crops_labels(elem)
|
| 306 |
+
{
|
| 307 |
+
console.log("update face labels");
|
| 308 |
+
var formdata=new FormData();
|
| 309 |
+
|
| 310 |
+
var faces=document.querySelectorAll(".person>.faces>img");
|
| 311 |
+
var faces_base64=[]
|
| 312 |
+
var selected_faces=[]
|
| 313 |
+
|
| 314 |
+
for (var i=0;i<faces.length;i++)
|
| 315 |
+
{
|
| 316 |
+
|
| 317 |
+
if(!faces[i].hasAttribute("data-features"))
|
| 318 |
+
{
|
| 319 |
+
selected_faces.push(faces[i]);
|
| 320 |
+
faces_base64.push(faces[i].src.split(',')[1]); // also removed datajpeg part
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
if (selected_faces.length>0)
|
| 326 |
+
{
|
| 327 |
+
formdata.append("faces",faces_base64);
|
| 328 |
+
// to see the the json
|
| 329 |
+
// var object = {};
|
| 330 |
+
// formdata.forEach(function(value, key){
|
| 331 |
+
// object[key] = value;
|
| 332 |
+
// });
|
| 333 |
+
// var json = JSON.stringify(object);
|
| 334 |
+
// console.log(json);
|
| 335 |
+
|
| 336 |
+
fetch("/demo/update_crops_labels/",{
|
| 337 |
+
method:"POST",
|
| 338 |
+
body:formdata
|
| 339 |
+
}).then(function(response){
|
| 340 |
+
console.log(response)
|
| 341 |
+
return response.json();
|
| 342 |
+
}).then(function(response){
|
| 343 |
+
console.log(response);
|
| 344 |
+
if (response.message!='success') return;
|
| 345 |
+
|
| 346 |
+
for(var i=0;i<response['features'].length;i++)
|
| 347 |
+
selected_faces[i].dataset.features=response['features'][i];
|
| 348 |
+
face_recognition(elem);
|
| 349 |
+
});
|
| 350 |
+
|
| 351 |
+
}
|
| 352 |
+
else{
|
| 353 |
+
console.log("already up to date !");
|
| 354 |
+
face_recognition(elem);
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
function face_recognition(elem)
|
| 363 |
+
{
|
| 364 |
+
|
| 365 |
+
if(elem.files[0])
|
| 366 |
+
{
|
| 367 |
+
// update_crops_labels(elem); // get features of all db images from backend
|
| 368 |
+
|
| 369 |
+
var formdata=new FormData();
|
| 370 |
+
var faces=document.querySelectorAll(".person>.faces>img");
|
| 371 |
+
var faces_features={};
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
var face_labels=document.querySelectorAll(".person");
|
| 375 |
+
|
| 376 |
+
for (var i=0;i<face_labels.length;i++)
|
| 377 |
+
{
|
| 378 |
+
var faces=face_labels[i].querySelectorAll(".faces>img");
|
| 379 |
+
var face_features=[]
|
| 380 |
+
if (faces.length>0)
|
| 381 |
+
{
|
| 382 |
+
for(var j=0;j<faces.length;j++)
|
| 383 |
+
{
|
| 384 |
+
face_features.push(faces[j].dataset.features)
|
| 385 |
+
}
|
| 386 |
+
// name:[features_array]
|
| 387 |
+
faces_features[face_labels[i].querySelector("input").value]=face_features;
|
| 388 |
+
|
| 389 |
+
}
|
| 390 |
+
}
|
| 391 |
+
formdata.append("db_images",JSON.stringify(faces_features));
|
| 392 |
+
formdata.append("image",elem.files[0]);
|
| 393 |
+
|
| 394 |
+
console.log(formdata.get("db_images"));
|
| 395 |
+
if(formdata.get("db_images")=="{}"){
|
| 396 |
+
console.log("Add db_images_first")
|
| 397 |
+
return ;
|
| 398 |
+
}
|
| 399 |
+
// console.log(elem.files[0]);
|
| 400 |
+
fetch("/demo/face_recognition/",{
|
| 401 |
+
method:"POST",
|
| 402 |
+
body:formdata
|
| 403 |
+
}).then(function(response){
|
| 404 |
+
return response.json();
|
| 405 |
+
}).then(function(response){
|
| 406 |
+
console.log(response);
|
| 407 |
+
document.querySelector("#face_rec_image").src="data:image/jpeg;base64,"+response['image'].split('\'')[1];
|
| 408 |
+
})
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
}
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
function remove_person(person){
|
| 416 |
+
var unassigned_faces=document.querySelector("#unassigned_faces");
|
| 417 |
+
var all_crops=person.querySelectorAll(".faces>img");
|
| 418 |
+
all_crops.forEach(function(crop){
|
| 419 |
+
unassigned_faces.appendChild(crop);
|
| 420 |
+
})
|
| 421 |
+
person.remove();
|
| 422 |
+
}
|
| 423 |
+
|
| 424 |
+
|
| 425 |
+
function show_settings(){
|
| 426 |
+
document.querySelector('#settings_menu').style.display='block';
|
| 427 |
+
document.querySelector('#container').classList.add("blur");
|
| 428 |
+
}
|
| 429 |
+
function hide_settings(){
|
| 430 |
+
document.querySelector('#settings_menu').style.display='none';
|
| 431 |
+
document.querySelector('#container').classList.remove("blur");
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
function get_settings(){
|
| 437 |
+
fetch("get_settings/",{
|
| 438 |
+
method:"GET"
|
| 439 |
+
}).then(function(response){
|
| 440 |
+
return response.json();
|
| 441 |
+
}).then(function(res){
|
| 442 |
+
console.log(res);
|
| 443 |
+
|
| 444 |
+
p_thres.value=p_thresValue.innerText=res['p_thres'];
|
| 445 |
+
nms_thres.value=nms_thresValue.innerText=res['nms_thres'];
|
| 446 |
+
|
| 447 |
+
small_size.value=small_sizeValue.innerText=res['small_size'];
|
| 448 |
+
large_size.value=large_sizeValue.innerText=res['large_size'];
|
| 449 |
+
|
| 450 |
+
d_thres.value=d_thresValue.innerText=res['d_thres'];
|
| 451 |
+
a_thres.value=a_thresValue.innerText=res['a_thres'];
|
| 452 |
+
|
| 453 |
+
db_mode.value=res['db_mode'];
|
| 454 |
+
fr_mode.value=res['fr_mode'];
|
| 455 |
+
console.log(res['db_mode'],res['fr_mode']);
|
| 456 |
+
})
|
| 457 |
+
}
|
| 458 |
+
get_settings();
|
| 459 |
+
// document.querySelector('#p_thres')
|
| 460 |
+
// document.querySelector('#p_thres').value
|
| 461 |
+
|
| 462 |
+
function update_settings(){
|
| 463 |
+
var new_settings=new Object();
|
| 464 |
+
new_settings['p_thres']=p_thres.value;
|
| 465 |
+
new_settings['nms_thres']=nms_thres.value;
|
| 466 |
+
new_settings['small_size']=small_size.value;
|
| 467 |
+
new_settings['large_size']=large_size.value;
|
| 468 |
+
new_settings['d_thres']=d_thres.value;
|
| 469 |
+
new_settings['a_thres']=a_thres.value;
|
| 470 |
+
new_settings['db_mode']=db_mode.value;
|
| 471 |
+
new_settings['fr_mode']=fr_mode.value;
|
| 472 |
+
|
| 473 |
+
fetch("update_settings/",{
|
| 474 |
+
method:"POST",
|
| 475 |
+
headers:{'content-type': 'application/json'},
|
| 476 |
+
body:JSON.stringify(new_settings)
|
| 477 |
+
}).then(function(response){
|
| 478 |
+
return response.json();
|
| 479 |
+
}).then(function(res){
|
| 480 |
+
console.log(res);
|
| 481 |
+
if(res['message']=='success')
|
| 482 |
+
hide_settings();
|
| 483 |
+
}
|
| 484 |
+
);
|
| 485 |
+
}
|
| 486 |
+
|
| 487 |
+
function reset_settings(){
|
| 488 |
+
fetch("reset_settings/",{
|
| 489 |
+
method:"GET"
|
| 490 |
+
}).then(function(response){
|
| 491 |
+
return response.json();
|
| 492 |
+
}).then(function(res){
|
| 493 |
+
console.log(res);
|
| 494 |
+
if(res['message']=='success')
|
| 495 |
+
get_settings();
|
| 496 |
+
}
|
| 497 |
+
);
|
| 498 |
+
}
|
app/static/demo/index/style.css
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*{
|
| 2 |
+
box-sizing: border-box;
|
| 3 |
+
}
|
| 4 |
+
|
| 5 |
+
/* width */
|
| 6 |
+
::-webkit-scrollbar {
|
| 7 |
+
width: 10px;
|
| 8 |
+
height:10px;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
/* Track */
|
| 12 |
+
::-webkit-scrollbar-track {
|
| 13 |
+
background: #B2EBF2;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
/* Handle */
|
| 17 |
+
::-webkit-scrollbar-thumb {
|
| 18 |
+
background: #5E35B1;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
/* Handle on hover */
|
| 22 |
+
::-webkit-scrollbar-thumb:hover {
|
| 23 |
+
background: #673AB7;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
#db_images_bar
|
| 28 |
+
{
|
| 29 |
+
display: flex;
|
| 30 |
+
height:100px;
|
| 31 |
+
/* border:2px solid rgb(39, 86, 255); */
|
| 32 |
+
width: 100%;
|
| 33 |
+
padding-bottom: 10px;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
#db_images_bar>.add_button{
|
| 37 |
+
background-color:rgb(205, 18, 234) ;
|
| 38 |
+
height: 60px;
|
| 39 |
+
width: 20%;
|
| 40 |
+
padding: 2px;
|
| 41 |
+
border-radius: 5px;
|
| 42 |
+
margin: auto 0;
|
| 43 |
+
margin-right: 5px;
|
| 44 |
+
display: flex;
|
| 45 |
+
align-items: center;
|
| 46 |
+
justify-content: center;
|
| 47 |
+
flex-direction: column;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
#db_images_bar>.add_button:hover{
|
| 51 |
+
cursor: pointer;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
#db_images_bar>.add_button>i{
|
| 55 |
+
color:white;
|
| 56 |
+
font-size: 3em;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
#db_images
|
| 62 |
+
{
|
| 63 |
+
display: flex;
|
| 64 |
+
height: 100%;
|
| 65 |
+
width: 80%;
|
| 66 |
+
overflow: scroll hidden;
|
| 67 |
+
grid-row-start: 1;
|
| 68 |
+
}
|
| 69 |
+
.db_image{
|
| 70 |
+
height:inherit;
|
| 71 |
+
filter: blur(0);
|
| 72 |
+
transition: 0.3s;
|
| 73 |
+
border-radius: inherit;
|
| 74 |
+
}
|
| 75 |
+
.db_image_container{
|
| 76 |
+
height: inherit;
|
| 77 |
+
background: black;
|
| 78 |
+
margin-right:10px ;
|
| 79 |
+
position: relative;
|
| 80 |
+
display: inline-block;
|
| 81 |
+
|
| 82 |
+
border-radius: 20%;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
.db_image_container:hover{
|
| 86 |
+
cursor: pointer;
|
| 87 |
+
}
|
| 88 |
+
.db_image_container:hover .db_image{
|
| 89 |
+
opacity: 0.5;
|
| 90 |
+
filter: blur(3px);
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
.db_image_container>.close_text{
|
| 94 |
+
opacity: 0;
|
| 95 |
+
font-size: xxx-large;
|
| 96 |
+
color:white;
|
| 97 |
+
position: absolute;
|
| 98 |
+
top:50%;
|
| 99 |
+
left:50%;
|
| 100 |
+
transform: translate(-50%,-50%);
|
| 101 |
+
padding: 0px;
|
| 102 |
+
margin: 0px;
|
| 103 |
+
transition: 0.3s;
|
| 104 |
+
}
|
| 105 |
+
.db_image_container:hover .close_text{
|
| 106 |
+
opacity: 1;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
/*new main container*/
|
| 116 |
+
#container{
|
| 117 |
+
height:452px;
|
| 118 |
+
width:1000px;
|
| 119 |
+
/* border:2px dotted black; */
|
| 120 |
+
margin: 0 auto;
|
| 121 |
+
display: flex;
|
| 122 |
+
position: absolute;
|
| 123 |
+
top:50%;
|
| 124 |
+
left:50%;
|
| 125 |
+
transform: translate(-50%,-50%);
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
#container>.left-section{
|
| 129 |
+
height: inherit;
|
| 130 |
+
width:50%;
|
| 131 |
+
border-right:2px solid black;
|
| 132 |
+
display: flex;
|
| 133 |
+
align-items: center;
|
| 134 |
+
flex-direction: column;
|
| 135 |
+
padding: 10px;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
#face_rec_image{
|
| 139 |
+
height: 65%;
|
| 140 |
+
/* border: 1px solid turquoise; */
|
| 141 |
+
border:0;
|
| 142 |
+
|
| 143 |
+
width: 150px;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
.buttons{
|
| 148 |
+
display: flex;
|
| 149 |
+
align-items: center;
|
| 150 |
+
justify-content: center;
|
| 151 |
+
/* border:2px solid red; */
|
| 152 |
+
height:50px;
|
| 153 |
+
width:100%;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
#container>.right-section{
|
| 157 |
+
height: inherit;
|
| 158 |
+
width:50%;
|
| 159 |
+
display: flex;
|
| 160 |
+
align-items: center;
|
| 161 |
+
flex-direction: column;
|
| 162 |
+
/* justify-content: center; */
|
| 163 |
+
padding: 10px;
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
#unassigned_faces{
|
| 167 |
+
width:100%;
|
| 168 |
+
height:100px;
|
| 169 |
+
/* border:1px solid rgb(255, 66, 255); */
|
| 170 |
+
padding:10px;
|
| 171 |
+
display: flex;
|
| 172 |
+
flex-direction: row;
|
| 173 |
+
align-items: center;
|
| 174 |
+
overflow: scroll hidden;
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
.crop_img{
|
| 178 |
+
height: 50px;
|
| 179 |
+
margin: 2px;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
#name_list{
|
| 183 |
+
width:100%;
|
| 184 |
+
height:55%;
|
| 185 |
+
/* border:2px solid rgb(66, 113, 255); */
|
| 186 |
+
overflow: hidden scroll;
|
| 187 |
+
padding:15px;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
#name_list>.person{
|
| 191 |
+
width:100%;
|
| 192 |
+
height: 80px;
|
| 193 |
+
border-bottom:2px solid chartreuse;
|
| 194 |
+
position: relative;
|
| 195 |
+
padding: 4px 1px;
|
| 196 |
+
display: flex;
|
| 197 |
+
align-items: end;
|
| 198 |
+
}
|
| 199 |
+
#name_list>.person>input{
|
| 200 |
+
position: absolute;
|
| 201 |
+
left:5px;
|
| 202 |
+
top:5px;
|
| 203 |
+
/* padding: inherit; */
|
| 204 |
+
background-color: transparent;
|
| 205 |
+
border:none;
|
| 206 |
+
outline: none;
|
| 207 |
+
}
|
| 208 |
+
#name_list>.person>input:focus{
|
| 209 |
+
border-bottom: 1px solid black;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
#name_list>.person>i{
|
| 213 |
+
color:red;
|
| 214 |
+
position: absolute;
|
| 215 |
+
top:50%;
|
| 216 |
+
transform: translate(0,-50%);
|
| 217 |
+
right:20px;
|
| 218 |
+
|
| 219 |
+
}
|
| 220 |
+
#name_list>.person>i:hover{
|
| 221 |
+
cursor:pointer;
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
#name_list>.person>.faces{
|
| 225 |
+
height:fit-content;
|
| 226 |
+
width: 100%;
|
| 227 |
+
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
#add_name_btn{
|
| 232 |
+
margin: 40px;
|
| 233 |
+
width:inherit;
|
| 234 |
+
height:inherit;
|
| 235 |
+
}
|
app/static/user/dashboard.css
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root{
|
| 2 |
+
/* --color-primary:#7380ec; */
|
| 3 |
+
/* --color-primary:rgb(205, 18, 234); */
|
| 4 |
+
--color-primary:rgb(163 18 234);
|
| 5 |
+
--color-secondary:rgb(173, 54, 232);
|
| 6 |
+
|
| 7 |
+
--color-name-border:red;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
#api-key{
|
| 12 |
+
border:2px dashed black;
|
| 13 |
+
width:fit-content;
|
| 14 |
+
display: flex;
|
| 15 |
+
flex-direction: row;
|
| 16 |
+
/* column-gap: 2px; */
|
| 17 |
+
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
#api-key>p{
|
| 21 |
+
border:none;
|
| 22 |
+
padding:0 4px;
|
| 23 |
+
/* border-right: 2px solid black; */
|
| 24 |
+
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
#api-key>button{
|
| 28 |
+
/* padding:6px; */
|
| 29 |
+
/* border: 2px solid black; */
|
| 30 |
+
/* line-height: 2; */
|
| 31 |
+
cursor: pointer;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
#database-form{
|
| 35 |
+
border:1px solid black;
|
| 36 |
+
display: flex;
|
| 37 |
+
flex-direction: column;
|
| 38 |
+
width:500px;
|
| 39 |
+
padding: 4px;
|
| 40 |
+
row-gap: 3px;
|
| 41 |
+
height: fit-content;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
#database-form>.crops{
|
| 45 |
+
width:300px;
|
| 46 |
+
height:60px;
|
| 47 |
+
border:1px solid black;
|
| 48 |
+
}
|
| 49 |
+
#database-form>.assigned-crops{
|
| 50 |
+
width:300px;
|
| 51 |
+
height:100px;
|
| 52 |
+
border:1px solid black;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
#database-form>.assigned-crops>.remark-crops{
|
| 56 |
+
width:300px;
|
| 57 |
+
height:100px;
|
| 58 |
+
border:1px solid black;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
#database-form>.field{
|
| 62 |
+
display: flex;
|
| 63 |
+
flex-direction: row;
|
| 64 |
+
justify-items: center;
|
| 65 |
+
height: 30px;
|
| 66 |
+
overflow: hidden;
|
| 67 |
+
height: fit-content;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
#database-form>.field>p{
|
| 71 |
+
padding: 0;
|
| 72 |
+
margin: 0;
|
| 73 |
+
width: fit-content;
|
| 74 |
+
height: fit-content;
|
| 75 |
+
margin-top: 0.3rem;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
/*Copied from demo page css*/
|
| 80 |
+
|
| 81 |
+
#unassigned_faces{
|
| 82 |
+
width:100%;
|
| 83 |
+
height:100px;
|
| 84 |
+
border:1px solid rgb(255, 66, 255);
|
| 85 |
+
padding:10px;
|
| 86 |
+
column-gap: 5px;
|
| 87 |
+
|
| 88 |
+
display: flex;
|
| 89 |
+
flex-direction: row;
|
| 90 |
+
align-items: center;
|
| 91 |
+
box-sizing: border-box;
|
| 92 |
+
overflow: scroll hidden;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
#remark_list{
|
| 101 |
+
width:100%;
|
| 102 |
+
height: 206px;
|
| 103 |
+
/* border:2px solid rgb(66, 113, 255); */
|
| 104 |
+
overflow: hidden scroll;
|
| 105 |
+
padding:15px;
|
| 106 |
+
box-sizing: border-box;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
#remark_list>.remark{
|
| 110 |
+
width:100%;
|
| 111 |
+
height: 80px;
|
| 112 |
+
border-bottom:2px solid var(--color-name-border);
|
| 113 |
+
position: relative;
|
| 114 |
+
padding: 4px 1px;
|
| 115 |
+
display: flex;
|
| 116 |
+
align-items: end;
|
| 117 |
+
}
|
| 118 |
+
#remark_list>.remark>input{
|
| 119 |
+
position: absolute;
|
| 120 |
+
left:5px;
|
| 121 |
+
top:5px;
|
| 122 |
+
/* padding: inherit; */
|
| 123 |
+
background-color: transparent;
|
| 124 |
+
border:none;
|
| 125 |
+
outline: none;
|
| 126 |
+
}
|
| 127 |
+
#remark_list>.remark>input:focus{
|
| 128 |
+
border-bottom: 1px solid var(--color-name-border);
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
#remark_list>.remark>i{
|
| 132 |
+
color:red;
|
| 133 |
+
position: absolute;
|
| 134 |
+
top:50%;
|
| 135 |
+
transform: translate(0,-50%);
|
| 136 |
+
right:20px;
|
| 137 |
+
|
| 138 |
+
}
|
| 139 |
+
#remark_list>.remark>i:hover{
|
| 140 |
+
cursor:pointer;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
#remark_list>.remark>.faces{
|
| 144 |
+
height:fit-content;
|
| 145 |
+
width: 100%;
|
| 146 |
+
display: flex;
|
| 147 |
+
column-gap: 5px;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
.buttons{
|
| 151 |
+
display: flex;
|
| 152 |
+
justify-content: center;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
#add_remark_btn{
|
| 156 |
+
margin: 40px;
|
| 157 |
+
padding: 4px 12px;
|
| 158 |
+
width:fit-content;
|
| 159 |
+
height:inherit;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.btn{
|
| 163 |
+
background-color: var(--color-primary);
|
| 164 |
+
box-shadow: 0px 0px 43px -4px var(--color-secondary);
|
| 165 |
+
border-radius: 5px;
|
| 166 |
+
border:none;
|
| 167 |
+
}
|
| 168 |
+
.btn:hover{
|
| 169 |
+
cursor: pointer;
|
| 170 |
+
background-color: var(--color-secondary);
|
| 171 |
+
/* box-shadow: 0px 0px 43px -4px var(--color-secondary); */
|
| 172 |
+
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
/*end of Copied from demo page css*/
|
| 176 |
+
|
| 177 |
+
#database-form>.field>.add_button{
|
| 178 |
+
height: 60px;
|
| 179 |
+
width: 20%;
|
| 180 |
+
padding: 2px;
|
| 181 |
+
margin: auto 0;
|
| 182 |
+
margin-right: 5px;
|
| 183 |
+
display: flex;
|
| 184 |
+
align-items: center;
|
| 185 |
+
justify-content: center;
|
| 186 |
+
flex-direction: column;
|
| 187 |
+
z-index: 2;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
#database-form>.field>.add_button>i{
|
| 192 |
+
color:white;
|
| 193 |
+
background-color: inherit;
|
| 194 |
+
font-size: 3em;
|
| 195 |
+
}
|
| 196 |
+
.crop_img{
|
| 197 |
+
height: 60px;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
#face_recognition_image{
|
| 201 |
+
height:200px;
|
| 202 |
+
width:200px;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
#db_people_table{
|
| 207 |
+
width:300px;
|
| 208 |
+
height:200px;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
table, th, td {
|
| 213 |
+
border:2px solid black;
|
| 214 |
+
border-collapse: collapse;
|
| 215 |
+
text-align: center;
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
.remove{
|
| 219 |
+
background-color: red;
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
button:hover{
|
| 223 |
+
cursor: pointer;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
/*copied from demo page*/
|
| 228 |
+
|
| 229 |
+
.settings_btn{
|
| 230 |
+
position: absolute;
|
| 231 |
+
top:40px;
|
| 232 |
+
right:40px;
|
| 233 |
+
font-size: larger;
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
#settings_menu{
|
| 237 |
+
background-color: white;
|
| 238 |
+
height: fit-content;
|
| 239 |
+
width:40%;
|
| 240 |
+
z-index: 10000;
|
| 241 |
+
position: absolute;
|
| 242 |
+
top:50%;
|
| 243 |
+
left:50%;
|
| 244 |
+
transform: translate(-50%,-50%);
|
| 245 |
+
border-radius: 10px;
|
| 246 |
+
box-shadow: 0px 0px 43px -4px var(--color-primary);
|
| 247 |
+
padding: 2px 20px;
|
| 248 |
+
display: none;
|
| 249 |
+
}
|
| 250 |
+
#settings_menu>h1{
|
| 251 |
+
background-color: inherit;
|
| 252 |
+
}
|
| 253 |
+
#settings_menu>.settings{
|
| 254 |
+
background-color: inherit;
|
| 255 |
+
height: 280px;
|
| 256 |
+
overflow: hidden scroll;
|
| 257 |
+
position: relative;
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
#settings_menu>.close_btn{
|
| 261 |
+
color:red;
|
| 262 |
+
position: absolute;
|
| 263 |
+
top: 20px;
|
| 264 |
+
right: 20px;
|
| 265 |
+
font-size: larger;
|
| 266 |
+
background-color: transparent;
|
| 267 |
+
}
|
| 268 |
+
#settings_menu>.close_btn:hover{
|
| 269 |
+
cursor: pointer;
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
.reset_btn_container{
|
| 273 |
+
position: absolute;
|
| 274 |
+
top: 20px;
|
| 275 |
+
right: 30px;
|
| 276 |
+
}
|
| 277 |
+
.reset_btn_container>.reset_btn{
|
| 278 |
+
color:white;
|
| 279 |
+
position: fixed;
|
| 280 |
+
font-size: larger;
|
| 281 |
+
background-color: transparent;
|
| 282 |
+
}
|
| 283 |
+
.reset_btn_container>.reset_btn:hover{
|
| 284 |
+
cursor: pointer;
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
#container.blur{
|
| 288 |
+
filter: blur(5px);
|
| 289 |
+
pointer-events: none;
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
#save_btn_container{
|
| 293 |
+
display: flex;
|
| 294 |
+
justify-content: center;
|
| 295 |
+
margin: 10px 0px;
|
| 296 |
+
font-size: large;
|
| 297 |
+
}
|
| 298 |
+
#save_settings_btn{
|
| 299 |
+
background-color: rgb(76, 76, 232);
|
| 300 |
+
outline: none;
|
| 301 |
+
border: none;
|
| 302 |
+
border-radius: 6px;
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
/*End copied from demo page*/
|
app/static/user/dashboard.js
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
var dragged_elem;
|
| 2 |
+
var features_updated=false;
|
| 3 |
+
var touch_start_time=null;
|
| 4 |
+
var elapsedTime=null;
|
| 5 |
+
var maxAllowedTime=500;
|
| 6 |
+
|
| 7 |
+
function drag(ev){
|
| 8 |
+
// ev.preventDefault();
|
| 9 |
+
console.log(ev.target.id);
|
| 10 |
+
dragged_elem=ev.target;
|
| 11 |
+
// ev.dataTransfer.setData("text",ev.target.id);
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
function dragTouchstart(e,elem) {
|
| 15 |
+
elapsedTime=null;
|
| 16 |
+
touch_start_time = new Date().getTime();
|
| 17 |
+
}
|
| 18 |
+
function dragTouchmove(e,elem) {
|
| 19 |
+
|
| 20 |
+
let touchX = e.touches[0].pageX;
|
| 21 |
+
let touchY = e.touches[0].pageY;
|
| 22 |
+
|
| 23 |
+
if(elapsedTime==null)
|
| 24 |
+
{
|
| 25 |
+
elapsedTime = new Date().getTime() - touch_start_time;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
// console.log(elapsedTime);
|
| 29 |
+
|
| 30 |
+
if (elapsedTime<maxAllowedTime) return;
|
| 31 |
+
|
| 32 |
+
// console.log("no-scroll")
|
| 33 |
+
e.preventDefault()
|
| 34 |
+
|
| 35 |
+
if (dragged_elem==null)
|
| 36 |
+
{
|
| 37 |
+
dragged_elem=elem;
|
| 38 |
+
console.log(dragged_elem);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
function allowDrop(ev){
|
| 44 |
+
ev.preventDefault();
|
| 45 |
+
console.log("ready to be dropped");
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
function drop(ev,element){
|
| 49 |
+
ev.preventDefault();
|
| 50 |
+
console.log("dropped!");
|
| 51 |
+
element.appendChild(dragged_elem);
|
| 52 |
+
dragged_elem=null;
|
| 53 |
+
features_updated=false;
|
| 54 |
+
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
function dragTouchend(e,elem) {
|
| 58 |
+
|
| 59 |
+
if(dragged_elem==null) return;
|
| 60 |
+
|
| 61 |
+
var droppable_element=document.elementFromPoint(e.changedTouches[0].clientX, e.changedTouches[0].clientY).closest(".droppable");
|
| 62 |
+
if(droppable_element==null) return;
|
| 63 |
+
|
| 64 |
+
if(droppable_element.hasAttribute("data-dropto")) droppable_element=droppable_element.querySelector(droppable_element.dataset.dropto)
|
| 65 |
+
|
| 66 |
+
droppable_element.appendChild(dragged_elem);
|
| 67 |
+
|
| 68 |
+
console.log(droppable_element)
|
| 69 |
+
dragged_elem=null;
|
| 70 |
+
features_updated=false;
|
| 71 |
+
// e.target.style.transform="scale(0.8)";
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
function count_words(elem){
|
| 77 |
+
var count = 0;
|
| 78 |
+
|
| 79 |
+
// Split the words on each
|
| 80 |
+
// space character
|
| 81 |
+
var split = elem.value.split(' ');
|
| 82 |
+
|
| 83 |
+
// Loop through the words and
|
| 84 |
+
// increase the counter when
|
| 85 |
+
// each split word is not empty
|
| 86 |
+
for (var i = 0; i < split.length; i++) {
|
| 87 |
+
if (split[i] != "") {
|
| 88 |
+
count += 1;
|
| 89 |
+
}
|
| 90 |
+
}
|
| 91 |
+
console.log("count:",count)
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
document.querySelector("#api-key>.copy").addEventListener("click",function(e){
|
| 95 |
+
var key_tag=document.querySelector("#api-key>p");
|
| 96 |
+
key_tag.focus();
|
| 97 |
+
console.log(key_tag.innerText);
|
| 98 |
+
navigator.clipboard.writeText(key_tag.innerText);
|
| 99 |
+
})
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
document.querySelector("#api-key>.refresh").addEventListener("click",function(e){
|
| 103 |
+
var key_tag=document.querySelector("#api-key>p");
|
| 104 |
+
fetch("update_key/").then(function(response){
|
| 105 |
+
return response.json();
|
| 106 |
+
}).then(function(response){
|
| 107 |
+
console.log(response);
|
| 108 |
+
if ("key" in response){
|
| 109 |
+
key_tag.innerText=response["key"];
|
| 110 |
+
}
|
| 111 |
+
});
|
| 112 |
+
})
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
function deselect(e,elem){
|
| 119 |
+
// document.body.focus();
|
| 120 |
+
|
| 121 |
+
if(e.key=="Enter")
|
| 122 |
+
elem.blur();
|
| 123 |
+
else if(elem.value!=elem.dataset.name)
|
| 124 |
+
features_updated=false;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
function remove_remark(remark){
|
| 129 |
+
var unassigned_faces=document.querySelector("#unassigned_faces");
|
| 130 |
+
var all_crops=remark.querySelectorAll("#remark_list>.remark>.faces>img");
|
| 131 |
+
all_crops.forEach(function(crop){
|
| 132 |
+
unassigned_faces.appendChild(crop);
|
| 133 |
+
})
|
| 134 |
+
remark.remove();
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
function add_remark(){
|
| 139 |
+
// make such a structure:
|
| 140 |
+
// <div class="remark" ondragover="allowDrop(event)" ondrop="drop(event,this)">
|
| 141 |
+
// <input type="text" value="Anuj" ondrop="return false;" onkeypress="deselect(event,this);">
|
| 142 |
+
// <div class="faces"></div>
|
| 143 |
+
// <i class="fa-solid fa-xmark" onclick="this.parentElement.remove();"></i>
|
| 144 |
+
// </div>
|
| 145 |
+
var remark=document.createElement("div");
|
| 146 |
+
remark.setAttribute("class","remark droppable");
|
| 147 |
+
remark.dataset.dropto=".faces";
|
| 148 |
+
remark.setAttribute("ondragover","allowDrop(event)");
|
| 149 |
+
remark.setAttribute("ondrop","drop(event,this.querySelector('#remark_list>.remark>.faces'))");
|
| 150 |
+
var name=document.createElement("input");
|
| 151 |
+
name.setAttribute("type","text");
|
| 152 |
+
name.setAttribute("ondrop","return false;");
|
| 153 |
+
name.setAttribute("onkeyup","deselect(event,this);");
|
| 154 |
+
var faces=document.createElement("div");
|
| 155 |
+
faces.setAttribute("class","faces");
|
| 156 |
+
var close_icon=document.createElement("i");
|
| 157 |
+
close_icon.setAttribute("class","fa-solid fa-xmark");
|
| 158 |
+
close_icon.setAttribute("onclick","remove_remark(this.parentElement);");
|
| 159 |
+
remark.appendChild(name);
|
| 160 |
+
remark.appendChild(faces);
|
| 161 |
+
remark.appendChild(close_icon);
|
| 162 |
+
document.querySelector("#remark_list").appendChild(remark);
|
| 163 |
+
name.focus();
|
| 164 |
+
// name.select();
|
| 165 |
+
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
function filterFunction() {
|
| 170 |
+
var input, filter, ul, li, a, i;
|
| 171 |
+
input = document.getElementById("myInput");
|
| 172 |
+
filter = input.value.toUpperCase();
|
| 173 |
+
div = document.getElementById("myDropdown");
|
| 174 |
+
a = div.getElementsByTagName("a");
|
| 175 |
+
for (i = 0; i < a.length; i++) {
|
| 176 |
+
txtValue = a[i].textContent || a[i].innerText;
|
| 177 |
+
if (txtValue.toUpperCase().indexOf(filter) > -1) {
|
| 178 |
+
a[i].style.display = "";
|
| 179 |
+
} else {
|
| 180 |
+
a[i].style.display = "none";
|
| 181 |
+
}
|
| 182 |
+
}
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
var database_input=document.querySelector("#database-form>.field>.add_button>input")
|
| 188 |
+
database_input.addEventListener("change", function(e){
|
| 189 |
+
if(e.target.files[0]){
|
| 190 |
+
console.log(e.target.files[0].name);
|
| 191 |
+
|
| 192 |
+
const formdata = new FormData();
|
| 193 |
+
formdata.append("image",e.target.files[0]);
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
fetch("get_crops/",
|
| 197 |
+
{
|
| 198 |
+
method:'POST',
|
| 199 |
+
body:formdata,
|
| 200 |
+
}).then(
|
| 201 |
+
function(response)
|
| 202 |
+
{
|
| 203 |
+
return response.json();
|
| 204 |
+
}
|
| 205 |
+
).then(
|
| 206 |
+
function(response)
|
| 207 |
+
{
|
| 208 |
+
console.log(response);
|
| 209 |
+
|
| 210 |
+
if (response['message']=='successful')
|
| 211 |
+
{
|
| 212 |
+
// add crops to the #unassigned_faces
|
| 213 |
+
var crop_container=document.querySelector("#unassigned_faces");
|
| 214 |
+
for(var i=0;i<response["crops"].length;i++)
|
| 215 |
+
{
|
| 216 |
+
|
| 217 |
+
var crop_img=document.createElement("img");
|
| 218 |
+
crop_img.src="data:image/jpeg;base64,"+response["crops"][i].split('\'')[1];
|
| 219 |
+
crop_img.setAttribute("class","crop_img");
|
| 220 |
+
// crop_img.setAttribute("id",crop_img.dataset.image_name+'\\'+crop_img.dataset.crop_name);
|
| 221 |
+
crop_img.setAttribute("draggable","true");
|
| 222 |
+
crop_img.setAttribute("ondragstart","drag(event)");
|
| 223 |
+
crop_container.appendChild(crop_img)
|
| 224 |
+
|
| 225 |
+
// disable right click
|
| 226 |
+
crop_img.addEventListener("contextmenu",function(ev){ev.preventDefault();});
|
| 227 |
+
//make it work with touch
|
| 228 |
+
crop_img.setAttribute("ontouchstart","dragTouchstart(event,this);");
|
| 229 |
+
crop_img.setAttribute("ontouchmove","dragTouchmove(event,this);");
|
| 230 |
+
crop_img.setAttribute("ontouchend","dragTouchend(event,this);");
|
| 231 |
+
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
// response['images'].forEach(img_name => {
|
| 235 |
+
// img_tag=document.createElement("img");
|
| 236 |
+
// img_tag.src=Flask.url_for('upload',{subfolder:'images',filename:img_name} );
|
| 237 |
+
// images_div.appendChild(img_tag);
|
| 238 |
+
// });
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
}
|
| 243 |
+
);
|
| 244 |
+
}
|
| 245 |
+
});
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
function update_db_crops(elem)
|
| 249 |
+
{
|
| 250 |
+
var all_remarks={};
|
| 251 |
+
var num_of_remarks=0;
|
| 252 |
+
|
| 253 |
+
var face_remarks=document.querySelectorAll("#remark_list>.remark");
|
| 254 |
+
|
| 255 |
+
for (var i=0;i<face_remarks.length;i++)
|
| 256 |
+
{
|
| 257 |
+
var faces=face_remarks[i].querySelectorAll("#remark_list>.remark>.faces>img");
|
| 258 |
+
var face_base64_list=[]
|
| 259 |
+
if (faces.length>0)
|
| 260 |
+
{
|
| 261 |
+
for(var j=0;j<faces.length;j++)
|
| 262 |
+
{
|
| 263 |
+
face_base64_list.push(faces[j].src.split(',',2)[1])
|
| 264 |
+
}
|
| 265 |
+
all_remarks[face_remarks[i].querySelector("input").value]=face_base64_list;
|
| 266 |
+
|
| 267 |
+
num_of_remarks+=1;
|
| 268 |
+
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
if (num_of_remarks>0)
|
| 274 |
+
{
|
| 275 |
+
|
| 276 |
+
var alldata={};
|
| 277 |
+
alldata["person_name"]=document.querySelector("#database-form>.field>.person_name").value;
|
| 278 |
+
alldata["remarks"]=all_remarks;
|
| 279 |
+
console.log(alldata);
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
fetch("set_crops/",{
|
| 284 |
+
method:"POST",
|
| 285 |
+
headers: { "Content-Type": "application/json"},
|
| 286 |
+
body:JSON.stringify(alldata)
|
| 287 |
+
}).then(function(response){
|
| 288 |
+
return response.json();
|
| 289 |
+
}).then(function(response){
|
| 290 |
+
console.log(response);
|
| 291 |
+
var person_ids=document.querySelectorAll("#db_people_table>tr>td:first-child");
|
| 292 |
+
var matching_person_id=null;
|
| 293 |
+
person_ids.forEach(element => {
|
| 294 |
+
if(element.innerText==alldata["person_name"])
|
| 295 |
+
{
|
| 296 |
+
element.parentElement.remove();
|
| 297 |
+
// element
|
| 298 |
+
}
|
| 299 |
+
});
|
| 300 |
+
alldata["remarks"]
|
| 301 |
+
add_person_row(alldata["person_name"],Object.keys(alldata["remarks"]).join(","));
|
| 302 |
+
|
| 303 |
+
});
|
| 304 |
+
|
| 305 |
+
}
|
| 306 |
+
else{
|
| 307 |
+
console.log("add database faces first");
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
function face_recoginization(elem){
|
| 315 |
+
if(elem.files[0]){
|
| 316 |
+
formdata=new FormData();
|
| 317 |
+
formdata.append("image",elem.files[0]);
|
| 318 |
+
fetch("face_recognize/",{
|
| 319 |
+
method:"POST",
|
| 320 |
+
body:formdata
|
| 321 |
+
}).then(function(response){
|
| 322 |
+
return response.json();
|
| 323 |
+
}).then(function(response){
|
| 324 |
+
console.log(response);
|
| 325 |
+
document.querySelector("#face_recognition_image").src="data:image/jpeg;base64,"+response["pred_image"].split('\'')[1];
|
| 326 |
+
document.querySelector("#face_recognition_image").style.width="unset";
|
| 327 |
+
})
|
| 328 |
+
}
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
function add_person_row(person_id,remarks)
|
| 336 |
+
{
|
| 337 |
+
//#db_people_table
|
| 338 |
+
// <tr>
|
| 339 |
+
// <td>anuj</td>
|
| 340 |
+
// <td>Front_face</td>
|
| 341 |
+
//
|
| 342 |
+
// <td>
|
| 343 |
+
// <button class="remove"><i class="fa-solid fa-close"></i></button>
|
| 344 |
+
// </td>
|
| 345 |
+
// </tr>
|
| 346 |
+
var table_row=document.createElement("tr");
|
| 347 |
+
var person_id_tag=document.createElement("td");
|
| 348 |
+
person_id_tag.innerText=person_id;
|
| 349 |
+
var remarks_tag=document.createElement("td");
|
| 350 |
+
remarks_tag.innerText=remarks;
|
| 351 |
+
|
| 352 |
+
var remove_tag=document.createElement("td");
|
| 353 |
+
remove_tag.innerHTML='<button class="remove" onclick="delete_person(this);"><i class="fa-solid fa-close"></i></button>';
|
| 354 |
+
table_row.appendChild(person_id_tag);
|
| 355 |
+
table_row.appendChild(remarks_tag);
|
| 356 |
+
table_row.appendChild(remove_tag);
|
| 357 |
+
|
| 358 |
+
// document.querySelector("#db_people_table").appendChild(table_row);
|
| 359 |
+
var table=document.querySelector("#db_people_table > tbody");
|
| 360 |
+
table.after(table_row);
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
// get_all_persons_from_db/
|
| 365 |
+
fetch("get_all_persons_from_db/").then(function(response){
|
| 366 |
+
return response.json();
|
| 367 |
+
}).then(function(response){
|
| 368 |
+
console.log(response);
|
| 369 |
+
for (var i=0 ; i<response['person_ids'].length;i++){
|
| 370 |
+
add_person_row(response['person_ids'][i],response['remarks'][i])
|
| 371 |
+
}
|
| 372 |
+
})
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
function delete_person(elem){
|
| 378 |
+
// console.log(elem);
|
| 379 |
+
console.log(elem.getAttribute("class"));
|
| 380 |
+
var row=elem.parentNode.parentNode;
|
| 381 |
+
// username:row.firstChild.innerText
|
| 382 |
+
|
| 383 |
+
if (elem.getAttribute("class")=="remove")
|
| 384 |
+
{
|
| 385 |
+
const response = confirm("Are you sure you want to do that?");
|
| 386 |
+
if (!response)
|
| 387 |
+
{
|
| 388 |
+
return;
|
| 389 |
+
}
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
fetch("remove_person_from_db/",{
|
| 394 |
+
method:"POST",
|
| 395 |
+
headers: { "Content-Type": "application/json"},
|
| 396 |
+
body:JSON.stringify({"person_id":row.firstChild.innerText})
|
| 397 |
+
}).then(function(response){
|
| 398 |
+
return response.json();
|
| 399 |
+
}).then(function(response){
|
| 400 |
+
console.log("response:",response);
|
| 401 |
+
|
| 402 |
+
row.remove();
|
| 403 |
+
})
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
function show_settings(){
|
| 411 |
+
document.querySelector('#settings_menu').style.display='block';
|
| 412 |
+
document.querySelector('#container').classList.add("blur");
|
| 413 |
+
}
|
| 414 |
+
function hide_settings(){
|
| 415 |
+
document.querySelector('#settings_menu').style.display='none';
|
| 416 |
+
document.querySelector('#container').classList.remove("blur");
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
function get_settings(){
|
| 422 |
+
fetch("get_settings/",{
|
| 423 |
+
method:"GET"
|
| 424 |
+
}).then(function(response){
|
| 425 |
+
return response.json();
|
| 426 |
+
}).then(function(res){
|
| 427 |
+
console.log(res);
|
| 428 |
+
|
| 429 |
+
p_thres.value=p_thresValue.innerText=res['p_thres'];
|
| 430 |
+
nms_thres.value=nms_thresValue.innerText=res['nms_thres'];
|
| 431 |
+
|
| 432 |
+
small_size.value=small_sizeValue.innerText=res['small_size'];
|
| 433 |
+
large_size.value=large_sizeValue.innerText=res['large_size'];
|
| 434 |
+
|
| 435 |
+
d_thres.value=d_thresValue.innerText=res['d_thres'];
|
| 436 |
+
a_thres.value=a_thresValue.innerText=res['a_thres'];
|
| 437 |
+
|
| 438 |
+
db_mode.value=res['db_mode'];
|
| 439 |
+
fr_mode.value=res['fr_mode'];
|
| 440 |
+
console.log(res['db_mode'],res['fr_mode']);
|
| 441 |
+
})
|
| 442 |
+
}
|
| 443 |
+
get_settings();
|
| 444 |
+
// document.querySelector('#p_thres')
|
| 445 |
+
// document.querySelector('#p_thres').value
|
| 446 |
+
|
| 447 |
+
function update_settings(){
|
| 448 |
+
var new_settings=new Object();
|
| 449 |
+
new_settings['p_thres']=p_thres.value;
|
| 450 |
+
new_settings['nms_thres']=nms_thres.value;
|
| 451 |
+
new_settings['small_size']=small_size.value;
|
| 452 |
+
new_settings['large_size']=large_size.value;
|
| 453 |
+
new_settings['d_thres']=d_thres.value;
|
| 454 |
+
new_settings['a_thres']=a_thres.value;
|
| 455 |
+
new_settings['db_mode']=db_mode.value;
|
| 456 |
+
new_settings['fr_mode']=fr_mode.value;
|
| 457 |
+
|
| 458 |
+
fetch("update_settings/",{
|
| 459 |
+
method:"POST",
|
| 460 |
+
headers:{'content-type': 'application/json'},
|
| 461 |
+
body:JSON.stringify(new_settings)
|
| 462 |
+
}).then(function(response){
|
| 463 |
+
return response.json();
|
| 464 |
+
}).then(function(res){
|
| 465 |
+
console.log(res);
|
| 466 |
+
if(res['message']=='success')
|
| 467 |
+
hide_settings();
|
| 468 |
+
}
|
| 469 |
+
);
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
function reset_settings(){
|
| 473 |
+
fetch("reset_settings/",{
|
| 474 |
+
method:"GET"
|
| 475 |
+
}).then(function(response){
|
| 476 |
+
return response.json();
|
| 477 |
+
}).then(function(res){
|
| 478 |
+
console.log(res);
|
| 479 |
+
if(res['message']=='success')
|
| 480 |
+
get_settings();
|
| 481 |
+
}
|
| 482 |
+
);
|
| 483 |
+
}
|
app/static/user/login.css
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#container{
|
| 2 |
+
position: absolute;
|
| 3 |
+
top:50%;
|
| 4 |
+
left:50%;
|
| 5 |
+
transform:translate(-50%,-50%);
|
| 6 |
+
display: flex;
|
| 7 |
+
row-gap: 20px;
|
| 8 |
+
flex-direction: column;
|
| 9 |
+
align-items: center;
|
| 10 |
+
border:2px solid black;
|
| 11 |
+
/* box-sizing: border-box; */
|
| 12 |
+
|
| 13 |
+
padding: 20px;
|
| 14 |
+
}
|
| 15 |
+
#container>h1{
|
| 16 |
+
text-align: center;
|
| 17 |
+
margin: 0px;
|
| 18 |
+
}
|
| 19 |
+
#container>p{
|
| 20 |
+
text-align: center;
|
| 21 |
+
margin: 0px;
|
| 22 |
+
margin-bottom: 5px;
|
| 23 |
+
visibility: hidden;
|
| 24 |
+
color:red;
|
| 25 |
+
}
|
| 26 |
+
.active{
|
| 27 |
+
visibility: visible !important;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
#container>div{
|
| 31 |
+
display: flex;
|
| 32 |
+
width:314px;
|
| 33 |
+
justify-content: space-between;
|
| 34 |
+
}
|
| 35 |
+
#container>div>p{
|
| 36 |
+
margin: 0;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
#container>div>input{
|
| 40 |
+
border:none;
|
| 41 |
+
outline:none;
|
| 42 |
+
border-bottom:1px solid black;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
#container>input{
|
| 46 |
+
width:30%;
|
| 47 |
+
margin: 10px;
|
| 48 |
+
}
|
| 49 |
+
|
app/static/user/registeration.css
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#container{
|
| 2 |
+
position: absolute;
|
| 3 |
+
top:50%;
|
| 4 |
+
left:50%;
|
| 5 |
+
transform:translate(-50%,-50%);
|
| 6 |
+
display: flex;
|
| 7 |
+
row-gap: 20px;
|
| 8 |
+
flex-direction: column;
|
| 9 |
+
align-items: center;
|
| 10 |
+
border:2px solid black;
|
| 11 |
+
/* box-sizing: border-box; */
|
| 12 |
+
|
| 13 |
+
padding: 20px;
|
| 14 |
+
}
|
| 15 |
+
#container>h1{
|
| 16 |
+
text-align: center;
|
| 17 |
+
margin: 0px;
|
| 18 |
+
}
|
| 19 |
+
#container>p{
|
| 20 |
+
text-align: center;
|
| 21 |
+
margin: 0px;
|
| 22 |
+
margin-bottom: 5px;
|
| 23 |
+
visibility: hidden;
|
| 24 |
+
color:red;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
#container>div{
|
| 28 |
+
display: flex;
|
| 29 |
+
width:314px;
|
| 30 |
+
justify-content: space-between;
|
| 31 |
+
}
|
| 32 |
+
#container>div>p{
|
| 33 |
+
margin: 0;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
#container>div>input{
|
| 37 |
+
border:none;
|
| 38 |
+
outline:none;
|
| 39 |
+
border-bottom:1px solid black;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
#container>input{
|
| 43 |
+
width:30%;
|
| 44 |
+
}
|
app/static/user/registeration.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function check_username(elem){
|
| 2 |
+
if(elem.value!=''){
|
| 3 |
+
fetch(`/user/is_username_available/?username=${elem.value}`,{
|
| 4 |
+
method:"GET",
|
| 5 |
+
|
| 6 |
+
}).then(function(response){
|
| 7 |
+
return response.json();
|
| 8 |
+
}).then(function(response){
|
| 9 |
+
// show a indicator that this username is available
|
| 10 |
+
if(response['available'])
|
| 11 |
+
{
|
| 12 |
+
// show green indicator
|
| 13 |
+
console.log("Username available");
|
| 14 |
+
elem.dataset.valid=true;
|
| 15 |
+
elem.style.borderColor='green';
|
| 16 |
+
elem.style.borderWidth=4;
|
| 17 |
+
}
|
| 18 |
+
else
|
| 19 |
+
{
|
| 20 |
+
// show red indicator
|
| 21 |
+
console.log("Username unavailable");
|
| 22 |
+
elem.dataset.valid=false;
|
| 23 |
+
elem.style.borderColor='red';
|
| 24 |
+
elem.style.borderWidth=4;
|
| 25 |
+
}
|
| 26 |
+
});
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
function check_password_is_matching(confirm_password){
|
| 31 |
+
var password=document.querySelector("#password");
|
| 32 |
+
if(confirm_password.value!=password.value){
|
| 33 |
+
confirm_password.style.borderColor='red';
|
| 34 |
+
confirm_password.style.borderWidth=4;
|
| 35 |
+
}
|
| 36 |
+
else{
|
| 37 |
+
confirm_password.style.borderColor='green';
|
| 38 |
+
confirm_password.style.borderWidth=4;
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
function validate_form(form){
|
| 43 |
+
if(form.username.dataset.valid=="false"){
|
| 44 |
+
document.querySelector("#container>p").style.visibility='visible';
|
| 45 |
+
document.querySelector("#container>p").innerText="username not available";
|
| 46 |
+
return false;
|
| 47 |
+
}
|
| 48 |
+
else if(form.password.value!=form.confirm_password.value){
|
| 49 |
+
document.querySelector("#container>p").style.visibility='visible';
|
| 50 |
+
document.querySelector("#container>p").innerText="password not matching";
|
| 51 |
+
return false;
|
| 52 |
+
}
|
| 53 |
+
else{
|
| 54 |
+
return true;
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
function count_words(elem){
|
| 59 |
+
var count = 0;
|
| 60 |
+
|
| 61 |
+
// Split the words on each
|
| 62 |
+
// space character
|
| 63 |
+
var split = elem.value.split(' ');
|
| 64 |
+
|
| 65 |
+
// Loop through the words and
|
| 66 |
+
// increase the counter when
|
| 67 |
+
// each split word is not empty
|
| 68 |
+
for (var i = 0; i < split.length; i++) {
|
| 69 |
+
if (split[i] != "") {
|
| 70 |
+
count += 1;
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
console.log("count:",count)
|
| 74 |
+
}
|
app/templates/admin/dashboard.html
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends 'base.html' %}
|
| 2 |
+
{% block title %}Dashboard{% endblock %}
|
| 3 |
+
{% block css %}
|
| 4 |
+
<link type="text/css" rel="stylesheet" href="{{ url_for('static',filename='admin/dashboard.css') }}">
|
| 5 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css">
|
| 6 |
+
{% endblock %}
|
| 7 |
+
{% block body %}
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
<table id="access_requests_table">
|
| 11 |
+
<tr>
|
| 12 |
+
<th>Username</th>
|
| 13 |
+
<th>request message</th>
|
| 14 |
+
<th>accept/cancel</th>
|
| 15 |
+
</tr>
|
| 16 |
+
</table>
|
| 17 |
+
<br>
|
| 18 |
+
<br>
|
| 19 |
+
<table id="access_granted_table">
|
| 20 |
+
<tr>
|
| 21 |
+
<th>Username</th>
|
| 22 |
+
<th>remove_access</th>
|
| 23 |
+
</tr>
|
| 24 |
+
</table>
|
| 25 |
+
|
| 26 |
+
<script src="{{ url_for('static',filename='admin/dashboard.js') }}"></script>
|
| 27 |
+
|
| 28 |
+
{% endblock %}
|
app/templates/admin/login.html
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends 'base.html' %}
|
| 2 |
+
{% block title %}Login{% endblock %}
|
| 3 |
+
{% block css %}
|
| 4 |
+
<link type="text/css" rel="stylesheet" href="{{ url_for('static',filename='admin/login.css') }}">
|
| 5 |
+
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css"> -->
|
| 6 |
+
{% endblock %}
|
| 7 |
+
{% block body %}
|
| 8 |
+
<form id="container" action="/admin/authenticate/" method="post">
|
| 9 |
+
<h1>Log-In</h1>
|
| 10 |
+
<p class="{{message_class}}">{{message}}</p>
|
| 11 |
+
<div>
|
| 12 |
+
<p>Enter Username:</p>
|
| 13 |
+
<input type="username" name="username" required>
|
| 14 |
+
</div>
|
| 15 |
+
<div>
|
| 16 |
+
<p>Enter password:</p>
|
| 17 |
+
<input type="password" name="password" required>
|
| 18 |
+
</div>
|
| 19 |
+
<input type="submit">
|
| 20 |
+
<!-- <button>Sign-in</button> -->
|
| 21 |
+
|
| 22 |
+
</form>
|
| 23 |
+
<!-- <script src="{{ url_for('static',filename='/login.js') }}"></script> -->
|
| 24 |
+
|
| 25 |
+
{% endblock %}
|
app/templates/base.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html>
|
| 2 |
+
<head>
|
| 3 |
+
|
| 4 |
+
<title>{% block title %}Face Recognization{% endblock %}</title>
|
| 5 |
+
|
| 6 |
+
<link type="image/*" rel="icon" href="{{ url_for('static',filename='demo/index/icon.jpg' ) }}">
|
| 7 |
+
|
| 8 |
+
{% block css %}{% endblock %}
|
| 9 |
+
</head>
|
| 10 |
+
<body>
|
| 11 |
+
{% block body %}{% endblock %}
|
| 12 |
+
</body>
|
| 13 |
+
</html>
|
app/templates/demo.html
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
<h1>demo page</h1>
|
app/templates/demo/index.html
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends 'base.html' %}
|
| 2 |
+
{% block title %}Face Recognization{% endblock %}
|
| 3 |
+
{% block css %}
|
| 4 |
+
<link type="text/css" rel="stylesheet" href="{{ url_for('static',filename='demo/index/dark.css') }}">
|
| 5 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css">
|
| 6 |
+
<link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
|
| 7 |
+
{% endblock %}
|
| 8 |
+
{% block body %}
|
| 9 |
+
<i class="fa-solid fa-gear settings_btn" onclick="show_settings();"></i>
|
| 10 |
+
<div id="container">
|
| 11 |
+
<div class="left-section">
|
| 12 |
+
<!-- Databases images upload -->
|
| 13 |
+
<div id="db_images_bar">
|
| 14 |
+
<button class="btn add_button" onclick="document.querySelector('.add_button>input').click();">
|
| 15 |
+
<input type="file" accept="image/*" style="display:none;" >
|
| 16 |
+
<i class="fa-solid fa-plus"></i>
|
| 17 |
+
add image
|
| 18 |
+
</button>
|
| 19 |
+
<div id="db_images"></div>
|
| 20 |
+
</div>
|
| 21 |
+
|
| 22 |
+
<!-- Face Recognition output -->
|
| 23 |
+
<img id="face_rec_image">
|
| 24 |
+
|
| 25 |
+
<!-- Face Recognition buttons -->
|
| 26 |
+
<div class="buttons">
|
| 27 |
+
<div>
|
| 28 |
+
<input type="file" id="face_rec_input" accept="image/*" style="display:none;" onchange="load_image_preview(this);">
|
| 29 |
+
<input type="button" value="Upload" class="btn" onclick="document.querySelector('.buttons>div>#face_rec_input').click();">
|
| 30 |
+
<input type="button" value="recognize" class="btn" onclick="update_crops_labels(document.querySelector('.buttons>div>#face_rec_input'))">
|
| 31 |
+
</div>
|
| 32 |
+
</div>
|
| 33 |
+
</div>
|
| 34 |
+
<div class="right-section">
|
| 35 |
+
<!--all unassigned cropped images-->
|
| 36 |
+
<div id="unassigned_faces" class="droppable" ondragover="allowDrop(event)" ondrop="drop(event,this)"></div>
|
| 37 |
+
<p>Drag faces to names</p>
|
| 38 |
+
<!--assigned faces with names list-->
|
| 39 |
+
<div id="name_list">
|
| 40 |
+
<div class="person droppable" data-dropto=".faces" ondragover="allowDrop(event)" ondrop="drop(event,this.querySelector('.faces'))">
|
| 41 |
+
<input type="text" value="person-1" ondrop="return false;" onkeyup="deselect(event,this);">
|
| 42 |
+
<div class="faces"></div>
|
| 43 |
+
<i class="fa-solid fa-xmark" onclick="remove_person(this.parentElement);"></i>
|
| 44 |
+
|
| 45 |
+
</div>
|
| 46 |
+
</div>
|
| 47 |
+
<!--add name button-->
|
| 48 |
+
<div class="buttons">
|
| 49 |
+
<input type="button" value="add name" class="btn" onclick="add_person();" id="add_name_btn">
|
| 50 |
+
</div>
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
</div>
|
| 54 |
+
</div>
|
| 55 |
+
|
| 56 |
+
<div id="settings_menu">
|
| 57 |
+
<h1>settings</h1>
|
| 58 |
+
<i class="fa-solid fa-xmark close_btn" onclick="hide_settings();"></i>
|
| 59 |
+
|
| 60 |
+
<div class="settings">
|
| 61 |
+
<div class="reset_btn_container">
|
| 62 |
+
<i class="fa-solid fa-rotate-left reset_btn" onclick="reset_settings();"></i>
|
| 63 |
+
</div>
|
| 64 |
+
<dl>
|
| 65 |
+
<li>Face detector Modes</li>
|
| 66 |
+
<dd>
|
| 67 |
+
<a>db_mode :</a>
|
| 68 |
+
<select id="db_mode">
|
| 69 |
+
<option value="small">small</option>
|
| 70 |
+
<option value="large">large</option>
|
| 71 |
+
<option value="both">both</option>
|
| 72 |
+
</select>
|
| 73 |
+
|
| 74 |
+
</dd>
|
| 75 |
+
<dd>
|
| 76 |
+
<a>fr_mode :</a>
|
| 77 |
+
<select id="fr_mode">
|
| 78 |
+
<option value="small">small</option>
|
| 79 |
+
<option value="large">large</option>
|
| 80 |
+
<option value="both">both</option>
|
| 81 |
+
</select>
|
| 82 |
+
|
| 83 |
+
</dd>
|
| 84 |
+
<br>
|
| 85 |
+
<li>Face detector</li>
|
| 86 |
+
<dd>
|
| 87 |
+
<a>p_thres :</a>
|
| 88 |
+
<input id="p_thres" type="range" min="0" max="1" step="0.05" oninput="p_thresValue.innerText=this.value">
|
| 89 |
+
<a id="p_thresValue"></a>
|
| 90 |
+
</dd>
|
| 91 |
+
<dd>
|
| 92 |
+
<a>nms_thres :</a>
|
| 93 |
+
<input id="nms_thres" type="range" min="0" max="1" step="0.05" oninput="nms_thresValue.innerText=this.value">
|
| 94 |
+
<a id="nms_thresValue"></a>
|
| 95 |
+
</dd>
|
| 96 |
+
<dd>
|
| 97 |
+
<a>small_size :</a>
|
| 98 |
+
<input id="small_size" type="range" min="64" max="2080" step="32" oninput="small_sizeValue.innerText=this.value">
|
| 99 |
+
<a id="small_sizeValue"></a>
|
| 100 |
+
</dd>
|
| 101 |
+
<dd>
|
| 102 |
+
<a>large_size :</a>
|
| 103 |
+
<input id="large_size" type="range" min="64" max="2080" step="32" oninput="large_sizeValue.innerText=this.value">
|
| 104 |
+
<a id="large_sizeValue"></a>
|
| 105 |
+
</dd>
|
| 106 |
+
<br>
|
| 107 |
+
<li>Face Recognizer</li>
|
| 108 |
+
<dd>
|
| 109 |
+
<a>d_thres :</a>
|
| 110 |
+
<input id="d_thres" type="range" min="0" max="1" step="0.05" oninput="d_thresValue.innerText=this.value">
|
| 111 |
+
<a id="d_thresValue"></a>
|
| 112 |
+
</dd>
|
| 113 |
+
<br>
|
| 114 |
+
<li>Aligner</li>
|
| 115 |
+
<dd>
|
| 116 |
+
<a>a_thres :</a>
|
| 117 |
+
<input id="a_thres" type="range" min="0" max="1" step="0.05" oninput="a_thresValue.innerText=this.value">
|
| 118 |
+
<a id="a_thresValue"></a>
|
| 119 |
+
</dd>
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
</dl>
|
| 123 |
+
</div>
|
| 124 |
+
<div id="save_btn_container"><button id="save_settings_btn" onclick="update_settings();">Save</button></div>
|
| 125 |
+
|
| 126 |
+
</div>
|
| 127 |
+
|
| 128 |
+
<script src="{{ url_for('static',filename='demo/index/script.js' ) }}"></script>
|
| 129 |
+
|
| 130 |
+
{% endblock %}
|
app/templates/user/dashboard.html
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends 'base.html' %}
|
| 2 |
+
{% block title %}Dashboard{% endblock %}
|
| 3 |
+
{% block css %}
|
| 4 |
+
<link type="text/css" rel="stylesheet" href="{{ url_for('static',filename='user/dashboard.css') }}">
|
| 5 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css">
|
| 6 |
+
{% endblock %}
|
| 7 |
+
{% block body %}
|
| 8 |
+
<!--
|
| 9 |
+
<div id="request-api-access">
|
| 10 |
+
<textarea onkeyup="count_words(this);" id="inputField" rows=10 cols="60">{{data_dict["request_message"]}}</textarea>
|
| 11 |
+
<button><i class="fa-solid fa-plus"></i> Request Api Access</button>
|
| 12 |
+
</div> -->
|
| 13 |
+
|
| 14 |
+
<br>
|
| 15 |
+
<br>
|
| 16 |
+
|
| 17 |
+
{% if data_dict['access_key']==None %}
|
| 18 |
+
|
| 19 |
+
<p>You will be soon provided access after someone reviews your api access request</p>
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
{% elif data_dict['access_key']=='rejected' %}
|
| 23 |
+
<p>sorry you are not given access </p>
|
| 24 |
+
{% else %}
|
| 25 |
+
<i class="fa-solid fa-gear settings_btn" onclick="show_settings();"></i>
|
| 26 |
+
<div id="container">
|
| 27 |
+
<div id="api-key">
|
| 28 |
+
<!-- <input type="text" value="{{data_dict['access_key']}}"> -->
|
| 29 |
+
<p>{{data_dict['access_key']}}</p>
|
| 30 |
+
<button class="copy"><i class="fa fa-copy"></i></button>
|
| 31 |
+
<button class="refresh"><i class="fa fa-refresh"></i> Request new key</button>
|
| 32 |
+
</div>
|
| 33 |
+
<br>
|
| 34 |
+
<form id="database-form" onsubmit="return false;">
|
| 35 |
+
|
| 36 |
+
<div class="field">
|
| 37 |
+
<p>Name:</p>
|
| 38 |
+
<input class="person_name" type="text">
|
| 39 |
+
</div>
|
| 40 |
+
|
| 41 |
+
<div class="field">
|
| 42 |
+
<button class="btn add_button" onclick="document.querySelector('.add_button>input').click();">
|
| 43 |
+
<input type="file" accept="image/*" style="display:none;" >
|
| 44 |
+
<i class="fa-solid fa-plus"></i>
|
| 45 |
+
add image
|
| 46 |
+
</button>
|
| 47 |
+
</div>
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
<div id="unassigned_faces" class="droppable" ondragover="allowDrop(event)" ondrop="drop(event,this)"></div>
|
| 51 |
+
<p>Drag faces to remarks</p>
|
| 52 |
+
|
| 53 |
+
<!--assigned faces with names list-->
|
| 54 |
+
<div id="remark_list">
|
| 55 |
+
<div class="remark droppable" data-dropto=".faces" ondragover="allowDrop(event)" ondrop="drop(event,this.querySelector('#remark_list>.remark>.faces'))">
|
| 56 |
+
<input type="text" value="front-face" ondrop="return false;" onkeyup="deselect(event,this);">
|
| 57 |
+
<div class="faces"></div>
|
| 58 |
+
<i class="fa-solid fa-xmark" onclick="remove_remark(this.parentElement);"></i>
|
| 59 |
+
|
| 60 |
+
</div>
|
| 61 |
+
</div>
|
| 62 |
+
<!--add name button-->
|
| 63 |
+
<div class="buttons">
|
| 64 |
+
<input type="button" value="add remark" class="btn" onclick="add_remark();" id="add_remark_btn">
|
| 65 |
+
</div>
|
| 66 |
+
<input type="button" value="submit" onclick="update_db_crops();">
|
| 67 |
+
</form>
|
| 68 |
+
<table id="db_people_table">
|
| 69 |
+
<tbody>
|
| 70 |
+
<tr>
|
| 71 |
+
<th>Person_ID</th>
|
| 72 |
+
<th>Remarks</th>
|
| 73 |
+
<th>Remove</th>
|
| 74 |
+
</tr>
|
| 75 |
+
</tbody>
|
| 76 |
+
</table>
|
| 77 |
+
<br>
|
| 78 |
+
<br>
|
| 79 |
+
<input type="file" onchange="face_recoginization(this);">
|
| 80 |
+
<img id="face_recognition_image">
|
| 81 |
+
</div>
|
| 82 |
+
|
| 83 |
+
<div id="settings_menu">
|
| 84 |
+
<h1>settings</h1>
|
| 85 |
+
<i class="fa-solid fa-xmark close_btn" onclick="hide_settings();"></i>
|
| 86 |
+
|
| 87 |
+
<div class="settings">
|
| 88 |
+
<div class="reset_btn_container">
|
| 89 |
+
<i class="fa-solid fa-rotate-left reset_btn" onclick="reset_settings();"></i>
|
| 90 |
+
</div>
|
| 91 |
+
<dl>
|
| 92 |
+
<li>Face detector Modes</li>
|
| 93 |
+
<dd>
|
| 94 |
+
<a>db_mode :</a>
|
| 95 |
+
<select id="db_mode">
|
| 96 |
+
<option value="small">small</option>
|
| 97 |
+
<option value="large">large</option>
|
| 98 |
+
<option value="both">both</option>
|
| 99 |
+
</select>
|
| 100 |
+
|
| 101 |
+
</dd>
|
| 102 |
+
<dd>
|
| 103 |
+
<a>fr_mode :</a>
|
| 104 |
+
<select id="fr_mode">
|
| 105 |
+
<option value="small">small</option>
|
| 106 |
+
<option value="large">large</option>
|
| 107 |
+
<option value="both">both</option>
|
| 108 |
+
</select>
|
| 109 |
+
|
| 110 |
+
</dd>
|
| 111 |
+
<br>
|
| 112 |
+
<li>Face detector</li>
|
| 113 |
+
<dd>
|
| 114 |
+
<a>p_thres :</a>
|
| 115 |
+
<input id="p_thres" type="range" min="0" max="1" step="0.05" oninput="p_thresValue.innerText=this.value">
|
| 116 |
+
<a id="p_thresValue"></a>
|
| 117 |
+
</dd>
|
| 118 |
+
<dd>
|
| 119 |
+
<a>nms_thres :</a>
|
| 120 |
+
<input id="nms_thres" type="range" min="0" max="1" step="0.05" oninput="nms_thresValue.innerText=this.value">
|
| 121 |
+
<a id="nms_thresValue"></a>
|
| 122 |
+
</dd>
|
| 123 |
+
<dd>
|
| 124 |
+
<a>small_size :</a>
|
| 125 |
+
<input id="small_size" type="range" min="64" max="2080" step="32" oninput="small_sizeValue.innerText=this.value">
|
| 126 |
+
<a id="small_sizeValue"></a>
|
| 127 |
+
</dd>
|
| 128 |
+
<dd>
|
| 129 |
+
<a>large_size :</a>
|
| 130 |
+
<input id="large_size" type="range" min="64" max="2080" step="32" oninput="large_sizeValue.innerText=this.value">
|
| 131 |
+
<a id="large_sizeValue"></a>
|
| 132 |
+
</dd>
|
| 133 |
+
<br>
|
| 134 |
+
<li>Face Recognizer</li>
|
| 135 |
+
<dd>
|
| 136 |
+
<a>d_thres :</a>
|
| 137 |
+
<input id="d_thres" type="range" min="0" max="1" step="0.05" oninput="d_thresValue.innerText=this.value">
|
| 138 |
+
<a id="d_thresValue"></a>
|
| 139 |
+
</dd>
|
| 140 |
+
<br>
|
| 141 |
+
<li>Aligner</li>
|
| 142 |
+
<dd>
|
| 143 |
+
<a>a_thres :</a>
|
| 144 |
+
<input id="a_thres" type="range" min="0" max="1" step="0.05" oninput="a_thresValue.innerText=this.value">
|
| 145 |
+
<a id="a_thresValue"></a>
|
| 146 |
+
</dd>
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
</dl>
|
| 150 |
+
</div>
|
| 151 |
+
<div id="save_btn_container"><button id="save_settings_btn" onclick="update_settings();">Save</button></div>
|
| 152 |
+
|
| 153 |
+
</div>
|
| 154 |
+
<script src="{{ url_for('static',filename='user/dashboard.js') }}"></script>
|
| 155 |
+
{% endif %}
|
| 156 |
+
|
| 157 |
+
{% endblock %}
|
app/templates/user/login.html
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends 'base.html' %}
|
| 2 |
+
{% block title %}Login{% endblock %}
|
| 3 |
+
{% block css %}
|
| 4 |
+
<link type="text/css" rel="stylesheet" href="{{ url_for('static',filename='user/login.css') }}">
|
| 5 |
+
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css"> -->
|
| 6 |
+
{% endblock %}
|
| 7 |
+
{% block body %}
|
| 8 |
+
<form id="container" action="/user/authenticate/" method="post">
|
| 9 |
+
<h1>Log-In</h1>
|
| 10 |
+
<p class="{{message_class}}">{{message}}</p>
|
| 11 |
+
<div>
|
| 12 |
+
<p>Enter Username:</p>
|
| 13 |
+
<input type="username" name="username" required>
|
| 14 |
+
</div>
|
| 15 |
+
<div>
|
| 16 |
+
<p>Enter password:</p>
|
| 17 |
+
<input type="password" name="password" required>
|
| 18 |
+
</div>
|
| 19 |
+
<input type="submit">
|
| 20 |
+
<!-- <button>Sign-in</button> -->
|
| 21 |
+
|
| 22 |
+
</form>
|
| 23 |
+
<script src="{{ url_for('static',filename='user/login.js') }}"></script>
|
| 24 |
+
|
| 25 |
+
{% endblock %}
|
app/templates/user/registeration.html
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends 'base.html' %}
|
| 2 |
+
{% block title %}Sign Up{% endblock %}
|
| 3 |
+
{% block css %}
|
| 4 |
+
<link type="text/css" rel="stylesheet" href="{{ url_for('static',filename='user/registeration.css') }}">
|
| 5 |
+
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css"> -->
|
| 6 |
+
{% endblock %}
|
| 7 |
+
{% block body %}
|
| 8 |
+
<form id="container" method="post" action="/user/add_account/" onsubmit="return validate_form(this)">
|
| 9 |
+
<h1>Sign-Up</h1>
|
| 10 |
+
<p>message</p>
|
| 11 |
+
<div>
|
| 12 |
+
<p>Enter Username:</p>
|
| 13 |
+
<input type="username" name="username" required onblur="check_username(this);">
|
| 14 |
+
</div>
|
| 15 |
+
<div>
|
| 16 |
+
<p>Enter password:</p>
|
| 17 |
+
<input type="password" name="password" id="password" required>
|
| 18 |
+
</div>
|
| 19 |
+
<div>
|
| 20 |
+
<p>Confirm password:</p>
|
| 21 |
+
<input type="password" name="confirm_password" required onkeyup="check_password_is_matching(this);">
|
| 22 |
+
</div>
|
| 23 |
+
<div>
|
| 24 |
+
<!-- <p>What will you use this api for:</p> -->
|
| 25 |
+
<textarea placeholder="What will you use this api for, Write atleast 2-3 lines" rows=10 cols="60" name="request_message" id="request_message" required></textarea>
|
| 26 |
+
<!-- <input type="text"> -->
|
| 27 |
+
</div>
|
| 28 |
+
<input type="submit" >
|
| 29 |
+
<!-- <button>Sign-in</button> -->
|
| 30 |
+
|
| 31 |
+
</form>
|
| 32 |
+
<script src="{{ url_for('static',filename='user/registeration.js') }}"></script>
|
| 33 |
+
|
| 34 |
+
{% endblock %}
|
app/user/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Blueprint
|
| 2 |
+
from datetime import timedelta
|
| 3 |
+
|
| 4 |
+
bp=Blueprint("user",__name__)
|
| 5 |
+
session_expiring_time=timedelta(minutes=45)
|
| 6 |
+
settings=dict()
|
| 7 |
+
|
| 8 |
+
from app.user import routes
|