From 27d1f429772ffb8c560afefa14bfc2c79d86bc40 Mon Sep 17 00:00:00 2001 From: mossmatrix Date: Mon, 8 Dec 2025 01:59:56 -0500 Subject: [PATCH 1/2] Move Lynx ArcFace weights to ComfyUI models directory Changes the download location for the ArcFace recognition model weights from the custom node directory to the centralized ComfyUI models directory. - Register new 'face_analysis' model folder at ComfyUI/models/face_analysis - Update init_recognition_model() to accept and use model_rootpath parameter - Update FaceEncoderArcFace.init_encoder_model() to pass model root path - Update LynxEncodeFaceIP node to provide face_analysis directory path Weights now download to: ComfyUI/models/face_analysis/facexlib/weights/ Instead of: custom_nodes/ComfyUI-WanVideoWrapper/lynx/face/facexlib/weights/ Benefits: - Centralized model storage following ComfyUI conventions - Easier model management and reuse - Models persist across node reinstalls --- lynx/face/face_encoder.py | 16 ++++++++++++---- lynx/nodes.py | 8 +++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lynx/face/face_encoder.py b/lynx/face/face_encoder.py index 60ee7369..e8193a02 100644 --- a/lynx/face/face_encoder.py +++ b/lynx/face/face_encoder.py @@ -79,8 +79,16 @@ def init_recognition_model(model_name, half=False, device='cuda', model_rootpath else: raise NotImplementedError(f'{model_name} is not implemented.') - model_path = load_file_from_url( - url=model_url, model_dir='facexlib/weights', progress=True, file_name=None, save_dir=model_rootpath) + # If model_rootpath is provided, use it directly (ComfyUI models directory) + # Otherwise fall back to the old behavior (node directory) + if model_rootpath is not None: + save_dir = os.path.join(model_rootpath, 'facexlib', 'weights') + model_path = load_file_from_url( + url=model_url, model_dir=None, progress=True, file_name=None, save_dir=save_dir) + else: + model_path = load_file_from_url( + url=model_url, model_dir='facexlib/weights', progress=True, file_name=None, save_dir=model_rootpath) + print("Loading model from:", model_path) model.load_state_dict(torch.load(model_path), strict=True) model.eval() @@ -93,9 +101,9 @@ class FaceEncoderArcFace(): def __repr__(self): return "ArcFace" - def init_encoder_model(self, device, eval_mode=True): + def init_encoder_model(self, device, eval_mode=True, model_rootpath=None): self.device = device - self.encoder_model = init_recognition_model('arcface', device=device) + self.encoder_model = init_recognition_model('arcface', device=device, model_rootpath=model_rootpath) if eval_mode: self.encoder_model.eval() diff --git a/lynx/nodes.py b/lynx/nodes.py index 919b8bf2..35d96199 100644 --- a/lynx/nodes.py +++ b/lynx/nodes.py @@ -11,6 +11,9 @@ device = mm.get_torch_device() offload_device = mm.unet_offload_device() +# Register face_analysis models folder +folder_paths.add_model_folder_path("face_analysis", os.path.join(folder_paths.models_dir, "face_analysis")) + from .resampler import Resampler class LoadLynxResampler: @@ -112,8 +115,11 @@ def encode(self, resampler, ip_image): image_in = ip_image.permute(0, 3, 1, 2).to(device) * 2 - 1 # to [-1, 1] # Face embedding via ArcFace + # Get the face_analysis models directory path + face_analysis_dir = folder_paths.get_folder_paths("face_analysis")[0] + face_encoder = FaceEncoderArcFace() - face_encoder.init_encoder_model(device) + face_encoder.init_encoder_model(device, model_rootpath=face_analysis_dir) arcface_embed = face_encoder(image_in).to(device, resampler.dtype)[0] arcface_embed = arcface_embed.reshape([1, -1, 512]) From 6efc50089b9c17fb9d29d5979771efee09e6ed14 Mon Sep 17 00:00:00 2001 From: mossmatrix Date: Mon, 8 Dec 2025 10:27:52 -0500 Subject: [PATCH 2/2] Remove fallback to node directory for ArcFace weights --- lynx/face/face_encoder.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lynx/face/face_encoder.py b/lynx/face/face_encoder.py index e8193a02..c4bee000 100644 --- a/lynx/face/face_encoder.py +++ b/lynx/face/face_encoder.py @@ -79,15 +79,10 @@ def init_recognition_model(model_name, half=False, device='cuda', model_rootpath else: raise NotImplementedError(f'{model_name} is not implemented.') - # If model_rootpath is provided, use it directly (ComfyUI models directory) - # Otherwise fall back to the old behavior (node directory) - if model_rootpath is not None: - save_dir = os.path.join(model_rootpath, 'facexlib', 'weights') - model_path = load_file_from_url( - url=model_url, model_dir=None, progress=True, file_name=None, save_dir=save_dir) - else: - model_path = load_file_from_url( - url=model_url, model_dir='facexlib/weights', progress=True, file_name=None, save_dir=model_rootpath) + # Always use ComfyUI models directory + save_dir = os.path.join(model_rootpath, 'facexlib', 'weights') + model_path = load_file_from_url( + url=model_url, model_dir=None, progress=True, file_name=None, save_dir=save_dir) print("Loading model from:", model_path) model.load_state_dict(torch.load(model_path), strict=True)