FaceAligner: added 'head_mode', can be used with HEAD models.

This commit is contained in:
iperov 2021-08-07 19:16:25 +04:00
commit 72166cc190
11 changed files with 177 additions and 40 deletions

View file

@ -37,6 +37,7 @@ class FaceAlignerWorker(BackendWorker):
cs.face_coverage.call_on_number(self.on_cs_face_coverage)
cs.resolution.call_on_number(self.on_cs_resolution)
cs.exclude_moving_parts.call_on_flag(self.on_cs_exclude_moving_parts)
cs.head_mode.call_on_flag(self.on_cs_head_mode)
cs.x_offset.call_on_number(self.on_cs_x_offset)
cs.y_offset.call_on_number(self.on_cs_y_offset)
@ -50,6 +51,9 @@ class FaceAlignerWorker(BackendWorker):
cs.exclude_moving_parts.enable()
cs.exclude_moving_parts.set_flag(state.exclude_moving_parts if state.exclude_moving_parts is not None else True)
cs.head_mode.enable()
cs.head_mode.set_flag(state.head_mode if state.head_mode is not None else False)
cs.x_offset.enable()
cs.x_offset.set_config(lib_csw.Number.Config(min=-1, max=1, step=0.01, decimals=2, allow_instant_update=True))
@ -82,6 +86,12 @@ class FaceAlignerWorker(BackendWorker):
self.save_state()
self.reemit_frame_signal.send()
def on_cs_head_mode(self, head_mode):
state, cs = self.get_state(), self.get_control_sheet()
state.head_mode = head_mode
self.save_state()
self.reemit_frame_signal.send()
def on_cs_x_offset(self, x_offset):
state, cs = self.get_state(), self.get_control_sheet()
cfg = cs.x_offset.get_config()
@ -113,13 +123,20 @@ class FaceAlignerWorker(BackendWorker):
if all_is_not_None(state.face_coverage, state.resolution, frame_name, frame_image):
for face_id,face_mark in enumerate( bcd.get_face_mark_list() ):
face_ulmrks = face_mark.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_2D_468)
face_ulmrks = face_mark.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_468)
if face_ulmrks is None:
face_ulmrks = face_mark.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_2D_68)
face_ulmrks = face_mark.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_68)
head_yaw = None
if state.head_mode:
face_pose = face_mark.get_face_pose()
if face_pose is not None:
head_yaw = face_pose.as_radians()[1]
if face_ulmrks is not None:
face_image, uni_mat = face_ulmrks.cut(frame_image, state.face_coverage, state.resolution,
exclude_moving_parts=state.exclude_moving_parts,
head_yaw=head_yaw,
x_offset=state.x_offset,
y_offset=state.y_offset)
@ -155,6 +172,7 @@ class Sheet:
self.face_coverage = lib_csw.Number.Client()
self.resolution = lib_csw.Number.Client()
self.exclude_moving_parts = lib_csw.Flag.Client()
self.head_mode = lib_csw.Flag.Client()
self.x_offset = lib_csw.Number.Client()
self.y_offset = lib_csw.Number.Client()
@ -164,6 +182,7 @@ class Sheet:
self.face_coverage = lib_csw.Number.Host()
self.resolution = lib_csw.Number.Host()
self.exclude_moving_parts = lib_csw.Flag.Host()
self.head_mode = lib_csw.Flag.Host()
self.x_offset = lib_csw.Number.Host()
self.y_offset = lib_csw.Number.Host()
@ -171,5 +190,6 @@ class WorkerState(BackendWorkerState):
face_coverage : float = None
resolution : int = None
exclude_moving_parts : bool = None
head_mode : bool = None
x_offset : float = None
y_offset : float = None

View file

