mirror of
https://github.com/iperov/DeepFaceLab.git
synced 2025-07-06 13:02:15 -07:00
Manual extractor: now you can specify face rectangle manually using ‘R Mouse button’.
It is useful for small, blurry, undetectable faces, animal faces. https://i.imgur.com/8kmVgg8.jpg Warning: such frames must be used only with XSeg workflow ! Landmarks cannot be placed on the face precisely, and they are actually used for positioning the red frame. Try to keep the red frame the same as the adjacent frames.
This commit is contained in:
parent
e77865ce18
commit
f91a604c6d
1 changed files with 95 additions and 56 deletions
|
@ -10,6 +10,7 @@ from pathlib import Path
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from numpy import linalg as npla
|
||||||
|
|
||||||
import facelib
|
import facelib
|
||||||
from core import imagelib
|
from core import imagelib
|
||||||
|
@ -358,6 +359,7 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
self.cache_text_lines_img = (None, None)
|
self.cache_text_lines_img = (None, None)
|
||||||
self.hide_help = False
|
self.hide_help = False
|
||||||
self.landmarks_accurate = True
|
self.landmarks_accurate = True
|
||||||
|
self.force_landmarks = False
|
||||||
|
|
||||||
self.landmarks = None
|
self.landmarks = None
|
||||||
self.x = 0
|
self.x = 0
|
||||||
|
@ -397,7 +399,6 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
def get_data(self, host_dict):
|
def get_data(self, host_dict):
|
||||||
if self.type == 'landmarks-manual':
|
if self.type == 'landmarks-manual':
|
||||||
need_remark_face = False
|
need_remark_face = False
|
||||||
redraw_needed = False
|
|
||||||
while len (self.input_data) > 0:
|
while len (self.input_data) > 0:
|
||||||
data = self.input_data[0]
|
data = self.input_data[0]
|
||||||
filepath, data_rects, data_landmarks = data.filepath, data.rects, data.landmarks
|
filepath, data_rects, data_landmarks = data.filepath, data.rects, data.landmarks
|
||||||
|
@ -410,11 +411,12 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
self.landmarks = data_landmarks.pop()
|
self.landmarks = data_landmarks.pop()
|
||||||
data_rects.clear()
|
data_rects.clear()
|
||||||
data_landmarks.clear()
|
data_landmarks.clear()
|
||||||
redraw_needed = True
|
|
||||||
self.rect_locked = True
|
self.rect_locked = True
|
||||||
self.rect_size = ( self.rect[2] - self.rect[0] ) / 2
|
self.rect_size = ( self.rect[2] - self.rect[0] ) / 2
|
||||||
self.x = ( self.rect[0] + self.rect[2] ) / 2
|
self.x = ( self.rect[0] + self.rect[2] ) / 2
|
||||||
self.y = ( self.rect[1] + self.rect[3] ) / 2
|
self.y = ( self.rect[1] + self.rect[3] ) / 2
|
||||||
|
self.redraw()
|
||||||
|
|
||||||
if len(data_rects) == 0:
|
if len(data_rects) == 0:
|
||||||
if self.cache_original_image[0] == filepath:
|
if self.cache_original_image[0] == filepath:
|
||||||
|
@ -440,8 +442,8 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
self.text_lines_img = self.cache_text_lines_img[1]
|
self.text_lines_img = self.cache_text_lines_img[1]
|
||||||
else:
|
else:
|
||||||
self.text_lines_img = (imagelib.get_draw_text_lines ( self.image, sh,
|
self.text_lines_img = (imagelib.get_draw_text_lines ( self.image, sh,
|
||||||
[ '[Mouse click] - lock/unlock selection',
|
[ '[L Mouse click] - lock/unlock selection. [Mouse wheel] - change rect',
|
||||||
'[Mouse wheel] - change rect',
|
'[R Mouse Click] - manual face rectangle',
|
||||||
'[Enter] / [Space] - confirm / skip frame',
|
'[Enter] / [Space] - confirm / skip frame',
|
||||||
'[,] [.]- prev frame, next frame. [Q] - skip remaining frames',
|
'[,] [.]- prev frame, next frame. [Q] - skip remaining frames',
|
||||||
'[a] - accuracy on/off (more fps)',
|
'[a] - accuracy on/off (more fps)',
|
||||||
|
@ -453,8 +455,10 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
while True:
|
while True:
|
||||||
io.process_messages(0.0001)
|
io.process_messages(0.0001)
|
||||||
|
|
||||||
|
if not self.force_landmarks:
|
||||||
new_x = self.x
|
new_x = self.x
|
||||||
new_y = self.y
|
new_y = self.y
|
||||||
|
|
||||||
new_rect_size = self.rect_size
|
new_rect_size = self.rect_size
|
||||||
|
|
||||||
mouse_events = io.get_mouse_events(self.wnd_name)
|
mouse_events = io.get_mouse_events(self.wnd_name)
|
||||||
|
@ -465,8 +469,19 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
diff = 1 if new_rect_size <= 40 else np.clip(new_rect_size / 10, 1, 10)
|
diff = 1 if new_rect_size <= 40 else np.clip(new_rect_size / 10, 1, 10)
|
||||||
new_rect_size = max (5, new_rect_size + diff*mod)
|
new_rect_size = max (5, new_rect_size + diff*mod)
|
||||||
elif ev == io.EVENT_LBUTTONDOWN:
|
elif ev == io.EVENT_LBUTTONDOWN:
|
||||||
|
if self.force_landmarks:
|
||||||
|
self.x = new_x
|
||||||
|
self.y = new_y
|
||||||
|
self.force_landmarks = False
|
||||||
|
self.rect_locked = True
|
||||||
|
self.redraw()
|
||||||
|
else:
|
||||||
self.rect_locked = not self.rect_locked
|
self.rect_locked = not self.rect_locked
|
||||||
self.extract_needed = True
|
self.extract_needed = True
|
||||||
|
elif ev == io.EVENT_RBUTTONDOWN:
|
||||||
|
self.force_landmarks = not self.force_landmarks
|
||||||
|
if self.force_landmarks:
|
||||||
|
self.rect_locked = False
|
||||||
elif not self.rect_locked:
|
elif not self.rect_locked:
|
||||||
new_x = np.clip (x, 0, w-1) / self.view_scale
|
new_x = np.clip (x, 0, w-1) / self.view_scale
|
||||||
new_y = np.clip (y, 0, h-1) / self.view_scale
|
new_y = np.clip (y, 0, h-1) / self.view_scale
|
||||||
|
@ -532,11 +547,35 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
self.landmarks_accurate = not self.landmarks_accurate
|
self.landmarks_accurate = not self.landmarks_accurate
|
||||||
break
|
break
|
||||||
|
|
||||||
if self.x != new_x or \
|
if self.force_landmarks:
|
||||||
|
pt2 = np.float32([new_x, new_y])
|
||||||
|
pt1 = np.float32([self.x, self.y])
|
||||||
|
|
||||||
|
pt_vec_len = npla.norm(pt2-pt1)
|
||||||
|
pt_vec = pt2-pt1
|
||||||
|
if pt_vec_len != 0:
|
||||||
|
pt_vec /= pt_vec_len
|
||||||
|
|
||||||
|
self.rect_size = pt_vec_len
|
||||||
|
self.rect = ( int(self.x-self.rect_size),
|
||||||
|
int(self.y-self.rect_size),
|
||||||
|
int(self.x+self.rect_size),
|
||||||
|
int(self.y+self.rect_size) )
|
||||||
|
|
||||||
|
if pt_vec_len > 0:
|
||||||
|
lmrks = np.concatenate ( (np.zeros ((17,2), np.float32), LandmarksProcessor.landmarks_2D), axis=0 )
|
||||||
|
lmrks -= lmrks[30:31,:]
|
||||||
|
mat = cv2.getRotationMatrix2D( (0, 0), -np.arctan2( pt_vec[1], pt_vec[0] )*180/math.pi , pt_vec_len)
|
||||||
|
mat[:, 2] += (self.x, self.y)
|
||||||
|
self.landmarks = LandmarksProcessor.transform_points(lmrks, mat )
|
||||||
|
|
||||||
|
|
||||||
|
self.redraw()
|
||||||
|
|
||||||
|
elif self.x != new_x or \
|
||||||
self.y != new_y or \
|
self.y != new_y or \
|
||||||
self.rect_size != new_rect_size or \
|
self.rect_size != new_rect_size or \
|
||||||
self.extract_needed or \
|
self.extract_needed:
|
||||||
redraw_needed:
|
|
||||||
self.x = new_x
|
self.x = new_x
|
||||||
self.y = new_y
|
self.y = new_y
|
||||||
self.rect_size = new_rect_size
|
self.rect_size = new_rect_size
|
||||||
|
@ -545,10 +584,6 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
int(self.x+self.rect_size),
|
int(self.x+self.rect_size),
|
||||||
int(self.y+self.rect_size) )
|
int(self.y+self.rect_size) )
|
||||||
|
|
||||||
if redraw_needed:
|
|
||||||
redraw_needed = False
|
|
||||||
return ExtractSubprocessor.Data (filepath, landmarks_accurate=self.landmarks_accurate)
|
|
||||||
else:
|
|
||||||
return ExtractSubprocessor.Data (filepath, rects=[self.rect], landmarks_accurate=self.landmarks_accurate)
|
return ExtractSubprocessor.Data (filepath, rects=[self.rect], landmarks_accurate=self.landmarks_accurate)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -571,14 +606,7 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
if not self.type != 'landmarks-manual':
|
if not self.type != 'landmarks-manual':
|
||||||
self.input_data.insert(0, data)
|
self.input_data.insert(0, data)
|
||||||
|
|
||||||
#override
|
def redraw(self):
|
||||||
def on_result (self, host_dict, data, result):
|
|
||||||
if self.type == 'landmarks-manual':
|
|
||||||
filepath, landmarks = result.filepath, result.landmarks
|
|
||||||
|
|
||||||
if len(landmarks) != 0 and landmarks[0] is not None:
|
|
||||||
self.landmarks = landmarks[0]
|
|
||||||
|
|
||||||
(h,w,c) = self.image.shape
|
(h,w,c) = self.image.shape
|
||||||
|
|
||||||
if not self.hide_help:
|
if not self.hide_help:
|
||||||
|
@ -610,6 +638,17 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
self.extract_needed = False
|
self.extract_needed = False
|
||||||
|
|
||||||
io.show_image (self.wnd_name, image)
|
io.show_image (self.wnd_name, image)
|
||||||
|
|
||||||
|
|
||||||
|
#override
|
||||||
|
def on_result (self, host_dict, data, result):
|
||||||
|
if self.type == 'landmarks-manual':
|
||||||
|
filepath, landmarks = result.filepath, result.landmarks
|
||||||
|
|
||||||
|
if len(landmarks) != 0 and landmarks[0] is not None:
|
||||||
|
self.landmarks = landmarks[0]
|
||||||
|
|
||||||
|
self.redraw()
|
||||||
else:
|
else:
|
||||||
self.result.append ( result )
|
self.result.append ( result )
|
||||||
io.progress_bar_inc(1)
|
io.progress_bar_inc(1)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue