日々適当

hibitekitou

顔の回転角度の推定

cg |2022-06-17

ってのをしばらくやっているのですけど、よくわからん。いや、数式的なところはとりあえず置いておいて(俺には理解できないと思うw)、openCVを使ってやってみたりとか云々。

花畑に座っている女性 · 無料の写真素材[Pexels] を使って、OpenCV の solvepnp()関数を使用して PnP の問題を解決する [Delft スタック] という解説をそのまま適用したら当然ちゃんとした結果が表示されました。
ちなみに画像上の特徴点はPhotoshopで手動で拾ってます。

2D上の特徴点に対応する元となる3Dのメッシュの頂点座標の値が重要なんだと思います。(つまり、顔の3D空間での形をできるだけ正しく数値で持っておく必要がある)

で、世の解説記事で見かける、無料で配布されている68点の特徴点で学習させた結果を使ってリアルタイムで顔の向きを推定させる記事に書いてる顔の形状の数値を使って、先の記事のコードの特徴点情報を書き換えたのが下です。

import cv2
import numpy as np

img = cv2.imread('woman-portrait-meadow-dandelions-157604.jpeg')
size = img.shape
image_points = np.array(   [[1066, 423], # 27 left brow left corner
			[1011,433], # 23 left brow right corner
			[977,436], # 22 right brow left corner
			[946,454], # 18 right brow right corner
			[1070,445], # 46 left eye left corner
			[1025,458], # 43 left eye right corner
			[983, 466], # 40 right eye left corner
			[955,478], # 37 right eye right corner
			[1021,496], # 36 nose left corner
			[982,506], # 32 nose right corner
			[1042,538], # 55 mouth left corner
			[982,549], # 49 mouth right corner
			[1006,563], # 58 mouth central bottom corner
			[1006,591] ] ,# 6 chin corner
			 dtype=np.float32)

# https://pyimagesearch.com/2017/04/03/facial-landmarks-dlib-opencv-python/
# https://myuon.github.io/posts/juniq/
object_points = np.array(   [[6.825897, 6.760612, 4.402142], # 27 left brow left corner
			[1.330353, 7.122144, 6.903745], # 23 left brow right corner
			[-1.330353, 7.122144, 6.903745], # 22 right brow left corner
			[-6.825897, 6.760612, 4.402142], # 18 right brow right corner
			[5.311432, 5.485328, 3.987654], # 46 left eye left corner
			[1.789930, 5.393625, 4.413414], # 43 left eye right corner
			[-1.789930, 5.393625, 4.413414], # 40 right eye left corner
			[-5.311432, 5.485328, 3.987654], # 37 right eye right corner
			[2.005628, 1.409845, 6.165652], # 36 nose left corner
			[-2.005628, 1.409845, 6.165652], # 32 nose right corner
			[2.774015, -2.080775, 5.048531], # 55 mouth left corner
			[-2.774015, -2.080775, 5.048531], # 49 mouth right corner
			[0.000000, -3.116408, 6.097667], # 58 mouth central bottom corner
			[0.000000, -7.415691, 4.070434] ] , # 6 chin corner
			dtype=np.float32)									
object_points = 10 * object_points #スケールを10倍しないといい感じにならない。mm単位かな。

chainPos = 10* np.array( [0.000000, -7.415691, 4.070434] )				
										 
distortion_coeffs = np.zeros(( 4, 1 ))
focal_length = size[1]

camera_matrix = np.array([ [focal_length, 0, size[1]//2 ],
			 [ 0, focal_length, size[0]//2 ],
			 [ 0, 0, 1] ], dtype = 'double')
										  
success, rotation_vector, translation_vector = cv2.solvePnP( object_points, image_points,  
			 camera_matrix, distortion_coeffs, 
			 flags=cv2.SOLVEPNP_ITERATIVE )

zero  , jacobian = cv2.projectPoints( chainPos , rotation_vector, 
			  translation_vector, camera_matrix, distortion_coeffs )
nose_end_point2D500 , jacobian = cv2.projectPoints( np.array(  [ 0., 0. , 500.] ) + chainPos, 
			rotation_vector, translation_vector, camera_matrix, distortion_coeffs )

i = 0
for p in image_points:
	cv2.circle( img, ( int( p[0] ) , int( p[1] ) ) , 7 , ( i , 0, 0 ) , -1 )
	i += 20
	
p1 = ( int( zero[0][0][0] ) , int( zero[0][0][1] )  )
p3 = ( int( nose_end_point2D500[0][0][0] ) , int( nose_end_point2D500[0][0][1] )  )

cv2.line( img, p1, p3, (0, 255, 255 ), 2 )

cv2.imshow( "RESULT", img )
cv2.waitKey(0)
cv2.destroyAllWindows()

なんとなくうまくいっているようにも見えます。アゴから顔の向きの棒が伸びるようにしていますが、ちょっと起点がずれてますね。アゴの位置を image_points から拾うのではなく、solvePnP で取得したrotation_vector, translation_vector でアゴの頂点座標を2D上にマッピングしているせいです(多分)。元となるメッシュの座標がこの女性の形状と一致していない、ということなのだと思います。けど、悪くはない、ような気がします。

コメント ( 0 )|Trackback ( )
  ・