@ -4,9 +4,8 @@ import numpy as np
from modelhub import onnx as onnx_models
from modelhub import cv as cv_models
from xlib import cv as lib_cv
from xlib import os as lib_os
from xlib.facemeta import FaceULandmarks
from xlib.facemeta import FaceULandmarks, FacePose
from xlib.image import ImageProcessor
from xlib.mp import csw as lib_csw
from xlib.python import all_is_not_None
@ -15,7 +14,6 @@ from .BackendBase import (BackendConnection, BackendDB, BackendHost,
BackendSignal, BackendWeakHeap, BackendWorker,
BackendWorkerState)
class MarkerType(IntEnum):
OPENCV_LBF = 0
GOOGLE_FACEMESH = 1
@ -175,9 +173,7 @@ class FaceMarkerWorker(BackendWorker):
if is_opencv_lbf:
lmrks = self.opencv_lbf.extract(face_image)[0]
elif is_google_facemesh:
lmrks = self.google_facemesh.extract(face_image)[0][...,0:2]
lmrks /= (W,H)
lmrks = self.google_facemesh.extract(face_image)[0]
if marker_state.temporal_smoothing != 1:
if not is_frame_reemitted or len(self.temporal_lmrks[face_id]) == 0:
@ -186,11 +182,21 @@ class FaceMarkerWorker(BackendWorker):
lmrks = np.mean(self.temporal_lmrks[face_id],0 )
face_ulmrks = FaceULandmarks.create (FaceULandmarks.Type.LANDMARKS_2D_68 if is_opencv_lbf else \
FaceULandmarks.Type.LANDMARKS_2D_468 if is_google_facemesh else None, lmrks)
if is_google_facemesh:
face_mark.set_face_pose(FacePose.from_3D_468_landmarks(lmrks))
if is_opencv_lbf:
lmrks /= (W,H)
elif is_google_facemesh:
lmrks = lmrks[...,0:2] / (W,H)
face_ulmrks = FaceULandmarks.create (FaceULandmarks.Type.LANDMARKS_68 if is_opencv_lbf else \
FaceULandmarks.Type.LANDMARKS_468 if is_google_facemesh else None, lmrks)
face_ulmrks = face_ulmrks.transform(face_uni_mat, invert=True)
face_mark.add_face_ulandmarks (face_ulmrks)
self.stop_profile_timing()
self.pending_bcd = bcd

View file

@ -24,6 +24,9 @@ class QFaceAligner(QBackendPanel):
q_exclude_moving_parts_label = QLabelPopupInfo(label=L('@QFaceAligner.exclude_moving_parts'), popup_info_text=L('@QFaceAligner.help.exclude_moving_parts') )
q_exclude_moving_parts = QCheckBoxCSWFlag(cs.exclude_moving_parts, reflect_state_widgets=[q_exclude_moving_parts_label])
q_head_mode_label = QLabelPopupInfo(label=L('@QFaceAligner.head_mode'), popup_info_text=L('@QFaceAligner.help.head_mode') )
q_head_mode = QCheckBoxCSWFlag(cs.head_mode, reflect_state_widgets=[q_head_mode_label])
q_x_offset_label = QLabelPopupInfo(label=L('@QFaceAligner.x_offset'))
q_x_offset = QSpinBoxCSWNumber(cs.x_offset, reflect_state_widgets=[q_x_offset_label])
@ -41,6 +44,9 @@ class QFaceAligner(QBackendPanel):
grid_l.addWidget(q_exclude_moving_parts_label, row, 0, alignment=Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter )
grid_l.addWidget(q_exclude_moving_parts, row, 1, alignment=Qt.AlignmentFlag.AlignLeft )
row += 1
grid_l.addWidget(q_head_mode_label, row, 0, alignment=Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter )
grid_l.addWidget(q_head_mode, row, 1, alignment=Qt.AlignmentFlag.AlignLeft )
row += 1
grid_l.addLayout( lib_qt.QXVBoxLayout([q_x_offset_label, q_y_offset_label]), row, 0, alignment=Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter )
grid_l.addLayout( lib_qt.QXHBoxLayout([q_x_offset, q_y_offset]), row, 1, alignment=Qt.AlignmentFlag.AlignLeft )
row += 1

View file

@ -57,9 +57,9 @@ class QBCFaceAlignViewer(lib_qt.QXCollapsibleSection):
if all_is_not_None(face_align_image_name):
face_image = bcd.get_image(face_align_image_name).copy()
face_ulmrks = face_align.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_2D_468)
face_ulmrks = face_align.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_468)
if face_ulmrks is None:
face_ulmrks = face_align.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_2D_68)
face_ulmrks = face_align.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_68)
if face_ulmrks is not None:
lmrks_layer = np.zeros( (self._preview_width, self._preview_width, 4), dtype=np.uint8)