diff --git a/deepface/commons/file_utils.py b/deepface/commons/image_utils.py similarity index 65% rename from deepface/commons/file_utils.py rename to deepface/commons/image_utils.py index f20ab50..c25e411 100644 --- a/deepface/commons/file_utils.py +++ b/deepface/commons/image_utils.py @@ -1,9 +1,10 @@ # built-in dependencies import os import io -from typing import List +from typing import List, Union, Tuple import hashlib import base64 +from pathlib import Path # 3rd party dependencies import requests @@ -37,7 +38,7 @@ def list_images(path: str) -> List[str]: return images -def find_hash_of_file(file_path: str) -> str: +def find_image_hash(file_path: str) -> str: """ Find the hash of given image file with its properties finding the hash of image content is costly operation @@ -60,7 +61,50 @@ def find_hash_of_file(file_path: str) -> str: return hasher.hexdigest() -def load_base64(uri: str) -> np.ndarray: +def load_image(img: Union[str, np.ndarray]) -> Tuple[np.ndarray, str]: + """ + Load image from path, url, base64 or numpy array. + Args: + img: a path, url, base64 or numpy array. + Returns: + image (numpy array): the loaded image in BGR format + image name (str): image name itself + """ + + # The image is already a numpy array + if isinstance(img, np.ndarray): + return img, "numpy array" + + if isinstance(img, Path): + img = str(img) + + if not isinstance(img, str): + raise ValueError(f"img must be numpy array or str but it is {type(img)}") + + # The image is a base64 string + if img.startswith("data:image/"): + return load_image_from_base64(img), "base64 encoded string" + + # The image is a url + if img.lower().startswith("http://") or img.lower().startswith("https://"): + return load_image_from_web(url=img), img + + # The image is a path + if os.path.isfile(img) is not True: + raise ValueError(f"Confirm that {img} exists") + + # image must be a file on the system then + + # image name must have english characters + if img.isascii() is False: + raise ValueError(f"Input image must not have non-english characters - {img}") + + img_obj_bgr = cv2.imread(img) + # img_obj_rgb = cv2.cvtColor(img_obj_bgr, cv2.COLOR_BGR2RGB) + return img_obj_bgr, img + + +def load_image_from_base64(uri: str) -> np.ndarray: """ Load image from base64 string. Args: diff --git a/deepface/modules/detection.py b/deepface/modules/detection.py index d509d60..ad4b288 100644 --- a/deepface/modules/detection.py +++ b/deepface/modules/detection.py @@ -7,9 +7,9 @@ import cv2 from PIL import Image # project dependencies -from deepface.modules import preprocessing from deepface.models.Detector import DetectedFace, FacialAreaRegion from deepface.detectors import DetectorWrapper +from deepface.commons import image_utils from deepface.commons import logger as log logger = log.get_singletonish_logger() @@ -63,7 +63,7 @@ def extract_faces( resp_objs = [] # img might be path, base64 or numpy array. Convert it to numpy whatever it is. - img, img_name = preprocessing.load_image(img_path) + img, img_name = image_utils.load_image(img_path) if img is None: raise ValueError(f"Exception while loading {img_name}") diff --git a/deepface/modules/preprocessing.py b/deepface/modules/preprocessing.py index 7c566be..459adba 100644 --- a/deepface/modules/preprocessing.py +++ b/deepface/modules/preprocessing.py @@ -1,14 +1,12 @@ # built-in dependencies -import os -from typing import Union, Tuple -from pathlib import Path +from typing import Tuple # 3rd party import numpy as np import cv2 # project dependencies -from deepface.commons import package_utils, file_utils +from deepface.commons import package_utils tf_major_version = package_utils.get_tf_major_version() @@ -18,49 +16,6 @@ elif tf_major_version == 2: from tensorflow.keras.preprocessing import image -def load_image(img: Union[str, np.ndarray]) -> Tuple[np.ndarray, str]: - """ - Load image from path, url, base64 or numpy array. - Args: - img: a path, url, base64 or numpy array. - Returns: - image (numpy array): the loaded image in BGR format - image name (str): image name itself - """ - - # The image is already a numpy array - if isinstance(img, np.ndarray): - return img, "numpy array" - - if isinstance(img, Path): - img = str(img) - - if not isinstance(img, str): - raise ValueError(f"img must be numpy array or str but it is {type(img)}") - - # The image is a base64 string - if img.startswith("data:image/"): - return file_utils.load_base64(img), "base64 encoded string" - - # The image is a url - if img.lower().startswith("http://") or img.lower().startswith("https://"): - return file_utils.load_image_from_web(url=img), img - - # The image is a path - if os.path.isfile(img) is not True: - raise ValueError(f"Confirm that {img} exists") - - # image must be a file on the system then - - # image name must have english characters - if img.isascii() is False: - raise ValueError(f"Input image must not have non-english characters - {img}") - - img_obj_bgr = cv2.imread(img) - # img_obj_rgb = cv2.cvtColor(img_obj_bgr, cv2.COLOR_BGR2RGB) - return img_obj_bgr, img - - def normalize_input(img: np.ndarray, normalization: str = "base") -> np.ndarray: """Normalize input image. diff --git a/deepface/modules/recognition.py b/deepface/modules/recognition.py index acbcfc8..0118634 100644 --- a/deepface/modules/recognition.py +++ b/deepface/modules/recognition.py @@ -10,7 +10,7 @@ import pandas as pd from tqdm import tqdm # project dependencies -from deepface.commons import file_utils +from deepface.commons import image_utils from deepface.modules import representation, detection, verification from deepface.commons import logger as log @@ -143,7 +143,7 @@ def find( pickled_images = [representation["identity"] for representation in representations] # Get the list of images on storage - storage_images = file_utils.list_images(path=db_path) + storage_images = image_utils.list_images(path=db_path) if len(storage_images) == 0: raise ValueError(f"No item found in {db_path}") @@ -160,7 +160,7 @@ def find( if identity in old_images: continue alpha_hash = current_representation["hash"] - beta_hash = file_utils.find_hash_of_file(identity) + beta_hash = image_utils.find_image_hash(identity) if alpha_hash != beta_hash: logger.debug(f"Even though {identity} represented before, it's replaced later.") replaced_images.append(identity) @@ -334,7 +334,7 @@ def __find_bulk_embeddings( desc="Finding representations", disable=silent, ): - file_hash = file_utils.find_hash_of_file(employee) + file_hash = image_utils.find_image_hash(employee) try: img_objs = detection.extract_faces( diff --git a/deepface/modules/representation.py b/deepface/modules/representation.py index dbaf1c7..9e8a1a6 100644 --- a/deepface/modules/representation.py +++ b/deepface/modules/representation.py @@ -5,6 +5,7 @@ from typing import Any, Dict, List, Union import numpy as np # project dependencies +from deepface.commons import image_utils from deepface.modules import modeling, detection, preprocessing from deepface.models.FacialRecognition import FacialRecognition @@ -74,7 +75,7 @@ def represent( ) else: # skip # Try load. If load error, will raise exception internal - img, _ = preprocessing.load_image(img_path) + img, _ = image_utils.load_image(img_path) if len(img.shape) != 3: raise ValueError(f"Input img must be 3 dimensional but it is {img.shape}") diff --git a/tests/test_find.py b/tests/test_find.py index 0edce59..83d9964 100644 --- a/tests/test_find.py +++ b/tests/test_find.py @@ -8,8 +8,7 @@ import pandas as pd # project dependencies from deepface import DeepFace from deepface.modules import verification -from deepface.modules import recognition -from deepface.commons import file_utils +from deepface.commons import image_utils from deepface.commons import logger as log logger = log.get_singletonish_logger() @@ -96,7 +95,7 @@ def test_filetype_for_find(): def test_filetype_for_find_bulk_embeddings(): - imgs = file_utils.list_images("dataset") + imgs = image_utils.list_images("dataset") assert len(imgs) > 0