Skip to content

Commit

Permalink
Merge pull request #959 from serengil/feat-task-1303-datastore-update
Browse files Browse the repository at this point in the history
some improvements
  • Loading branch information
serengil authored Jan 16, 2024
2 parents aa427d1 + fee33c8 commit 51bb180
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 84 deletions.
13 changes: 10 additions & 3 deletions deepface/basemodels/SFace.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@ class _Layer:
class SFaceModel:
def __init__(self, model_path):

self.model = cv.FaceRecognizerSF.create(
model=model_path, config="", backend_id=0, target_id=0
)
try:
self.model = cv.FaceRecognizerSF.create(
model=model_path, config="", backend_id=0, target_id=0
)
except Exception as err:
raise ValueError(
"Exception while calling opencv.FaceRecognizerSF module."
+ "This is an optional dependency."
+ "You can install it as pip install opencv-contrib-python."
) from err

self.layers = [_Layer()]

Expand Down
21 changes: 19 additions & 2 deletions deepface/detectors/OpenCvWrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,19 @@ def detect_face(detector: dict, img: np.ndarray, align: bool = True) -> list:
return resp


def align_face(eye_detector, img):
def align_face(eye_detector: Any, img: np.ndarray) -> np.ndarray:
"""
Align a given image with the pre-built eye_detector
Args:
eye_detector (Any): cascade classifier object
img (np.ndarray): given image
Returns:
aligned_img (np.ndarray)
"""
# if image has unexpectedly 0 dimension then skip alignment
if img.shape[0] == 0 or img.shape[1] == 0:
return img

detected_face_gray = cv2.cvtColor(
img, cv2.COLOR_BGR2GRAY
) # eye detector expects gray scale image
Expand Down Expand Up @@ -130,7 +142,12 @@ def align_face(eye_detector, img):
return img # return img anyway


def get_opencv_path():
def get_opencv_path() -> str:
"""
Returns where opencv installed
Returns:
installation_path (str)
"""
opencv_home = cv2.__file__
folders = opencv_home.split(os.path.sep)[0:-1]

Expand Down
15 changes: 11 additions & 4 deletions deepface/detectors/SsdWrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,17 @@ def build_model() -> dict:

gdown.download(url, output, quiet=False)

face_detector = cv2.dnn.readNetFromCaffe(
home + "/.deepface/weights/deploy.prototxt",
home + "/.deepface/weights/res10_300x300_ssd_iter_140000.caffemodel",
)
try:
face_detector = cv2.dnn.readNetFromCaffe(
home + "/.deepface/weights/deploy.prototxt",
home + "/.deepface/weights/res10_300x300_ssd_iter_140000.caffemodel",
)
except Exception as err:
raise ValueError(
"Exception while calling opencv.dnn module."
+ "This is an optional dependency."
+ "You can install it as pip install opencv-contrib-python."
) from err

eye_detector = OpenCvWrapper.build_cascade("haarcascade_eye")

Expand Down
12 changes: 11 additions & 1 deletion deepface/detectors/YunetWrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ def build_model() -> Any:
logger.info(f"{file_name} will be downloaded...")
output = home + f"/.deepface/weights/{file_name}"
gdown.download(url, output, quiet=False)
face_detector = cv2.FaceDetectorYN_create(home + f"/.deepface/weights/{file_name}", "", (0, 0))

try:
face_detector = cv2.FaceDetectorYN_create(
home + f"/.deepface/weights/{file_name}", "", (0, 0)
)
except Exception as err:
raise ValueError(
"Exception while calling opencv.FaceDetectorYN_create module."
+ "This is an optional dependency."
+ "You can install it as pip install opencv-contrib-python."
) from err
return face_detector


