Skip to content

Instantly share code, notes, and snippets.

@zalo
Last active August 16, 2022 06:30
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zalo/71fbd5dbfe23cb46406d211b84be9f7e to your computer and use it in GitHub Desktop.
Save zalo/71fbd5dbfe23cb46406d211b84be9f7e to your computer and use it in GitHub Desktop.
This is an example using Adrian Bulat's face_alignment library and Python to draw the head's basis vectors
import numpy as np
import cv2
import face_alignment
# Initialize the face alignment tracker
fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._3D, flip_input=True, device="cuda")
# Start the webcam capture, exit with 'q'
cap = cv2.VideoCapture(0)
while(not (cv2.waitKey(1) & 0xFF == ord('q'))):
ret, frame = cap.read()
if(ret):
# Clear the indices frame
canonical = np.zeros(frame.shape)
# Run the face alignment tracker on the webcam image
imagePoints = fa.get_landmarks_from_image(frame)
if(imagePoints is not None):
imagePoints = imagePoints[0]
# Compute the Mean-Centered-Scaled Points
mean = np.mean(imagePoints, axis=0) # <- This is the unscaled mean
scaled = (imagePoints / np.linalg.norm(imagePoints[42] - imagePoints[39])) * 0.06 # Set the inner eye distance to 60cm (just because)
centered = scaled - np.mean(scaled, axis=0) # <- This is the scaled mean
# Construct a "rotation" matrix (strong simplification, might have shearing)
rotationMatrix = np.empty((3,3))
rotationMatrix[0,:] = (centered[16] - centered[0])/np.linalg.norm(centered[16] - centered[0])
rotationMatrix[1,:] = (centered[8] - centered[27])/np.linalg.norm(centered[8] - centered[27])
rotationMatrix[2,:] = np.cross(rotationMatrix[0, :], rotationMatrix[1, :])
invRot = np.linalg.inv(rotationMatrix)
# Object-space points, these are what you'd run OpenCV's solvePnP() with
objectPoints = centered.dot(invRot)
# Draw the computed data
for i, (imagePoint, objectPoint) in enumerate(zip(imagePoints, objectPoints)):
# Draw the Point Predictions
cv2.circle(frame, (imagePoint[0], imagePoint[1]), 3, (0,255,0))
# Draw the X Axis
cv2.line(frame, tuple(mean[:2].astype(int)),
tuple((mean+(rotationMatrix[0,:] * 100.0))[:2].astype(int)), (0, 0, 255), 3)
# Draw the Y Axis
cv2.line(frame, tuple(mean[:2].astype(int)),
tuple((mean-(rotationMatrix[1,:] * 100.0))[:2].astype(int)), (0, 255, 0), 3)
# Draw the Z Axis
cv2.line(frame, tuple(mean[:2].astype(int)),
tuple((mean+(rotationMatrix[2,:] * 100.0))[:2].astype(int)), (255, 0, 0), 3)
# Draw the indices in Object Space
cv2.putText(canonical, str(i),
((int)((objectPoint[0] * 1000.0) + 320.0),
(int)((objectPoint[1] * 1000.0) + 240.0)),
cv2.FONT_HERSHEY_SIMPLEX, 0.3, (255, 255, 255), 1, cv2.LINE_AA)
cv2.imshow('Webcam View', frame)
cv2.imshow('Canonical View', canonical)
# When everything is done, release the capture
cap.release()
cv2.destroyAllWindows()
@yogurt7771
Copy link

Hi,
Thanks for your reply.
Sorry for late response.

