Spaces:
Running
Running
File size: 5,831 Bytes
59ece0e 0347340 59ece0e 54e742e 59ece0e 54e742e 59ece0e 54e742e 59ece0e 54e742e 59ece0e 54e742e cd9af60 54e742e 59ece0e 988dde3 0f7be63 59ece0e 0347340 988dde3 c3143ea 0347340 59ece0e 54e742e 59ece0e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
import cv2
import numpy as np
from src.cv_utils import get_image, resize_image_height
from typing import Tuple, List, Union
from skimage.metrics import structural_similarity as ssim
from scipy.spatial import distance
from sklearn.metrics import mean_squared_error, mean_absolute_error
from PIL import Image as PILImage
import yaml
with open("parameters.yml", "r") as stream:
try:
parameters = yaml.safe_load(stream)
except yaml.YAMLError as exc:
print(exc)
class GetFaceSymmetry:
def __init__(self):
pass
def get_faces(self, image: np.array) -> np.array:
self.h, self.w = image.shape[:2]
blob = cv2.dnn.blobFromImage(image=image, scalefactor=1.0, size=(300, 300))
face_detector_net = cv2.dnn.readNetFromCaffe(
parameters["face_detection"]["config"],
parameters["face_detection"]["model"],
)
face_detector_net.setInput(blob)
face_detections = face_detector_net.forward()
return face_detections
@staticmethod
def postprocess_face(face: np.array) -> np.array:
face = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)
face = cv2.equalizeHist(face) # remove illumination
face = cv2.GaussianBlur(face, (5, 5), 0) # remove noise
return face
@staticmethod
def get_face_halves(face: np.array) -> Tuple:
mid = face.shape[1] // 2
left_half = face[:, :mid]
right_half = face[:, mid:]
right_half = cv2.resize(right_half, (left_half.shape[1], left_half.shape[0]))
right_half = cv2.flip(right_half, 1)
return left_half, right_half
@staticmethod
def histogram_performance(
left_half: np.array, right_half: np.array
) -> List[Union[float, float, float, float]]:
hist_left = cv2.calcHist([left_half], [0], None, [256], [0, 256])
hist_right = cv2.calcHist([right_half], [0], None, [256], [0, 256])
# Normalize histograms
hist_left /= hist_left.sum()
hist_right /= hist_right.sum()
correlation = cv2.compareHist(hist_left, hist_right, cv2.HISTCMP_CORREL)
chi_square = cv2.compareHist(hist_left, hist_right, cv2.HISTCMP_CHISQR)
intersection = cv2.compareHist(hist_left, hist_right, cv2.HISTCMP_INTERSECT)
bhattacharyya = cv2.compareHist(
hist_left, hist_right, cv2.HISTCMP_BHATTACHARYYA
)
return correlation, chi_square, intersection, bhattacharyya
@staticmethod
def orb_detector(left_half: np.array, right_half: np.array) -> int:
"""The fewer the matches (or the greater the average distance), the more dissimilar the images"""
orb = cv2.ORB_create()
keypoints_left, descriptors_left = orb.detectAndCompute(left_half, None)
keypoints_right, descriptors_right = orb.detectAndCompute(right_half, None)
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(descriptors_left, descriptors_right)
matches = sorted(matches, key=lambda x: x.distance)
return len(matches)
def get_face_similarity_results(
self, left_half: np.array, right_half: np.array
) -> dict:
structural_similarity, _ = ssim(left_half, right_half, full=True)
cosine_distance = distance.cosine(left_half.ravel(), right_half.ravel())
mse = mean_squared_error(left_half, right_half)
mae = mean_absolute_error(left_half, right_half)
(
correlation,
chi_square,
intersection,
bhattacharyya,
) = self.histogram_performance(left_half, right_half)
matches = self.orb_detector(left_half, right_half)
pixel_difference = np.sum((left_half - right_half) ** 2)
d = {
"structural_similarity": structural_similarity,
"cosine_distance": cosine_distance,
"mse": mse,
"mae": mae,
"histogram_correlation": correlation,
"histogram_intersection": intersection,
"orb_detector_matches": matches,
"pixel_difference": pixel_difference,
}
return d
def main(self, image_input) -> Tuple:
image = get_image(image_input)
face_detections = self.get_faces(image)
lowest_mse = float("inf")
best_face_data, best_left_half, best_right_half = None, None, None
for i in range(0, face_detections.shape[2]):
confidence = face_detections[0, 0, i, 2]
if confidence > 0.98:
box = face_detections[0, 0, i, 3:7] * np.array(
[self.w, self.h, self.w, self.h]
)
(startX, startY, endX, endY) = box.astype("int")
face = image[startY:endY, startX:endX]
if (
face.shape[0] != 0
): # temp fix bug where image of dim (0, 0, 3) appear
face = self.postprocess_face(face)
left_half, right_half = self.get_face_halves(face)
d = self.get_face_similarity_results(left_half, right_half)
if d["mse"] < lowest_mse:
best_face_data, best_left_half, best_right_half = (
d,
left_half,
right_half,
)
lowest_mse = d["mse"]
full_face = np.hstack((best_left_half, best_right_half))
full_face_image = PILImage.fromarray(full_face)
full_face_image = resize_image_height(full_face_image, new_height=300)
best_face_data = {k: float(round(v, 2)) for k, v in best_face_data.items()}
return full_face_image, best_face_data
if __name__ == "__main__":
image_path = "data/gigi_hadid.webp"
results = GetFaceSymmetry().main(image_path)
print(results)
|