├── .gitignore ├── LICENSE ├── README.md ├── db.py ├── docker-compose.yml ├── face-add.py └── face-find.py /.gitignore: -------------------------------------------------------------------------------- 1 | /.faces 2 | /.idea 3 | /postgres-data -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Viacheslav Poturaev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Face index with PostgreSQL 2 | 3 | A small demo based on [`ageitgey/face_recognition`](https://github.com/ageitgey/face_recognition/tree/master/examples) 4 | 5 | ## Setup 6 | 7 | Deps: 8 | 9 | ``` 10 | pip install py-postgresql face_recognition opencv-python 11 | ``` 12 | 13 | Start PostgreSQL 9.6 in docker or natively 14 | 15 | ``` 16 | docker-compose up -d 17 | ``` 18 | 19 | Initialize DB 20 | 21 | ``` 22 | python db.py 23 | ``` 24 | 25 | ## Usage 26 | 27 | Add images to index 28 | 29 | ``` 30 | python face-add.py 31 | ``` 32 | 33 | ``` 34 | python face-add.py ../lfw/Bill_Clinton/Bill_Clinton_0009.jpg 35 | Found 1 faces in the image file ../lfw/Bill_Clinton/Bill_Clinton_0009.jpg 36 | - Face #0 found at Left: 86 Top: 96 Right: 175 Bottom: 186 37 | 38 | 39 | python face-add.py ../lfw/John_Malkovich/John_Malkovich_0001.jpg 40 | Found 1 faces in the image file ../lfw/John_Malkovich/John_Malkovich_0001.jpg 41 | - Face #0 found at Left: 76 Top: 86 Right: 165 Bottom: 176 42 | 43 | 44 | python face-add.py ../lfw/Bill_Gates/Bill_Gates_0006.jpg 45 | Found 1 faces in the image file ../lfw/Bill_Gates/Bill_Gates_0006.jpg 46 | - Face #0 found at Left: 67 Top: 80 Right: 175 Bottom: 187 47 | 48 | ``` 49 | 50 | Search for face by image 51 | 52 | ``` 53 | python face-find.py 54 | ``` 55 | 56 | ``` 57 | python face-find.py ../lfw/John_Malkovich/John_Malkovich_0003.jpg 58 | Found 1 faces in the image file ../lfw/John_Malkovich/John_Malkovich_0003.jpg 59 | - Face #0 found at Left: 67 Top: 68 Right: 175 Bottom: 175 60 | [('../lfw/John_Malkovich/John_Malkovich_0001.jpg',)] 61 | 62 | 63 | python face-find.py ../lfw/Bill_Clinton/Bill_Clinton_0002.jpg 64 | Found 1 faces in the image file ../lfw/Bill_Clinton/Bill_Clinton_0002.jpg 65 | - Face #0 found at Left: 86 Top: 86 Right: 175 Bottom: 176 66 | [('../lfw/Bill_Clinton/Bill_Clinton_0009.jpg',)] 67 | ``` -------------------------------------------------------------------------------- /db.py: -------------------------------------------------------------------------------- 1 | import postgresql 2 | 3 | 4 | def setup_db(): 5 | db = postgresql.open('pq://user:pass@localhost:5434/db') 6 | db.execute("create extension if not exists cube;") 7 | db.execute("drop table if exists vectors") 8 | db.execute("create table vectors (id serial, file varchar, vec_low cube, vec_high cube);") 9 | db.execute("create index vectors_vec_idx on vectors (vec_low, vec_high);") 10 | 11 | 12 | setup_db() 13 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | db: 5 | image: postgres:9.6 6 | environment: 7 | POSTGRES_PASSWORD: pass 8 | POSTGRES_USER: user 9 | POSTGRES_DB: db 10 | ports: 11 | - "5434:5432" 12 | -------------------------------------------------------------------------------- /face-add.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import dlib 3 | import cv2 4 | import face_recognition 5 | import os 6 | import postgresql 7 | 8 | if len(sys.argv) < 2: 9 | print("Usage: face-add ") 10 | exit(1) 11 | 12 | # Take the image file name from the command line 13 | file_name = sys.argv[1] 14 | 15 | # Create a HOG face detector using the built-in dlib class 16 | face_detector = dlib.get_frontal_face_detector() 17 | 18 | # Load the image 19 | image = cv2.imread(file_name) 20 | 21 | # Run the HOG face detector on the image data 22 | detected_faces = face_detector(image, 1) 23 | 24 | print("Found {} faces in the image file {}".format(len(detected_faces), file_name)) 25 | 26 | if not os.path.exists("./.faces"): 27 | os.mkdir("./.faces") 28 | 29 | db = postgresql.open('pq://user:pass@localhost:5434/db') 30 | 31 | # Loop through each face we found in the image 32 | for i, face_rect in enumerate(detected_faces): 33 | # Detected faces are returned as an object with the coordinates 34 | # of the top, left, right and bottom edges 35 | print("- Face #{} found at Left: {} Top: {} Right: {} Bottom: {}".format(i, face_rect.left(), face_rect.top(), 36 | face_rect.right(), face_rect.bottom())) 37 | crop = image[face_rect.top():face_rect.bottom(), face_rect.left():face_rect.right()] 38 | encodings = face_recognition.face_encodings(crop) 39 | 40 | if len(encodings) > 0: 41 | query = "INSERT INTO vectors (file, vec_low, vec_high) VALUES ('{}', CUBE(array[{}]), CUBE(array[{}]))".format( 42 | file_name, 43 | ','.join(str(s) for s in encodings[0][0:64]), 44 | ','.join(str(s) for s in encodings[0][64:128]), 45 | ) 46 | db.execute(query) 47 | 48 | cv2.imwrite("./.faces/aligned_face_{}_{}_crop.jpg".format(file_name.replace('/', '_'), i), crop) 49 | 50 | -------------------------------------------------------------------------------- /face-find.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import dlib 3 | import cv2 4 | import face_recognition 5 | import os 6 | import postgresql 7 | 8 | if len(sys.argv) < 2: 9 | print("Usage: face-find ") 10 | exit(1) 11 | 12 | # Take the image file name from the command line 13 | file_name = sys.argv[1] 14 | 15 | # Create a HOG face detector using the built-in dlib class 16 | face_detector = dlib.get_frontal_face_detector() 17 | 18 | # Load the image 19 | image = cv2.imread(file_name) 20 | 21 | # Run the HOG face detector on the image data 22 | detected_faces = face_detector(image, 1) 23 | 24 | print("Found {} faces in the image file {}".format(len(detected_faces), file_name)) 25 | 26 | if not os.path.exists("./.faces"): 27 | os.mkdir("./.faces") 28 | 29 | db = postgresql.open('pq://user:pass@localhost:5434/db') 30 | 31 | # Loop through each face we found in the image 32 | for i, face_rect in enumerate(detected_faces): 33 | # Detected faces are returned as an object with the coordinates 34 | # of the top, left, right and bottom edges 35 | print("- Face #{} found at Left: {} Top: {} Right: {} Bottom: {}".format(i, face_rect.left(), face_rect.top(), 36 | face_rect.right(), face_rect.bottom())) 37 | crop = image[face_rect.top():face_rect.bottom(), face_rect.left():face_rect.right()] 38 | 39 | encodings = face_recognition.face_encodings(crop) 40 | threshold = 0.6 41 | if len(encodings) > 0: 42 | query = "SELECT file FROM vectors WHERE sqrt(power(CUBE(array[{}]) <-> vec_low, 2) + power(CUBE(array[{}]) <-> vec_high, 2)) <= {} ".format( 43 | ','.join(str(s) for s in encodings[0][0:64]), 44 | ','.join(str(s) for s in encodings[0][64:128]), 45 | threshold, 46 | ) + \ 47 | "ORDER BY sqrt(power(CUBE(array[{}]) <-> vec_low, 2) + power(CUBE(array[{}]) <-> vec_high, 2)) <-> vec_high) ASC LIMIT 1".format( 48 | ','.join(str(s) for s in encodings[0][0:64]), 49 | ','.join(str(s) for s in encodings[0][64:128]), 50 | ) 51 | print(db.query(query)) 52 | else: 53 | print("No encodings") 54 | --------------------------------------------------------------------------------