Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify and strengthen rotate_facial_area #1047

Merged
61 changes: 43 additions & 18 deletions deepface/detectors/DetectorWrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,30 +103,45 @@ def detect_faces(
right_eye = facial_area.right_eye
confidence = facial_area.confidence

# expand the facial area to be extracted and stay within img.shape limits
x2 = max(0, x - int((w * expand_percentage) / 100)) # expand left
y2 = max(0, y - int((h * expand_percentage) / 100)) # expand top
w2 = min(img.shape[1], w + int((w * 2 * expand_percentage) / 100)) # expand right
h2 = min(img.shape[0], h + int((h * 2 * expand_percentage) / 100)) # expand bottom
if expand_percentage > 0:
# Uncomment this if you want to :
# Expand the facial area to be extracted and recompute the height and width
# keeping the same aspect ratio and ensuring that the expanded area stays
# within img.shape limits

# current_area = w * h
# expanded_area = current_area + int((current_area * expand_percentage) / 100)
# scale_factor = math.sqrt(expanded_area / current_area)
# expanded_w = int(w * scale_factor)
# expanded_h = int(h * scale_factor)

# Or uncomment this if you want to :
# Expand the facial region height and width by the provided percentage
# ensuring that the expanded region stays within img.shape limits
expanded_w = int(w * expand_percentage / 100)
expanded_h = int(h * expand_percentage / 100)

x = max(0, x - int((expanded_w - w) / 2))
y = max(0, y - int((expanded_h - h) / 2))
w = min(img.shape[1] - x, expanded_w)
h = min(img.shape[0] - y, expanded_h)

# extract detected face unaligned
detected_face = img[int(y2) : int(y2 + h2), int(x2) : int(x2 + w2)]

# aligning detected face causes a lot of black pixels
# if align is True:
# detected_face, _ = detection.align_face(
# img=detected_face, left_eye=left_eye, right_eye=right_eye
# )
detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]

# align original image, then find projection of detected face area after alignment
if align is True: # and left_eye is not None and right_eye is not None:
aligned_img, angle = detection.align_face(
img=img, left_eye=left_eye, right_eye=right_eye
)
x1_new, y1_new, x2_new, y2_new = rotate_facial_area(
facial_area=(x2, y2, x2 + w2, y2 + h2), angle=angle, direction=1, size=img.shape
rotated_x1, rotated_y1, rotated_x2, rotated_y2 = rotate_facial_area(
facial_area=(x, y, x + w, y + h),
angle=angle,
size=(img.shape[0], img.shape[1])
)
detected_face = aligned_img[int(y1_new) : int(y2_new), int(x1_new) : int(x2_new)]
detected_face = aligned_img[
int(rotated_y1) : int(rotated_y2),
int(rotated_x1) : int(rotated_x2)]

result = DetectedFace(
img=detected_face,
Expand All @@ -140,7 +155,9 @@ def detect_faces(


def rotate_facial_area(
facial_area: Tuple[int, int, int, int], angle: float, direction: int, size: Tuple[int, int]
facial_area: Tuple[int, int, int, int],
angle: float, # in degrees. The sign determines the direction of rotation
size: Tuple[int, int] # (width, height)
) -> Tuple[int, int, int, int]:
"""
Rotate the facial area around its center.
Expand All @@ -149,14 +166,22 @@ def rotate_facial_area(
Args:
facial_area (tuple of int): Representing the (x1, y1, x2, y2) of the facial area.
x2 is equal to x1 + w1, and y2 is equal to y1 + h1
angle (float): Angle of rotation in degrees.
direction (int): Direction of rotation (-1 for clockwise, 1 for counterclockwise).
angle (float): Angle of rotation in degrees. Its sign determines the direction of rotation.
Note that angles > 360 degrees are normalized to the range [0, 360).
size (tuple of int): Tuple representing the size of the image (width, height).

Returns:
rotated_coordinates (tuple of int): Representing the new coordinates
(x1, y1, x2, y2) or (x1, y1, x1+w1, y1+h1) of the rotated facial area.
"""

# Normalize the witdh of the angle so we don't have to
# worry about rotations greater than 360 degrees
angle = angle % 360
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

angle will be positive always because of the module, so you can get rid of the direction because it will be 1 always

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not right.
75 % 360 == 75
-75 % 360 == -75

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my experiment.

Python 3.9.16 (main, Jan 11 2023, 16:05:54)
[GCC 11.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = -75
>>> a % 360
285
>>>
>>>
>>> b = 75
>>> b % 360

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still, both outputs will work as is

never mind this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch ... python mod operator is pretty fucked up. Every other language and calculators keeps the sign of dividend not the divisor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

if angle == 0:
return facial_area # No rotation needed
direction = 1 if angle > 0 else -1

# Angle in radians
angle = angle * np.pi / 180

Expand Down
Loading