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()
@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