commit 3530d7f4c543e859e6e3be91e8bcdd15f61454c2 Author: Alexandre Gut Date: Tue Mar 31 13:27:52 2026 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd59182 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# ── Python ────────────────────────────────────────────────────────────────── +__pycache__/ +*.py[cod] +*.pyo +*.pyd +*.pyc + +# ── Environnements virtuels ────────────────────────────────────────────────── +venv/ +.venv/ +env/ +ENV/ +Pipfile.lock + +# ── Secrets ─────────────────────────────────────────────────────────────────── +.env +.env.* +secret.env +secrets.py +*.secret +credentials.json +token.json + +# ── Logs ───────────────────────────────────────────────────────────────────── +*.log +logs/ + +# ── IDE ─────────────────────────────────────────────────────────────────────── +.vscode/ +.idea/ +*.swp +.DS_Store +Thumbs.db + +# ── Tests / Coverage ───────────────────────────────────────────────────────── +.coverage +htmlcov/ +.pytest_cache/ + +# ── Build ───────────────────────────────────────────────────────────────────── +dist/ +build/ +*.egg-info/ + +# ── Modèles IA (trop lourds pour git) ─────────────────────────────────────── +*.gguf +*.bin +*.safetensors +models/ +weights/ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c262630 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright (c) 2011-2019 GitHub Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1864499 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# Easy Facial Recognition + +Recognition by minimum norm between vectors (128D dlib descriptor) +![Alt Text](readme.gif) + + +### Prerequisites + +#### Install requirements + +Make sure to have the following libraries installed in your Python environment: + +- opencv +- dlib +- numpy +- imutils +- pillow + +#### Setup faces to recognize + +Update the `known_faces` directory with images of people you want to detect and be sure to crop around the faces as the Zuckerberg example (if you don't, the program execution might raise an error). + +Please only use .jpg or .png image format files in the `known_faces` folder. + +For instance, you may have the following files: + +``` +/known_faces/Zuckerberg.png +/known_faces/YourPicture.jpg +``` + +Note that the recognition name displayed is taken from the file name (without extension) it matches in the `known_faces` folder. + +#### Camera + +You need a camera connected to your PC since the program will stream the image of camera on your screen and will recognize the face displayed should the face be part of the `known_faces` folder. + +## Run + +``` +easy_facial_recognition.py --i known_faces +``` +## Youtube Video explanation (French only) +Click on the image below: + +[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/54WmrwVWu1w/0.jpg)](https://www.youtube.com/watch?v=54WmrwVWu1w) + +## Authors + +* **Anis Ayari** - *Lead Data Scientist* + +## License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details + +## Windows environment Notes + +On Windows, you may have to additionnally install: +- opencv-python +- CMake +- Visual Studio and the extension for C++ so that `dlib` installation completes successfully diff --git a/easy_facial_recognition.py b/easy_facial_recognition.py new file mode 100644 index 0000000..590f258 --- /dev/null +++ b/easy_facial_recognition.py @@ -0,0 +1,115 @@ +# Code Anis - Defend Intelligence +import cv2 +import dlib +import PIL.Image +import numpy as np +from imutils import face_utils +import argparse +from pathlib import Path +import os +import ntpath + +parser = argparse.ArgumentParser(description='Easy Facial Recognition App') +parser.add_argument('-i', '--input', type=str, required=True, help='directory of input known faces') + +print('[INFO] Starting System...') +print('[INFO] Importing pretrained model..') +pose_predictor_68_point = dlib.shape_predictor("pretrained_model/shape_predictor_68_face_landmarks.dat") +pose_predictor_5_point = dlib.shape_predictor("pretrained_model/shape_predictor_5_face_landmarks.dat") +face_encoder = dlib.face_recognition_model_v1("pretrained_model/dlib_face_recognition_resnet_model_v1.dat") +face_detector = dlib.get_frontal_face_detector() +print('[INFO] Importing pretrained model..') + + +def transform(image, face_locations): + coord_faces = [] + for face in face_locations: + rect = face.top(), face.right(), face.bottom(), face.left() + coord_face = max(rect[0], 0), min(rect[1], image.shape[1]), min(rect[2], image.shape[0]), max(rect[3], 0) + coord_faces.append(coord_face) + return coord_faces + + +def encode_face(image): + face_locations = face_detector(image, 1) + face_encodings_list = [] + landmarks_list = [] + for face_location in face_locations: + # DETECT FACES + shape = pose_predictor_68_point(image, face_location) + face_encodings_list.append(np.array(face_encoder.compute_face_descriptor(image, shape, num_jitters=1))) + # GET LANDMARKS + shape = face_utils.shape_to_np(shape) + landmarks_list.append(shape) + face_locations = transform(image, face_locations) + return face_encodings_list, face_locations, landmarks_list + + +def easy_face_reco(frame, known_face_encodings, known_face_names): + rgb_small_frame = frame[:, :, ::-1] + # ENCODING FACE + face_encodings_list, face_locations_list, landmarks_list = encode_face(rgb_small_frame) + face_names = [] + for face_encoding in face_encodings_list: + if len(face_encoding) == 0: + return np.empty((0)) + # CHECK DISTANCE BETWEEN KNOWN FACES AND FACES DETECTED + vectors = np.linalg.norm(known_face_encodings - face_encoding, axis=1) + tolerance = 0.6 + result = [] + for vector in vectors: + if vector <= tolerance: + result.append(True) + else: + result.append(False) + if True in result: + first_match_index = result.index(True) + name = known_face_names[first_match_index] + else: + name = "Unknown" + face_names.append(name) + + for (top, right, bottom, left), name in zip(face_locations_list, face_names): + cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2) + cv2.rectangle(frame, (left, bottom - 30), (right, bottom), (0, 255, 0), cv2.FILLED) + cv2.putText(frame, name, (left + 2, bottom - 2), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 1) + + for shape in landmarks_list: + for (x, y) in shape: + cv2.circle(frame, (x, y), 1, (255, 0, 255), -1) + + +if __name__ == '__main__': + args = parser.parse_args() + + print('[INFO] Importing faces...') + face_to_encode_path = Path(args.input) + files = [file_ for file_ in face_to_encode_path.rglob('*.jpg')] + + for file_ in face_to_encode_path.rglob('*.png'): + files.append(file_) + if len(files)==0: + raise ValueError('No faces detect in the directory: {}'.format(face_to_encode_path)) + known_face_names = [os.path.splitext(ntpath.basename(file_))[0] for file_ in files] + + known_face_encodings = [] + for file_ in files: + image = PIL.Image.open(file_) + image = np.array(image) + face_encoded = encode_face(image)[0][0] + known_face_encodings.append(face_encoded) + + print('[INFO] Faces well imported') + print('[INFO] Starting Webcam...') + video_capture = cv2.VideoCapture(0) + print('[INFO] Webcam well started') + print('[INFO] Detecting...') + while True: + ret, frame = video_capture.read() + easy_face_reco(frame, known_face_encodings, known_face_names) + cv2.imshow('Easy Facial Recognition App', frame) + if cv2.waitKey(1) & 0xFF == ord('q'): + break + print('[INFO] Stopping System') + video_capture.release() + cv2.destroyAllWindows() diff --git a/known_faces/Zuckerberg.png b/known_faces/Zuckerberg.png new file mode 100644 index 0000000..5b4f0b6 Binary files /dev/null and b/known_faces/Zuckerberg.png differ diff --git a/pretrained_model/dlib_face_recognition_resnet_model_v1.dat b/pretrained_model/dlib_face_recognition_resnet_model_v1.dat new file mode 100644 index 0000000..ddb5158 Binary files /dev/null and b/pretrained_model/dlib_face_recognition_resnet_model_v1.dat differ diff --git a/pretrained_model/shape_predictor_5_face_landmarks.dat b/pretrained_model/shape_predictor_5_face_landmarks.dat new file mode 100644 index 0000000..67878ed Binary files /dev/null and b/pretrained_model/shape_predictor_5_face_landmarks.dat differ diff --git a/pretrained_model/shape_predictor_68_face_landmarks.dat b/pretrained_model/shape_predictor_68_face_landmarks.dat new file mode 100644 index 0000000..e0ec20d Binary files /dev/null and b/pretrained_model/shape_predictor_68_face_landmarks.dat differ diff --git a/readme.gif b/readme.gif new file mode 100644 index 0000000..852998e Binary files /dev/null and b/readme.gif differ