Expand Down
220 changes: 149 additions & 71 deletions deepface/modules/recognition.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def find(

file_name = f"representations_{model_name}.pkl"
file_name = file_name.replace("-", "_").lower()
datastore_path = f"{db_path}/{file_name}"

df_cols = [
"identity",
Expand All @@ -85,100 +86,93 @@ def find(
"target_h",
]

if os.path.exists(db_path + "/" + file_name):
if not silent:
logger.warn(
f"Representations for images in {db_path} folder were previously stored"
f" in {file_name}. If you added new instances after the creation, then please "
"delete this file and call find function again. It will create it again."
)

with open(f"{db_path}/{file_name}", "rb") as f:
if os.path.exists(datastore_path):
with open(datastore_path, "rb") as f:
representations = pickle.load(f)

if len(representations) > 0 and len(representations[0]) != len(df_cols):
raise ValueError(
f"Seems existing {db_path}/{file_name} is out-of-the-date."
"Delete it and re-run."
f"Seems existing {datastore_path} is out-of-the-date."
"Please delete it and re-run."
)

if not silent:
logger.info(f"There are {len(representations)} representations found in {file_name}")
alpha_employees = __list_images(path=db_path)
beta_employees = [representation[0] for representation in representations]

else: # create representation.pkl from scratch
employees = []

for r, _, f in os.walk(db_path):
for file in f:
if (
(".jpg" in file.lower())
or (".jpeg" in file.lower())
or (".png" in file.lower())
):
exact_path = r + "/" + file
employees.append(exact_path)
newbies = list(set(alpha_employees) - set(beta_employees))
oldies = list(set(beta_employees) - set(alpha_employees))

if len(employees) == 0:
raise ValueError(
"There is no image in ",
db_path,
" folder! Validate .jpg or .png files exist in this path.",
if newbies:
logger.warn(
f"Items {newbies} were added into {db_path}"
f" just after data source {datastore_path} created!"
)

# ------------------------
# find representations for db images

representations = []

# for employee in employees:
pbar = tqdm(
range(0, len(employees)),
desc="Finding representations",
disable=silent,
)
for index in pbar:
employee = employees[index]

img_objs = functions.extract_faces(
img=employee,
newbies_representations = __find_bulk_embeddings(
employees=newbies,
model_name=model_name,
target_size=target_size,
detector_backend=detector_backend,
grayscale=False,
enforce_detection=enforce_detection,
align=align,
normalization=normalization,
silent=silent,
)
representations = representations + newbies_representations

if oldies:
logger.warn(
f"Items {oldies} were dropped from {db_path}"
f" just after data source {datastore_path} created!"
)
representations = [rep for rep in representations if rep[0] not in oldies]

for img_content, img_region, _ in img_objs:
embedding_obj = representation.represent(
img_path=img_content,
model_name=model_name,
enforce_detection=enforce_detection,
detector_backend="skip",
align=align,
normalization=normalization,
if newbies or oldies:
if len(representations) == 0:
raise ValueError(f"There is no image in {db_path} anymore!")

# save new representations
with open(datastore_path, "wb") as f:
pickle.dump(representations, f)

if not silent:
logger.info(
f"{len(newbies)} new representations are just added"
f" whereas {len(oldies)} represented one(s) are just dropped"
f" in {db_path}/{file_name} file."
)

img_representation = embedding_obj[0]["embedding"]
if not silent:
logger.info(f"There are {len(representations)} representations found in {file_name}")

instance = []
instance.append(employee)
instance.append(img_representation)
instance.append(img_region["x"])
instance.append(img_region["y"])
instance.append(img_region["w"])
instance.append(img_region["h"])
representations.append(instance)
else: # create representation.pkl from scratch
employees = __list_images(path=db_path)

if len(employees) == 0:
raise ValueError(
f"There is no image in {db_path} folder!"
"Validate .jpg, .jpeg or .png files exist in this path.",
)

# ------------------------
# find representations for db images
representations = __find_bulk_embeddings(
employees=employees,
model_name=model_name,
target_size=target_size,
detector_backend=detector_backend,
enforce_detection=enforce_detection,
align=align,
normalization=normalization,
silent=silent,
)

# -------------------------------

with open(f"{db_path}/{file_name}", "wb") as f:
with open(datastore_path, "wb") as f:
pickle.dump(representations, f)

if not silent:
logger.info(
f"Representations stored in {db_path}/{file_name} file."
+ "Please delete this file when you add new identities in your database."
)
logger.info(f"Representations stored in {db_path}/{file_name} file.")

# ----------------------------
# now, we got representations for facial database
Expand Down Expand Up @@ -218,7 +212,7 @@ def find(
result_df["source_h"] = source_region["h"]

distances = []
for index, instance in df.iterrows():
for _, instance in df.iterrows():
source_representation = instance[f"{model_name}_representation"]

target_dims = len(list(target_representation))
Expand Down Expand Up @@ -266,3 +260,87 @@ def find(
logger.info(f"find function lasts {toc - tic} seconds")

return resp_obj


def __list_images(path: str) -> list:
"""
List images in a given path
Args:
path (str): path's location
Returns:
images (list): list of exact image paths
"""
images = []
for r, _, f in os.walk(path):
for file in f:
if file.lower().endswith((".jpg", ".jpeg", ".png")):
exact_path = f"{r}/{file}"
images.append(exact_path)
return images


def __find_bulk_embeddings(
employees: List[str],
model_name: str = "VGG-Face",
target_size: tuple = (224, 224),
detector_backend: str = "opencv",
enforce_detection: bool = True,
align: bool = True,
normalization: str = "base",
silent: bool = False,
):
"""
Find embeddings of a list of images
Args:
employees (list): list of exact image paths
model_name (str): facial recognition model name
target_size (tuple): expected input shape of facial
recognition model
detector_backend (str): face detector model name
enforce_detection (bool): set this to False if you
want to proceed when you cannot detect any face
align (bool): enable or disable alignment of image
before feeding to facial recognition model
normalization (bool): normalization technique
silent (bool): enable or disable informative logging
Returns:
representations (list): pivot list of embeddings with
image name and detected face area's coordinates
"""
representations = []
for employee in tqdm(
employees,
desc="Finding representations",
disable=silent,
):
img_objs = functions.extract_faces(
img=employee,
target_size=target_size,
detector_backend=detector_backend,
grayscale=False,
enforce_detection=enforce_detection,
align=align,
)

for img_content, img_region, _ in img_objs:
embedding_obj = representation.represent(
img_path=img_content,
model_name=model_name,
enforce_detection=enforce_detection,
detector_backend="skip",
align=align,
normalization=normalization,
)

img_representation = embedding_obj[0]["embedding"]

instance = []
instance.append(employee)
instance.append(img_representation)
instance.append(img_region["x"])
instance.append(img_region["y"])
instance.append(img_region["w"])
instance.append(img_region["h"])
representations.append(instance)
return representations
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

setuptools.setup(
name="deepface",
version="0.0.81",
version="0.0.82",
author="Sefik Ilkin Serengil",
author_email="serengil@gmail.com",
description="A Lightweight Face Recognition and Facial Attribute Analysis Framework (Age, Gender, Emotion, Race) for Python",
data_files=[('', ['README.md', 'requirements.txt'])],
data_files=[("", ["README.md", "requirements.txt"])],
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/serengil/deepface",
Expand Down
Loading

0 comments on commit 51bb180

Please sign in to comment.