fr_flask.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. from PIL import ImageFile, Image
  2. from flask import Flask, request, jsonify
  3. import face_recognition
  4. import base64
  5. from io import BytesIO
  6. import joblib
  7. import numpy as np
  8. import re
  9. import os
  10. import time
  11. import mysql.connector
  12. import shlex
  13. import subprocess
  14. from face_recognition_svm import train_svm
  15. ImageFile.SAFEBLOCK = 2048 * 2048
  16. app = Flask(__name__)
  17. model_file_name = "saved_model_2.pkl"
  18. clf = None
  19. classes = None
  20. ids = []
  21. ssl = None
  22. known_people = "application_data/verification_images"
  23. known_faces = []
  24. total_threshold = 0.1
  25. face_model = "large"
  26. if face_model == "large":
  27. model_file_name = "saved_model_2_large.pkl"
  28. TRAINING_FOLDER = os.path.join("data", "train")
  29. PROFILE_FOLDER = os.path.join("application_data", "verification_images")
  30. app.config["TRAINING_FOLDER"] = TRAINING_FOLDER
  31. app.config["PROFILE_FOLDER"] = PROFILE_FOLDER
  32. db_user = os.environ.get("DB_USER", "facer")
  33. db_pass = os.environ.get("DB_PASS", "9Y6Bqg3JwQxXa")
  34. # Pick one
  35. db_host, db_port = os.environ.get("DB_HOST", "db"), os.environ.get("DB_PORT", "5050")
  36. db_unix_socket = ""
  37. # Pick one
  38. db_name = os.environ.get("DB_NAME", "face_recognition")
  39. def scan_known_people(known_people_folder):
  40. known_face_encodings = []
  41. for file in image_files_in_folder(known_people_folder):
  42. img = face_recognition.load_image_file(file)
  43. encodings = face_recognition.face_encodings(img, model=face_model)
  44. if len(encodings) > 1:
  45. print("WARNING: More than one face found in {}. Only considering the first face.".format(file))
  46. if len(encodings) == 0:
  47. print("WARNING: No faces found in {}. Ignoring file.".format(file))
  48. else:
  49. known_face_encodings.append(encodings[0])
  50. return known_face_encodings
  51. def image_files_in_folder(folder):
  52. img_list = [os.path.join(folder, f) for f in os.listdir(folder) if
  53. re.match(r'.*\.(jpg|jpeg|png|webp)', f, flags=re.I)]
  54. img_list.sort()
  55. return img_list
  56. def load_db(data_id):
  57. db_con = None
  58. if db_unix_socket:
  59. db_con = mysql.connector.connect(user=db_user, password=db_pass, unix_socket=db_unix_socket,
  60. database=db_name)
  61. elif db_host and db_port:
  62. db_con = mysql.connector.connect(user=db_user, password=db_pass, host=db_host, port=db_port,
  63. database=db_name)
  64. else:
  65. return None
  66. db_cursor = db_con.cursor()
  67. db_result = {"name": "Unknown", "address": "", "nik": ""}
  68. try:
  69. db_query = "SELECT `NIK`, `NAME`, `ADDRESS` FROM `face_recognition` WHERE `ID` = %s"
  70. db_cursor.execute(db_query, (data_id,))
  71. for (nik, name, address) in db_cursor:
  72. db_result['nik'] = nik
  73. db_result['name'] = name
  74. db_result['address'] = address
  75. finally:
  76. db_cursor.close()
  77. db_con.close()
  78. return db_result
  79. def save_db(nik, name, address):
  80. db_con = None
  81. if db_unix_socket:
  82. db_con = mysql.connector.connect(user=db_user, password=db_pass, unix_socket=db_unix_socket,
  83. database=db_name)
  84. elif db_host and db_port:
  85. db_con = mysql.connector.connect(user=db_user, password=db_pass, host=db_host, port=db_port,
  86. database=db_name)
  87. else:
  88. return None
  89. data_id = None
  90. db_cursor = db_con.cursor()
  91. try:
  92. db_query = 'INSERT INTO `face_recognition` (`NIK`, `NAME`, `ADDRESS`) VALUES (%s, %s, %s)'
  93. db_cursor.execute(db_query, (nik, name, address))
  94. db_con.commit()
  95. data_id = db_cursor.lastrowid
  96. finally:
  97. db_cursor.close()
  98. db_con.close()
  99. return data_id
  100. def get_face(image, crop=False):
  101. max_height = image.height
  102. max_width = image.width
  103. profile_nparray = np.asarray(image)
  104. profile_face_loc = face_recognition.face_locations(profile_nparray)
  105. profile_len = len(profile_face_loc)
  106. if profile_len >= 1:
  107. if crop:
  108. top = profile_face_loc[0][0]
  109. right = profile_face_loc[0][1]
  110. bottom = profile_face_loc[0][2]
  111. left = profile_face_loc[0][3]
  112. width = right - left
  113. height = bottom - top
  114. center_x = int(left + width / 2)
  115. center_y = int(top + height / 2)
  116. calc_new_width = min(int(width * 2), (max_width - center_x) * 2, abs((0 - center_x) * 2))
  117. calc_new_height = min(int(height * 2), (max_height - center_y) * 2, abs((0 - center_y) * 2))
  118. new_size = min(calc_new_height, calc_new_width)
  119. new_top = int(center_y - new_size / 2)
  120. new_right = int(center_x + new_size / 2)
  121. new_bottom = int(center_y + new_size / 2)
  122. new_left = int(center_x - new_size / 2)
  123. cropped = image.crop((new_left, new_top, new_right, new_bottom))
  124. resized = cropped.resize((250, 250))
  125. else:
  126. resized = image.resize((250, 250))
  127. return resized
  128. return None
  129. @app.route('/', methods=['GET', 'POST'])
  130. def test():
  131. return jsonify({"status": "0"})
  132. @app.route('/upload', methods=['POST'])
  133. def upload():
  134. try:
  135. profile = request.files["profile"]
  136. nik = request.form["nik"]
  137. name = request.form["name"]
  138. address = request.form["address"]
  139. training = []
  140. print(len(request.files))
  141. if 'training' in request.files:
  142. training = request.files.getlist("training")
  143. elif 'training[]' in request.files:
  144. training = request.files.getlist("training[]")
  145. else:
  146. files_length = len(request.files)
  147. training_names = [f'training{x}' for x in range(files_length - 1)]
  148. for x in training_names:
  149. training.append(request.files[x])
  150. print(training)
  151. print(len(training))
  152. if not training:
  153. return jsonify({"status": "2", "message": "Training files not exist"})
  154. profile_image = Image.open(profile.stream)
  155. profile_image.load()
  156. new_profile_image = get_face(profile_image)
  157. if not new_profile_image:
  158. return jsonify({"status": "3", "message": "Profile face not found, please upload a different picture"})
  159. detected_training = []
  160. detected_training_fn = []
  161. for file in training:
  162. training_image = Image.open(file.stream)
  163. new_training_image = get_face(training_image)
  164. if new_training_image:
  165. detected_training.append(new_training_image)
  166. detected_training_fn.append(file.filename)
  167. if not detected_training:
  168. return jsonify(
  169. {"status": "4", "message": "No face found on any training images, please upload a different picture"})
  170. print(detected_training)
  171. print(detected_training_fn)
  172. # Save
  173. user_id = save_db(nik, name, address)
  174. print(user_id)
  175. if not user_id:
  176. return jsonify({"status": "1", "message": "Error uploading data, please try again"})
  177. new_profile_image.save(os.path.join(app.config["PROFILE_FOLDER"], f'{user_id}.jpg'))
  178. id_dir = os.path.join(app.config['TRAINING_FOLDER'], str(user_id))
  179. if not os.path.exists(id_dir):
  180. os.mkdir(id_dir)
  181. for (image, filename) in zip(detected_training, detected_training_fn):
  182. image.save(os.path.join(app.config['TRAINING_FOLDER'], str(user_id), filename))
  183. return jsonify({"status": "0", "message": "Upload successful"})
  184. except Exception as exc:
  185. return jsonify({"status": "1", "message": f"Error uploading data, please try again | {exc}"})
  186. @app.route('/train', methods=['GET', 'POST'])
  187. def train():
  188. try:
  189. train_svm(model_file_name)
  190. return jsonify({"status": "0", "message": "Train successful"})
  191. except Exception as exc:
  192. return jsonify({"status": "1", "message": f"Error training model: {exc}"})
  193. @app.route('/reload', methods=['GET', 'POST'])
  194. def face_reload():
  195. pid = None
  196. try:
  197. with open("app.pid", "r") as f:
  198. pid = f.readline()
  199. if pid:
  200. cmd = f"kill -s HUP {pid}"
  201. cmd_array = shlex.split(cmd)
  202. print(cmd_array)
  203. subprocess.Popen(cmd_array, start_new_session=True)
  204. else:
  205. return jsonify({"status": "1", "message": f"Reload unsucessful"})
  206. except Exception as exc:
  207. return jsonify({"status": "2", "message": f"Reload unsucessful: {exc}"})
  208. @app.route('/predict', methods=['POST'])
  209. def predict():
  210. db_result = []
  211. if "image" in request.json:
  212. im_b64 = request.json["image"]
  213. elif "image" in request.files:
  214. im_b64 = request.files["image"]
  215. elif "image" in request.form:
  216. im_b64 = request.form["image"]
  217. else:
  218. return {"error": "Error reading image"}
  219. im_bytes = base64.b64decode(im_b64)
  220. im_file = BytesIO(im_bytes)
  221. test_image = face_recognition.load_image_file(im_file)
  222. face_locations = face_recognition.face_locations(test_image)
  223. no = len(face_locations)
  224. if no > 0:
  225. for i in range(no):
  226. start_time = time.perf_counter()
  227. test_image_enc = face_recognition.face_encodings(test_image, model=face_model)[i]
  228. proba_list = clf.predict_proba([test_image_enc])
  229. dist = face_recognition.face_distance(known_faces, test_image_enc)
  230. total = np.subtract(proba_list, dist)
  231. i = np.argmax(total)
  232. proba = list(*proba_list)[i]
  233. face_id = ids[i]
  234. data = load_db(face_id)
  235. name = data["name"]
  236. address = data["address"]
  237. nik = data["nik"]
  238. print("total threshold: ", total_threshold)
  239. print("total: ", total[0][i])
  240. print({
  241. "id": str(face_id),
  242. "name": name,
  243. "address": address,
  244. "nik": nik,
  245. "proba": proba,
  246. "delta": total[0][i]
  247. })
  248. if total[0][i] > total_threshold:
  249. js = {
  250. "id": str(face_id),
  251. "name": name,
  252. "address": address,
  253. "nik": nik,
  254. "proba": proba,
  255. "delta": total[0][i]
  256. }
  257. else:
  258. js = {
  259. "id": "-1",
  260. "name": "Unknown",
  261. "address": "",
  262. "nik": "",
  263. "proba": 0.0,
  264. "delta": 0.0
  265. }
  266. if total[0][i] > total_threshold or (not db_result and i == no - 1):
  267. db_result.append(js)
  268. end_time = time.perf_counter()
  269. process_time = end_time - start_time
  270. print(time.strftime("%c"))
  271. print(total[0])
  272. print("Process time:", process_time, "s")
  273. print(db_result)
  274. return jsonify(db_result)
  275. try:
  276. clf = joblib.load(model_file_name)
  277. classes = clf.classes_
  278. print('model loaded')
  279. known_faces = scan_known_people(known_people)
  280. print('known faces scanned')
  281. db_con = None
  282. if db_unix_socket:
  283. db_con = mysql.connector.connect(user=db_user, password=db_pass, unix_socket=db_unix_socket,
  284. database=db_name)
  285. elif db_host and db_port:
  286. db_con = mysql.connector.connect(user=db_user, password=db_pass, host=db_host, port=db_port,
  287. database=db_name)
  288. else:
  289. exit(1)
  290. cursor = db_con.cursor()
  291. result = {"name": "Unknown", "address": "", "nik": ""}
  292. try:
  293. query = "SELECT `ID` FROM `face_recognition` ORDER BY `ID`"
  294. cursor.execute(query)
  295. for data_id in cursor:
  296. ids.append(data_id[0])
  297. finally:
  298. cursor.close()
  299. db_con.close()
  300. print("ids: ", ids)
  301. except FileNotFoundError as e:
  302. print('No model here')
  303. exit(1)
  304. if __name__ == '__main__':
  305. app.run(host='0.0.0.0', port=8349, debug=True, ssl_context=ssl)