# Standard libraries import os import time import json import traceback import multiprocessing from datetime import datetime # Numerical and image processing libraries import numpy as np import cv2 import torch # Core functionalities from lib.faceverse_process.core import get_recon_model import lib.faceverse_process.core.utils as utils import lib.faceverse_process.core.losses as losses # Third-party libraries from tqdm import tqdm # Progress bar from pytorch3d.renderer import look_at_view_transform # 3D transformations import mediapipe as mp # Face landmark detection count = multiprocessing.Value('i', 0) # multiprocessing.Value对象和Process一起使用的时候,可以像上面那样作为全局变量使用,也可以作为传入参数使用。但是和Pool一起使用的时候,只能作为全局变量使用 total = multiprocessing.Value('i', 0) def fit_faceverse( base_dir: str, save_dir: str = None, skip: bool = False, save_fvmask: str = None, save_lmscounter: str = None, num_threads: int = 8, trick: int = 0, focal_ratio: float = 4.2647, # Focal length used by EG3D is_img = False ): """ Processes multiple video frames for face reconstruction using multiprocessing. Args: base_dir (str): Base directory containing input images. save_dir (str): Directory to save results (default: auto-generated). skip (bool): Whether to skip already processed frames. save_fvmask (str or None): Path to save face visibility mask. save_lmscounter (str or None): Path to save landmark counter visualization. num_threads (int): Number of threads to use for multiprocessing. trick (int): Processing strategy (-1, 0, or 1) for selecting frames. focal_ratio (float): Focal length scaling factor. """ data_save_dir = os.path.join(save_dir, 'dataset', "images512x512") # Final processed images save_tracking_dir = os.path.join(save_dir, 'crop_fv_tracking') # Ensure base directory exists assert os.path.exists(base_dir), f"Base directory '{base_dir}' does not exist." # Ensure base_dir contains 'images512x512' when saving masks or landmark counters if save_lmscounter or save_fvmask: assert 'images512x512' in base_dir, "Base directory must contain 'images512x512' when saving masks or landmark counters." # Define save directory (default: `fv_tracking`) save_dir = save_dir if save_dir else os.path.join(os.path.dirname(os.path.dirname(base_dir)), 'fv_tracking') os.makedirs(save_dir, exist_ok=True) # Image resolution img_res = 512 # Initialize camera intrinsic matrix cam_K = np.eye(3, dtype=np.float32) cam_K[0, 0] = cam_K[1, 1] = focal_ratio * img_res cam_K[0, 2] = cam_K[1, 2] = img_res // 2 all_frames = 0 sub_class_ls = [] # List to store video metadata # Get list of subdirectories (video folders) that haven't been processed sub_classes = [ sub_class for sub_class in os.listdir(base_dir) if os.path.isdir(os.path.join(base_dir, sub_class)) and sub_class not in os.listdir(save_dir) ] # Apply processing strategy based on `trick` argument if trick != 0: assert trick in [-1, 1], "Invalid trick value. Must be -1, 0, or 1." sub_classes = sub_classes[::2] if trick == 1 else sub_classes[1::2] # Process each subdirectory (video folder) for sub_class in tqdm(sub_classes, desc="Processing Videos"): sub_dir = os.path.join(base_dir, sub_class) if not os.path.isdir(sub_dir): continue frame_ls = [] # List to store frames for the current video # Iterate through images in the subdirectory for img_name in os.listdir(sub_dir): if not img_name.endswith('png'): continue # Define save folder for the current frame res_folder = os.path.join(sub_dir.replace(base_dir, save_dir), img_name.split('.')[0]) # Skip processing if a 'finish' flag exists if skip and os.path.exists(os.path.join(res_folder, 'finish')): continue # Store frame metadata frame_ls.append({ 'img_path': os.path.join(sub_dir, img_name), 'save_dir': res_folder }) # Skip videos with no valid frames if not frame_ls: continue # Sort frames by numerical index extracted from filename if not is_img: frame_ls.sort(key=lambda x: int(os.path.basename(x['img_path']).split('.')[0].split('_')[-1])) # Store video metadata sub_class_ls.append({'video_name': sub_class, 'frame_ls': frame_ls}) all_frames += len(frame_ls) # Store total number of frames for processing total.value = all_frames num_threads = min(num_threads, len(sub_class_ls)) # Adjust thread count based on available videos # Logging print(f"Base Directory: {base_dir}") print(f"Save Directory: {save_dir}") print(f"Skip Processed: {skip}") print(f"Number of Threads: {num_threads}") print(f"Total Frames: {total.value}") # Multi-threaded processing if num_threads > 1: p = multiprocessing.Pool(num_threads) # Distribute videos across threads num_videos = len(sub_class_ls) all_list = [ sub_class_ls[i * (num_videos // num_threads): (i + 1) * (num_videos // num_threads)] for i in range(num_threads) ] + [sub_class_ls[num_threads * (num_videos // num_threads):]] # Prepare data for parallel processing data_ls = [ { 'img_res': img_res, 'video_ls': ls, 'save_dir': save_dir, 'cam_K': cam_K, 'save_fvmask': save_fvmask, 'save_lmscounter': save_lmscounter, 'is_img':is_img } for ls in all_list ] # Start multiprocessing p.map(fit_videos_, data_ls) p.close() p.join() else: # Single-threaded processing (fallback) fit_videos_({ 'img_res': img_res, 'video_ls': sub_class_ls, 'save_dir': save_dir, 'cam_K': cam_K, 'save_fvmask': save_fvmask, 'save_lmscounter': save_lmscounter, 'is_img':is_img }) # Collect and aggregate no-face logs no_face_log = [] for name in os.listdir(save_dir): if name.endswith('no_face_log.json'): with open(os.path.join(save_dir, name), 'r') as f: no_face_log += json.load(f) # Save aggregated no-face log if any entries exist if no_face_log: log_filename = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') + '_total_no_face_log.json' with open(os.path.join(save_dir, log_filename), 'w') as f: json.dump(no_face_log, f, indent=4) def fit_videos_(data): """ Process and fit multiple videos using a face reconstruction model. Args: data (dict): Dictionary containing the parameters: - 'img_res' (int): Image resolution. - 'video_ls' (list): List of video dictionaries containing frame information. - 'save_dir' (str): Directory to save results. - 'cam_K' (numpy array): Camera intrinsic matrix. - 'save_fvmask' (str or None): Path to save face visibility mask. - 'save_lmscounter' (str or None): Path to save landmark counter visualization. """ config = { "tar_size": 512, "recon_model": "meta_simplify_v31", "lm_loss_w": 1e3, "rgb_loss_w": 1e-2, "id_reg_w": 3e-3, "exp_reg_w": 1e-3, # Previously 8e-3 "tex_reg_w": 3e-5, "tex_w": 1.0, "skip": False, "save_fvmask": None, "save_lmscounter": None, "num_threads": 8, "trick": 0, "focal_ratio": 4.2647, # Focal length used by EG3D "cam_dist": 5.0, "device": "cuda:0" } # Extract data parameters img_res = data['img_res'] video_ls = data['video_ls'] save_dir = data['save_dir'] cam_K = data['cam_K'] save_fvmask = data['save_fvmask'] save_lmscounter = data['save_lmscounter'] is_img = data['is_img'] print(f'Fitting {len(video_ls)} Videos') # Scale camera intrinsic matrix based on target image size cam_K[:2] *= config["tar_size"]/ img_res # Initialize MediaPipe face mesh detector mp_tracker = mp.solutions.face_mesh.FaceMesh( static_image_mode=True, max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.2, min_tracking_confidence=0.2 ) # Initialize the face reconstruction model recon_model = get_recon_model( model=config["recon_model"], device=config["device"], batch_size=1, img_size=config["tar_size"], intr=cam_K, cam_dist=config["cam_dist"] ) no_face_log = [] # Log for frames where no face is detected # Iterate through each video in the list for vidx, video_info in enumerate(video_ls): print(video_info['frame_ls'][0]['img_path'], vidx) # Process the frames using the `fit_()` function no_face_log_ = fit_( video_info['frame_ls'], recon_model, img_res, config, mp_tracker, cont_opt=False, first_video=(vidx == 0), reg_RT=True, save_fvmask=save_fvmask, save_lmscounter=save_lmscounter, is_img=is_img ) # Create a "finish" flag file or log issues if face fitting fails video_save_path = os.path.join(save_dir, video_info['video_name']) if not no_face_log_: open(os.path.join(video_save_path, 'finish'), "w").close() else: issue_type = no_face_log_[0][0] if issue_type == 'LargeRot': open(os.path.join(video_save_path, 'LargeRot'), "w").close() elif issue_type == 'NoFace': open(os.path.join(video_save_path, 'NoFace'), "w").close() elif issue_type == 'SamllFace': # Fixed typo ('SamllFace' → 'SmallFace') open(os.path.join(video_save_path, 'SmallFace'), "w").close() # Append detected no-face logs no_face_log += no_face_log_ # Save log of frames where no face was detected if no_face_log: log_path = os.path.join(save_dir, f"{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}_no_face_log.json") with open(log_path, 'w') as f: json.dump(no_face_log, f, indent=4) else: print('No face log entries recorded.') def fit_(frame_ls, recon_model, img_res, config, mp_tracker, first_video=False, save_mesh=False, keep_id=True, reg_RT=False, save_fvmask=None, save_lmscounter=None, cont_opt=False, is_img=False): if is_img: keep_id = False lm_weights = utils.get_lm_weights(config["device"], use_mediapipe=True) resize_factor = config["tar_size"] / img_res rt_reg_w = 0.1 if reg_RT else 0. num_iters_rf = 100 if keep_id else 500 frame_ind = 0 no_face_log = [] for frame_dict in frame_ls: frame_ind += 1 if is_img: frame_ind = 0 res_folder = frame_dict['save_dir'] # Create the results folder if it doesn't exist os.makedirs(res_folder, exist_ok=True) img_path = frame_dict['img_path'] # Use a lock to safely update and print the processing count with count.get_lock(): count.value += 1 print('(%d / %d) Processing frame %s, first_video=%d' % (count.value, total.value, img_path, int(first_video))) # Read the image and convert from BGR to RGB img_arr = cv2.imread(img_path)[:, :, ::-1] # Resize the face image to the target size resized_face_img = cv2.resize(img_arr, (config["tar_size"], config["tar_size"])) # Process the image using MediaPipe face tracking results = mp_tracker.process(resized_face_img) # If no face landmarks are detected, log and skip processing if results.multi_face_landmarks is None: print('No face detected!', img_path) no_face_log.append(['NoFace', img_path]) continue # Initialize a numpy array to store facial landmarks (478 points, 2D coordinates) lms = np.zeros((478, 2), dtype=np.int64) # Extract face landmarks and store in the array for idx, landmark in enumerate(results.multi_face_landmarks[0].landmark): lms[idx, 0] = int(landmark.x * config["tar_size"]) lms[idx, 1] = int(landmark.y * config["tar_size"]) # Check if the detected face is too small based on bounding box size if max(max(lms[:, 0]) - min(lms[:, 0]), max(lms[:, 1]) - min(lms[:, 1])) < config["tar_size"] / 3: print('Too small face detected!', img_path) no_face_log.append(['SmallFace', img_path]) continue # Convert landmarks to a PyTorch tensor and move to the specified device lms_tensor = torch.tensor(lms[np.newaxis, :, :], dtype=torch.float32, device=config["device"]) # If continuation option is enabled, check for existing coefficient file if cont_opt: coeffs_path = os.path.join(res_folder, 'coeffs.npy') # Load and initialize coefficients if they already exist if os.path.exists(coeffs_path): coeffs = torch.from_numpy(np.load(coeffs_path)).unsqueeze(0).cuda() # Split the loaded coefficients into respective components (id_coeff, exp_coeff, tex_coeff, angles, gamma, translation, eye_coeff, scale) = recon_model.split_coeffs(coeffs) # Initialize the reconstruction model with the loaded coefficients recon_model.init_coeff_tensors( id_coeff=id_coeff, tex_coeff=tex_coeff, exp_coeff=exp_coeff, gamma_coeff=gamma, trans_coeff=translation, rot_coeff=angles, scale_coeff=scale, eye_coeff=eye_coeff ) first_video = False # Indicate that this is not the first video # Determine which parameters to optimize based on `keep_id` and frame index if keep_id and frame_ind > 1: # Keep identity coefficients fixed when optimizing rigid parameters rigid_optim_params = [ recon_model.get_rot_tensor(), recon_model.get_trans_tensor(), recon_model.get_exp_tensor(), recon_model.get_eye_tensor() ] else: # Optimize identity coefficients along with other rigid parameters rigid_optim_params = [ recon_model.get_rot_tensor(), recon_model.get_trans_tensor(), recon_model.get_exp_tensor(), recon_model.get_eye_tensor(), recon_model.get_id_tensor() ] # Define optimizers for rigid parameter optimization rigid_optimizer = torch.optim.Adam( rigid_optim_params, lr=5e-2 if (first_video and frame_ind == 1) else 1e-2, betas=(0.8, 0.95) ) # Learning-rate-adjusted optimizer for rigid parameters lr_rigid_optimizer = torch.optim.Adam( rigid_optim_params, lr=1e-3, betas=(0.5, 0.9) ) # Determine the number of iterations for rigid optimization num_iters = 5 * num_iters_rf if (keep_id and frame_ind == 1) else num_iters_rf # Increase iterations significantly for the first frame of the first video if first_video and frame_ind == 1: num_iters *= 5 # Perform rigid optimization for num_iters * 5 iterations for iter_rf in range(num_iters * 5): # Forward pass: get predicted landmarks without rendering pred_dict = recon_model(recon_model.get_packed_tensors(), render=False) # Compute landmark loss between predicted and ground truth landmarks lm_loss_val = losses.lm_loss(pred_dict['lms_proj'], lms_tensor, lm_weights, img_size=config["tar_size"]) # Early stopping condition: if loss is sufficiently low, break the loop if iter_rf > num_iters and lm_loss_val.item() < 5e-5: break # Regularization losses to prevent overfitting id_reg_loss = losses.get_l2(recon_model.get_id_tensor()) # Identity regularization exp_reg_loss = losses.get_l2(recon_model.get_exp_tensor()) # Expression regularization # Compute total loss with weighted sum of different loss components total_loss = (config["lm_loss_w"] * lm_loss_val + exp_reg_loss * config["exp_reg_w"] + id_reg_loss * config["id_reg_w"]) # Add rotation and translation regularization if not processing the first frame if frame_ind > 1: rt_reg_loss = (losses.get_l2(recon_model.get_rot_tensor() - rot_c) + losses.get_l2(recon_model.get_trans_tensor() - trans_c)) total_loss += rt_reg_loss * rt_reg_w # Apply regularization weight # Choose optimizer based on iteration count and frame number if frame_ind > 1 and iter_rf > num_iters * 0.6: lr_rigid_optimizer.zero_grad() total_loss.backward() lr_rigid_optimizer.step() else: rigid_optimizer.zero_grad() total_loss.backward() rigid_optimizer.step() # Ensure all expression values remain non-negative (zero negative expressions) with torch.no_grad(): recon_model.exp_tensor[recon_model.exp_tensor < 0] *= 0 rot_c, trans_c = recon_model.get_rot_tensor().clone().detach(), recon_model.get_trans_tensor().clone().detach() with torch.no_grad(): # Get the packed coefficient tensors from the reconstruction model coeffs = recon_model.get_packed_tensors() # Forward pass to get predictions, including rendering and face masking pred_dict = recon_model(coeffs, render=True, mask_face=True) # Clip rendered image values to [0, 255] and convert to NumPy format rendered_img = torch.clip(pred_dict['rendered_img'], 0, 255).cpu().numpy().squeeze() out_img = rendered_img[:, :, :3].astype(np.uint8) # Resize output image to match the specified resolution resized_out_img = cv2.resize(out_img, (img_res, img_res)) # Save the coefficient tensors as a NumPy file np.save(os.path.join(res_folder, 'coeffs.npy'), coeffs.detach().cpu().numpy().squeeze()) # Extract specific coefficients for later use split_coeffs = recon_model.split_coeffs(coeffs) tex_coeff, angles, translation, scale = split_coeffs[2], split_coeffs[3], split_coeffs[5], split_coeffs[-1] # Save the 3D mesh in .obj format if required if save_mesh: vs = pred_dict['vs'].cpu().numpy().squeeze() # Vertex positions tri = pred_dict['tri'].cpu().numpy().squeeze() # Triangle indices # Compute vertex colors and normalize to [0,1] color = torch.clip(recon_model.get_color(tex_coeff), 0, 255).cpu().numpy().squeeze().astype( np.float32) / 255 # Save the mesh as an OBJ file utils.save_obj(os.path.join(res_folder, 'mesh.obj'), vs, tri + 1, color) # Compute extrinsic camera parameters rotation = recon_model.compute_rotation_matrix(angles) # Compute rotation matrix # Initialize transformation matrices cam_T = torch.eye(4, dtype=torch.float32).to(config["device"]) # Camera transformation tmp_T = torch.eye(4, dtype=torch.float32).to(config["device"]) # Temporary transformation # Compute camera rotation and translation matrices cam_R, cam_t = look_at_view_transform(dist=config["cam_dist"], elev=0, azim=0) tmp_T[:3, :3] = cam_R[0] # Set rotation tmp_T[-1, :3] = cam_t[0] # Set translation # Compute metaFace extrinsic matrix cam_T[:3, :3] = torch.abs(scale[0]) * torch.eye(3, dtype=torch.float32).to(config["device"]) cam_T[-1, :3] = translation[0] metaFace_extr = torch.matmul(cam_T, tmp_T).clone() # Left-multiply transformation # Compute final transformation matrix cam_T[:3, :3] = torch.abs(scale[0]) * rotation[0] cam_T[-1, :3] = translation[0] transformation = torch.matmul(cam_T, tmp_T) # Left-multiply transformation # Save extrinsic parameters as a NumPy archive np.savez(os.path.join(res_folder, 'metaFace_extr'), extr=metaFace_extr.cpu().numpy().astype(np.float32).T, # Transposed for right multiplication transformation=transformation.cpu().numpy().astype(np.float32).T, self_rotation=rotation[0].cpu().numpy().astype(np.float32).T, self_scale=scale[0].cpu().numpy().astype(np.float32), self_translation=translation[0].cpu().numpy().astype(np.float32), self_angle=angles[0].cpu().numpy().astype(np.float32)) # Blend original and rendered images for visualization composed_img = img_arr * 0.6 + resized_out_img * 0.4 # Resize and normalize landmark coordinates resized_lms = lms_tensor.cpu().detach().squeeze().numpy() / resize_factor resized_lms_proj = pred_dict['lms_proj'].cpu().detach().squeeze().numpy() / resize_factor # Overlay landmarks on the composed image composed_img = visualize_render_lms(composed_img, resized_lms, resized_lms_proj) cv2.imwrite(os.path.join(res_folder, 'composed_render.png'), composed_img[:, :, ::-1].astype(np.uint8)) # Save face visibility mask if required if save_fvmask is not None: out_mask = (np.linalg.norm(resized_out_img, axis=-1) > 0).astype(np.float32) * 255 os.makedirs(os.path.dirname(img_path.replace('images512x512', save_fvmask)), exist_ok=True) cv2.imwrite(img_path.replace('images512x512', save_fvmask), out_mask.astype(np.uint8)) # Save landmark counter visualization if required if save_lmscounter is not None: lms_proj = pred_dict['lms_proj'].cpu().detach().squeeze().numpy() black_img = np.zeros((config["tar_size"], config["tar_size"], 3), dtype=np.uint8) draw_img = draw_lms_counter(black_img, lms_proj) os.makedirs(os.path.dirname(img_path.replace('images512x512', save_lmscounter)), exist_ok=True) cv2.imwrite(img_path.replace('images512x512', save_lmscounter), draw_img) # Create a 'finish' file to indicate processing completion open(os.path.join(res_folder, 'finish'), "w") return no_face_log def visualize_render_lms(composed_img, resized_lms, resized_lms_proj): """ Visualizes facial landmarks on an image. Args: composed_img (np.ndarray): The input image to draw on. resized_lms (np.ndarray): Original 2D facial landmarks (shape: [N, 2]). resized_lms_proj (np.ndarray): Projected facial landmarks (shape: [N, 2]). Returns: np.ndarray: The image with drawn facial landmarks. """ # Convert landmark coordinates to integer values for drawing resized_lms = np.round(resized_lms).astype(np.int32) resized_lms_proj = np.round(resized_lms_proj).astype(np.int32) # Landmark indices to annotate with numbers annotated_indices = [0, 8, 16, 20, 24, 30, 47, 58, 62] # Draw original landmarks (Blue) for (x, y) in resized_lms: cv2.circle(composed_img, (x, y), radius=1, color=(255, 0, 0), thickness=-1) # Annotate specific original landmarks (Yellow) for i in annotated_indices: cv2.putText(composed_img, str(i), tuple(resized_lms[i]), cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.5, color=(255, 255, 0), thickness=1) # Draw projected landmarks (Green) for (x, y) in resized_lms_proj: cv2.circle(composed_img, (x, y), radius=1, color=(0, 255, 0), thickness=-1) # Annotate specific projected landmarks (Cyan) for i in annotated_indices: cv2.putText(composed_img, str(i), tuple(resized_lms_proj[i]), cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.5, color=(0, 255, 255), thickness=1) return composed_img def draw_lms_counter(img, lms_proj): """ Draws facial landmarks on an image, including mouth, eyes, and specific points. Args: img (np.ndarray): The input image. lms_proj (np.ndarray): The projected 2D facial landmarks (shape: [N, 2]). Returns: np.ndarray: The image with drawn facial landmarks. """ # Convert landmark coordinates to integer values lms_proj_coords = np.round(lms_proj).astype(np.int32) # Define landmark indices for different facial features outter_mouth_idx = [0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146, 76, 185, 40, 39, 37] inner_mouth_idx = [13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95, 78, 191, 80, 81, 82] left_eye_idx = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] right_eye_idx = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] left_brow_idx = [283, 282, 295, 285, 336, 296, 334] right_brow_idx = [53, 52, 65, 55, 107, 66, 105] # Create a copy of the image to draw on draw_img = img.copy() # Draw facial landmarks for mouth (outer and inner) draw_img = cv2.polylines(draw_img, [lms_proj_coords[outter_mouth_idx]], isClosed=True, color=(255, 0, 0), thickness=4) draw_img = cv2.polylines(draw_img, [lms_proj_coords[inner_mouth_idx]], isClosed=True, color=(255, 0, 0), thickness=4) # Draw facial landmarks for eyes draw_img = cv2.polylines(draw_img, [lms_proj_coords[left_eye_idx]], isClosed=True, color=(0, 255, 0), thickness=2) draw_img = cv2.polylines(draw_img, [lms_proj_coords[right_eye_idx]], isClosed=True, color=(0, 255, 0), thickness=2) # Uncomment to draw eyebrows # draw_img = cv2.polylines(draw_img, [lms_proj_coords[left_brow_idx]], # isClosed=True, color=(0, 255, 0), thickness=2) # draw_img = cv2.polylines(draw_img, [lms_proj_coords[right_brow_idx]], # isClosed=True, color=(0, 255, 0), thickness=2) # Draw specific landmark points (e.g., pupils or reference points) draw_img = cv2.circle(draw_img, tuple(lms_proj_coords[473]), radius=4, color=(0, 0, 255), thickness=-1) draw_img = cv2.circle(draw_img, tuple(lms_proj_coords[468]), radius=4, color=(0, 0, 255), thickness=-1) # Uncomment to draw additional facial contours # draw_img = cv2.polylines(draw_img, [lms_proj_coords[474:478]], # isClosed=True, color=(0, 255, 0), thickness=1) # draw_img = cv2.polylines(draw_img, [lms_proj_coords[469:473]], # isClosed=True, color=(0, 255, 0), thickness=1) return draw_img # # # if __name__ == '__main__': # import argparse # # parser = argparse.ArgumentParser() # parser.add_argument('--base_dir', type=str, default=None) # parser.add_argument('--save_dir', type=str, default=None) # parser.add_argument('--tar_size', type=int, default=512, help='size for rendering window. We use a square window.') # parser.add_argument('--recon_model', type=str, default='meta', help='choose a 3dmm model, default: meta') # parser.add_argument('--lm_loss_w', type=float, default=1e3, help='weight for landmark loss') # parser.add_argument('--rgb_loss_w', type=float, default=1e-2, help='weight for rgb loss') # parser.add_argument('--id_reg_w', type=float, default=3e-3, help='weight for id coefficient regularizer') # parser.add_argument('--exp_reg_w', type=float, default=1e-3, # 8e-3 # help='weight for expression coefficient regularizer') # parser.add_argument('--tex_reg_w', type=float, default=3e-5, help='weight for texture coefficient regularizer') # parser.add_argument('--tex_w', type=float, default=1, help='weight for texture reflectance loss.') # parser.add_argument('--skip', action='store_true', default=False) # parser.add_argument('--save_fvmask', type=str, default=None) # parser.add_argument('--save_lmscounter', type=str, default=None) # parser.add_argument('--num_threads', default=8) # parser.add_argument('--trick', type=int, default=0) # args = parser.parse_args() # args.focal_ratio = 4.2647 # the focal used by EG3D # args.cam_dist = 5. # args.device = 'cuda:0' # args.recon_model = 'meta_simplify_v31' # fit_faceverse(args)