From 2f69e4e7d7ebdec4fd22533483fc0eeeef4acef0 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 00:07:34 +0200 Subject: [PATCH 01/38] Add Makefile for Docker build and run management Introduced a Makefile to streamline Docker image building and container management. Added targets for building, running, and executing commands within the Docker container, as well as a production build process. Included help instructions for usage. --- Makefile | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..687beee --- /dev/null +++ b/Makefile @@ -0,0 +1,79 @@ +# [USER: ADJUST PATH] +PROJECT_NAME := fmpose3d +project_name_lo := $(shell echo $(PROJECT_NAME) | tr '[:upper:]' '[:lower:]') +####### +# RUN # +####### + +# for local server +IMG_NAME := fmpose3d +IMG_TAG := v0.1 +DOCKERFILE := Dockerfile +HOST_UID := $(shell id -u) +HOST_GID := $(shell id -g) +BUILD_ARGS := \ + --build-arg USERNAME=fmpose3d \ + --build-arg USER_GID=$(HOST_GID) \ + --build-arg USER_UID=$(HOST_UID) + +build: + docker build $(BUILD_ARGS) \ + -t $(IMG_NAME):$(IMG_TAG) -f $(DOCKERFILE) . + +build-clean: + docker build --no-cache $(BUILD_ARGS) \ + -t $(IMG_NAME):$(IMG_TAG) -f $(DOCKERFILE) . + + +CONTAINER_NAME := fmpose3d_dev1 +# [USER: ADJUST] Mount the project root into the container +HOST_SRC := $(shell pwd) +DOCKER_SRC := /fmpose3d +VOLUMES := \ + --volume $(HOST_SRC):$(DOCKER_SRC) + + +run: + docker run -it --gpus all --shm-size=64g --name $(CONTAINER_NAME) -w $(DOCKER_SRC) $(VOLUMES) $(IMG_NAME):$(IMG_TAG) + +exec: + docker exec -it -w $(DOCKER_SRC) $(CONTAINER_NAME) /bin/zsh + +exec_bash: + docker exec -it -w $(DOCKER_SRC) $(CONTAINER_NAME) /bin/bash + +stop: + docker stop $(CONTAINER_NAME) + +rm: + docker rm $(CONTAINER_NAME) + + +####################### +# BUILD CORE WITH SRC # +####################### +# [USER: ADJUST SRC PATH] +SRC_PATH="/home/user/project" +build_production: + cp -r $(SRC_PATH) src + docker build $(BUILD_ARGS) --build-arg src=src \ + -t $(IMG_NAME):$(IMG_TAG) -f $(DOCKERFILE) . + rm -r src + +# Help message +help: + @echo "Available targets:" + @echo " build - Build Docker image for local server." + @echo " run - Run a Docker container with GPU." + @echo " exec - Attach to running container (zsh)." + @echo " exec_bash - Attach to running container (bash)." + @echo " stop - Stop the running container." + @echo " rm - Remove the stopped container." + @echo " build_production - Build a Docker image with source code." + @echo " help - Show this help message." + @echo "" + @echo "Usage:" + @echo " 1. make build" + @echo " 2. make run" + @echo " 3. Inside container: pip install -e '.[animals,viz]'" + @echo " 4. Inside container: sh scripts/FMPose3D_train.sh" From 530a886d6987a703c805018c4f4a273d121f8e5d Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 00:07:48 +0200 Subject: [PATCH 02/38] Add Dockerfile for FMPose3D environment setup Created a Dockerfile to establish a development environment for FMPose3D, including installation of necessary packages, user setup, and configuration for Oh My Zsh. The Dockerfile also ensures proper permissions for model weights and initializes conda for the user. --- Dockerfile | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..792ca64 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,64 @@ +FROM pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime + +ARG USERNAME=fmpose3d +ARG USER_UID=1000 +ARG USER_GID=1000 +RUN groupadd ${USERNAME} --gid ${USER_GID} +RUN useradd -m -s /bin/bash -g ${USERNAME} -u ${USER_UID} ${USERNAME} +RUN mkdir /app /logs /data + +ENV DEBIAN_FRONTEND=noninteractive +SHELL ["/bin/bash", "-c"] +RUN apt-get update -y && apt-get install -yy --no-install-recommends \ + vim zsh tmux wget curl htop git sudo ssh git-lfs \ + python3 python3-pip \ + libgl1-mesa-glx libglib2.0-0 \ + ffmpeg \ + && apt-get -y autoclean \ + && apt-get -y autoremove \ + && rm -rf /var/lib/apt/lists/* \ + && apt-get clean + +ENV PATH="/opt/conda/bin:${PATH}" + +# Initialize conda for root just in case and fix symlinks +RUN ln -fs /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh + +# --- Install FMPose3D from PyPI (as documented in README) --- +RUN python -m pip install --no-cache-dir --upgrade pip && \ + python -m pip install --no-cache-dir "fmpose3d[animals,viz]" gdown + +# Allow non-root user to download DLC model weights at runtime +RUN mkdir -p /opt/conda/lib/python3.11/site-packages/deeplabcut/modelzoo/checkpoints && \ + chown -R ${USERNAME}:${USERNAME} /opt/conda/lib/python3.11/site-packages/deeplabcut/modelzoo + +# Set your user as owner of the home directory before switching +RUN chown -R ${USERNAME}:${USERNAME} /home/${USERNAME} + +ENV NVIDIA_DRIVER_CAPABILITIES=all + +# --- SWITCH TO USER --- +USER ${USERNAME} +ENV HOME=/home/${USERNAME} +WORKDIR ${HOME} + +# Ensure dotfiles exist +RUN touch ${HOME}/.bashrc ${HOME}/.zshrc && \ + chmod 644 ${HOME}/.bashrc ${HOME}/.zshrc + +# Install Oh My Zsh and plugins (MUST come before conda init, since OMZ overwrites .zshrc) +RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended \ + && git clone https://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh/custom/plugins/zsh-autosuggestions \ + && git clone https://github.com/zsh-users/zsh-syntax-highlighting ~/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting \ + && sed -i 's/^plugins=(.*)$/plugins=(git zsh-autosuggestions zsh-syntax-highlighting)/' ~/.zshrc \ + && echo "export PATH=\$PATH:${HOME}/.local/bin" >> ~/.zshrc + +# Initialize conda AFTER Oh My Zsh (so conda init block is not overwritten) +RUN /opt/conda/bin/conda init bash && /opt/conda/bin/conda init zsh + +# Add conda activation to bashrc and zshrc +RUN echo "conda activate base" >> ${HOME}/.bashrc && \ + echo "conda activate base" >> ${HOME}/.zshrc + +SHELL ["/bin/zsh", "-c"] +CMD ["zsh"] From 43aef6e80c367e40b68e4cc192df7168503e70e6 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 00:07:59 +0200 Subject: [PATCH 03/38] Ensure checkpoints are downloaded only when HRNet is requested in gen_video_kpts function --- fmpose3d/lib/hrnet/gen_kpts.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fmpose3d/lib/hrnet/gen_kpts.py b/fmpose3d/lib/hrnet/gen_kpts.py index a75e704..6d4eef9 100755 --- a/fmpose3d/lib/hrnet/gen_kpts.py +++ b/fmpose3d/lib/hrnet/gen_kpts.py @@ -34,7 +34,6 @@ # Auto-download checkpoints if missing and get checkpoint paths from fmpose3d.lib.checkpoint.download_checkpoints import ensure_checkpoints, get_checkpoint_path -ensure_checkpoints() # Loading human detector model from fmpose3d.lib.yolov3.human_detector import load_model as yolo_model @@ -153,6 +152,9 @@ def gen_from_image(args, frame, people_sort, human_model, pose_model, det_dim=41 def gen_video_kpts(path, det_dim=416, num_peroson=1, gen_output=False, type='image'): + # Ensure checkpoints are downloaded only when HRNet is actually requested + ensure_checkpoints() + # Updating configuration args1 = parse_args() reset_config(args1) From bb79602464d6f59db721088b91fee98bceb3700d Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 00:08:11 +0200 Subject: [PATCH 04/38] Update FMPose3D_test.sh to allow optional local model weights Modified the script to enable automatic downloading of model weights from Hugging Face Hub by default. Added a commented line for users to specify local weights if desired. --- scripts/FMPose3D_test.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/FMPose3D_test.sh b/scripts/FMPose3D_test.sh index afc1f86..a1cadf7 100755 --- a/scripts/FMPose3D_test.sh +++ b/scripts/FMPose3D_test.sh @@ -11,7 +11,11 @@ exp_temp=0.005 folder_name=test_s${eval_multi_steps}_${mode}_h${num_hypothesis_list}_$(date +%Y%m%d_%H%M%S) model_type='fmpose3d_humans' -model_weights_path='./pre_trained_models/fmpose3d_h36m/FMpose3D_pretrained_weights.pth' + +# By default, weights are automatically downloaded from Hugging Face Hub. +# To use local weights instead, uncomment the line below: +# model_weights_path='./pre_trained_models/fmpose3d_h36m/FMpose3D_pretrained_weights.pth' +model_weights_path='' #Test python3 scripts/FMPose3D_main.py \ From d66a0a0c8e4a9b3026ae0e09162199a04a2b861c Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 00:08:20 +0200 Subject: [PATCH 05/38] Update vis_in_the_wild.sh to allow optional local model weights Modified the script to set model weights to an empty string by default, enabling automatic downloading from Hugging Face Hub. Added a commented line for users to specify local weights if needed. --- demo/vis_in_the_wild.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/demo/vis_in_the_wild.sh b/demo/vis_in_the_wild.sh index df88474..426f547 100755 --- a/demo/vis_in_the_wild.sh +++ b/demo/vis_in_the_wild.sh @@ -6,7 +6,11 @@ batch_size=1 sh_file='vis_in_the_wild.sh' model_type='fmpose3d_humans' -model_weights_path='../pre_trained_models/fmpose3d_h36m/FMpose3D_pretrained_weights.pth' + +# By default, weights are automatically downloaded from Hugging Face Hub. +# To use local weights instead, uncomment the line below: +# model_weights_path='../pre_trained_models/fmpose3d_h36m/FMpose3D_pretrained_weights.pth' +model_weights_path='' target_path='./images/' # folder containing multiple images # target_path='./images/xx.png' # single image From c43b46b7b4f6386e6f3effff57697fb0f145bafb Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 00:08:30 +0200 Subject: [PATCH 06/38] Enhance model weight loading in vis_in_the_wild.py to support automatic downloading from Hugging Face Hub. Added logic to download weights if no local path is specified, improving user experience and flexibility. --- demo/vis_in_the_wild.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/demo/vis_in_the_wild.py b/demo/vis_in_the_wild.py index 90b2953..a445d2f 100755 --- a/demo/vis_in_the_wild.py +++ b/demo/vis_in_the_wild.py @@ -279,7 +279,16 @@ def get_pose3D(path, output_dir, type='image'): # if args.reload: model_dict = model['CFM'].state_dict() model_path = args.model_weights_path - print(model_path) + + # If no local path is provided, download from Hugging Face Hub + if not model_path: + from huggingface_hub import hf_hub_download + _HF_REPO_ID = "deruyter92/fmpose_temp" + hf_filename = f"{args.model_type}.pth" + print(f"No local weights path specified. Downloading '{hf_filename}' from Hugging Face ({_HF_REPO_ID})...") + model_path = hf_hub_download(repo_id=_HF_REPO_ID, filename=hf_filename) + + print(f"Loading weights from: {model_path}") pre_dict = torch.load(model_path, map_location=device, weights_only=True) for name, key in model_dict.items(): model_dict[name] = pre_dict[name] From 74d2dab258f8b0178bb4c778378fb48544a6d4dc Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 10:35:29 +0200 Subject: [PATCH 07/38] Enhance FMPose3D_main.py to support automatic downloading of model weights from Hugging Face Hub. Updated logic to handle cases where no local weights path is specified, improving usability and flexibility for users. --- scripts/FMPose3D_main.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/FMPose3D_main.py b/scripts/FMPose3D_main.py index beb88da..3bf581d 100644 --- a/scripts/FMPose3D_main.py +++ b/scripts/FMPose3D_main.py @@ -268,7 +268,11 @@ def print_error_action(action_error_sum, is_train): args.checkpoint = "./checkpoint/" + folder_name elif args.train == False: # create a new folder for the test results - args.previous_dir = os.path.dirname(args.model_weights_path) + if args.model_weights_path: + args.previous_dir = os.path.dirname(args.model_weights_path) + else: + # HuggingFace-downloaded weights: no local dir, use ./checkpoint/ + args.previous_dir = "./checkpoint" args.checkpoint = os.path.join(args.previous_dir, folder_name) if not os.path.exists(args.checkpoint): @@ -338,7 +342,16 @@ def print_error_action(action_error_sum, is_train): if args.reload: model_dict = model["CFM"].state_dict() model_path = args.model_weights_path - print(model_path) + + # If no local path is provided, download from Hugging Face Hub + if not model_path: + from huggingface_hub import hf_hub_download + _HF_REPO_ID = "deruyter92/fmpose_temp" + hf_filename = f"{args.model_type}.pth" + print(f"No local weights path specified. Downloading '{hf_filename}' from Hugging Face ({_HF_REPO_ID})...") + model_path = hf_hub_download(repo_id=_HF_REPO_ID, filename=hf_filename) + + print(f"Loading weights from: {model_path}") pre_dict = torch.load(model_path, map_location=device, weights_only=True) for name, key in model_dict.items(): model_dict[name] = pre_dict[name] From 30eb7fc78ef840c79638f8f77714464e1fe6196f Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 11:15:49 +0200 Subject: [PATCH 08/38] Refactor model weight loading in vis_in_the_wild.py to utilize resolve_weights_path function. This change simplifies the logic for loading model weights, enhancing usability by automatically handling local and remote paths. --- demo/vis_in_the_wild.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/demo/vis_in_the_wild.py b/demo/vis_in_the_wild.py index a445d2f..715ec5d 100755 --- a/demo/vis_in_the_wild.py +++ b/demo/vis_in_the_wild.py @@ -278,15 +278,8 @@ def get_pose3D(path, output_dir, type='image'): # if args.reload: model_dict = model['CFM'].state_dict() - model_path = args.model_weights_path - - # If no local path is provided, download from Hugging Face Hub - if not model_path: - from huggingface_hub import hf_hub_download - _HF_REPO_ID = "deruyter92/fmpose_temp" - hf_filename = f"{args.model_type}.pth" - print(f"No local weights path specified. Downloading '{hf_filename}' from Hugging Face ({_HF_REPO_ID})...") - model_path = hf_hub_download(repo_id=_HF_REPO_ID, filename=hf_filename) + from fmpose3d.utils.weights import resolve_weights_path + model_path = resolve_weights_path(args.model_weights_path, args.model_type) print(f"Loading weights from: {model_path}") pre_dict = torch.load(model_path, map_location=device, weights_only=True) From 53aefde5f0d631e8376d3532e67c45b2770c515c Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 11:16:08 +0200 Subject: [PATCH 09/38] Refactor model weight loading in FMPose3D_main.py to utilize resolve_weights_path function. This change simplifies the logic for loading model weights, improving usability by automatically handling local and remote paths. --- scripts/FMPose3D_main.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/scripts/FMPose3D_main.py b/scripts/FMPose3D_main.py index 3bf581d..0a8ff98 100644 --- a/scripts/FMPose3D_main.py +++ b/scripts/FMPose3D_main.py @@ -341,15 +341,8 @@ def print_error_action(action_error_sum, is_train): if args.reload: model_dict = model["CFM"].state_dict() - model_path = args.model_weights_path - - # If no local path is provided, download from Hugging Face Hub - if not model_path: - from huggingface_hub import hf_hub_download - _HF_REPO_ID = "deruyter92/fmpose_temp" - hf_filename = f"{args.model_type}.pth" - print(f"No local weights path specified. Downloading '{hf_filename}' from Hugging Face ({_HF_REPO_ID})...") - model_path = hf_hub_download(repo_id=_HF_REPO_ID, filename=hf_filename) + from fmpose3d.utils.weights import resolve_weights_path + model_path = resolve_weights_path(args.model_weights_path, args.model_type) print(f"Loading weights from: {model_path}") pre_dict = torch.load(model_path, map_location=device, weights_only=True) From 1ff5d018698249356d6919fb6e56e613c2ec6a82 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 11:16:19 +0200 Subject: [PATCH 10/38] Refactor FMPose3D to import Hugging Face repository ID from utils. This change centralizes the repository ID definition, improving maintainability and consistency across the codebase. --- fmpose3d/inference_api/fmpose3d.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fmpose3d/inference_api/fmpose3d.py b/fmpose3d/inference_api/fmpose3d.py index 07a3417..3a3936d 100644 --- a/fmpose3d/inference_api/fmpose3d.py +++ b/fmpose3d/inference_api/fmpose3d.py @@ -34,8 +34,7 @@ ProgressCallback = Callable[[int, int], None] -#: HuggingFace repository hosting the official FMPose3D checkpoints. -_HF_REPO_ID: str = "deruyter92/fmpose_temp" +from fmpose3d.utils.weights import HF_REPO_ID as _HF_REPO_ID # Default camera-to-world rotation quaternion (from the demo script). _DEFAULT_CAM_ROTATION = np.array( From 8768cc460d6eede71a3cb84557813009950b31eb Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 11:17:46 +0200 Subject: [PATCH 11/38] Add utility for resolving and downloading FMPose3D model weights Introduced a new utility function in weights.py to handle the resolution of model weights paths. This function allows users to specify a local path or automatically download weights from the Hugging Face Hub if no local path is provided, enhancing usability and flexibility in model weight management. --- fmpose3d/utils/weights.py | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 fmpose3d/utils/weights.py diff --git a/fmpose3d/utils/weights.py b/fmpose3d/utils/weights.py new file mode 100644 index 0000000..bc76624 --- /dev/null +++ b/fmpose3d/utils/weights.py @@ -0,0 +1,40 @@ +"""Shared helpers for resolving / downloading FMPose3D model weights.""" + +HF_REPO_ID: str = "deruyter92/fmpose_temp" + + +def resolve_weights_path(model_weights_path: str, model_type: str) -> str: + """Return a local weights path, downloading from Hugging Face Hub if needed. + + Parameters + ---------- + model_weights_path : str + User-supplied local path. If falsy the weights are fetched from the + Hugging Face Hub automatically. + model_type : str + Model variant name used to derive the remote filename + (e.g. ``"fmpose3d_humans"`` -> ``fmpose3d_humans.pth``). + + Returns + ------- + str + Absolute path to the weight file on disk. + """ + if model_weights_path: + return model_weights_path + + try: + from huggingface_hub import hf_hub_download + except ImportError: + raise ImportError( + "huggingface_hub is required to download model weights. " + "Install it with: pip install huggingface_hub\n" + "Or download the weights manually and pass the local path." + ) from None + + filename = f"{model_type}.pth" + print( + f"No local weights path specified. " + f"Downloading '{filename}' from Hugging Face ({HF_REPO_ID})..." + ) + return hf_hub_download(repo_id=HF_REPO_ID, filename=filename) From ce1c4f0eefe2d0dd6b91fe934ad487045fb87265 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 11:18:08 +0200 Subject: [PATCH 12/38] Add __init__.py file to utils directory for FMPose3D This new file serves as an initializer for the utils module, allowing for better organization and potential future utility functions related to FMPose3D. --- fmpose3d/utils/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 fmpose3d/utils/__init__.py diff --git a/fmpose3d/utils/__init__.py b/fmpose3d/utils/__init__.py new file mode 100644 index 0000000..e69de29 From e122e51e6f85b6291747fac56bb3f795857918da Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 11:27:11 +0200 Subject: [PATCH 13/38] add header --- fmpose3d/utils/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fmpose3d/utils/__init__.py b/fmpose3d/utils/__init__.py index e69de29..adfc683 100644 --- a/fmpose3d/utils/__init__.py +++ b/fmpose3d/utils/__init__.py @@ -0,0 +1,8 @@ +""" +FMPose3D: monocular 3D Pose Estimation via Flow Matching + +Official implementation of the paper: +"FMPose3D: monocular 3D Pose Estimation via Flow Matching" +by Ti Wang, Xiaohang Yu, and Mackenzie Weygandt Mathis +Licensed under Apache 2.0 +""" From 3d1253a42517f9a8427e049cb7aab07093b0b2cf Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 11:27:31 +0200 Subject: [PATCH 14/38] add header --- fmpose3d/utils/weights.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fmpose3d/utils/weights.py b/fmpose3d/utils/weights.py index bc76624..e35eab8 100644 --- a/fmpose3d/utils/weights.py +++ b/fmpose3d/utils/weights.py @@ -1,3 +1,12 @@ +""" +FMPose3D: monocular 3D Pose Estimation via Flow Matching + +Official implementation of the paper: +"FMPose3D: monocular 3D Pose Estimation via Flow Matching" +by Ti Wang, Xiaohang Yu, and Mackenzie Weygandt Mathis +Licensed under Apache 2.0 +""" + """Shared helpers for resolving / downloading FMPose3D model weights.""" HF_REPO_ID: str = "deruyter92/fmpose_temp" From ec107694f8f34871b635ab096df35fbc41f3e483 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 12:11:05 +0200 Subject: [PATCH 15/38] Update test for SuperAnimalPrediction to handle ImportError for missing dependencies Modified the test to raise an ImportError with a specific message when the required 'fmpose3d[animals]' package is not installed, improving error handling and clarity in dependency management. --- tests/fmpose3d_api/test_fmpose3d.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fmpose3d_api/test_fmpose3d.py b/tests/fmpose3d_api/test_fmpose3d.py index 1e0b29e..f4d4a02 100644 --- a/tests/fmpose3d_api/test_fmpose3d.py +++ b/tests/fmpose3d_api/test_fmpose3d.py @@ -747,8 +747,8 @@ def test_predict_raises_clear_error_without_deeplabcut(self): frames = np.random.randint(0, 255, (1, 64, 64, 3), dtype=np.uint8) with patch( - "fmpose3d.inference_api.fmpose3d.importlib.util.find_spec", - return_value=None, + "fmpose3d.inference_api.fmpose3d._require_superanimal_analyze_images", + side_effect=ImportError('pip install "fmpose3d[animals]"'), ): with pytest.raises(ImportError, match=r"fmpose3d\[animals\]"): estimator.predict(frames) From 6254cc8a4d9f30d1090b32d999b070d3ef17fa65 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:06:30 +0000 Subject: [PATCH 16/38] Fix typo: num_peroson -> num_person in gen_kpts.py and call sites Agent-Logs-Url: https://github.com/AdaptiveMotorControlLab/FMPose3D/sessions/2136af56-29a8-4635-9ed4-faf9b8146672 Co-authored-by: xiu-cs <81274389+xiu-cs@users.noreply.github.com> --- demo/vis_in_the_wild.py | 2 +- fmpose3d/lib/hrnet/gen_kpts.py | 16 ++++++++-------- tests/test_demo_human.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/demo/vis_in_the_wild.py b/demo/vis_in_the_wild.py index 715ec5d..cfd4881 100755 --- a/demo/vis_in_the_wild.py +++ b/demo/vis_in_the_wild.py @@ -113,7 +113,7 @@ def show3Dpose(vals, ax): def get_pose2D(path, output_dir, type): print('\nGenerating 2D pose...') - keypoints, scores = hrnet_pose(path, det_dim=416, num_peroson=1, gen_output=True, type=type) + keypoints, scores = hrnet_pose(path, det_dim=416, num_person=1, gen_output=True, type=type) keypoints, scores, valid_frames = h36m_coco_format(keypoints, scores) re_kpts = revise_kpts(keypoints, scores, valid_frames) print('Generating 2D pose successful!') diff --git a/fmpose3d/lib/hrnet/gen_kpts.py b/fmpose3d/lib/hrnet/gen_kpts.py index 6d4eef9..110d0a2 100755 --- a/fmpose3d/lib/hrnet/gen_kpts.py +++ b/fmpose3d/lib/hrnet/gen_kpts.py @@ -99,7 +99,7 @@ def model_load(config): return model -def gen_from_image(args, frame, people_sort, human_model, pose_model, det_dim=416, num_peroson=1, gen_output=False): +def gen_from_image(args, frame, people_sort, human_model, pose_model, det_dim=416, num_person=1, gen_output=False): bboxs, scores = yolo_det(frame, human_model, reso=det_dim, confidence=args.thred_score) if bboxs is None or not bboxs.any(): @@ -117,7 +117,7 @@ def gen_from_image(args, frame, people_sort, human_model, pose_model, det_dim=41 if people_track.shape[0] == 1: people_track_ = people_track[-1, :-1].reshape(1, 4) elif people_track.shape[0] >= 2: - people_track_ = people_track[-num_peroson:, :-1].reshape(num_peroson, 4) + people_track_ = people_track[-num_person:, :-1].reshape(num_person, 4) people_track_ = people_track_[::-1] else: return [], [] @@ -129,7 +129,7 @@ def gen_from_image(args, frame, people_sort, human_model, pose_model, det_dim=41 with torch.no_grad(): # bbox is coordinate location - inputs, origin_img, center, scale = PreProcess(frame, track_bboxs, cfg, num_peroson) + inputs, origin_img, center, scale = PreProcess(frame, track_bboxs, cfg, num_person) inputs = inputs[:, [2, 1, 0]] @@ -140,8 +140,8 @@ def gen_from_image(args, frame, people_sort, human_model, pose_model, det_dim=41 # compute coordinate preds, maxvals = get_final_preds(cfg, output.clone().cpu().numpy(), np.asarray(center), np.asarray(scale)) - kpts = np.zeros((num_peroson, 17, 2), dtype=np.float32) - scores = np.zeros((num_peroson, 17), dtype=np.float32) + kpts = np.zeros((num_person, 17, 2), dtype=np.float32) + scores = np.zeros((num_person, 17), dtype=np.float32) for i, kpt in enumerate(preds): kpts[i] = kpt @@ -151,7 +151,7 @@ def gen_from_image(args, frame, people_sort, human_model, pose_model, det_dim=41 return kpts, scores -def gen_video_kpts(path, det_dim=416, num_peroson=1, gen_output=False, type='image'): +def gen_video_kpts(path, det_dim=416, num_person=1, gen_output=False, type='image'): # Ensure checkpoints are downloaded only when HRNet is actually requested ensure_checkpoints() @@ -168,7 +168,7 @@ def gen_video_kpts(path, det_dim=416, num_peroson=1, gen_output=False, type='ima scores_result = [] if type == "image": frame = cv2.imread(path) - kpts, scores = gen_from_image(args1, frame, people_sort, human_model, pose_model, det_dim=det_dim, num_peroson=num_peroson, gen_output=gen_output) + kpts, scores = gen_from_image(args1, frame, people_sort, human_model, pose_model, det_dim=det_dim, num_person=num_person, gen_output=gen_output) kpts_result.append(kpts) scores_result.append(scores) @@ -180,7 +180,7 @@ def gen_video_kpts(path, det_dim=416, num_peroson=1, gen_output=False, type='ima if not ret: continue - kpts, scores = gen_from_image(args1, frame, people_sort, human_model, pose_model, det_dim=det_dim, num_peroson=num_peroson, gen_output=gen_output) + kpts, scores = gen_from_image(args1, frame, people_sort, human_model, pose_model, det_dim=det_dim, num_person=num_person, gen_output=gen_output) kpts_result.append(kpts) scores_result.append(scores) diff --git a/tests/test_demo_human.py b/tests/test_demo_human.py index ae8b71a..02d3133 100644 --- a/tests/test_demo_human.py +++ b/tests/test_demo_human.py @@ -38,7 +38,7 @@ def test_2d_pose_estimation(test_image_path, test_output_dir): from fmpose3d.lib.preprocess import h36m_coco_format, revise_kpts # Run 2D pose estimation - keypoints, scores = hrnet_pose(test_image_path, det_dim=416, num_peroson=1, gen_output=True, type='image') + keypoints, scores = hrnet_pose(test_image_path, det_dim=416, num_person=1, gen_output=True, type='image') # Check output shapes assert keypoints is not None, "Keypoints should not be None" From d6e1fdb7e5015b6e64e874e25ef32ea2684260ca Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 13:57:38 +0200 Subject: [PATCH 17/38] Remove build_production target from Makefile and update help message accordingly. This change simplifies the build process by eliminating the need for a user-defined source path. --- Makefile | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Makefile b/Makefile index 687beee..9a5c17a 100644 --- a/Makefile +++ b/Makefile @@ -48,18 +48,6 @@ stop: rm: docker rm $(CONTAINER_NAME) - -####################### -# BUILD CORE WITH SRC # -####################### -# [USER: ADJUST SRC PATH] -SRC_PATH="/home/user/project" -build_production: - cp -r $(SRC_PATH) src - docker build $(BUILD_ARGS) --build-arg src=src \ - -t $(IMG_NAME):$(IMG_TAG) -f $(DOCKERFILE) . - rm -r src - # Help message help: @echo "Available targets:" @@ -69,7 +57,6 @@ help: @echo " exec_bash - Attach to running container (bash)." @echo " stop - Stop the running container." @echo " rm - Remove the stopped container." - @echo " build_production - Build a Docker image with source code." @echo " help - Show this help message." @echo "" @echo "Usage:" From 3bb81e51dac783c7ae8c33b8aa547c8f523dc8e3 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:56:53 +0200 Subject: [PATCH 18/38] Fix typos in comments for clarity in lifter3d.py, correcting "matrics" to "matrices" and "projectio" to "projection". Additionally, update comment for testing section to reflect correct function name "triangulate". --- fmpose3d/animals/common/lifter3d.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fmpose3d/animals/common/lifter3d.py b/fmpose3d/animals/common/lifter3d.py index 4911a5a..afebffb 100644 --- a/fmpose3d/animals/common/lifter3d.py +++ b/fmpose3d/animals/common/lifter3d.py @@ -306,7 +306,7 @@ def triangulate_3d_batch(points_2d_batch, cameras): num_cams, num_frames, num_joints, _ = points_2d_batch.shape # (6, num_frames, 23, 2) print("num_cams,num_frames,num_joinits", points_2d_batch.shape) - # **1. compute projection matrics (6, 3, 4)** + # **1. compute projection matrices (6, 3, 4)** proj_matrices = np.array( [cam["intrinsic_matrix"] @ np.hstack((cam["R"], cam["T"])) for cam in cameras] ) # numpy array (6, 3, 4) @@ -361,7 +361,7 @@ def triangulate_3d(points_2d, cameras): for i, cam in enumerate(cameras): K, dist, R, T = cam["intrinsic_matrix"], cam["distortion_coeffs"], cam["R"], cam["T"] - P = K @ np.hstack((R, T)) # projectio matrix + P = K @ np.hstack((R, T)) # projection matrix # print("Projection Matrix P:\n", P) proj_matrices.append(P) @@ -628,7 +628,7 @@ def main(): # reprojected_2d = project_3d_to_2d(points_3d, cameras[i]) # # visualize_2d_on_video(video_files[i], frame_number, points_2d_frame[i], reprojected_2d, output_path) - # # test on trangulate 3D batch + # # test on triangulate 3D batch # left_frame_id = 10 # right_frame_id = 60 From f1631c954bbf8bfddf65382343411e6a6be353c9 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:57:04 +0200 Subject: [PATCH 19/38] Remove temporary weights validation sentinel from fmpose3d.py, streamlining the inference API code. This change reflects the transition towards a more permanent solution for weight management. --- fmpose3d/inference_api/fmpose3d.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/fmpose3d/inference_api/fmpose3d.py b/fmpose3d/inference_api/fmpose3d.py index 3a3936d..970a5e3 100644 --- a/fmpose3d/inference_api/fmpose3d.py +++ b/fmpose3d/inference_api/fmpose3d.py @@ -758,8 +758,6 @@ class _IngestedInput: # --------------------------------------------------------------------------- -# FIXME @deruyter92: THIS IS TEMPORARY UNTIL WE DOWNLOAD THE WEIGHTS FROM HUGGINGFACE -SKIP_WEIGHTS_VALIDATION = object() # sentinel value to indicate that the weights should not be validated class FMPose3DInference: """High-level, two-step inference API for FMPose3D. From 962f395ba9fcb7d8fcbeb56937b1bd63e7680b26 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:57:18 +0200 Subject: [PATCH 20/38] Fix typo in comment of bbox.py, correcting "corrdinates" to "coordinates" for improved clarity. --- fmpose3d/lib/yolov3/bbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fmpose3d/lib/yolov3/bbox.py b/fmpose3d/lib/yolov3/bbox.py index 60373d5..aef9ee4 100755 --- a/fmpose3d/lib/yolov3/bbox.py +++ b/fmpose3d/lib/yolov3/bbox.py @@ -66,7 +66,7 @@ def bbox_iou(box1, box2): b1_x1, b1_y1, b1_x2, b1_y2 = box1[:, 0], box1[:, 1], box1[:, 2], box1[:, 3] b2_x1, b2_y1, b2_x2, b2_y2 = box2[:, 0], box2[:, 1], box2[:, 2], box2[:, 3] - # get the corrdinates of the intersection rectangle + # get the coordinates of the intersection rectangle inter_rect_x1 = torch.max(b1_x1, b2_x1) inter_rect_y1 = torch.max(b1_y1, b2_y1) inter_rect_x2 = torch.min(b1_x2, b2_x2) From 09ce42fc3d9419abe7ca536bede437f3b7dafd20 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:57:31 +0200 Subject: [PATCH 21/38] Fix typo in comment of darknet.py, correcting "anotation" to "annotation" for improved clarity. --- fmpose3d/lib/yolov3/darknet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fmpose3d/lib/yolov3/darknet.py b/fmpose3d/lib/yolov3/darknet.py index a5e6e7d..177dd85 100755 --- a/fmpose3d/lib/yolov3/darknet.py +++ b/fmpose3d/lib/yolov3/darknet.py @@ -216,7 +216,7 @@ def create_modules(blocks): except: end = 0 - # Positive anotation + # Positive annotation if start > 0: start = start - index From 94b510057dab619da34c2586019142feab9da3c5 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:57:41 +0200 Subject: [PATCH 22/38] Fix typos in comments for clarity in human_detector.py, correcting "arguements" to "arguments" and "availible" to "available". --- fmpose3d/lib/yolov3/human_detector.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fmpose3d/lib/yolov3/human_detector.py b/fmpose3d/lib/yolov3/human_detector.py index 5f9cdab..b669d77 100755 --- a/fmpose3d/lib/yolov3/human_detector.py +++ b/fmpose3d/lib/yolov3/human_detector.py @@ -15,7 +15,6 @@ import os import sys import random -import pickle as pkl import argparse from fmpose3d.lib.yolov3.util import * @@ -63,7 +62,7 @@ def write(x, img, colors): def arg_parse(): """" - Parse arguements to the detect module + Parse arguments to the detect module """ parser = argparse.ArgumentParser(description='YOLO v3 Cam Demo') @@ -104,7 +103,7 @@ def load_model(args=None, CUDA=None, inp_dim=416): assert inp_dim % 32 == 0 assert inp_dim > 32 - # If there's a GPU availible, put the model on GPU + # If there's a GPU available, put the model on GPU if CUDA: model.cuda() From b2151a684fd311142f38d00992680caa94ac9a88 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:57:58 +0200 Subject: [PATCH 23/38] Remove unused import of plot_keypoint and write from gen_kpts.py, streamlining the code and improving clarity. --- fmpose3d/lib/hrnet/gen_kpts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fmpose3d/lib/hrnet/gen_kpts.py b/fmpose3d/lib/hrnet/gen_kpts.py index 110d0a2..0049997 100755 --- a/fmpose3d/lib/hrnet/gen_kpts.py +++ b/fmpose3d/lib/hrnet/gen_kpts.py @@ -24,7 +24,7 @@ import cv2 import copy -from fmpose3d.lib.hrnet.lib.utils.utilitys import plot_keypoint, PreProcess, write, load_json +from fmpose3d.lib.hrnet.lib.utils.utilitys import PreProcess from fmpose3d.lib.hrnet.lib.config import cfg, update_config from fmpose3d.lib.hrnet.lib.utils.transforms import * from fmpose3d.lib.hrnet.lib.utils.inference import get_final_preds From ef803e7d6d71621cd816b816d6e87a97e170920d Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:58:24 +0200 Subject: [PATCH 24/38] Fix typos in comments for clarity in utils.py, correcting "origial" to "original" and "parameteres" to "parameters". --- fmpose3d/common/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fmpose3d/common/utils.py b/fmpose3d/common/utils.py index 549ef2b..3fef672 100755 --- a/fmpose3d/common/utils.py +++ b/fmpose3d/common/utils.py @@ -378,7 +378,7 @@ def save_top_N_models( def back_to_ori_uv(cropped_uv, bb_box): """ - for cropped uv, back to origial uv to help do the uvd->xyz operation + for cropped uv, back to original uv to help do the uvd->xyz operation :return: """ N, T, V, _ = cropped_uv.size() @@ -453,7 +453,7 @@ def project_to_2d(X, camera_params): Arguments: X -- 3D points in *camera space* to transform (N, *, 3) - camera_params -- intrinsic parameteres (N, 2+2+3+2=9) + camera_params -- intrinsic parameters (N, 2+2+3+2=9) """ assert X.shape[-1] == 3 # B,J,3 assert len(camera_params.shape) == 2 # camera_params:[B,1,9] From bdd95b1ad11d54d038af604620fa2f7bb7cab2d1 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:58:35 +0200 Subject: [PATCH 25/38] Fix typo in animal3d_dataset.py, correcting "hight" to "height" for improved clarity in keypoint normalization. --- fmpose3d/animals/common/animal3d_dataset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fmpose3d/animals/common/animal3d_dataset.py b/fmpose3d/animals/common/animal3d_dataset.py index 15c9891..c69482b 100644 --- a/fmpose3d/animals/common/animal3d_dataset.py +++ b/fmpose3d/animals/common/animal3d_dataset.py @@ -38,9 +38,9 @@ def __getitem__(self, item): else: keypoint_2d = np.array(data.get("keypoint_2d", []), dtype=np.float32) # normalize 2D keypoints - hight = np.array(data["height"]) + height = np.array(data["height"]) width = np.array(data["width"]) - keypoint_2d = normalize_screen_coordinates(keypoint_2d[..., :2], width, hight) + keypoint_2d = normalize_screen_coordinates(keypoint_2d[..., :2], width, height) # build 3D keypoints; append ones; fallback to zeros if missing if "keypoint_3d" in data and data["keypoint_3d"] is not None: From 6107a7aad40f360cd9c6c3fd23a7b18d73759dd0 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:58:44 +0200 Subject: [PATCH 26/38] Fix typos in comments for clarity in utils.py, correcting "origial" to "original" and "parameteres" to "parameters". --- fmpose3d/animals/common/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fmpose3d/animals/common/utils.py b/fmpose3d/animals/common/utils.py index cdafd8c..9872dde 100755 --- a/fmpose3d/animals/common/utils.py +++ b/fmpose3d/animals/common/utils.py @@ -354,7 +354,7 @@ def save_top_N_models( def back_to_ori_uv(cropped_uv, bb_box): """ - for cropped uv, back to origial uv to help do the uvd->xyz operation + for cropped uv, back to original uv to help do the uvd->xyz operation :return: """ N, T, V, _ = cropped_uv.size() @@ -423,7 +423,7 @@ def project_to_2d(X, camera_params): Arguments: X -- 3D points in *camera space* to transform (N, *, 3) - camera_params -- intrinsic parameteres (N, 2+2+3+2=9) + camera_params -- intrinsic parameters (N, 2+2+3+2=9) """ assert X.shape[-1] == 3 # B,J,3 assert len(camera_params.shape) == 2 # camera_params:[B,1,9] From 2b1626d257232b4dd47f146e2a371c18af8c3cbb Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 14:59:20 +0200 Subject: [PATCH 27/38] Remove unused functions plot_keypoint, write, and load_json from utilitys.py to streamline the code and improve maintainability. --- fmpose3d/lib/hrnet/lib/utils/utilitys.py | 77 ------------------------ 1 file changed, 77 deletions(-) diff --git a/fmpose3d/lib/hrnet/lib/utils/utilitys.py b/fmpose3d/lib/hrnet/lib/utils/utilitys.py index fde7b3d..ba587ff 100755 --- a/fmpose3d/lib/hrnet/lib/utils/utilitys.py +++ b/fmpose3d/lib/hrnet/lib/utils/utilitys.py @@ -30,83 +30,6 @@ [170, 0, 255], [255, 0, 255]] -def plot_keypoint(image, coordinates, confidence, keypoint_thresh=0.3): - # USE cv2 - joint_visible = confidence[:, :, 0] > keypoint_thresh - coordinates = coco_h36m(coordinates) - for i in range(coordinates.shape[0]): - pts = coordinates[i] - - for joint in pts: - cv2.circle(image, (int(joint[0]), int(joint[1])), 8, (255, 255, 255), 1) - - for color_i, jp in zip(colors, h36m_pairs): - if joint_visible[i, jp[0]] and joint_visible[i, jp[1]]: - pt0 = pts[jp, 0] - pt1 = pts[jp, 1] - pt0_0, pt0_1, pt1_0, pt1_1 = int(pt0[0]), int(pt0[1]), int(pt1[0]), int(pt1[1]) - - cv2.line(image, (pt0_0, pt1_0), (pt0_1, pt1_1), color_i, 6) - # cv2.circle(image,(pt0_0, pt0_1), 2, color_i, thickness=-1) - # cv2.circle(image,(pt1_0, pt1_1), 2, color_i, thickness=-1) - return image - - -def write(x, img): - x = [int(i) for i in x] - c1 = tuple(x[0:2]) - c2 = tuple(x[2:4]) - - color = [0, 97, 255] - label = 'People {}'.format(x[-1]) - cv2.rectangle(img, c1, c2, color, 2) - t_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_PLAIN, 1, 1)[0] - c2 = c1[0] + t_size[0] + 3, c1[1] + t_size[1] + 4 - cv2.rectangle(img, c1, c2, [0, 128, 255], -1) - cv2.putText(img, label, (c1[0], c1[1] + t_size[1] + 4), cv2.FONT_HERSHEY_PLAIN, 1, [225, 255, 255], 1) - return img - - -def load_json(file_path): - with open(file_path, 'r') as fr: - video_info = json.load(fr) - - label = video_info['label'] - label_index = video_info['label_index'] - - num_frames = video_info['data'][-1]['frame_index'] - keypoints = np.zeros((2, num_frames, 17, 2), dtype=np.float32) # (M, T, N, 2) - scores = np.zeros((2, num_frames, 17), dtype=np.float32) # (M, T, N) - - for frame_info in video_info['data']: - frame_index = frame_info['frame_index'] - - for index, skeleton_info in enumerate(frame_info['skeleton']): - pose = skeleton_info['pose'] - score = skeleton_info['score'] - bbox = skeleton_info['bbox'] - - if len(bbox) == 0 or index+1 > 2: - continue - - pose = np.asarray(pose, dtype=np.float32) - score = np.asarray(score, dtype=np.float32) - score = score.reshape(-1) - - keypoints[index, frame_index-1] = pose - scores[index, frame_index-1] = score - - new_kpts = [] - for i in range(keypoints.shape[0]): - kps = keypoints[i] - if np.sum(kps) != 0.: - new_kpts.append(kps) - - new_kpts = np.asarray(new_kpts, dtype=np.float32) - scores = np.asarray(scores, dtype=np.float32) - scores = scores[:, :, :, np.newaxis] - return new_kpts, scores, label, label_index - def box_to_center_scale(box, model_image_width, model_image_height): """convert a box to center,scale information required for pose transformation From c944d25d12fd21ecbeca3df8e2bd9f245c62b134 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 15:14:53 +0200 Subject: [PATCH 28/38] Add testpaths configuration to pytest in pyproject.toml for improved test organization --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index b40c122..e7df467 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,6 +68,7 @@ profile = "black" line_length = 100 [tool.pytest.ini_options] +testpaths = ["tests"] markers = [ "functional: marks tests that require pretrained weights (deselect with '-m \"not functional\"')", "network: marks tests that may need internet access on first run (deselect with '-m \"not network\"')", From e18518582c2ed2eeda5626b3069d867cd7b245ed Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 17:26:49 +0200 Subject: [PATCH 29/38] Update Dockerfile to install FMPose3D from GitHub instead of PyPI, enhancing installation process by cloning the repository and cleaning up afterwards. --- Dockerfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 792ca64..67a41da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,9 +24,11 @@ ENV PATH="/opt/conda/bin:${PATH}" # Initialize conda for root just in case and fix symlinks RUN ln -fs /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh -# --- Install FMPose3D from PyPI (as documented in README) --- +# --- Install FMPose3D from GitHub --- RUN python -m pip install --no-cache-dir --upgrade pip && \ - python -m pip install --no-cache-dir "fmpose3d[animals,viz]" gdown + git clone --depth 1 https://github.com/AdaptiveMotorControlLab/FMPose3D.git /tmp/fmpose3d && \ + python -m pip install --no-cache-dir "/tmp/fmpose3d[animals,viz]" gdown && \ + rm -rf /tmp/fmpose3d # Allow non-root user to download DLC model weights at runtime RUN mkdir -p /opt/conda/lib/python3.11/site-packages/deeplabcut/modelzoo/checkpoints && \ From 11ab3d3f493d8d865573b381f8762828c5e57c26 Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 17:27:14 +0200 Subject: [PATCH 30/38] Update HF_REPO_ID --- fmpose3d/utils/weights.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fmpose3d/utils/weights.py b/fmpose3d/utils/weights.py index e35eab8..941e481 100644 --- a/fmpose3d/utils/weights.py +++ b/fmpose3d/utils/weights.py @@ -9,7 +9,7 @@ """Shared helpers for resolving / downloading FMPose3D model weights.""" -HF_REPO_ID: str = "deruyter92/fmpose_temp" +HF_REPO_ID: str = "MLAdaptiveIntelligence/FMPose3D" def resolve_weights_path(model_weights_path: str, model_type: str) -> str: From 2f5f771c39e5760880c22b50df5c134a04aad2ef Mon Sep 17 00:00:00 2001 From: ti Date: Tue, 7 Apr 2026 17:27:28 +0200 Subject: [PATCH 31/38] Update README.md to clarify pre-trained model setup, highlighting automatic downloads from Hugging Face and providing alternative options for local weights. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b421862..1005e6a 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,9 @@ pip install "fmpose3d[animals]" This visualization script is designed for single-frame based model, allowing you to easily run 3D human pose estimation on any single image. -Before testing, make sure you have the pre-trained model ready. -You may either use the model trained by your own or download ours from [here](https://drive.google.com/drive/folders/1235_UgUQXYZtjprBOv2ZJJHY2KOAS_6p?usp=sharing) and place it in the `./pre_trained_models` directory. +Pre-trained weights are downloaded automatically from [Hugging Face](https://huggingface.co/MLAdaptiveIntelligence/FMPose3D) the first time you run inference, so no manual setup is needed. + +Alternatively, you can use your own trained weights or download ours from [Google Drive](https://drive.google.com/drive/folders/1aRZ6t_6IxSfM1nCTFOUXcYVaOk-5koGA?usp=sharing), place them in the `./pre_trained_models` directory, and set `model_weights_path` in the shell script (e.g. `demo/vis_in_the_wild.sh`). Next, put your test images into folder `demo/images`. Then run the visualization script: ```bash @@ -93,7 +94,7 @@ sh ./scripts/FMPose3D_train.sh ### Inference -First, download the folder with pre-trained model from [here](https://drive.google.com/drive/folders/1235_UgUQXYZtjprBOv2ZJJHY2KOAS_6p?usp=sharing) and place it in the './pre_trained_models' directory. +Pre-trained weights are fetched automatically from Hugging Face on the first run. You can also use local weights by setting `model_weights_path` in the shell script (see [Demos](#testing-on-in-the-wild-images-humans) above for details). To run inference on Human3.6M: From 52c2497475e57486d17abd81c9bc60acb9145538 Mon Sep 17 00:00:00 2001 From: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:36:37 +0200 Subject: [PATCH 32/38] fix test_hugginface.py: change huggingface repo ID (use centralized constant `HF_REPO_ID`) --- tests/fmpose3d_api/test_huggingface.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/fmpose3d_api/test_huggingface.py b/tests/fmpose3d_api/test_huggingface.py index 39139b3..01217dc 100644 --- a/tests/fmpose3d_api/test_huggingface.py +++ b/tests/fmpose3d_api/test_huggingface.py @@ -21,6 +21,7 @@ import pytest from fmpose3d.inference_api.fmpose3d import FMPose3DInference +from fmpose3d.utils.weights import HF_REPO_ID class TestDownloadModelWeights: @@ -37,7 +38,7 @@ def test_calls_hf_hub_download_humans(self): api._download_model_weights() mock_dl.assert_called_once_with( - repo_id="deruyter92/fmpose_temp", + repo_id=HF_REPO_ID, filename="fmpose3d_humans.pth", ) assert api.model_weights_path == "/fake/cache/fmpose3d_humans.pth" @@ -52,7 +53,7 @@ def test_calls_hf_hub_download_animals(self): api._download_model_weights() mock_dl.assert_called_once_with( - repo_id="deruyter92/fmpose_temp", + repo_id=HF_REPO_ID, filename="fmpose3d_animals.pth", ) assert api.model_weights_path == "/fake/cache/fmpose3d_animals.pth" From 89ff316a6de7990bd3ef729f6df79059846f7cad Mon Sep 17 00:00:00 2001 From: ti Date: Wed, 8 Apr 2026 11:53:24 +0200 Subject: [PATCH 33/38] Refactor vis_in_the_wild.py: remove redundant import of resolve_weights_path and add it back in a more appropriate location for better code organization. --- demo/vis_in_the_wild.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/vis_in_the_wild.py b/demo/vis_in_the_wild.py index cfd4881..8f88819 100755 --- a/demo/vis_in_the_wild.py +++ b/demo/vis_in_the_wild.py @@ -19,6 +19,7 @@ from fmpose3d.lib.checkpoint.download_checkpoints import ensure_checkpoints ensure_checkpoints() +from fmpose3d.utils.weights import resolve_weights_path from fmpose3d.lib.preprocess import h36m_coco_format, revise_kpts from fmpose3d.lib.hrnet.gen_kpts import gen_video_kpts as hrnet_pose from fmpose3d.common.arguments import opts as parse_args @@ -278,7 +279,6 @@ def get_pose3D(path, output_dir, type='image'): # if args.reload: model_dict = model['CFM'].state_dict() - from fmpose3d.utils.weights import resolve_weights_path model_path = resolve_weights_path(args.model_weights_path, args.model_type) print(f"Loading weights from: {model_path}") From 9f46de8fb70f1c82d8d54d00c58f842d4e132a8a Mon Sep 17 00:00:00 2001 From: Ti Wang <81274389+xiu-cs@users.noreply.github.com> Date: Thu, 9 Apr 2026 12:08:48 +0200 Subject: [PATCH 34/38] Update Dockerfile Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 67a41da..85e0dd0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,8 +31,9 @@ RUN python -m pip install --no-cache-dir --upgrade pip && \ rm -rf /tmp/fmpose3d # Allow non-root user to download DLC model weights at runtime -RUN mkdir -p /opt/conda/lib/python3.11/site-packages/deeplabcut/modelzoo/checkpoints && \ - chown -R ${USERNAME}:${USERNAME} /opt/conda/lib/python3.11/site-packages/deeplabcut/modelzoo +RUN DLC_MODELZOO_DIR="$(python -c "import site, pathlib; print(pathlib.Path(site.getsitepackages()[0]) / 'deeplabcut' / 'modelzoo')")" \ + && mkdir -p "${DLC_MODELZOO_DIR}/checkpoints" \ + && chown -R "${USERNAME}:${USERNAME}" "${DLC_MODELZOO_DIR}" # Set your user as owner of the home directory before switching RUN chown -R ${USERNAME}:${USERNAME} /home/${USERNAME} From 478abcdda50456c463c6f4ed6ebaf9fc2a803c6d Mon Sep 17 00:00:00 2001 From: Ti Wang <81274389+xiu-cs@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:13:27 +0200 Subject: [PATCH 35/38] Update Makefile Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9a5c17a..d28099c 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,9 @@ project_name_lo := $(shell echo $(PROJECT_NAME) | tr '[:upper:]' '[:lower:]') IMG_NAME := fmpose3d IMG_TAG := v0.1 DOCKERFILE := Dockerfile -HOST_UID := $(shell id -u) -HOST_GID := $(shell id -g) +# Linux/macOS: use host UID/GID; Windows fallback to 1000 +HOST_UID ?= $(shell sh -c 'id -u 2>/dev/null || echo 1000') +HOST_GID ?= $(shell sh -c 'id -g 2>/dev/null || echo 1000') BUILD_ARGS := \ --build-arg USERNAME=fmpose3d \ --build-arg USER_GID=$(HOST_GID) \ From 8098412d28a0960b585235a106a70bce49d173c7 Mon Sep 17 00:00:00 2001 From: Ti Wang <81274389+xiu-cs@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:14:18 +0200 Subject: [PATCH 36/38] Update Makefile Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d28099c..c5613c5 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ CONTAINER_NAME := fmpose3d_dev1 HOST_SRC := $(shell pwd) DOCKER_SRC := /fmpose3d VOLUMES := \ - --volume $(HOST_SRC):$(DOCKER_SRC) + --volume "$(HOST_SRC):$(DOCKER_SRC)" run: From 1eb00b42e55e11dc4df32f314c3066f268a13d24 Mon Sep 17 00:00:00 2001 From: Ti Wang <81274389+xiu-cs@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:14:49 +0200 Subject: [PATCH 37/38] Update Makefile Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index c5613c5..1c07b32 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ # [USER: ADJUST PATH] PROJECT_NAME := fmpose3d -project_name_lo := $(shell echo $(PROJECT_NAME) | tr '[:upper:]' '[:lower:]') ####### # RUN # ####### From 8b402c399fe105c873d3593a940899c30c052ae6 Mon Sep 17 00:00:00 2001 From: ti Date: Thu, 9 Apr 2026 14:18:00 +0200 Subject: [PATCH 38/38] Refactor FMPose3D_main.py: move import of resolve_weights_path to the top for better organization and remove redundant import in the main execution block. --- scripts/FMPose3D_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/FMPose3D_main.py b/scripts/FMPose3D_main.py index 0a8ff98..172a482 100644 --- a/scripts/FMPose3D_main.py +++ b/scripts/FMPose3D_main.py @@ -18,6 +18,7 @@ import torch.optim as optim from tqdm import tqdm +from fmpose3d.utils.weights import resolve_weights_path from fmpose3d.common import opts, Human36mDataset, Fusion from fmpose3d.common.utils import * @@ -341,7 +342,6 @@ def print_error_action(action_error_sum, is_train): if args.reload: model_dict = model["CFM"].state_dict() - from fmpose3d.utils.weights import resolve_weights_path model_path = resolve_weights_path(args.model_weights_path, args.model_type) print(f"Loading weights from: {model_path}")