import numpy as np
import cv2
import face_alignment
import math
def rotationMatrixToEulerAngles(R) :
sy = math.sqrt(R[0,0] * R[0,0] + R[1,0] * R[1,0])
singular = sy < 1e-6
if not singular :
x = math.atan2(R[2,1] , R[2,2])
y = math.atan2(-R[2,0], sy)
z = math.atan2(R[1,0], R[0,0])
else :
x = math.atan2(-R[1,2], R[1,1])
y = math.atan2(-R[2,0], sy)
z = 0
return np.array([x, y, z])
#Initialize the face alignment tracker
fa = face_alignment.FaceAlignment(face_alignment.LandmarksType.3D, flip_input=True, device="cpu") frame=cv2.imread("model9.jpg") #("model9.jpg") canonical = np.zeros(frame.shape) #Run the face alignment tracker on the image #imagePoints = fa.get_landmarks_from_image(frame) imagePoints = fa.get_landmarks(frame) if(imagePoints is not None): imagePoints = imagePoints[0] # Compute the Mean-Centered-Scaled Points mean = np.mean(imagePoints, axis=0) # <- This is the unscaled mean scaled = (imagePoints / np.linalg.norm(imagePoints[42] - imagePoints[39])) * 0.06 # Set the inner eye distance to 60cm (just because) centered = scaled - np.mean(scaled, axis=0) # <- This is the scaled mean # Construct a "rotation" matrix (strong simplification, might have shearing) rotationMatrix = np.empty((3,3)) rotationMatrix[0,:] = (centered[16] - centered[0])/np.linalg.norm(centered[16] - centered[0]) rotationMatrix[1,:] = (centered[8] - centered[27])/np.linalg.norm(centered[8] - centered[27]) rotationMatrix[2,:] = np.cross(rotationMatrix[0, :], rotationMatrix[1, :]) eulerAngles=rotationMatrixToEulerAngles(rotationMatrix) pitch, yaw, roll = [math.radians()*100.0 for _ in eulerAngles]
pitch = math.degrees(math.asin(math.sin(pitch)))
roll = -math.degrees(math.asin(math.sin(roll)))
yaw = math.degrees(math.asin(math.sin(yaw)))

rotate_degree=[ str(int(pitch)), str(int(roll)),str(int(yaw))]

rotate_degree=[math.floor(pitch),math.floor(roll),math.floor(yaw)]
print ("Rotate_Degree:",rotate_degree)
cv2.imshow('Webcam View', frame)
cv2.imshow('Canonical View', canonical)
cv2.waitKey(0)

With the above code, if i print rotate_degree giving result as Rotate_Degree: ['16', '44', '-20'], where as from https://azure.microsoft.com/en-us/services/cognitive-services/face/#demo link getting values for pitch, roll, yaw as like this "headPose": { "pitch": 6.0, "roll": 24.7, "yaw": 11.1 }
What should i modify to get the results??

My model9.jpg image is
model9

Thanks in Advance.
Regards,
Sandhyalaxmi Kanna

        rotationMatrix = np.empty((3, 3))
        rotationMatrix[0, :] = (centered[16] - centered[0]) / np.linalg.norm(centered[16] - centered[0])
        rotationMatrix[1, :] = (centered[8] - centered[27]) / np.linalg.norm(centered[8] - centered[27])
        rotationMatrix[2, :] = np.cross(rotationMatrix[0, :], rotationMatrix[1, :])

        eulerAngles = rotationMatrixToEulerAngles(rotationMatrix)
        pitch, yaw, roll = eulerAngles
        pitch = np.degrees(np.arcsin(np.sin(pitch)))
        roll = -np.degrees(np.arcsin(np.sin(roll)))
        yaw = -np.degrees(np.arcsin(np.sin(yaw)))

@i-amgeek
Copy link

i-amgeek commented Feb 4, 2021

Thanks @patrickwangqy. It is working very well.

@iperov
Copy link

iperov commented Aug 7, 2021

np.arcsin(np.sin(pitch))

why?

@yogurt7771
Copy link

np.arcsin(np.sin(pitch))

why?

angle range

@iperov
Copy link

iperov commented Aug 9, 2021

you clip it with -90+90 deg? but what if actual YAW > 90 deg ?

@yogurt7771
Copy link

it is impossible

@iperov
Copy link

iperov commented Aug 16, 2021

possible.
12280

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment