From 5bed0f0aaf011fc06d05d8586b1273d710445942 Mon Sep 17 00:00:00 2001 From: crosstyan Date: Wed, 11 Mar 2026 22:03:42 +0800 Subject: [PATCH] Remove ONNX and Skelda integration --- .gitmodules | 3 - README.md | 46 +- data/q1/vis_steps.py | 326 -- dockerfile | 30 +- extras/jetson/README.md | 145 - extras/jetson/RESULTS.md | 2801 ----------------- extras/jetson/docker-compose-2d.yml | 37 - extras/jetson/dockerfile | 25 - extras/mmdeploy/README.md | 131 - extras/mmdeploy/add_extra_steps.py | 233 -- .../detection_onnxruntime_static-320x320.py | 18 - ...tection_onnxruntime_static-320x320_fp16.py | 18 - ...ction_simcc_onnxruntime_dynamic-384x288.py | 19 - ..._simcc_onnxruntime_dynamic-384x288_fp16.py | 19 - ...ection_simcc_onnxruntime_static-384x288.py | 8 - ...n_simcc_onnxruntime_static-384x288_fp16.py | 8 - extras/mmdeploy/dockerfile | 41 - extras/mmdeploy/exports/.gitignore | 2 - extras/mmdeploy/make_extra_graphs_pt.py | 338 -- extras/mmdeploy/make_extra_graphs_tf.py | 276 -- extras/mmdeploy/run_container.sh | 9 - extras/mmdeploy/testimages/human-pose.jpeg | Bin 39020 -> 0 bytes extras/mmpose/README.md | 23 - ...ose-l_8xb32-270e_coco-wholebody-384x288.py | 235 -- extras/mmpose/dockerfile | 9 - extras/mmpose/run_container.sh | 11 - extras/ros/README.md | 6 +- extras/ros/docker-compose-2d.yml | 71 - extras/ros/docker-compose-3d.yml | 2 - extras/ros/dockerfile_2d | 67 - extras/ros/pose2d_visualizer/package.xml | 20 - .../pose2d_visualizer/__init__.py | 0 .../pose2d_visualizer/pose2d_visualizer.py | 158 - .../resource/pose2d_visualizer | 0 extras/ros/pose2d_visualizer/setup.cfg | 4 - extras/ros/pose2d_visualizer/setup.py | 23 - .../pose2d_visualizer/test/test_copyright.py | 27 - .../ros/pose2d_visualizer/test/test_flake8.py | 25 - .../ros/pose2d_visualizer/test/test_pep257.py | 23 - extras/ros/rpt2d_wrapper_cpp/CMakeLists.txt | 69 - extras/ros/rpt2d_wrapper_cpp/package.xml | 25 - .../rpt2d_wrapper_cpp/src/rpt2d_wrapper.cpp | 237 -- pyproject.toml | 2 +- run_container.sh | 1 - scripts/test_skelda_dataset.cpp | 327 -- scripts/test_skelda_dataset.py | 513 --- scripts/test_triangulate.py | 144 - scripts/utils_2d_pose.hpp | 1161 ------- scripts/utils_pipeline.hpp | 316 -- scripts/utils_pipeline.py | 189 -- skelda | 1 - tests/README.md | 25 - tests/test_utils2d.cpp | 96 - 53 files changed, 6 insertions(+), 8337 deletions(-) delete mode 100644 .gitmodules delete mode 100644 data/q1/vis_steps.py delete mode 100644 extras/jetson/README.md delete mode 100644 extras/jetson/RESULTS.md delete mode 100644 extras/jetson/docker-compose-2d.yml delete mode 100644 extras/jetson/dockerfile delete mode 100644 extras/mmdeploy/README.md delete mode 100644 extras/mmdeploy/add_extra_steps.py delete mode 100644 extras/mmdeploy/configs/detection_onnxruntime_static-320x320.py delete mode 100644 extras/mmdeploy/configs/detection_onnxruntime_static-320x320_fp16.py delete mode 100644 extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_dynamic-384x288.py delete mode 100644 extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_dynamic-384x288_fp16.py delete mode 100644 extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_static-384x288.py delete mode 100644 extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_static-384x288_fp16.py delete mode 100644 extras/mmdeploy/dockerfile delete mode 100644 extras/mmdeploy/exports/.gitignore delete mode 100644 extras/mmdeploy/make_extra_graphs_pt.py delete mode 100644 extras/mmdeploy/make_extra_graphs_tf.py delete mode 100755 extras/mmdeploy/run_container.sh delete mode 100644 extras/mmdeploy/testimages/human-pose.jpeg delete mode 100644 extras/mmpose/README.md delete mode 100644 extras/mmpose/configs/rtmpose-l_8xb32-270e_coco-wholebody-384x288.py delete mode 100644 extras/mmpose/dockerfile delete mode 100755 extras/mmpose/run_container.sh delete mode 100644 extras/ros/docker-compose-2d.yml delete mode 100644 extras/ros/dockerfile_2d delete mode 100644 extras/ros/pose2d_visualizer/package.xml delete mode 100644 extras/ros/pose2d_visualizer/pose2d_visualizer/__init__.py delete mode 100644 extras/ros/pose2d_visualizer/pose2d_visualizer/pose2d_visualizer.py delete mode 100644 extras/ros/pose2d_visualizer/resource/pose2d_visualizer delete mode 100644 extras/ros/pose2d_visualizer/setup.cfg delete mode 100644 extras/ros/pose2d_visualizer/setup.py delete mode 100644 extras/ros/pose2d_visualizer/test/test_copyright.py delete mode 100644 extras/ros/pose2d_visualizer/test/test_flake8.py delete mode 100644 extras/ros/pose2d_visualizer/test/test_pep257.py delete mode 100644 extras/ros/rpt2d_wrapper_cpp/CMakeLists.txt delete mode 100644 extras/ros/rpt2d_wrapper_cpp/package.xml delete mode 100644 extras/ros/rpt2d_wrapper_cpp/src/rpt2d_wrapper.cpp delete mode 100644 scripts/test_skelda_dataset.cpp delete mode 100644 scripts/test_skelda_dataset.py delete mode 100644 scripts/test_triangulate.py delete mode 100644 scripts/utils_2d_pose.hpp delete mode 100644 scripts/utils_pipeline.hpp delete mode 100644 scripts/utils_pipeline.py delete mode 160000 skelda delete mode 100644 tests/test_utils2d.cpp diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 4c97fe8..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "skelda"] - path = skelda - url = https://gitlab.com/Percipiote/skelda.git diff --git a/README.md b/README.md index e087b90..c60436a 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ A general overview can be found in the paper [RapidPoseTriangulation: Multi-view ## Build -- Clone this project with submodules: +- Clone this project: ```bash - git clone --recurse-submodules https://gitlab.com/Percipiote/RapidPoseTriangulation.git + git clone https://gitlab.com/Percipiote/RapidPoseTriangulation.git cd RapidPoseTriangulation/ ``` @@ -57,56 +57,14 @@ A general overview can be found in the paper [RapidPoseTriangulation: Multi-view uv sync --group dev uv run pytest tests/test_interface.py uv build - - cd /RapidPoseTriangulation/scripts/ && \ - g++ -std=c++2a -fPIC -O3 -march=native -Wall -Werror -flto=auto \ - -I /RapidPoseTriangulation/rpt/ \ - -isystem /usr/include/opencv4/ \ - -isystem /onnxruntime/include/ \ - -isystem /onnxruntime/include/onnxruntime/core/session/ \ - -isystem /onnxruntime/include/onnxruntime/core/providers/tensorrt/ \ - -L /onnxruntime/build/Linux/Release/ \ - test_skelda_dataset.cpp \ - /RapidPoseTriangulation/rpt/*.cpp \ - -o test_skelda_dataset.bin \ - -Wl,--start-group \ - -lonnxruntime_providers_tensorrt \ - -lonnxruntime_providers_shared \ - -lonnxruntime_providers_cuda \ - -lonnxruntime \ - -Wl,--end-group \ - $(pkg-config --libs opencv4) \ - -Wl,-rpath,/onnxruntime/build/Linux/Release/ \ - && cd .. - ``` - -- Download _ONNX_ models from [model registry](https://gitlab.com/Percipiote/RapidPoseTriangulation/-/ml/models) and save them to `mmdeploy/extras/exports/`. - Upon the first usage, they will be converted to _TensorRT_ models, which will take a few minutes. \ - (Note that this conversion is not deterministic and will each time result in slightly different models and therefore also slightly different benchmark results.) - -- Test with samples: - - ```bash - python3 /RapidPoseTriangulation/scripts/test_triangulate.py - ``` - -- Test with [skelda](https://gitlab.com/Percipiote/skelda/) dataset: - - ```bash - export CUDA_VISIBLE_DEVICES=0 - python3 /RapidPoseTriangulation/scripts/test_skelda_dataset.py ```
## Extras -- Exporting tools for 2D models are at [mmdeploy](extras/mmdeploy/README.md) directory. - - For usage in combination with ROS2 see [ros](extras/ros/README.md) directory. -- Running on a Nvidia Jetson is also possible following [jetson](extras/jetson/README.md) directory. -
## Citation diff --git a/data/q1/vis_steps.py b/data/q1/vis_steps.py deleted file mode 100644 index 3b8ffdb..0000000 --- a/data/q1/vis_steps.py +++ /dev/null @@ -1,326 +0,0 @@ -import json -import os - -import cv2 -import matplotlib.pyplot as plt -import numpy as np - -from skelda import utils_pose, utils_view - -# ================================================================================================== - -filepath = os.path.dirname(os.path.realpath(__file__)) + "/" - -core_triangs = [ - [ - [0.287, -0.282, 1.264, 1.000], - [0.504, -0.052, 1.272, 1.000], - [0.276, -0.160, 0.764, 1.000], - [0.443, -0.099, 0.768, 1.000], - [0.258, -0.313, 0.999, 1.000], - [0.513, -0.009, 1.008, 1.000], - [0.204, -0.126, 0.439, 1.000], - [0.422, -0.132, 0.436, 1.000], - [0.195, -0.265, 0.807, 1.000], - [0.415, 0.039, 0.823, 1.000], - [0.113, -0.103, 0.096, 1.000], - [0.389, -0.175, 0.097, 1.000], - ], - [ - [0.322, -0.192, 1.349, 1.000], - [0.268, -0.594, 1.336, 1.000], - [0.272, -0.100, 0.882, 1.000], - [0.281, -0.379, 0.870, 1.000], - [0.336, -0.104, 1.124, 1.000], - [0.249, -0.578, 1.089, 1.000], - [0.229, 0.009, 0.571, 1.000], - [0.269, -0.345, 0.553, 1.000], - [0.289, -0.016, 0.951, 1.000], - [0.216, -0.327, 0.908, 1.000], - [0.188, 0.128, 0.268, 1.000], - [0.267, -0.273, 0.243, 1.000], - ], - [ - [0.865, 1.058, 1.613, 1.000], - [0.862, 0.870, 1.604, 1.000], - [0.927, 1.562, 1.491, 1.000], - [0.954, 1.505, 1.486, 1.000], - [0.908, 1.309, 1.542, 1.000], - [0.905, 1.170, 1.525, 1.000], - [0.968, 1.911, 1.454, 1.000], - [1.019, 1.919, 1.457, 1.000], - [0.921, 1.542, 1.514, 1.000], - [0.931, 1.539, 1.506, 1.000], - [1.008, 2.230, 1.455, 1.000], - [1.071, 2.271, 1.460, 1.000], - ], - [ - [-0.260, 0.789, 1.316, 1.000], - [0.039, 1.073, 1.322, 1.000], - [-0.236, 0.798, 0.741, 1.000], - [-0.048, 0.952, 0.759, 1.000], - [-0.315, 0.734, 0.995, 1.000], - [0.080, 1.026, 1.046, 1.000], - [-0.291, 0.721, 0.339, 1.000], - [-0.101, 0.887, 0.366, 1.000], - [-0.300, 0.600, 0.742, 1.000], - [0.066, 0.768, 0.897, 1.000], - [-0.381, 0.685, -0.113, 1.000], - [-0.169, 0.775, -0.040, 1.000], - ], - [ - [-0.199, 0.854, 1.414, 1.000], - [-0.401, 0.566, 1.409, 1.000], - [-0.242, 0.818, 0.870, 1.000], - [-0.343, 0.654, 0.856, 1.000], - [-0.176, 0.903, 1.140, 1.000], - [-0.398, 0.480, 1.132, 1.000], - [-0.245, 0.812, 0.492, 1.000], - [-0.380, 0.642, 0.471, 1.000], - [-0.145, 0.817, 0.912, 1.000], - [-0.251, 0.396, 0.973, 1.000], - [-0.255, 0.879, 0.107, 1.000], - [-0.383, 0.633, 0.116, 1.000], - ], - [ - [0.641, 1.796, 1.681, 1.000], - [0.603, 1.719, 1.680, 1.000], - [0.711, 2.000, 1.518, 1.000], - [0.706, 1.970, 1.515, 1.000], - [0.689, 1.920, 1.588, 1.000], - [0.651, 1.784, 1.585, 1.000], - [0.786, 2.190, 1.448, 1.000], - [0.780, 2.167, 1.444, 1.000], - [0.747, 1.994, 1.531, 1.000], - [0.720, 1.783, 1.546, 1.000], - [0.868, 2.432, 1.427, 1.000], - [0.849, 2.341, 1.410, 1.000], - ], -] - -core_joints = [ - "shoulder_left", - "shoulder_right", - "hip_left", - "hip_right", - "elbow_left", - "elbow_right", - "knee_left", - "knee_right", - "wrist_left", - "wrist_right", - "ankle_left", - "ankle_right", -] - -poses_2d = [ - [ - [ - [383.443, 144.912, 0.923], - [382.629, 135.143, 0.83], - [374.488, 134.329, 1.0], - [349.251, 136.771, 0.478], - [343.552, 139.213, 1.0], - [356.578, 201.899, 0.73], - [323.2, 201.899, 0.825], - [357.392, 282.494, 0.663], - [324.014, 289.821, 0.854], - [378.558, 339.481, 0.621], - [355.764, 356.577, 0.821], - [370.417, 357.391, 0.714], - [332.155, 359.834, 0.71], - [391.584, 452.641, 0.768], - [331.341, 458.34, 0.789], - [414.379, 547.076, 0.864], - [332.969, 550.333, 0.9], - [351.286, 358.613, 0.71], - [339.889, 201.899, 0.73], - [346.402, 137.992, 0.478], - ], - [ - [640.948, 116.443, 0.908], - [650.057, 100.249, 0.788], - [642.972, 100.249, 0.681], - [682.445, 103.285, 0.862], - [684.469, 100.249, 0.518], - [707.748, 181.219, 0.836], - [693.578, 180.207, 0.705], - [702.688, 290.528, 0.867], - [664.227, 276.358, 0.786], - [662.202, 375.547, 0.847], - [605.523, 319.88, 0.881], - [692.566, 373.522, 0.758], - [679.409, 372.51, 0.739], - [679.409, 500.038, 0.844], - [672.324, 494.978, 0.837], - [679.409, 635.663, 0.913], - [659.166, 599.226, 0.894], - [685.987, 373.016, 0.739], - [700.663, 180.713, 0.705], - [683.457, 101.767, 0.518], - ], - ], - [ - [ - [495.125, 304.671, 0.581], - [492.338, 301.885, 0.462], - [502.091, 301.885, 0.295], - [495.125, 308.851, 0.92], - [528.562, 306.064, 0.754], - [477.013, 359.703, 0.822], - [557.819, 355.523, 0.855], - [466.564, 431.452, 0.855], - [565.481, 425.879, 0.836], - [458.902, 480.911, 0.85], - [544.583, 464.889, 0.596], - [491.642, 490.663, 0.741], - [539.707, 492.056, 0.746], - [480.496, 569.379, 0.779], - [531.348, 577.041, 0.784], - [464.475, 646.005, 0.872], - [518.809, 661.33, 0.913], - [515.675, 491.36, 0.741], - [517.416, 357.613, 0.822], - [511.843, 307.458, 0.754], - ], - [ - [472.982, 273.983, 0.911], - [477.875, 266.645, 0.848], - [464.421, 266.645, 0.896], - [483.378, 268.48, 0.599], - [448.521, 268.48, 0.88], - [493.163, 308.841, 0.753], - [425.894, 311.899, 0.837], - [502.336, 363.268, 0.625], - [417.944, 368.16, 0.847], - [499.278, 407.91, 0.495], - [438.736, 410.357, 0.844], - [484.602, 426.868, 0.682], - [448.521, 427.48, 0.681], - [485.825, 504.534, 0.745], - [441.794, 505.757, 0.796], - [489.494, 571.803, 0.688], - [442.405, 577.918, 0.867], - [466.561, 427.174, 0.681], - [459.528, 310.37, 0.753], - [465.95, 268.48, 0.599], - ], - [ - [702.349, 208.747, 0.215], - [705.862, 207.944, 0.212], - [700.341, 207.944, 0.209], - [708.472, 196.399, 0.182], - [699.538, 196.299, 0.193], - [708.071, 196.7, 0.194], - [696.927, 196.098, 0.22], - [709.175, 206.137, 0.191], - [696.526, 206.94, 0.188], - [707.368, 210.052, 0.128], - [699.738, 209.751, 0.145], - [704.658, 215.172, 0.172], - [701.445, 215.273, 0.168], - [705.159, 224.007, 0.179], - [703.654, 225.211, 0.185], - [705.059, 225.914, 0.23], - [704.457, 230.532, 0.241], - [703.051, 215.223, 0.168], - [702.499, 196.399, 0.194], - [704.005, 196.349, 0.182], - ], - ], -] - -joints_2d = [ - "nose", - "eye_left", - "eye_right", - "ear_left", - "ear_right", - "shoulder_left", - "shoulder_right", - "elbow_left", - "elbow_right", - "wrist_left", - "wrist_right", - "hip_left", - "hip_right", - "knee_left", - "knee_right", - "ankle_left", - "ankle_right", - "hip_middle", - "shoulder_middle", - "head", -] - - -# ================================================================================================== - - -def main(): - - with open(os.path.join(filepath, "sample.json"), "r", encoding="utf-8") as file: - sample = json.load(file) - - camparams = sample["cameras"] - roomparams = { - "room_size": sample["room_size"], - "room_center": sample["room_center"], - } - - fig2 = utils_view.draw_poses3d(core_triangs, core_joints, roomparams, camparams) - fig2.axes[0].view_init(elev=30, azim=0) - fig2.savefig(os.path.join(filepath, "core-triangs.png"), dpi=fig2.dpi) - - core_projections = [] - for i in range(len(camparams)): - b2d, _ = utils_pose.project_poses(np.array(core_triangs), camparams[i]) - core_projections.append(b2d) - - img_size = [900, 900] - scale = 0.66 - fig_size = 2 - plotsize = (35, 30) - fig, axs = plt.subplots(fig_size, fig_size, figsize=plotsize) - fig.suptitle("core reprojections", fontsize=20) - fig.tight_layout(rect=[0, 0, 1, 0.97]) - - num_persons = max((len(b2d) for b2d in core_projections)) - colors = plt.cm.hsv(np.linspace(0, 1, num_persons, endpoint=False)).tolist() - colors = [[int(c[0] * 255), int(c[1] * 255), int(c[2] * 255)] for c in colors] - - for i in range(len(camparams)): - img = np.ones((img_size[0], img_size[1], 3), dtype=np.uint8) * 255 - - for j in range(len(core_projections[i])): - color = colors[j] - body = np.array(core_projections[i][j]) - img = utils_view.draw_body_in_image(img, body, core_joints, color) - - for k in range(len(poses_2d[i])): - body = np.array(poses_2d[i][k]) - cjids = [joints_2d.index(j) for j in core_joints] - body = body[cjids] - img = utils_view.draw_body_in_image( - img, body, core_joints, [0, 0, 0], thickness=2 - ) - - img = cv2.resize(img, (int(img.shape[1] * scale), int(img.shape[0] * scale))) - title = str(i) - - x, y = divmod(i, fig_size) - axs[x][y].imshow(img) - axs[x][y].set_title(title) - - # Delete empty plots - for i in range(2, fig_size**2): - x, y = divmod(i, fig_size) - fig.delaxes(axs[x][y]) - - fig.savefig(os.path.join(filepath, "core-reprojections.png"), dpi=fig.dpi) - - -# ================================================================================================== - -if __name__ == "__main__": - main() diff --git a/dockerfile b/dockerfile index 347de75..beda08b 100644 --- a/dockerfile +++ b/dockerfile @@ -12,7 +12,7 @@ RUN pip uninstall -y opencv-python && pip install --no-cache-dir "opencv-python< # Show matplotlib images RUN apt-get update && apt-get install -y --no-install-recommends python3-tk -# Update pip to allow installation of skelda in editable mode +# Python build frontend RUN pip3 install --upgrade --no-cache-dir pip # Install build dependencies @@ -20,33 +20,5 @@ RUN apt-get update && apt-get install -y --no-install-recommends build-essential RUN apt-get update && apt-get install -y --no-install-recommends libopencv-dev RUN pip3 install --no-cache-dir uv -# Install ONNX runtime -# See: https://github.com/microsoft/onnxruntime/blob/main/dockerfiles/Dockerfile.tensorrt -RUN pip3 uninstall -y onnxruntime-gpu -RUN pip3 install --no-cache-dir psutil -RUN git clone --recursive --depth=1 --branch=v1.20.1 https://github.com/Microsoft/onnxruntime.git -# Next line fixes: https://github.com/microsoft/onnxruntime/issues/24861 -RUN cat /onnxruntime/cmake/deps.txt && \ - sed -i 's/;be8be39fdbc6e60e94fa7870b280707069b5b81a/;32b145f525a8308d7ab1c09388b2e288312d8eba/g' /onnxruntime/cmake/deps.txt && \ - cat /onnxruntime/cmake/deps.txt -ENV PATH=/usr/local/nvidia/bin:/usr/local/cuda/bin:/cmake-3.30.1-linux-x86_64/bin:${PATH} -ARG CMAKE_CUDA_ARCHITECTURES=75;80;90 -ENV TRT_VERSION=10.5.0.18 -RUN /bin/sh onnxruntime/dockerfiles/scripts/install_common_deps.sh \ - && /bin/sh onnxruntime/dockerfiles/scripts/checkout_submodules.sh ${trt_version} - -RUN cd onnxruntime && \ - /bin/sh build.sh --allow_running_as_root --parallel --build_shared_lib \ - --cuda_home /usr/local/cuda --cudnn_home /usr/lib/x86_64-linux-gnu/ --use_tensorrt \ - --tensorrt_home /usr/lib/x86_64-linux-gnu/ --config Release --build_wheel --skip_tests \ - --skip_submodule_sync --cmake_extra_defines '"CMAKE_CUDA_ARCHITECTURES='${CMAKE_CUDA_ARCHITECTURES}'"' -RUN cd onnxruntime && pip install build/Linux/Release/dist/*.whl - -# Install skelda -RUN pip3 install --upgrade --no-cache-dir scipy -RUN apt-get update && apt-get install -y --no-install-recommends xvfb -COPY ./skelda/ /skelda/ -RUN pip3 install --no-cache-dir -e /skelda/ - WORKDIR /RapidPoseTriangulation/ CMD ["/bin/bash"] diff --git a/extras/jetson/README.md b/extras/jetson/README.md deleted file mode 100644 index 079ba2c..0000000 --- a/extras/jetson/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# Setup with Nvidia-Jetson-Orin - -Initial setup and installation of _RapidPoseTriangulation_ on a _Nvidia Jetson_ device. \ -Tested with a _Jetson AGX Orin Developer Kit_ module. - -
- -## Base installation - -- Install newest software image: \ - () - - - Use manual recovery mode setup for first installation - - - Find out the _ip-address_ of the _Jetson_ for the runtime component installation with: - - ```bash - sudo nmap -sn $(ip route get 1 | awk '{print $(NF-2);exit}')/24 - ``` - -- Initialize system: \ - () - - - Connect via _ssh_, because using _screen_ did not work, skip _oem-config_ step - - - Skip installation of _nvidia-jetpack_ - -- Install basic tools: - - ```bash - sudo apt install -y curl nano wget git - ``` - -- Update hostname: - - ```bash - sudo nano /etc/hostname - sudo nano /etc/hosts - sudo reboot - ``` - -- Enable maximum performance mode: - - ```bash - sudo nvpmodel -m 0 - sudo jetson_clocks - ``` - -- Test docker is working: - - ```bash - sudo docker run --rm hello-world - ``` - -- Enable _docker_ without _sudo_: \ - () - -- Enable GPU-access for docker building: - - - Run `sudo nano /etc/docker/daemon.json` and add: - - ```json - { - "runtimes": { - "nvidia": { - "args": [], - "path": "nvidia-container-runtime" - } - }, - "default-runtime": "nvidia" - } - ``` - - - Restart docker: `sudo systemctl restart docker` - -- Test docker is working: - - ```bash - docker run --rm hello-world - docker run -it --rm --runtime=nvidia --network=host dustynv/onnxruntime:1.20-r36.4.0 - ``` - -
- -## RPT installation - -- Build docker container: - - ```bash - docker build --progress=plain -f extras/jetson/dockerfile -t rapidposetriangulation . - ./run_container.sh - ``` - -- Build _rpt_ package inside container: - - ```bash - cd /RapidPoseTriangulation/ - uv sync --group dev - uv run pytest tests/test_interface.py - uv build - - cd /RapidPoseTriangulation/scripts/ && \ - g++ -std=c++2a -fPIC -O3 -march=native -Wall -Werror -flto=auto \ - -I /RapidPoseTriangulation/rpt/ \ - -isystem /usr/include/opencv4/ \ - -isystem /usr/local/include/onnxruntime/ \ - -L /usr/local/lib/ \ - test_skelda_dataset.cpp \ - /RapidPoseTriangulation/rpt/*.cpp \ - -o test_skelda_dataset.bin \ - -Wl,--start-group \ - -lonnxruntime_providers_tensorrt \ - -lonnxruntime_providers_shared \ - -lonnxruntime_providers_cuda \ - -lonnxruntime \ - -Wl,--end-group \ - $(pkg-config --libs opencv4) \ - -Wl,-rpath,/onnxruntime/build/Linux/Release/ \ - && cd .. - ``` - -- Test with samples: - - ```bash - python3 /RapidPoseTriangulation/scripts/test_triangulate.py - ``` - -
- -## ROS interface - -- Build docker container: - - ```bash - docker build --progress=plain -f extras/ros/dockerfile_2d -t rapidposetriangulation_ros2d . - ``` - -- Run and test: - - ```bash - export CAMID="camera01" && docker compose -f extras/jetson/docker-compose-2d.yml up - - docker exec -it jetson-test_node-1 bash - export ROS_DOMAIN_ID=18 - ``` diff --git a/extras/jetson/RESULTS.md b/extras/jetson/RESULTS.md deleted file mode 100644 index e61d4ef..0000000 --- a/extras/jetson/RESULTS.md +++ /dev/null @@ -1,2801 +0,0 @@ -# Evaluation Results - -Results of the model in various experiments on different datasets. \ -(Note that batching poses did not work due to insufficient memory) - -### Human36m - -```json -{ - "img_loading": 0.0302383, - "demosaicing": 0.000569236, - "avg_time_2d": 0.0182207, - "avg_time_3d": 8.59194e-05, - "fps": 52.9777 -} -{ - "triangulator_calls": 600, - "init_time": 7.91138e-06, - "undistort_time": 6.66517e-06, - "project_time": 1.07465e-07, - "match_time": 4.032e-08, - "pairs_time": 5.46962e-07, - "pair_scoring_time": 1.78561e-05, - "grouping_time": 2.83546e-06, - "full_time": 2.12859e-05, - "merge_time": 7.70588e-06, - "post_time": 1.15723e-05, - "convert_time": 1.67303e-07, - "total_time": 7.7241e-05 -} -{ - "person_nums": { - "total_frames": 600, - "total_labels": 600, - "total_preds": 600, - "considered_empty": 0, - "valid_preds": 600, - "invalid_preds": 0, - "missing": 0, - "invalid_fraction": 0.0, - "precision": 1.0, - "recall": 1.0, - "f1": 1.0, - "non_empty": 600 - }, - "mpjpe": { - "count": 600, - "mean": 0.061492, - "median": 0.053155, - "std": 0.028911, - "sem": 0.001181, - "min": 0.036169, - "max": 0.188557, - "recall-0.025": 0.0, - "recall-0.05": 0.333333, - "recall-0.1": 0.931667, - "recall-0.15": 0.95, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 600, - "ap-0.025": 0.0, - "ap-0.05": 0.191369, - "ap-0.1": 0.883717, - "ap-0.15": 0.912197, - "ap-0.25": 1.0, - "ap-0.5": 1.0 - }, - "nose": { - "count": 600, - "mean": 0.107604, - "median": 0.095831, - "std": 0.037702, - "sem": 0.00154, - "min": 0.024352, - "max": 0.258607, - "recall-0.025": 0.003333, - "recall-0.05": 0.023333, - "recall-0.1": 0.556667, - "recall-0.15": 0.865, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 600 - }, - "shoulder_left": { - "count": 600, - "mean": 0.035969, - "median": 0.026188, - "std": 0.033887, - "sem": 0.001385, - "min": 0.000962, - "max": 0.170539, - "recall-0.025": 0.461667, - "recall-0.05": 0.82, - "recall-0.1": 0.941667, - "recall-0.15": 0.965, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 600 - }, - "shoulder_right": { - "count": 600, - "mean": 0.044382, - "median": 0.032033, - "std": 0.040034, - "sem": 0.001636, - "min": 0.003562, - "max": 0.211991, - "recall-0.025": 0.318333, - "recall-0.05": 0.786667, - "recall-0.1": 0.918333, - "recall-0.15": 0.94, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 600 - }, - "elbow_left": { - "count": 600, - "mean": 0.045168, - "median": 0.03552, - "std": 0.036168, - "sem": 0.001478, - "min": 0.002655, - "max": 0.19497, - "recall-0.025": 0.251667, - "recall-0.05": 0.781667, - "recall-0.1": 0.935, - "recall-0.15": 0.953333, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 600 - }, - "elbow_right": { - "count": 600, - "mean": 0.04269, - "median": 0.031891, - "std": 0.035634, - "sem": 0.001456, - "min": 0.004397, - "max": 0.30887, - "recall-0.025": 0.258333, - "recall-0.05": 0.818333, - "recall-0.1": 0.931667, - "recall-0.15": 0.946667, - "recall-0.25": 0.998333, - "recall-0.5": 1.0, - "num_labels": 600 - }, - "wrist_left": { - "count": 600, - "mean": 0.04157, - "median": 0.024197, - "std": 0.045672, - "sem": 0.001866, - "min": 0.000785, - "max": 0.307112, - "recall-0.025": 0.508333, - "recall-0.05": 0.75, - "recall-0.1": 0.895, - "recall-0.15": 0.941667, - "recall-0.25": 0.996667, - "recall-0.5": 1.0, - "num_labels": 600 - }, - "wrist_right": { - "count": 599, - "mean": 0.046022, - "median": 0.027678, - "std": 0.052245, - "sem": 0.002136, - "min": 0.002387, - "max": 0.456498, - "recall-0.025": 0.45, - "recall-0.05": 0.766667, - "recall-0.1": 0.891667, - "recall-0.15": 0.905, - "recall-0.25": 0.99, - "recall-0.5": 0.998333, - "num_labels": 600 - }, - "hip_left": { - "count": 600, - "mean": 0.068516, - "median": 0.057202, - "std": 0.035751, - "sem": 0.001461, - "min": 0.029037, - "max": 0.217634, - "recall-0.025": 0.0, - "recall-0.05": 0.263333, - "recall-0.1": 0.885, - "recall-0.15": 0.946667, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 600 - }, - "hip_right": { - "count": 600, - "mean": 0.084867, - "median": 0.081572, - "std": 0.028768, - "sem": 0.001175, - "min": 0.016043, - "max": 0.210257, - "recall-0.025": 0.016667, - "recall-0.05": 0.063333, - "recall-0.1": 0.881667, - "recall-0.15": 0.95, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 600 - }, - "knee_left": { - "count": 599, - "mean": 0.057348, - "median": 0.043401, - "std": 0.057834, - "sem": 0.002365, - "min": 0.016103, - "max": 0.488334, - "recall-0.025": 0.081667, - "recall-0.05": 0.691667, - "recall-0.1": 0.913333, - "recall-0.15": 0.921667, - "recall-0.25": 0.981667, - "recall-0.5": 0.998333, - "num_labels": 600 - }, - "knee_right": { - "count": 600, - "mean": 0.04705, - "median": 0.034704, - "std": 0.041186, - "sem": 0.001683, - "min": 0.011544, - "max": 0.301894, - "recall-0.025": 0.113333, - "recall-0.05": 0.82, - "recall-0.1": 0.925, - "recall-0.15": 0.94, - "recall-0.25": 0.996667, - "recall-0.5": 1.0, - "num_labels": 600 - }, - "ankle_left": { - "count": 597, - "mean": 0.090924, - "median": 0.08248, - "std": 0.040973, - "sem": 0.001678, - "min": 0.05157, - "max": 0.495193, - "recall-0.025": 0.0, - "recall-0.05": 0.0, - "recall-0.1": 0.87, - "recall-0.15": 0.936667, - "recall-0.25": 0.986667, - "recall-0.5": 0.995, - "num_labels": 600 - }, - "ankle_right": { - "count": 599, - "mean": 0.081952, - "median": 0.066793, - "std": 0.053367, - "sem": 0.002182, - "min": 0.028777, - "max": 0.394296, - "recall-0.025": 0.0, - "recall-0.05": 0.028333, - "recall-0.1": 0.891667, - "recall-0.15": 0.913333, - "recall-0.25": 0.97, - "recall-0.5": 0.998333, - "num_labels": 600 - }, - "joint_recalls": { - "num_labels": 7800, - "recall-0.025": 0.18885, - "recall-0.05": 0.50782, - "recall-0.1": 0.87949, - "recall-0.15": 0.93244, - "recall-0.25": 0.99333, - "recall-0.5": 0.99885 - } -} -{ - "total_parts": 8400, - "correct_parts": 8118, - "pcp": 0.966429 -} -``` - -### Shelf - -```json -{ - "img_loading": 0.0613517, - "demosaicing": 0.000514999, - "avg_time_2d": 0.0385883, - "avg_time_3d": 0.000296986, - "fps": 25.3805 -} -{ - "triangulator_calls": 301, - "init_time": 8.85134e-06, - "undistort_time": 2.26089e-05, - "project_time": 6.12477e-06, - "match_time": 2.3381e-05, - "pairs_time": 1.26549e-05, - "pair_scoring_time": 7.41986e-05, - "grouping_time": 1.17478e-05, - "full_time": 7.31069e-05, - "merge_time": 2.36468e-05, - "post_time": 1.91408e-05, - "convert_time": 3.9615e-07, - "total_time": 0.000276402 -} -{ - "person_nums": { - "total_frames": 301, - "total_labels": 477, - "total_preds": 828, - "considered_empty": 0, - "valid_preds": 477, - "invalid_preds": 351, - "missing": 0, - "invalid_fraction": 0.42391, - "precision": 0.57609, - "recall": 1.0, - "f1": 0.73103, - "non_empty": 828 - }, - "mpjpe": { - "count": 477, - "mean": 0.047914, - "median": 0.042836, - "std": 0.014864, - "sem": 0.000681, - "min": 0.029889, - "max": 0.117015, - "recall-0.025": 0.0, - "recall-0.05": 0.689727, - "recall-0.1": 0.987421, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477, - "ap-0.025": 0.0, - "ap-0.05": 0.376744, - "ap-0.1": 0.724781, - "ap-0.15": 0.737472, - "ap-0.25": 0.737472, - "ap-0.5": 0.737472 - }, - "head": { - "count": 477, - "mean": 0.05384, - "median": 0.050167, - "std": 0.024098, - "sem": 0.001105, - "min": 0.004172, - "max": 0.170521, - "recall-0.025": 0.085954, - "recall-0.05": 0.494759, - "recall-0.1": 0.937107, - "recall-0.15": 0.997904, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "shoulder_left": { - "count": 477, - "mean": 0.042385, - "median": 0.037408, - "std": 0.02045, - "sem": 0.000937, - "min": 0.004485, - "max": 0.136944, - "recall-0.025": 0.161426, - "recall-0.05": 0.725367, - "recall-0.1": 0.987421, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "shoulder_right": { - "count": 477, - "mean": 0.049633, - "median": 0.04552, - "std": 0.023226, - "sem": 0.001065, - "min": 0.005348, - "max": 0.147448, - "recall-0.025": 0.100629, - "recall-0.05": 0.561845, - "recall-0.1": 0.955975, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "elbow_left": { - "count": 477, - "mean": 0.040727, - "median": 0.032035, - "std": 0.029242, - "sem": 0.00134, - "min": 0.003942, - "max": 0.326226, - "recall-0.025": 0.318658, - "recall-0.05": 0.748428, - "recall-0.1": 0.949686, - "recall-0.15": 0.997904, - "recall-0.25": 0.997904, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "elbow_right": { - "count": 477, - "mean": 0.053395, - "median": 0.044653, - "std": 0.04129, - "sem": 0.001893, - "min": 0.002412, - "max": 0.240443, - "recall-0.025": 0.259958, - "recall-0.05": 0.559748, - "recall-0.1": 0.90566, - "recall-0.15": 0.955975, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "wrist_left": { - "count": 477, - "mean": 0.060063, - "median": 0.053941, - "std": 0.039463, - "sem": 0.001809, - "min": 0.002965, - "max": 0.314883, - "recall-0.025": 0.138365, - "recall-0.05": 0.404612, - "recall-0.1": 0.907757, - "recall-0.15": 0.962264, - "recall-0.25": 0.989518, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "wrist_right": { - "count": 477, - "mean": 0.059287, - "median": 0.054279, - "std": 0.033944, - "sem": 0.001556, - "min": 0.009487, - "max": 0.370517, - "recall-0.025": 0.1174, - "recall-0.05": 0.417191, - "recall-0.1": 0.893082, - "recall-0.15": 0.976939, - "recall-0.25": 0.997904, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "hip_left": { - "count": 477, - "mean": 0.048015, - "median": 0.042252, - "std": 0.026197, - "sem": 0.001201, - "min": 0.008132, - "max": 0.142831, - "recall-0.025": 0.184486, - "recall-0.05": 0.626834, - "recall-0.1": 0.955975, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "hip_right": { - "count": 477, - "mean": 0.058081, - "median": 0.056675, - "std": 0.023893, - "sem": 0.001095, - "min": 0.005253, - "max": 0.131652, - "recall-0.025": 0.09434, - "recall-0.05": 0.406709, - "recall-0.1": 0.939203, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "knee_left": { - "count": 477, - "mean": 0.040331, - "median": 0.037937, - "std": 0.024461, - "sem": 0.001121, - "min": 0.00621, - "max": 0.196135, - "recall-0.025": 0.249476, - "recall-0.05": 0.744235, - "recall-0.1": 0.974843, - "recall-0.15": 0.989518, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "knee_right": { - "count": 477, - "mean": 0.040068, - "median": 0.036102, - "std": 0.023063, - "sem": 0.001057, - "min": 0.006699, - "max": 0.184932, - "recall-0.025": 0.30608, - "recall-0.05": 0.710692, - "recall-0.1": 0.974843, - "recall-0.15": 0.997904, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "ankle_left": { - "count": 477, - "mean": 0.036404, - "median": 0.027798, - "std": 0.030604, - "sem": 0.001403, - "min": 0.004789, - "max": 0.223748, - "recall-0.025": 0.431866, - "recall-0.05": 0.815514, - "recall-0.1": 0.943396, - "recall-0.15": 0.983229, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "ankle_right": { - "count": 477, - "mean": 0.040652, - "median": 0.030955, - "std": 0.037264, - "sem": 0.001708, - "min": 0.002681, - "max": 0.269572, - "recall-0.025": 0.303983, - "recall-0.05": 0.813417, - "recall-0.1": 0.930818, - "recall-0.15": 0.968553, - "recall-0.25": 0.997904, - "recall-0.5": 1.0, - "num_labels": 477 - }, - "joint_recalls": { - "num_labels": 6201, - "recall-0.025": 0.21093, - "recall-0.05": 0.617, - "recall-0.1": 0.94211, - "recall-0.15": 0.98645, - "recall-0.25": 0.99871, - "recall-0.5": 1.0 - } -} -{ - "total_parts": 6678, - "correct_parts": 6619, - "pcp": 0.991165 -} -``` - -### Panoptic - -```json -{ - "img_loading": 0.0810738, - "demosaicing": 0.00128778, - "avg_time_2d": 0.0430177, - "avg_time_3d": 0.00061816, - "fps": 22.26 -} -{ - "triangulator_calls": 420, - "init_time": 9.17263e-06, - "undistort_time": 2.80859e-05, - "project_time": 4.63262e-08, - "match_time": 4.00762e-08, - "pairs_time": 4.43976e-06, - "pair_scoring_time": 0.000387332, - "grouping_time": 2.51575e-05, - "full_time": 8.44224e-05, - "merge_time": 2.88022e-05, - "post_time": 2.191e-05, - "convert_time": 6.62648e-07, - "total_time": 0.000590614 -} -{ - "person_nums": { - "total_frames": 420, - "total_labels": 1466, - "total_preds": 1588, - "considered_empty": 0, - "valid_preds": 1462, - "invalid_preds": 126, - "missing": 4, - "invalid_fraction": 0.07935, - "precision": 0.92065, - "recall": 0.99727, - "f1": 0.95743, - "non_empty": 1588 - }, - "mpjpe": { - "count": 1462, - "mean": 0.032647, - "median": 0.029716, - "std": 0.01527, - "sem": 0.0004, - "min": 0.010324, - "max": 0.173435, - "recall-0.025": 0.330832, - "recall-0.05": 0.889495, - "recall-0.1": 0.989768, - "recall-0.15": 0.995907, - "recall-0.25": 0.997271, - "recall-0.5": 0.997271, - "num_labels": 1466, - "ap-0.025": 0.178394, - "ap-0.05": 0.853362, - "ap-0.1": 0.979514, - "ap-0.15": 0.988084, - "ap-0.25": 0.989861, - "ap-0.5": 0.989861 - }, - "nose": { - "count": 1461, - "mean": 0.015625, - "median": 0.011556, - "std": 0.018701, - "sem": 0.000489, - "min": 0.001492, - "max": 0.302115, - "recall-0.025": 0.901572, - "recall-0.05": 0.964457, - "recall-0.1": 0.993848, - "recall-0.15": 0.995899, - "recall-0.25": 0.995899, - "recall-0.5": 0.998633, - "num_labels": 1463 - }, - "shoulder_left": { - "count": 1462, - "mean": 0.016743, - "median": 0.014836, - "std": 0.011022, - "sem": 0.000288, - "min": 0.001022, - "max": 0.108094, - "recall-0.025": 0.836971, - "recall-0.05": 0.983629, - "recall-0.1": 0.996589, - "recall-0.15": 0.997271, - "recall-0.25": 0.997271, - "recall-0.5": 0.997271, - "num_labels": 1466 - }, - "shoulder_right": { - "count": 1461, - "mean": 0.016806, - "median": 0.014481, - "std": 0.011788, - "sem": 0.000309, - "min": 0.000571, - "max": 0.147482, - "recall-0.025": 0.838225, - "recall-0.05": 0.980205, - "recall-0.1": 0.995904, - "recall-0.15": 0.99727, - "recall-0.25": 0.99727, - "recall-0.5": 0.99727, - "num_labels": 1465 - }, - "elbow_left": { - "count": 1461, - "mean": 0.022878, - "median": 0.016645, - "std": 0.021303, - "sem": 0.000558, - "min": 0.001625, - "max": 0.210887, - "recall-0.025": 0.735154, - "recall-0.05": 0.913993, - "recall-0.1": 0.988396, - "recall-0.15": 0.994539, - "recall-0.25": 0.99727, - "recall-0.5": 0.99727, - "num_labels": 1465 - }, - "elbow_right": { - "count": 1461, - "mean": 0.021114, - "median": 0.015923, - "std": 0.016509, - "sem": 0.000432, - "min": 0.000646, - "max": 0.165407, - "recall-0.025": 0.77717, - "recall-0.05": 0.925496, - "recall-0.1": 0.997949, - "recall-0.15": 0.997949, - "recall-0.25": 0.998633, - "recall-0.5": 0.998633, - "num_labels": 1463 - }, - "wrist_left": { - "count": 1432, - "mean": 0.036125, - "median": 0.016999, - "std": 0.055321, - "sem": 0.001462, - "min": 0.001937, - "max": 0.459424, - "recall-0.025": 0.665272, - "recall-0.05": 0.842399, - "recall-0.1": 0.90516, - "recall-0.15": 0.953975, - "recall-0.25": 0.97629, - "recall-0.5": 0.998605, - "num_labels": 1434 - }, - "wrist_right": { - "count": 1455, - "mean": 0.026799, - "median": 0.016736, - "std": 0.033876, - "sem": 0.000888, - "min": 0.001247, - "max": 0.278664, - "recall-0.025": 0.692995, - "recall-0.05": 0.885989, - "recall-0.1": 0.964286, - "recall-0.15": 0.98283, - "recall-0.25": 0.995192, - "recall-0.5": 0.999313, - "num_labels": 1456 - }, - "hip_left": { - "count": 1461, - "mean": 0.035134, - "median": 0.031398, - "std": 0.019999, - "sem": 0.000523, - "min": 0.002515, - "max": 0.177406, - "recall-0.025": 0.339249, - "recall-0.05": 0.837543, - "recall-0.1": 0.986348, - "recall-0.15": 0.995904, - "recall-0.25": 0.99727, - "recall-0.5": 0.99727, - "num_labels": 1465 - }, - "hip_right": { - "count": 1462, - "mean": 0.038572, - "median": 0.033352, - "std": 0.028142, - "sem": 0.000736, - "min": 0.003214, - "max": 0.460624, - "recall-0.025": 0.3206, - "recall-0.05": 0.790587, - "recall-0.1": 0.96794, - "recall-0.15": 0.992497, - "recall-0.25": 0.995907, - "recall-0.5": 0.997271, - "num_labels": 1466 - }, - "knee_left": { - "count": 1461, - "mean": 0.039333, - "median": 0.033045, - "std": 0.035258, - "sem": 0.000923, - "min": 0.003964, - "max": 0.476098, - "recall-0.025": 0.286007, - "recall-0.05": 0.799317, - "recall-0.1": 0.974061, - "recall-0.15": 0.984983, - "recall-0.25": 0.989761, - "recall-0.5": 0.99727, - "num_labels": 1465 - }, - "knee_right": { - "count": 1455, - "mean": 0.038522, - "median": 0.031532, - "std": 0.027606, - "sem": 0.000724, - "min": 0.004249, - "max": 0.291018, - "recall-0.025": 0.348184, - "recall-0.05": 0.751199, - "recall-0.1": 0.966415, - "recall-0.15": 0.990404, - "recall-0.25": 0.995888, - "recall-0.5": 0.997258, - "num_labels": 1459 - }, - "ankle_left": { - "count": 1458, - "mean": 0.057085, - "median": 0.033833, - "std": 0.063616, - "sem": 0.001667, - "min": 0.002162, - "max": 0.431506, - "recall-0.025": 0.351333, - "recall-0.05": 0.660287, - "recall-0.1": 0.852358, - "recall-0.15": 0.911825, - "recall-0.25": 0.969925, - "recall-0.5": 0.996582, - "num_labels": 1463 - }, - "ankle_right": { - "count": 1446, - "mean": 0.054388, - "median": 0.030749, - "std": 0.070352, - "sem": 0.001851, - "min": 0.001113, - "max": 0.497085, - "recall-0.025": 0.384932, - "recall-0.05": 0.739726, - "recall-0.1": 0.858904, - "recall-0.15": 0.903425, - "recall-0.25": 0.956849, - "recall-0.5": 0.990411, - "num_labels": 1460 - }, - "joint_recalls": { - "num_labels": 18990, - "recall-0.025": 0.57478, - "recall-0.05": 0.85166, - "recall-0.1": 0.95714, - "recall-0.15": 0.97657, - "recall-0.25": 0.9892, - "recall-0.5": 0.99684 - } -} -{ - "total_parts": 20444, - "correct_parts": 20194, - "pcp": 0.987771 -} -``` - -### H3WB - -##### default joints - -```json -{ - "img_loading": 0.038096, - "demosaicing": 0.000570326, - "avg_time_2d": 0.0184476, - "avg_time_3d": 8.38664e-05, - "fps": 52.3511 -} -{ - "triangulator_calls": 200, - "init_time": 7.36142e-06, - "undistort_time": 6.69951e-06, - "project_time": 4.096e-08, - "match_time": 4.032e-08, - "pairs_time": 5.845e-07, - "pair_scoring_time": 1.70049e-05, - "grouping_time": 2.61303e-06, - "full_time": 2.09906e-05, - "merge_time": 7.44657e-06, - "post_time": 1.17096e-05, - "convert_time": 1.47525e-07, - "total_time": 7.51791e-05 -} -{ - "person_nums": { - "total_frames": 200, - "total_labels": 200, - "total_preds": 200, - "considered_empty": 0, - "valid_preds": 200, - "invalid_preds": 0, - "missing": 0, - "invalid_fraction": 0.0, - "precision": 1.0, - "recall": 1.0, - "f1": 1.0, - "non_empty": 200 - }, - "mpjpe": { - "count": 200, - "mean": 0.023513, - "median": 0.021453, - "std": 0.010644, - "sem": 0.000755, - "min": 0.00968, - "max": 0.093505, - "recall-0.025": 0.665, - "recall-0.05": 0.975, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200, - "ap-0.025": 0.598818, - "ap-0.05": 0.974228, - "ap-0.1": 1.0, - "ap-0.15": 1.0, - "ap-0.25": 1.0, - "ap-0.5": 1.0 - }, - "nose": { - "count": 200, - "mean": 0.039868, - "median": 0.03088, - "std": 0.035438, - "sem": 0.002512, - "min": 0.003064, - "max": 0.25664, - "recall-0.025": 0.41, - "recall-0.05": 0.77, - "recall-0.1": 0.945, - "recall-0.15": 0.985, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "shoulder_left": { - "count": 200, - "mean": 0.018209, - "median": 0.01532, - "std": 0.011114, - "sem": 0.000788, - "min": 0.00307, - "max": 0.08137, - "recall-0.025": 0.805, - "recall-0.05": 0.985, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "shoulder_right": { - "count": 200, - "mean": 0.022296, - "median": 0.019255, - "std": 0.01239, - "sem": 0.000878, - "min": 0.004945, - "max": 0.079958, - "recall-0.025": 0.66, - "recall-0.05": 0.975, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "elbow_left": { - "count": 200, - "mean": 0.015854, - "median": 0.012731, - "std": 0.012731, - "sem": 0.000902, - "min": 0.002373, - "max": 0.09088, - "recall-0.025": 0.845, - "recall-0.05": 0.975, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "elbow_right": { - "count": 200, - "mean": 0.021291, - "median": 0.014584, - "std": 0.01986, - "sem": 0.001408, - "min": 0.000743, - "max": 0.112112, - "recall-0.025": 0.715, - "recall-0.05": 0.93, - "recall-0.1": 0.99, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "wrist_left": { - "count": 200, - "mean": 0.024887, - "median": 0.017495, - "std": 0.029797, - "sem": 0.002112, - "min": 0.00217, - "max": 0.27968, - "recall-0.025": 0.685, - "recall-0.05": 0.915, - "recall-0.1": 0.985, - "recall-0.15": 0.99, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "wrist_right": { - "count": 200, - "mean": 0.030128, - "median": 0.022392, - "std": 0.026076, - "sem": 0.001848, - "min": 0.001982, - "max": 0.15564, - "recall-0.025": 0.525, - "recall-0.05": 0.855, - "recall-0.1": 0.965, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "hip_left": { - "count": 200, - "mean": 0.030835, - "median": 0.028464, - "std": 0.015695, - "sem": 0.001113, - "min": 0.003129, - "max": 0.092047, - "recall-0.025": 0.415, - "recall-0.05": 0.885, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "hip_right": { - "count": 200, - "mean": 0.029373, - "median": 0.026892, - "std": 0.013671, - "sem": 0.000969, - "min": 0.003684, - "max": 0.062972, - "recall-0.025": 0.435, - "recall-0.05": 0.895, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "knee_left": { - "count": 200, - "mean": 0.020261, - "median": 0.013787, - "std": 0.026966, - "sem": 0.001912, - "min": 0.002161, - "max": 0.321862, - "recall-0.025": 0.78, - "recall-0.05": 0.96, - "recall-0.1": 0.99, - "recall-0.15": 0.995, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "knee_right": { - "count": 200, - "mean": 0.018755, - "median": 0.013997, - "std": 0.031466, - "sem": 0.002231, - "min": 0.001406, - "max": 0.423445, - "recall-0.025": 0.84, - "recall-0.05": 0.955, - "recall-0.1": 0.995, - "recall-0.15": 0.995, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "ankle_left": { - "count": 200, - "mean": 0.015587, - "median": 0.010735, - "std": 0.026038, - "sem": 0.001846, - "min": 0.003313, - "max": 0.329013, - "recall-0.025": 0.9, - "recall-0.05": 0.97, - "recall-0.1": 0.99, - "recall-0.15": 0.995, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "ankle_right": { - "count": 199, - "mean": 0.015906, - "median": 0.010047, - "std": 0.027685, - "sem": 0.001968, - "min": 0.000852, - "max": 0.34465, - "recall-0.025": 0.89, - "recall-0.05": 0.965, - "recall-0.1": 0.985, - "recall-0.15": 0.99, - "recall-0.25": 0.99, - "recall-0.5": 0.995, - "num_labels": 200 - }, - "joint_recalls": { - "num_labels": 2600, - "recall-0.025": 0.685, - "recall-0.05": 0.92577, - "recall-0.1": 0.98808, - "recall-0.15": 0.99538, - "recall-0.25": 0.99731, - "recall-0.5": 0.99962 - } -} -{ - "total_parts": 2800, - "correct_parts": 2794, - "pcp": 0.997857 -} -``` - -##### whole-body - -```json -{ - "img_loading": 0.0380362, - "demosaicing": 0.000576236, - "avg_time_2d": 0.0356966, - "avg_time_3d": 0.000274105, - "fps": 27.3621 -} -{ - "triangulator_calls": 200, - "init_time": 7.56849e-06, - "undistort_time": 4.34473e-05, - "project_time": 4.16e-08, - "match_time": 4.0805e-08, - "pairs_time": 6.84805e-07, - "pair_scoring_time": 1.78283e-05, - "grouping_time": 2.88577e-06, - "full_time": 0.000115345, - "merge_time": 2.88308e-05, - "post_time": 4.59524e-05, - "convert_time": 1.0688e-06, - "total_time": 0.000264242 -} -{ - "person_nums": { - "total_frames": 200, - "total_labels": 200, - "total_preds": 200, - "considered_empty": 0, - "valid_preds": 200, - "invalid_preds": 0, - "missing": 0, - "invalid_fraction": 0.0, - "precision": 1.0, - "recall": 1.0, - "f1": 1.0, - "non_empty": 200 - }, - "mpjpe": { - "count": 200, - "mean": 0.04303, - "median": 0.039269, - "std": 0.014132, - "sem": 0.001002, - "min": 0.021539, - "max": 0.086984, - "recall-0.025": 0.045, - "recall-0.05": 0.745, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200, - "ap-0.025": 0.003881, - "ap-0.05": 0.662439, - "ap-0.1": 1.0, - "ap-0.15": 1.0, - "ap-0.25": 1.0, - "ap-0.5": 1.0 - }, - "nose": { - "count": 200, - "mean": 0.05, - "median": 0.041294, - "std": 0.036001, - "sem": 0.002552, - "min": 0.005973, - "max": 0.236403, - "recall-0.025": 0.275, - "recall-0.05": 0.62, - "recall-0.1": 0.895, - "recall-0.15": 0.985, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "eye_left": { - "count": 200, - "mean": 0.044288, - "median": 0.037923, - "std": 0.028209, - "sem": 0.002, - "min": 0.00171, - "max": 0.137155, - "recall-0.025": 0.32, - "recall-0.05": 0.635, - "recall-0.1": 0.96, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "eye_right": { - "count": 200, - "mean": 0.056325, - "median": 0.046619, - "std": 0.038686, - "sem": 0.002742, - "min": 0.007166, - "max": 0.226585, - "recall-0.025": 0.21, - "recall-0.05": 0.54, - "recall-0.1": 0.88, - "recall-0.15": 0.98, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "ear_left": { - "count": 200, - "mean": 0.034649, - "median": 0.028446, - "std": 0.025909, - "sem": 0.001837, - "min": 0.003826, - "max": 0.175177, - "recall-0.025": 0.425, - "recall-0.05": 0.815, - "recall-0.1": 0.975, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "ear_right": { - "count": 200, - "mean": 0.042026, - "median": 0.037544, - "std": 0.026198, - "sem": 0.001857, - "min": 0.003791, - "max": 0.128287, - "recall-0.025": 0.305, - "recall-0.05": 0.65, - "recall-0.1": 0.975, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "shoulder_left": { - "count": 200, - "mean": 0.019929, - "median": 0.01693, - "std": 0.011985, - "sem": 0.00085, - "min": 0.001094, - "max": 0.095807, - "recall-0.025": 0.75, - "recall-0.05": 0.96, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "shoulder_right": { - "count": 200, - "mean": 0.024932, - "median": 0.022244, - "std": 0.013747, - "sem": 0.000975, - "min": 0.00522, - "max": 0.080711, - "recall-0.025": 0.58, - "recall-0.05": 0.935, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "elbow_left": { - "count": 200, - "mean": 0.020252, - "median": 0.015771, - "std": 0.01732, - "sem": 0.001228, - "min": 0.001505, - "max": 0.105529, - "recall-0.025": 0.775, - "recall-0.05": 0.95, - "recall-0.1": 0.985, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "elbow_right": { - "count": 200, - "mean": 0.025033, - "median": 0.017226, - "std": 0.025091, - "sem": 0.001779, - "min": 0.002717, - "max": 0.200619, - "recall-0.025": 0.68, - "recall-0.05": 0.885, - "recall-0.1": 0.975, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "wrist_left": { - "count": 200, - "mean": 0.027222, - "median": 0.019391, - "std": 0.025146, - "sem": 0.001783, - "min": 0.002502, - "max": 0.164594, - "recall-0.025": 0.64, - "recall-0.05": 0.885, - "recall-0.1": 0.975, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "wrist_right": { - "count": 200, - "mean": 0.041146, - "median": 0.025616, - "std": 0.048429, - "sem": 0.003433, - "min": 0.003859, - "max": 0.404037, - "recall-0.025": 0.485, - "recall-0.05": 0.78, - "recall-0.1": 0.925, - "recall-0.15": 0.95, - "recall-0.25": 0.99, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "hip_left": { - "count": 200, - "mean": 0.030428, - "median": 0.028178, - "std": 0.014864, - "sem": 0.001054, - "min": 0.002594, - "max": 0.072983, - "recall-0.025": 0.4, - "recall-0.05": 0.87, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "hip_right": { - "count": 200, - "mean": 0.030707, - "median": 0.026996, - "std": 0.016509, - "sem": 0.00117, - "min": 0.004434, - "max": 0.084628, - "recall-0.025": 0.445, - "recall-0.05": 0.85, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "knee_left": { - "count": 200, - "mean": 0.020818, - "median": 0.016714, - "std": 0.019284, - "sem": 0.001367, - "min": 0.000745, - "max": 0.165307, - "recall-0.025": 0.75, - "recall-0.05": 0.965, - "recall-0.1": 0.99, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "knee_right": { - "count": 200, - "mean": 0.02654, - "median": 0.014401, - "std": 0.047846, - "sem": 0.003392, - "min": 0.002234, - "max": 0.348059, - "recall-0.025": 0.775, - "recall-0.05": 0.925, - "recall-0.1": 0.955, - "recall-0.15": 0.975, - "recall-0.25": 0.98, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "ankle_left": { - "count": 200, - "mean": 0.016566, - "median": 0.011593, - "std": 0.019664, - "sem": 0.001394, - "min": 0.001291, - "max": 0.139635, - "recall-0.025": 0.845, - "recall-0.05": 0.965, - "recall-0.1": 0.98, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "ankle_right": { - "count": 200, - "mean": 0.018111, - "median": 0.013128, - "std": 0.021771, - "sem": 0.001543, - "min": 0.001292, - "max": 0.172154, - "recall-0.025": 0.845, - "recall-0.05": 0.965, - "recall-0.1": 0.98, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "foot_toe_big_left": { - "count": 200, - "mean": 0.029063, - "median": 0.021991, - "std": 0.029218, - "sem": 0.002071, - "min": 0.00125, - "max": 0.217976, - "recall-0.025": 0.58, - "recall-0.05": 0.905, - "recall-0.1": 0.965, - "recall-0.15": 0.985, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "foot_toe_small_left": { - "count": 200, - "mean": 0.026559, - "median": 0.019642, - "std": 0.031111, - "sem": 0.002205, - "min": 0.003994, - "max": 0.2917, - "recall-0.025": 0.67, - "recall-0.05": 0.935, - "recall-0.1": 0.96, - "recall-0.15": 0.98, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "foot_heel_left": { - "count": 200, - "mean": 0.02443, - "median": 0.017401, - "std": 0.02463, - "sem": 0.001746, - "min": 0.002178, - "max": 0.21736, - "recall-0.025": 0.68, - "recall-0.05": 0.935, - "recall-0.1": 0.98, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "foot_toe_big_right": { - "count": 200, - "mean": 0.031939, - "median": 0.024265, - "std": 0.031535, - "sem": 0.002235, - "min": 0.001342, - "max": 0.262809, - "recall-0.025": 0.52, - "recall-0.05": 0.87, - "recall-0.1": 0.96, - "recall-0.15": 0.99, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "foot_toe_small_right": { - "count": 200, - "mean": 0.032333, - "median": 0.024078, - "std": 0.032025, - "sem": 0.00227, - "min": 0.00475, - "max": 0.220805, - "recall-0.025": 0.53, - "recall-0.05": 0.87, - "recall-0.1": 0.965, - "recall-0.15": 0.98, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "foot_heel_right": { - "count": 200, - "mean": 0.023705, - "median": 0.017291, - "std": 0.020747, - "sem": 0.001471, - "min": 0.001192, - "max": 0.135471, - "recall-0.025": 0.685, - "recall-0.05": 0.915, - "recall-0.1": 0.98, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_right_1": { - "count": 199, - "mean": 0.090186, - "median": 0.081091, - "std": 0.042041, - "sem": 0.002988, - "min": 0.029641, - "max": 0.296506, - "recall-0.025": 0.0, - "recall-0.05": 0.095, - "recall-0.1": 0.71, - "recall-0.15": 0.91, - "recall-0.25": 0.98, - "recall-0.5": 0.995, - "num_labels": 200 - }, - "face_jaw_right_2": { - "count": 200, - "mean": 0.086496, - "median": 0.080626, - "std": 0.041034, - "sem": 0.002909, - "min": 0.022659, - "max": 0.291609, - "recall-0.025": 0.005, - "recall-0.05": 0.145, - "recall-0.1": 0.7, - "recall-0.15": 0.905, - "recall-0.25": 0.99, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_right_3": { - "count": 200, - "mean": 0.090164, - "median": 0.080694, - "std": 0.047646, - "sem": 0.003378, - "min": 0.014401, - "max": 0.240963, - "recall-0.025": 0.04, - "recall-0.05": 0.185, - "recall-0.1": 0.665, - "recall-0.15": 0.875, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_right_4": { - "count": 200, - "mean": 0.090295, - "median": 0.077929, - "std": 0.047947, - "sem": 0.003399, - "min": 0.006873, - "max": 0.243925, - "recall-0.025": 0.035, - "recall-0.05": 0.185, - "recall-0.1": 0.66, - "recall-0.15": 0.88, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_right_5": { - "count": 200, - "mean": 0.087022, - "median": 0.073364, - "std": 0.047157, - "sem": 0.003343, - "min": 0.004597, - "max": 0.244694, - "recall-0.025": 0.05, - "recall-0.05": 0.205, - "recall-0.1": 0.685, - "recall-0.15": 0.875, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_right_6": { - "count": 200, - "mean": 0.078144, - "median": 0.06802, - "std": 0.040178, - "sem": 0.002848, - "min": 0.008041, - "max": 0.219142, - "recall-0.025": 0.05, - "recall-0.05": 0.235, - "recall-0.1": 0.755, - "recall-0.15": 0.935, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_right_7": { - "count": 200, - "mean": 0.068325, - "median": 0.056898, - "std": 0.040372, - "sem": 0.002862, - "min": 0.008542, - "max": 0.226815, - "recall-0.025": 0.06, - "recall-0.05": 0.425, - "recall-0.1": 0.815, - "recall-0.15": 0.95, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_right_8": { - "count": 200, - "mean": 0.056317, - "median": 0.045115, - "std": 0.045669, - "sem": 0.003237, - "min": 0.008879, - "max": 0.483117, - "recall-0.025": 0.19, - "recall-0.05": 0.545, - "recall-0.1": 0.935, - "recall-0.15": 0.965, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_middle": { - "count": 200, - "mean": 0.04195, - "median": 0.03765, - "std": 0.025286, - "sem": 0.001793, - "min": 0.005106, - "max": 0.112937, - "recall-0.025": 0.33, - "recall-0.05": 0.655, - "recall-0.1": 0.96, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_left_1": { - "count": 200, - "mean": 0.049126, - "median": 0.038409, - "std": 0.031014, - "sem": 0.002198, - "min": 0.006884, - "max": 0.208441, - "recall-0.025": 0.225, - "recall-0.05": 0.61, - "recall-0.1": 0.935, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_left_2": { - "count": 200, - "mean": 0.055484, - "median": 0.045459, - "std": 0.034806, - "sem": 0.002467, - "min": 0.014189, - "max": 0.238655, - "recall-0.025": 0.13, - "recall-0.05": 0.575, - "recall-0.1": 0.89, - "recall-0.15": 0.975, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_left_3": { - "count": 200, - "mean": 0.064117, - "median": 0.052038, - "std": 0.039353, - "sem": 0.00279, - "min": 0.004509, - "max": 0.267647, - "recall-0.025": 0.055, - "recall-0.05": 0.465, - "recall-0.1": 0.87, - "recall-0.15": 0.955, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_left_4": { - "count": 200, - "mean": 0.070067, - "median": 0.060842, - "std": 0.046212, - "sem": 0.003276, - "min": 0.009068, - "max": 0.489191, - "recall-0.025": 0.035, - "recall-0.05": 0.355, - "recall-0.1": 0.845, - "recall-0.15": 0.955, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_left_5": { - "count": 199, - "mean": 0.071107, - "median": 0.060703, - "std": 0.038609, - "sem": 0.002744, - "min": 0.002978, - "max": 0.203766, - "recall-0.025": 0.03, - "recall-0.05": 0.33, - "recall-0.1": 0.805, - "recall-0.15": 0.935, - "recall-0.25": 0.995, - "recall-0.5": 0.995, - "num_labels": 200 - }, - "face_jaw_left_6": { - "count": 200, - "mean": 0.071863, - "median": 0.061698, - "std": 0.042063, - "sem": 0.002982, - "min": 0.00737, - "max": 0.248067, - "recall-0.025": 0.06, - "recall-0.05": 0.365, - "recall-0.1": 0.81, - "recall-0.15": 0.94, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_left_7": { - "count": 200, - "mean": 0.070767, - "median": 0.060658, - "std": 0.038384, - "sem": 0.002721, - "min": 0.007471, - "max": 0.193507, - "recall-0.025": 0.05, - "recall-0.05": 0.37, - "recall-0.1": 0.79, - "recall-0.15": 0.95, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_jaw_left_8": { - "count": 200, - "mean": 0.069908, - "median": 0.063692, - "std": 0.037747, - "sem": 0.002676, - "min": 0.004256, - "max": 0.205657, - "recall-0.025": 0.04, - "recall-0.05": 0.375, - "recall-0.1": 0.805, - "recall-0.15": 0.955, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_right_1": { - "count": 200, - "mean": 0.065137, - "median": 0.052178, - "std": 0.046069, - "sem": 0.003266, - "min": 0.00491, - "max": 0.304229, - "recall-0.025": 0.09, - "recall-0.05": 0.47, - "recall-0.1": 0.85, - "recall-0.15": 0.96, - "recall-0.25": 0.985, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_right_2": { - "count": 200, - "mean": 0.056394, - "median": 0.044664, - "std": 0.04334, - "sem": 0.003072, - "min": 0.00459, - "max": 0.344106, - "recall-0.025": 0.215, - "recall-0.05": 0.53, - "recall-0.1": 0.905, - "recall-0.15": 0.955, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_right_3": { - "count": 200, - "mean": 0.050022, - "median": 0.037612, - "std": 0.040997, - "sem": 0.002906, - "min": 0.003698, - "max": 0.326209, - "recall-0.025": 0.29, - "recall-0.05": 0.65, - "recall-0.1": 0.9, - "recall-0.15": 0.97, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_right_4": { - "count": 200, - "mean": 0.042468, - "median": 0.033034, - "std": 0.034806, - "sem": 0.002467, - "min": 0.002955, - "max": 0.275102, - "recall-0.025": 0.365, - "recall-0.05": 0.74, - "recall-0.1": 0.935, - "recall-0.15": 0.975, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_right_5": { - "count": 200, - "mean": 0.037063, - "median": 0.029386, - "std": 0.03015, - "sem": 0.002137, - "min": 0.004151, - "max": 0.267448, - "recall-0.025": 0.395, - "recall-0.05": 0.79, - "recall-0.1": 0.965, - "recall-0.15": 0.995, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_left_1": { - "count": 200, - "mean": 0.03367, - "median": 0.024625, - "std": 0.030311, - "sem": 0.002149, - "min": 0.00367, - "max": 0.212602, - "recall-0.025": 0.505, - "recall-0.05": 0.825, - "recall-0.1": 0.97, - "recall-0.15": 0.985, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_left_2": { - "count": 200, - "mean": 0.032057, - "median": 0.025607, - "std": 0.025639, - "sem": 0.001818, - "min": 0.003148, - "max": 0.179948, - "recall-0.025": 0.48, - "recall-0.05": 0.825, - "recall-0.1": 0.98, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_left_3": { - "count": 200, - "mean": 0.033774, - "median": 0.026215, - "std": 0.027979, - "sem": 0.001983, - "min": 0.002209, - "max": 0.173778, - "recall-0.025": 0.49, - "recall-0.05": 0.8, - "recall-0.1": 0.975, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_left_4": { - "count": 200, - "mean": 0.036848, - "median": 0.028273, - "std": 0.028712, - "sem": 0.002035, - "min": 0.00303, - "max": 0.230255, - "recall-0.025": 0.425, - "recall-0.05": 0.76, - "recall-0.1": 0.97, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eyebrow_left_5": { - "count": 200, - "mean": 0.042395, - "median": 0.033633, - "std": 0.029695, - "sem": 0.002105, - "min": 0.00347, - "max": 0.193113, - "recall-0.025": 0.325, - "recall-0.05": 0.67, - "recall-0.1": 0.95, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_nose_1": { - "count": 200, - "mean": 0.032583, - "median": 0.025128, - "std": 0.027478, - "sem": 0.001948, - "min": 0.004162, - "max": 0.243484, - "recall-0.025": 0.495, - "recall-0.05": 0.855, - "recall-0.1": 0.98, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_nose_2": { - "count": 200, - "mean": 0.037432, - "median": 0.026723, - "std": 0.034646, - "sem": 0.002456, - "min": 0.003926, - "max": 0.21618, - "recall-0.025": 0.43, - "recall-0.05": 0.815, - "recall-0.1": 0.955, - "recall-0.15": 0.975, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_nose_3": { - "count": 200, - "mean": 0.039218, - "median": 0.030531, - "std": 0.031833, - "sem": 0.002257, - "min": 0.004975, - "max": 0.206698, - "recall-0.025": 0.38, - "recall-0.05": 0.77, - "recall-0.1": 0.94, - "recall-0.15": 0.98, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_nose_4": { - "count": 200, - "mean": 0.042108, - "median": 0.031621, - "std": 0.034613, - "sem": 0.002454, - "min": 0.004579, - "max": 0.224392, - "recall-0.025": 0.385, - "recall-0.05": 0.72, - "recall-0.1": 0.92, - "recall-0.15": 0.98, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_nose_5": { - "count": 200, - "mean": 0.050299, - "median": 0.040057, - "std": 0.042128, - "sem": 0.002986, - "min": 0.005103, - "max": 0.397263, - "recall-0.025": 0.26, - "recall-0.05": 0.65, - "recall-0.1": 0.92, - "recall-0.15": 0.96, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_nose_6": { - "count": 200, - "mean": 0.044236, - "median": 0.034263, - "std": 0.031104, - "sem": 0.002205, - "min": 0.006119, - "max": 0.187049, - "recall-0.025": 0.31, - "recall-0.05": 0.675, - "recall-0.1": 0.935, - "recall-0.15": 0.985, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_nose_7": { - "count": 200, - "mean": 0.037019, - "median": 0.03075, - "std": 0.026101, - "sem": 0.00185, - "min": 0.006775, - "max": 0.153869, - "recall-0.025": 0.405, - "recall-0.05": 0.76, - "recall-0.1": 0.97, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_nose_8": { - "count": 200, - "mean": 0.036512, - "median": 0.029187, - "std": 0.024736, - "sem": 0.001753, - "min": 0.005065, - "max": 0.145505, - "recall-0.025": 0.415, - "recall-0.05": 0.76, - "recall-0.1": 0.98, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_nose_9": { - "count": 200, - "mean": 0.037414, - "median": 0.029256, - "std": 0.028403, - "sem": 0.002013, - "min": 0.00169, - "max": 0.15322, - "recall-0.025": 0.435, - "recall-0.05": 0.765, - "recall-0.1": 0.955, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_right_1": { - "count": 200, - "mean": 0.05918, - "median": 0.047236, - "std": 0.038971, - "sem": 0.002763, - "min": 0.007489, - "max": 0.235613, - "recall-0.025": 0.15, - "recall-0.05": 0.515, - "recall-0.1": 0.885, - "recall-0.15": 0.96, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_right_2": { - "count": 200, - "mean": 0.051284, - "median": 0.043347, - "std": 0.033688, - "sem": 0.002388, - "min": 0.003984, - "max": 0.210391, - "recall-0.025": 0.195, - "recall-0.05": 0.605, - "recall-0.1": 0.91, - "recall-0.15": 0.985, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_right_3": { - "count": 200, - "mean": 0.045563, - "median": 0.037221, - "std": 0.031299, - "sem": 0.002219, - "min": 0.004218, - "max": 0.186976, - "recall-0.025": 0.285, - "recall-0.05": 0.665, - "recall-0.1": 0.94, - "recall-0.15": 0.985, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_right_4": { - "count": 200, - "mean": 0.040261, - "median": 0.032768, - "std": 0.030755, - "sem": 0.00218, - "min": 0.003566, - "max": 0.276559, - "recall-0.025": 0.355, - "recall-0.05": 0.75, - "recall-0.1": 0.96, - "recall-0.15": 0.99, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_right_5": { - "count": 200, - "mean": 0.042503, - "median": 0.035272, - "std": 0.027721, - "sem": 0.001965, - "min": 0.002076, - "max": 0.177924, - "recall-0.025": 0.29, - "recall-0.05": 0.71, - "recall-0.1": 0.95, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_right_6": { - "count": 200, - "mean": 0.051571, - "median": 0.040153, - "std": 0.037041, - "sem": 0.002626, - "min": 0.006639, - "max": 0.213775, - "recall-0.025": 0.245, - "recall-0.05": 0.605, - "recall-0.1": 0.9, - "recall-0.15": 0.97, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_left_1": { - "count": 200, - "mean": 0.032026, - "median": 0.0253, - "std": 0.022861, - "sem": 0.001621, - "min": 0.005487, - "max": 0.151369, - "recall-0.025": 0.495, - "recall-0.05": 0.82, - "recall-0.1": 0.975, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_left_2": { - "count": 200, - "mean": 0.032216, - "median": 0.025704, - "std": 0.023708, - "sem": 0.001681, - "min": 0.003795, - "max": 0.122308, - "recall-0.025": 0.475, - "recall-0.05": 0.82, - "recall-0.1": 0.98, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_left_3": { - "count": 200, - "mean": 0.03404, - "median": 0.024649, - "std": 0.027985, - "sem": 0.001984, - "min": 0.001734, - "max": 0.165697, - "recall-0.025": 0.51, - "recall-0.05": 0.825, - "recall-0.1": 0.96, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_left_4": { - "count": 200, - "mean": 0.037922, - "median": 0.028656, - "std": 0.029045, - "sem": 0.002059, - "min": 0.00163, - "max": 0.169992, - "recall-0.025": 0.425, - "recall-0.05": 0.775, - "recall-0.1": 0.935, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_left_5": { - "count": 200, - "mean": 0.035021, - "median": 0.027634, - "std": 0.026689, - "sem": 0.001892, - "min": 0.00109, - "max": 0.147629, - "recall-0.025": 0.455, - "recall-0.05": 0.775, - "recall-0.1": 0.965, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_eye_left_6": { - "count": 200, - "mean": 0.033258, - "median": 0.02609, - "std": 0.023644, - "sem": 0.001676, - "min": 0.003624, - "max": 0.125818, - "recall-0.025": 0.475, - "recall-0.05": 0.815, - "recall-0.1": 0.975, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_1": { - "count": 200, - "mean": 0.052417, - "median": 0.039059, - "std": 0.045006, - "sem": 0.00319, - "min": 0.00469, - "max": 0.413482, - "recall-0.025": 0.28, - "recall-0.05": 0.595, - "recall-0.1": 0.9, - "recall-0.15": 0.975, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_2": { - "count": 200, - "mean": 0.043331, - "median": 0.035459, - "std": 0.031829, - "sem": 0.002256, - "min": 0.004419, - "max": 0.219084, - "recall-0.025": 0.35, - "recall-0.05": 0.67, - "recall-0.1": 0.94, - "recall-0.15": 0.985, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_3": { - "count": 200, - "mean": 0.038693, - "median": 0.031125, - "std": 0.029204, - "sem": 0.00207, - "min": 0.004738, - "max": 0.194787, - "recall-0.025": 0.39, - "recall-0.05": 0.76, - "recall-0.1": 0.96, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_4": { - "count": 200, - "mean": 0.037193, - "median": 0.027998, - "std": 0.030438, - "sem": 0.002158, - "min": 0.003016, - "max": 0.216648, - "recall-0.025": 0.4, - "recall-0.05": 0.76, - "recall-0.1": 0.965, - "recall-0.15": 0.985, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_5": { - "count": 200, - "mean": 0.0362, - "median": 0.029058, - "std": 0.026208, - "sem": 0.001858, - "min": 0.006694, - "max": 0.153436, - "recall-0.025": 0.43, - "recall-0.05": 0.785, - "recall-0.1": 0.97, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_6": { - "count": 200, - "mean": 0.033512, - "median": 0.027338, - "std": 0.022872, - "sem": 0.001621, - "min": 0.003556, - "max": 0.125841, - "recall-0.025": 0.435, - "recall-0.05": 0.815, - "recall-0.1": 0.98, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_7": { - "count": 200, - "mean": 0.037067, - "median": 0.028309, - "std": 0.026543, - "sem": 0.001882, - "min": 0.002307, - "max": 0.164978, - "recall-0.025": 0.425, - "recall-0.05": 0.755, - "recall-0.1": 0.975, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_8": { - "count": 200, - "mean": 0.035943, - "median": 0.027546, - "std": 0.029331, - "sem": 0.002079, - "min": 0.004591, - "max": 0.25722, - "recall-0.025": 0.46, - "recall-0.05": 0.765, - "recall-0.1": 0.975, - "recall-0.15": 0.99, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_9": { - "count": 200, - "mean": 0.035293, - "median": 0.029039, - "std": 0.02704, - "sem": 0.001917, - "min": 0.00276, - "max": 0.190415, - "recall-0.025": 0.445, - "recall-0.05": 0.765, - "recall-0.1": 0.98, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_10": { - "count": 200, - "mean": 0.038797, - "median": 0.028855, - "std": 0.033653, - "sem": 0.002386, - "min": 0.005085, - "max": 0.294249, - "recall-0.025": 0.43, - "recall-0.05": 0.73, - "recall-0.1": 0.965, - "recall-0.15": 0.99, - "recall-0.25": 0.995, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_11": { - "count": 200, - "mean": 0.041176, - "median": 0.033584, - "std": 0.027888, - "sem": 0.001977, - "min": 0.006178, - "max": 0.224664, - "recall-0.025": 0.325, - "recall-0.05": 0.69, - "recall-0.1": 0.97, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_12": { - "count": 200, - "mean": 0.04335, - "median": 0.035058, - "std": 0.029542, - "sem": 0.002094, - "min": 0.004884, - "max": 0.221595, - "recall-0.025": 0.335, - "recall-0.05": 0.66, - "recall-0.1": 0.95, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_13": { - "count": 199, - "mean": 0.049791, - "median": 0.036583, - "std": 0.037244, - "sem": 0.002647, - "min": 0.001204, - "max": 0.201367, - "recall-0.025": 0.29, - "recall-0.05": 0.615, - "recall-0.1": 0.89, - "recall-0.15": 0.98, - "recall-0.25": 0.995, - "recall-0.5": 0.995, - "num_labels": 200 - }, - "face_mouth_14": { - "count": 200, - "mean": 0.042289, - "median": 0.034645, - "std": 0.03222, - "sem": 0.002284, - "min": 0.007891, - "max": 0.22009, - "recall-0.025": 0.325, - "recall-0.05": 0.705, - "recall-0.1": 0.955, - "recall-0.15": 0.985, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_15": { - "count": 200, - "mean": 0.037423, - "median": 0.032205, - "std": 0.026285, - "sem": 0.001863, - "min": 0.003932, - "max": 0.17102, - "recall-0.025": 0.4, - "recall-0.05": 0.74, - "recall-0.1": 0.975, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_16": { - "count": 200, - "mean": 0.033631, - "median": 0.028634, - "std": 0.021759, - "sem": 0.001542, - "min": 0.003328, - "max": 0.118601, - "recall-0.025": 0.42, - "recall-0.05": 0.8, - "recall-0.1": 0.995, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_17": { - "count": 200, - "mean": 0.037143, - "median": 0.027897, - "std": 0.026863, - "sem": 0.001904, - "min": 0.003264, - "max": 0.161595, - "recall-0.025": 0.42, - "recall-0.05": 0.76, - "recall-0.1": 0.97, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_18": { - "count": 200, - "mean": 0.032196, - "median": 0.025563, - "std": 0.024801, - "sem": 0.001758, - "min": 0.002826, - "max": 0.200336, - "recall-0.025": 0.48, - "recall-0.05": 0.835, - "recall-0.1": 0.985, - "recall-0.15": 0.995, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_19": { - "count": 200, - "mean": 0.033841, - "median": 0.02622, - "std": 0.02801, - "sem": 0.001986, - "min": 0.002484, - "max": 0.225828, - "recall-0.025": 0.475, - "recall-0.05": 0.785, - "recall-0.1": 0.97, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "face_mouth_20": { - "count": 200, - "mean": 0.03805, - "median": 0.029733, - "std": 0.028118, - "sem": 0.001993, - "min": 0.006261, - "max": 0.225434, - "recall-0.025": 0.43, - "recall-0.05": 0.72, - "recall-0.1": 0.965, - "recall-0.15": 0.99, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "hand_wrist_left": { - "count": 200, - "mean": 0.034222, - "median": 0.021707, - "std": 0.037783, - "sem": 0.002678, - "min": 0.001226, - "max": 0.181711, - "recall-0.025": 0.575, - "recall-0.05": 0.83, - "recall-0.1": 0.915, - "recall-0.15": 0.965, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "hand_wrist_right": { - "count": 200, - "mean": 0.042875, - "median": 0.023659, - "std": 0.056983, - "sem": 0.004039, - "min": 0.003907, - "max": 0.386514, - "recall-0.025": 0.53, - "recall-0.05": 0.785, - "recall-0.1": 0.905, - "recall-0.15": 0.95, - "recall-0.25": 0.975, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "hip_middle": { - "count": 200, - "mean": 0.029232, - "median": 0.025708, - "std": 0.015173, - "sem": 0.001076, - "min": 0.002008, - "max": 0.099106, - "recall-0.025": 0.47, - "recall-0.05": 0.875, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "shoulder_middle": { - "count": 200, - "mean": 0.018579, - "median": 0.016433, - "std": 0.009356, - "sem": 0.000663, - "min": 0.002107, - "max": 0.054166, - "recall-0.025": 0.78, - "recall-0.05": 0.995, - "recall-0.1": 1.0, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "head": { - "count": 200, - "mean": 0.031422, - "median": 0.028525, - "std": 0.019139, - "sem": 0.001357, - "min": 0.003049, - "max": 0.112382, - "recall-0.025": 0.465, - "recall-0.05": 0.855, - "recall-0.1": 0.99, - "recall-0.15": 1.0, - "recall-0.25": 1.0, - "recall-0.5": 1.0, - "num_labels": 200 - }, - "body_errors": { - "body": { - "count": 5200, - "mean": 0.029855, - "median": 0.023667, - "std": 0.024984, - "sem": 0.001771, - "min": 0.002839, - "max": 0.174979, - "num_labels": 5200 - }, - "face": { - "count": 13597, - "mean": 0.048086, - "median": 0.039195, - "std": 0.032977, - "sem": 0.002338, - "min": 0.005486, - "max": 0.224024, - "num_labels": 13600 - }, - "hand": { - "count": 400, - "mean": 0.038549, - "median": 0.022683, - "std": 0.047383, - "sem": 0.003358, - "min": 0.002566, - "max": 0.284113, - "num_labels": 400 - } - }, - "joint_recalls": { - "num_labels": 19200, - "recall-0.025": 0.38266, - "recall-0.05": 0.70099, - "recall-0.1": 0.92948, - "recall-0.15": 0.97917, - "recall-0.25": 0.99813, - "recall-0.5": 0.99984 - } -} -{ - "total_parts": 2800, - "correct_parts": 2791, - "pcp": 0.996786 -} -``` diff --git a/extras/jetson/docker-compose-2d.yml b/extras/jetson/docker-compose-2d.yml deleted file mode 100644 index 2310d33..0000000 --- a/extras/jetson/docker-compose-2d.yml +++ /dev/null @@ -1,37 +0,0 @@ -services: - - test_node: - image: rapidposetriangulation_ros2d - network_mode: "host" - ipc: "host" - runtime: nvidia - privileged: true - volumes: - - ../../:/RapidPoseTriangulation/ - - ../../skelda/:/skelda/ - - /tmp/.X11-unix:/tmp/.X11-unix - - /dev/shm:/dev/shm - environment: - - CAMID - - DISPLAY - - QT_X11_NO_MITSHM=1 - - "PYTHONUNBUFFERED=1" - command: /bin/bash -i -c 'sleep infinity' - - estimator: - image: rapidposetriangulation_ros2d - network_mode: "host" - ipc: "host" - runtime: nvidia - privileged: true - volumes: - - ../../:/RapidPoseTriangulation/ - - ../../skelda/:/skelda/ - - /tmp/.X11-unix:/tmp/.X11-unix - - /dev/shm:/dev/shm - environment: - - CAMID - - DISPLAY - - QT_X11_NO_MITSHM=1 - - "PYTHONUNBUFFERED=1" - command: /bin/bash -i -c 'export ROS_DOMAIN_ID=18 && ros2 run rpt2d_wrapper_cpp rpt2d_wrapper' diff --git a/extras/jetson/dockerfile b/extras/jetson/dockerfile deleted file mode 100644 index e245a42..0000000 --- a/extras/jetson/dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -FROM dustynv/onnxruntime:1.20-r36.4.0 - -ARG DEBIAN_FRONTEND=noninteractive -ENV LANG=C.UTF-8 -ENV LC_ALL=C.UTF-8 -WORKDIR / - -RUN apt-get update && apt-get install -y --no-install-recommends feh -RUN apt-get update && apt-get install -y --no-install-recommends python3-opencv -RUN apt-get update && apt-get install -y --no-install-recommends libatlas-base-dev - -# Show matplotlib images -RUN apt-get update && apt-get install -y --no-install-recommends python3-tk - -# Install build dependencies -RUN apt-get update && apt-get install -y --no-install-recommends build-essential -RUN apt-get update && apt-get install -y --no-install-recommends libopencv-dev -RUN pip3 install --no-cache-dir uv - -RUN pip3 install --no-cache-dir scipy -COPY ./skelda/ /skelda/ -RUN pip3 install --no-cache-dir -e /skelda/ - -WORKDIR /RapidPoseTriangulation/ -CMD ["/bin/bash"] diff --git a/extras/mmdeploy/README.md b/extras/mmdeploy/README.md deleted file mode 100644 index ab35740..0000000 --- a/extras/mmdeploy/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# Exporting MMPose models - -```bash -docker build --progress=plain -f extras/mmdeploy/dockerfile -t rpt_mmdeploy . - -./extras/mmdeploy/run_container.sh -``` - -
- -## ONNX - -```bash -cd /mmdeploy/ -export withFP16="_fp16" -cp /RapidPoseTriangulation/extras/mmdeploy/configs/detection_onnxruntime_static-320x320"$withFP16".py configs/mmdet/detection/ - -python3 ./tools/deploy.py \ - configs/mmdet/detection/detection_onnxruntime_static-320x320"$withFP16".py \ - /mmpose/projects/rtmpose/rtmdet/person/rtmdet_nano_320-8xb32_coco-person.py \ - https://download.openmmlab.com/mmpose/v1/projects/rtmpose/rtmdet_nano_8xb32-100e_coco-obj365-person-05d8511e.pth \ - /mmpose/projects/rtmpose/examples/onnxruntime/human-pose.jpeg \ - --work-dir work_dir \ - --show -mv /mmdeploy/work_dir/end2end.onnx /RapidPoseTriangulation/extras/mmdeploy/exports/rtmdet-nano_1x3x320x320"$withFP16".onnx - -python3 ./tools/deploy.py \ - configs/mmdet/detection/detection_onnxruntime_static-320x320"$withFP16".py \ - /mmpose/projects/rtmpose/rtmdet/person/rtmdet_m_640-8xb32_coco-person.py \ - https://download.openmmlab.com/mmpose/v1/projects/rtmpose/rtmdet_m_8xb32-100e_coco-obj365-person-235e8209.pth \ - /mmpose/projects/rtmpose/examples/onnxruntime/human-pose.jpeg \ - --work-dir work_dir \ - --show -mv /mmdeploy/work_dir/end2end.onnx /RapidPoseTriangulation/extras/mmdeploy/exports/rtmdet-m_1x3x320x320"$withFP16".onnx -``` - -```bash -cd /mmdeploy/ -export withFP16="_fp16" -cp /RapidPoseTriangulation/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_static-384x288"$withFP16".py configs/mmpose/ -cp /RapidPoseTriangulation/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_dynamic-384x288"$withFP16".py configs/mmpose/ - -python3 ./tools/deploy.py \ - configs/mmpose/pose-detection_simcc_onnxruntime_static-384x288"$withFP16".py \ - /mmpose/projects/rtmpose/rtmpose/body_2d_keypoint/rtmpose-m_8xb256-420e_coco-384x288.py \ - https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmpose-m_simcc-body7_pt-body7_420e-384x288-65e718c4_20230504.pth \ - /mmpose/projects/rtmpose/examples/onnxruntime/human-pose.jpeg \ - --work-dir work_dir \ - --show -mv /mmdeploy/work_dir/end2end.onnx /RapidPoseTriangulation/extras/mmdeploy/exports/rtmpose-m_1x3x384x288"$withFP16".onnx - -python3 ./tools/deploy.py \ - configs/mmpose/pose-detection_simcc_onnxruntime_dynamic-384x288"$withFP16".py \ - /mmpose/projects/rtmpose/rtmpose/body_2d_keypoint/rtmpose-m_8xb256-420e_coco-384x288.py \ - https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmpose-m_simcc-body7_pt-body7_420e-384x288-65e718c4_20230504.pth \ - /mmpose/projects/rtmpose/examples/onnxruntime/human-pose.jpeg \ - --work-dir work_dir \ - --show -mv /mmdeploy/work_dir/end2end.onnx /RapidPoseTriangulation/extras/mmdeploy/exports/rtmpose-m_Bx3x384x288"$withFP16".onnx - -python3 ./tools/deploy.py \ - configs/mmpose/pose-detection_simcc_onnxruntime_static-384x288"$withFP16".py \ - /mmpose/projects/rtmpose/rtmpose/wholebody_2d_keypoint/rtmpose-l_8xb32-270e_coco-wholebody-384x288.py \ - https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmpose-l_simcc-ucoco_dw-ucoco_270e-384x288-2438fd99_20230728.pth \ - /mmpose/projects/rtmpose/examples/onnxruntime/human-pose.jpeg \ - --work-dir work_dir \ - --show -mv /mmdeploy/work_dir/end2end.onnx /RapidPoseTriangulation/extras/mmdeploy/exports/rtmpose-l_wb_1x3x384x288"$withFP16".onnx - -python3 ./tools/deploy.py \ - configs/mmpose/pose-detection_simcc_onnxruntime_dynamic-384x288"$withFP16".py \ - /mmpose/projects/rtmpose/rtmpose/wholebody_2d_keypoint/rtmpose-l_8xb32-270e_coco-wholebody-384x288.py \ - https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmpose-l_simcc-ucoco_dw-ucoco_270e-384x288-2438fd99_20230728.pth \ - /mmpose/projects/rtmpose/examples/onnxruntime/human-pose.jpeg \ - --work-dir work_dir \ - --show -mv /mmdeploy/work_dir/end2end.onnx /RapidPoseTriangulation/extras/mmdeploy/exports/rtmpose-l_wb_Bx3x384x288"$withFP16".onnx -``` - -```bash -python3 /RapidPoseTriangulation/extras/mmdeploy/make_extra_graphs_pt.py -python3 /RapidPoseTriangulation/extras/mmdeploy/make_extra_graphs_tf.py -``` - -```bash -python3 /RapidPoseTriangulation/extras/mmdeploy/add_extra_steps.py -``` - -
- -## TensorRT - -Run this directly in the inference container (the TensorRT versions need to be the same) - -```bash -export withFP16="_fp16" - -trtexec --fp16 \ - --onnx=/RapidPoseTriangulation/extras/mmdeploy/exports/rtmdet-nano_1x320x320x3"$withFP16"_extra-steps.onnx \ - --saveEngine=end2end.engine - -mv ./end2end.engine /RapidPoseTriangulation/extras/mmdeploy/exports/rtmdet-nano_1x320x320x3"$withFP16"_extra-steps.engine - -trtexec --fp16 \ - --onnx=/RapidPoseTriangulation/extras/mmdeploy/exports/rtmpose-m_Bx384x288x3"$withFP16"_extra-steps.onnx \ - --saveEngine=end2end.engine \ - --minShapes=image_input:1x384x288x3 \ - --optShapes=image_input:1x384x288x3 \ - --maxShapes=image_input:1x384x288x3 - -mv ./end2end.engine /RapidPoseTriangulation/extras/mmdeploy/exports/rtmpose-m_1x384x288x3"$withFP16"_extra-steps.engine -``` - -
- -## Benchmark - -```bash -cd /mmdeploy/ -export withFP16="_fp16" - -python3 ./tools/profiler.py \ - configs/mmpose/pose-detection_simcc_onnxruntime_static-384x288"$withFP16".py \ - /mmpose/projects/rtmpose/rtmpose/body_2d_keypoint/rtmpose-m_8xb256-420e_coco-384x288.py \ - /RapidPoseTriangulation/extras/mmdeploy/testimages/ \ - --model /RapidPoseTriangulation/extras/mmdeploy/exports/rtmpose-m_1x3x384x288"$withFP16".onnx \ - --shape 384x288 \ - --device cuda \ - --warmup 50 \ - --num-iter 200 -``` diff --git a/extras/mmdeploy/add_extra_steps.py b/extras/mmdeploy/add_extra_steps.py deleted file mode 100644 index e8b9740..0000000 --- a/extras/mmdeploy/add_extra_steps.py +++ /dev/null @@ -1,233 +0,0 @@ -import re - -import numpy as np -import onnx -from onnx import TensorProto, helper, numpy_helper - -# ================================================================================================== - -base_path = "/RapidPoseTriangulation/extras/mmdeploy/exports/" -det_model_path1 = base_path + "rtmdet-nano_1x3x320x320.onnx" -det_model_path2 = base_path + "rtmdet-m_1x3x320x320.onnx" -pose_model_path1 = base_path + "rtmpose-m_Bx3x384x288.onnx" -pose_model_path2 = base_path + "rtmpose-m_1x3x384x288.onnx" -pose_model_path3 = base_path + "rtmpose-l_wb_Bx3x384x288.onnx" -pose_model_path4 = base_path + "rtmpose-l_wb_1x3x384x288.onnx" - -norm_mean = -1 * (np.array([0.485, 0.456, 0.406]) * 255) -norm_std = 1.0 / (np.array([0.229, 0.224, 0.225]) * 255) - - -# ================================================================================================== - - -def add_steps_to_onnx(model_path): - - # Load existing model - model = onnx.load(model_path) - graph = model.graph - - mean = norm_mean.astype(np.float32) - std = norm_std.astype(np.float32) - - mean = np.reshape(mean, (1, 3, 1, 1)).astype(np.float32) - std = np.reshape(std, (1, 3, 1, 1)).astype(np.float32) - - use_fp16 = bool("fp16" in model_path) - if use_fp16: - mean = mean.astype(np.float16) - std = std.astype(np.float16) - - # Add the initializers to the graph - mean_initializer = numpy_helper.from_array(mean, name="norm_mean") - std_initializer = numpy_helper.from_array(std, name="norm_std") - graph.initializer.extend([mean_initializer, std_initializer]) - - # Define layer names, assuming the first input is the image tensor - input_name = graph.input[0].name - - # Cast to internal type - # This has to be the first node, because tensorrt does not support uint8 layers - cast_type = 10 if use_fp16 else 1 - casted_output = "casted_output" - cast_node = helper.make_node( - "Cast", - inputs=[input_name], - outputs=[casted_output], - to=cast_type, - name="Cast_Input", - ) - - # Node to transpose - transpose_output = "transpose_output" - transpose_node = helper.make_node( - "Transpose", - inputs=[casted_output], - outputs=[transpose_output], - perm=[0, 3, 1, 2], - name="Transpose", - ) - - # Node to add mean - mean_added_output = "mean_added_output" - mean_add_node = helper.make_node( - "Add", - inputs=[transpose_output, "norm_mean"], - outputs=[mean_added_output], - name="Mean_Addition", - ) - - # Node to multiply by std - std_mult_output = "std_mult_output" - std_mul_node = helper.make_node( - "Mul", - inputs=[mean_added_output, "norm_std"], - outputs=[std_mult_output], - name="Std_Multiplication", - ) - - # Replace original input of the model with the output of normalization - for node in graph.node: - for idx, input_name_in_node in enumerate(node.input): - if input_name_in_node == input_name: - node.input[idx] = std_mult_output - - # Add the new nodes to the graph - graph.node.insert(0, cast_node) - graph.node.insert(1, transpose_node) - graph.node.insert(2, mean_add_node) - graph.node.insert(3, std_mul_node) - - # Transpose the input shape - input_shape = graph.input[0].type.tensor_type.shape.dim - dims = [dim.dim_value for dim in input_shape] - for i, j in enumerate([0, 3, 1, 2]): - input_shape[j].dim_value = dims[i] - - # Set the batch size to a defined string - input_shape = graph.input[0].type.tensor_type.shape.dim - if input_shape[0].dim_value == 0: - input_shape[0].dim_param = "batch_size" - - # Rename the input tensor - main_input_image_name = model.graph.input[0].name - for node in model.graph.node: - for idx, name in enumerate(node.input): - if name == main_input_image_name: - node.input[idx] = "image_input" - model.graph.input[0].name = "image_input" - - # Set input image type to int8 - model.graph.input[0].type.tensor_type.elem_type = TensorProto.UINT8 - - # Cast all outputs to fp32 to avoid half precision issues in cpp code - for output in graph.output: - orig_output_name = output.name - internal_output_name = orig_output_name + "_internal" - - # Rename the output tensor - for node in model.graph.node: - for idx, name in enumerate(node.output): - if name == orig_output_name: - node.output[idx] = internal_output_name - - # Insert a Cast node that casts the internal output to fp32 - cast_fp32_name = orig_output_name - cast_node_output = helper.make_node( - "Cast", - inputs=[internal_output_name], - outputs=[cast_fp32_name], - to=1, - name="Cast_Output_" + orig_output_name, - ) - # Append the cast node to the graph - graph.node.append(cast_node_output) - - # Update the output's data type info - output.type.tensor_type.elem_type = TensorProto.FLOAT - - # Merge the two outputs - if "det" in model_path: - r1_output = "dets" - r2_output = "labels" - out_name = "bboxes" - out_dim = 6 - if "pose" in model_path: - r1_output = "kpts" - r2_output = "scores" - out_name = "keypoints" - out_dim = 3 - if "det" in model_path or "pose" in model_path: - # Node to expand - r2_expanded = r2_output + "_expanded" - unsqueeze_node = helper.make_node( - "Unsqueeze", - inputs=[r2_output], - outputs=[r2_expanded], - axes=[2], - name="Unsqueeze", - ) - - # Node to concatenate - r12_merged = out_name - concat_node = helper.make_node( - "Concat", - inputs=[r1_output, r2_expanded], - outputs=[r12_merged], - axis=2, - name="Merged", - ) - - # Define the new concatenated output - merged_output = helper.make_tensor_value_info( - r12_merged, - TensorProto.FLOAT, - [ - ( - graph.input[0].type.tensor_type.shape.dim[0].dim_value - if graph.input[0].type.tensor_type.shape.dim[0].dim_value > 0 - else None - ), - ( - graph.output[0].type.tensor_type.shape.dim[1].dim_value - if graph.output[0].type.tensor_type.shape.dim[1].dim_value > 0 - else None - ), - out_dim, - ], - ) - - # Update the graph - graph.node.append(unsqueeze_node) - graph.node.append(concat_node) - graph.output.pop() - graph.output.pop() - graph.output.append(merged_output) - - path = re.sub(r"(x)(\d+)x(\d+)x(\d+)", r"\1\3x\4x\2", model_path) - path = path.replace(".onnx", "_extra-steps.onnx") - onnx.save(model, path) - - -# ================================================================================================== - - -def main(): - add_steps_to_onnx(det_model_path1) - add_steps_to_onnx(det_model_path2) - add_steps_to_onnx(pose_model_path1) - add_steps_to_onnx(pose_model_path2) - add_steps_to_onnx(pose_model_path3) - add_steps_to_onnx(pose_model_path4) - add_steps_to_onnx(det_model_path1.replace(".onnx", "_fp16.onnx")) - add_steps_to_onnx(det_model_path2.replace(".onnx", "_fp16.onnx")) - add_steps_to_onnx(pose_model_path1.replace(".onnx", "_fp16.onnx")) - add_steps_to_onnx(pose_model_path2.replace(".onnx", "_fp16.onnx")) - add_steps_to_onnx(pose_model_path3.replace(".onnx", "_fp16.onnx")) - add_steps_to_onnx(pose_model_path4.replace(".onnx", "_fp16.onnx")) - - -# ================================================================================================== - -if __name__ == "__main__": - main() diff --git a/extras/mmdeploy/configs/detection_onnxruntime_static-320x320.py b/extras/mmdeploy/configs/detection_onnxruntime_static-320x320.py deleted file mode 100644 index d7d5b57..0000000 --- a/extras/mmdeploy/configs/detection_onnxruntime_static-320x320.py +++ /dev/null @@ -1,18 +0,0 @@ -_base_ = ["../_base_/base_static.py", "../../_base_/backends/onnxruntime.py"] - -onnx_config = dict( - input_shape=[320, 320], -) - -codebase_config = dict( - # For later TensorRT inference, the number of output boxes needs to be as stable as possible, - # because a drop in the box count leads to a re-optimization which takes a lot of time, - # therefore reduce the maximum number of output boxes to the smallest usable value and sort out - # low confidence boxes outside the model. - post_processing=dict( - score_threshold=0.0, - confidence_threshold=0.0, - iou_threshold=0.5, - max_output_boxes_per_class=10, - ), -) diff --git a/extras/mmdeploy/configs/detection_onnxruntime_static-320x320_fp16.py b/extras/mmdeploy/configs/detection_onnxruntime_static-320x320_fp16.py deleted file mode 100644 index 1dd243b..0000000 --- a/extras/mmdeploy/configs/detection_onnxruntime_static-320x320_fp16.py +++ /dev/null @@ -1,18 +0,0 @@ -_base_ = ["../_base_/base_static.py", "../../_base_/backends/onnxruntime-fp16.py"] - -onnx_config = dict( - input_shape=[320, 320], -) - -codebase_config = dict( - # For later TensorRT inference, the number of output boxes needs to be as stable as possible, - # because a drop in the box count leads to a re-optimization which takes a lot of time, - # therefore reduce the maximum number of output boxes to the smallest usable value and sort out - # low confidence boxes outside the model. - post_processing=dict( - score_threshold=0.0, - confidence_threshold=0.0, - iou_threshold=0.5, - max_output_boxes_per_class=10, - ), -) diff --git a/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_dynamic-384x288.py b/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_dynamic-384x288.py deleted file mode 100644 index 3d52547..0000000 --- a/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_dynamic-384x288.py +++ /dev/null @@ -1,19 +0,0 @@ -_base_ = ["./pose-detection_static.py", "../_base_/backends/onnxruntime.py"] - -onnx_config = dict( - input_shape=[288, 384], - output_names=["kpts", "scores"], - dynamic_axes={ - "input": { - 0: "batch", - }, - "kpts": { - 0: "batch", - }, - "scores": { - 0: "batch", - }, - }, -) - -codebase_config = dict(export_postprocess=True) # export get_simcc_maximum diff --git a/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_dynamic-384x288_fp16.py b/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_dynamic-384x288_fp16.py deleted file mode 100644 index fe0ca45..0000000 --- a/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_dynamic-384x288_fp16.py +++ /dev/null @@ -1,19 +0,0 @@ -_base_ = ["./pose-detection_static.py", "../_base_/backends/onnxruntime-fp16.py"] - -onnx_config = dict( - input_shape=[288, 384], - output_names=["kpts", "scores"], - dynamic_axes={ - "input": { - 0: "batch", - }, - "kpts": { - 0: "batch", - }, - "scores": { - 0: "batch", - }, - }, -) - -codebase_config = dict(export_postprocess=True) # export get_simcc_maximum diff --git a/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_static-384x288.py b/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_static-384x288.py deleted file mode 100644 index bfa43b4..0000000 --- a/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_static-384x288.py +++ /dev/null @@ -1,8 +0,0 @@ -_base_ = ["./pose-detection_static.py", "../_base_/backends/onnxruntime.py"] - -onnx_config = dict( - input_shape=[288, 384], - output_names=["kpts", "scores"], -) - -codebase_config = dict(export_postprocess=True) # export get_simcc_maximum diff --git a/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_static-384x288_fp16.py b/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_static-384x288_fp16.py deleted file mode 100644 index 6263dac..0000000 --- a/extras/mmdeploy/configs/pose-detection_simcc_onnxruntime_static-384x288_fp16.py +++ /dev/null @@ -1,8 +0,0 @@ -_base_ = ["./pose-detection_static.py", "../_base_/backends/onnxruntime-fp16.py"] - -onnx_config = dict( - input_shape=[288, 384], - output_names=["kpts", "scores"], -) - -codebase_config = dict(export_postprocess=True) # export get_simcc_maximum diff --git a/extras/mmdeploy/dockerfile b/extras/mmdeploy/dockerfile deleted file mode 100644 index 313be15..0000000 --- a/extras/mmdeploy/dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM openmmlab/mmdeploy:ubuntu20.04-cuda11.8-mmdeploy1.3.1 - -ARG DEBIAN_FRONTEND=noninteractive -ENV LANG=C.UTF-8 -ENV LC_ALL=C.UTF-8 -WORKDIR / - -RUN apt-get update && apt-get install -y --no-install-recommends feh - -RUN git clone https://github.com/open-mmlab/mmdeploy.git --depth=1 -RUN cd mmdeploy/; python3 tools/scripts/build_ubuntu_x64_ort.py - -# Install MMPose -ENV FORCE_CUDA="1" -ENV MMCV_WITH_OPS=1 -RUN pip3 install --upgrade --no-cache-dir openmim -RUN mim install mmengine -RUN mim install "mmcv>=2,<2.2.0" -RUN mim install "mmdet>=3" -RUN mim install "mmpose>=1.1.0" -# Fix an error when importing mmpose -RUN pip3 install --upgrade --no-cache-dir "numpy<2" scipy -RUN git clone --depth=1 --branch=main https://github.com/open-mmlab/mmpose.git - -RUN echo 'export PYTHONPATH=/mmdeploy/build/lib:$PYTHONPATH' >> ~/.bashrc -RUN echo 'export LD_LIBRARY_PATH=/mmdeploy/../mmdeploy-dep/onnxruntime-linux-x64-1.8.1/lib/:$LD_LIBRARY_PATH' >> ~/.bashrc - -# Show images -RUN apt-get update && apt-get install -y --no-install-recommends python3-tk - -# Tool for fp16 conversion -RUN pip3 install --upgrade --no-cache-dir onnxconverter_common - -# Fix an error when profiling -RUN pip3 install --upgrade --no-cache-dir "onnxruntime-gpu<1.17" - -RUN pip3 install --upgrade --no-cache-dir tensorflow -RUN pip3 install --upgrade --no-cache-dir tf2onnx - -WORKDIR /mmdeploy/ -CMD ["/bin/bash"] diff --git a/extras/mmdeploy/exports/.gitignore b/extras/mmdeploy/exports/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/extras/mmdeploy/exports/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/extras/mmdeploy/make_extra_graphs_pt.py b/extras/mmdeploy/make_extra_graphs_pt.py deleted file mode 100644 index 0a920f7..0000000 --- a/extras/mmdeploy/make_extra_graphs_pt.py +++ /dev/null @@ -1,338 +0,0 @@ -import cv2 -import torch -import torch.nn as nn -import torch.nn.functional as F -from torchvision.ops import roi_align - -# ================================================================================================== - -base_path = "/RapidPoseTriangulation/extras/mmdeploy/exports/" -det_target_size = (320, 320) -pose_target_size = (384, 288) - -# ================================================================================================== - - -class Letterbox(nn.Module): - def __init__(self, target_size, fill_value=128): - """Resize and pad image while keeping aspect ratio""" - super(Letterbox, self).__init__() - - self.target_size = target_size - self.fill_value = fill_value - - def calc_params(self, ishape): - ih, iw = ishape[1], ishape[2] - th, tw = self.target_size - - scale = torch.min(tw / iw, th / ih) - nw = torch.round(iw * scale) - nh = torch.round(ih * scale) - - pad_w = tw - nw - pad_h = th - nh - pad_left = pad_w // 2 - pad_top = pad_h // 2 - pad_right = pad_w - pad_left - pad_bottom = pad_h - pad_top - paddings = (pad_left, pad_right, pad_top, pad_bottom) - - return paddings, scale, (nw, nh) - - def forward(self, img): - paddings, _, (nw, nh) = self.calc_params(img.shape) - - # Resize the image - img = img.to(torch.float32) - img = img.permute(0, 3, 1, 2) - img = F.interpolate( - img, - size=(nh, nw), - mode="bilinear", - align_corners=False, - ) - img = img.permute(0, 2, 3, 1) - img = img.round() - - # Pad the image - img = F.pad( - img.permute(0, 3, 1, 2), - pad=paddings, - mode="constant", - value=self.fill_value, - ) - img = img.permute(0, 2, 3, 1) - - return img - - -# ================================================================================================== - - -class BoxCrop(nn.Module): - def __init__(self, target_size): - """Crop bounding box from image""" - super(BoxCrop, self).__init__() - - self.target_size = target_size - self.padding_scale = 1.25 - - def calc_params(self, bbox): - start_x, start_y, end_x, end_y = bbox[0, 0], bbox[0, 1], bbox[0, 2], bbox[0, 3] - target_h, target_w = self.target_size - - # Calculate original bounding box width, height and center - bbox_w = end_x - start_x - bbox_h = end_y - start_y - center_x = (start_x + end_x) / 2.0 - center_y = (start_y + end_y) / 2.0 - - # Calculate the aspect ratios - bbox_aspect = bbox_w / bbox_h - target_aspect = target_w / target_h - - # Adjust the scaled bounding box to match the target aspect ratio - if bbox_aspect > target_aspect: - adjusted_h = bbox_w / target_aspect - adjusted_w = bbox_w - else: - adjusted_w = bbox_h * target_aspect - adjusted_h = bbox_h - - # Scale the bounding box by the padding_scale - scaled_bbox_w = adjusted_w * self.padding_scale - scaled_bbox_h = adjusted_h * self.padding_scale - - # Calculate scaled bounding box coordinates - new_start_x = center_x - scaled_bbox_w / 2.0 - new_start_y = center_y - scaled_bbox_h / 2.0 - new_end_x = center_x + scaled_bbox_w / 2.0 - new_end_y = center_y + scaled_bbox_h / 2.0 - - # Define the new box coordinates - new_box = torch.stack((new_start_x, new_start_y, new_end_x, new_end_y), dim=0) - new_box = new_box.unsqueeze(0) - scale = torch.stack( - ((target_w / scaled_bbox_w), (target_h / scaled_bbox_h)), dim=0 - ) - - return scale, new_box - - def forward(self, img, bbox): - _, bbox = self.calc_params(bbox) - - batch_indices = torch.zeros(bbox.shape[0], 1) - rois = torch.cat([batch_indices, bbox], dim=1) - - # Resize and crop - img = img.to(torch.float32) - img = img.permute(0, 3, 1, 2) - img = roi_align( - img, - rois, - output_size=self.target_size, - spatial_scale=1.0, - sampling_ratio=0, - ) - img = img.permute(0, 2, 3, 1) - img = img.round() - - return img - - -# ================================================================================================== - - -class DetPreprocess(nn.Module): - def __init__(self, target_size, fill_value=114): - super(DetPreprocess, self).__init__() - self.letterbox = Letterbox(target_size, fill_value) - - def forward(self, img): - # img: torch.Tensor of shape [batch, H, W, C], dtype=torch.uint8 - img = self.letterbox(img) - return img - - -# ================================================================================================== - - -class DetPostprocess(nn.Module): - def __init__(self, target_size): - super(DetPostprocess, self).__init__() - - self.target_size = target_size - self.letterbox = Letterbox(target_size) - - def forward(self, img, boxes): - paddings, scale, _ = self.letterbox.calc_params(img.shape) - - boxes = boxes.float() - boxes[:, :, 0] -= paddings[0] - boxes[:, :, 2] -= paddings[0] - boxes[:, :, 1] -= paddings[2] - boxes[:, :, 3] -= paddings[2] - - zero = torch.tensor(0) - boxes = torch.max(boxes, zero) - - th, tw = self.target_size - pad_w = paddings[0] + paddings[1] - pad_h = paddings[2] + paddings[3] - max_w = tw - pad_w - 1 - max_h = th - pad_h - 1 - b0 = boxes[:, :, 0] - b1 = boxes[:, :, 1] - b2 = boxes[:, :, 2] - b3 = boxes[:, :, 3] - b0 = torch.min(b0, max_w) - b1 = torch.min(b1, max_h) - b2 = torch.min(b2, max_w) - b3 = torch.min(b3, max_h) - boxes[:, :, 0] = b0 - boxes[:, :, 1] = b1 - boxes[:, :, 2] = b2 - boxes[:, :, 3] = b3 - - boxes[:, :, 0:4] /= scale - return boxes - - -# ================================================================================================== - - -class PosePreprocess(nn.Module): - def __init__(self, target_size, fill_value=114): - super(PosePreprocess, self).__init__() - self.boxcrop = BoxCrop(target_size) - - def forward(self, img, bbox): - # img: torch.Tensor of shape [1, H, W, C], dtype=torch.uint8 - # bbox: torch.Tensor of shape [1, 4], dtype=torch.float32 - img = self.boxcrop(img, bbox) - return img - - -# ================================================================================================== - - -class PosePostprocess(nn.Module): - def __init__(self, target_size): - super(PosePostprocess, self).__init__() - self.boxcrop = BoxCrop(target_size) - self.target_size = target_size - - def forward(self, img, bbox, keypoints): - scale, bbox = self.boxcrop.calc_params(bbox) - - kp = keypoints.float() - kp[:, :, 0:2] /= scale - kp[:, :, 0] += bbox[0, 0] - kp[:, :, 1] += bbox[0, 1] - - zero = torch.tensor(0) - kp = torch.max(kp, zero) - - max_w = img.shape[2] - 1 - max_h = img.shape[1] - 1 - k0 = kp[:, :, 0] - k1 = kp[:, :, 1] - k0 = torch.min(k0, max_w) - k1 = torch.min(k1, max_h) - kp[:, :, 0] = k0 - kp[:, :, 1] = k1 - - return kp - - -# ================================================================================================== - - -def main(): - - img_path = "/RapidPoseTriangulation/scripts/../data/h1/54138969-img_003201.jpg" - image = cv2.imread(img_path, 3) - image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) - - # Initialize the DetPreprocess module - preprocess_model = DetPreprocess(target_size=det_target_size) - det_dummy_input_a0 = torch.from_numpy(image).unsqueeze(0) - - # Export to ONNX - torch.onnx.export( - preprocess_model, - det_dummy_input_a0, - base_path + "det_preprocess.onnx", - opset_version=11, - input_names=["input_image"], - output_names=["preprocessed_image"], - dynamic_axes={ - "input_image": {0: "batch_size", 1: "height", 2: "width"}, - "preprocessed_image": {0: "batch_size"}, - }, - ) - - # Initialize the DetPostprocess module - postprocess_model = DetPostprocess(target_size=det_target_size) - det_dummy_input_b0 = torch.from_numpy(image).unsqueeze(0) - det_dummy_input_b1 = torch.rand(1, 10, 5) - - # Export to ONNX - torch.onnx.export( - postprocess_model, - (det_dummy_input_b0, det_dummy_input_b1), - base_path + "det_postprocess.onnx", - opset_version=11, - input_names=["input_image", "boxes"], - output_names=["output_boxes"], - dynamic_axes={ - "input_image": {0: "batch_size", 1: "height", 2: "width"}, - "boxes": {0: "batch_size", 1: "num_boxes"}, - "output_boxes": {0: "batch_size", 1: "num_boxes"}, - }, - ) - - # Initialize the PosePreprocess module - preprocess_model = PosePreprocess(target_size=pose_target_size) - det_dummy_input_c0 = torch.from_numpy(image).unsqueeze(0) - det_dummy_input_c1 = torch.tensor([[352, 339, 518, 594]]).to(torch.int32) - - # Export to ONNX - torch.onnx.export( - preprocess_model, - (det_dummy_input_c0, det_dummy_input_c1), - base_path + "pose_preprocess.onnx", - opset_version=11, - input_names=["input_image", "bbox"], - output_names=["preprocessed_image"], - dynamic_axes={ - "input_image": {0: "batch_size", 1: "height", 2: "width"}, - "preprocessed_image": {0: "batch_size"}, - }, - ) - - # Initialize the PosePostprocess module - postprocess_model = PosePostprocess(target_size=pose_target_size) - det_dummy_input_d0 = torch.from_numpy(image).unsqueeze(0) - det_dummy_input_d1 = torch.tensor([[352, 339, 518, 594]]).to(torch.int32) - det_dummy_input_d2 = torch.rand(1, 17, 2) - - # Export to ONNX - torch.onnx.export( - postprocess_model, - (det_dummy_input_d0, det_dummy_input_d1, det_dummy_input_d2), - base_path + "pose_postprocess.onnx", - opset_version=11, - input_names=["input_image", "bbox", "keypoints"], - output_names=["output_keypoints"], - dynamic_axes={ - "input_image": {0: "batch_size", 1: "height", 2: "width"}, - "output_keypoints": {0: "batch_size"}, - }, - ) - - -# ================================================================================================== - -if __name__ == "__main__": - main() diff --git a/extras/mmdeploy/make_extra_graphs_tf.py b/extras/mmdeploy/make_extra_graphs_tf.py deleted file mode 100644 index 3cf1d27..0000000 --- a/extras/mmdeploy/make_extra_graphs_tf.py +++ /dev/null @@ -1,276 +0,0 @@ -import cv2 - -import numpy as np -import tensorflow as tf -import tf2onnx - -# ================================================================================================== - -base_path = "/RapidPoseTriangulation/extras/mmdeploy/exports/" -det_target_size = (320, 320) - -# ================================================================================================== - - -class BayerToRGB(tf.keras.layers.Layer): - """Convert Bayer image to RGB - See: https://stanford.edu/class/ee367/reading/Demosaicing_ICASSP04.pdf - See: https://github.com/cheind/pytorch-debayer/blob/master/debayer/modules.py#L231 - """ - - def __init__(self): - super().__init__() - self.layout = "RGGB" - self.max_val = 255.0 - - self.kernels = tf.constant( - np.array( - [ - # G at R/B locations - [ - [0, 0, -1, 0, 0], - [0, 0, 2, 0, 0], - [-1, 2, 4, 2, -1], - [0, 0, 2, 0, 0], - [0, 0, -1, 0, 0], - ], - # R/B at G in R/B rows and B/R columns - [ - [0, 0, 0.5, 0, 0], - [0, -1, 0, -1, 0], - [-1, 4, 5, 4, -1], - [0, -1, 0, -1, 0], - [0, 0, 0.5, 0, 0], - ], - # R/B at G in B/R rows and R/B columns - [ - [0, 0, 0.5, 0, 0], - [0, -1, 4, -1, 0], - [-1, 0, 5, 0, -1], - [0, -1, 4, -1, 0], - [0, 0, 0.5, 0, 0], - ], - # R/B at B/R in B/R rows and B/R columns - [ - [0, 0, -1.5, 0, 0], - [0, 2, 0, 2, 0], - [-1.5, 0, 6, 0, -1.5], - [0, 2, 0, 2, 0], - [0, 0, -1.5, 0, 0], - ], - ], - dtype=np.float32, - ) - .reshape(1, 4, 5, 5) - .transpose(2, 3, 0, 1) - / 8.0 - ) - self.index = tf.constant( - np.array( - # Describes the kernel indices that calculate the corresponding RGB values for - # the 2x2 layout (RGGB) sub-structure - [ - # Destination R - [ - [4, 1], # identity, R at G in R row B column - [2, 3], # R at G in B row R column, R at B in B row R column - ], - # Destination G - [ - [0, 4], - [4, 0], - ], - # Destination B - [ - [3, 2], - [1, 4], - ], - ] - ).reshape(1, 3, 2, 2) - ) - - def call(self, img): - H, W = tf.shape(img)[1], tf.shape(img)[2] - - # Pad the image - tpad = img[:, 0:2, :, :] - bpad = img[:, H - 2 : H, :, :] - ipad = tf.concat([tpad, img, bpad], axis=1) - lpad = ipad[:, :, 0:2, :] - rpad = ipad[:, :, W - 2 : W, :] - ipad = tf.concat([lpad, ipad, rpad], axis=2) - - # Convolve with kernels - planes = tf.nn.conv2d(ipad, self.kernels, strides=[1, 1, 1, 1], padding="VALID") - - # Concatenate identity kernel - planes = tf.concat([planes, img], axis=-1) - - # Gather values - index_repeated = tf.tile(self.index, multiples=[1, 1, H // 2, W // 2]) - index_repeated = tf.transpose(index_repeated, perm=[0, 2, 3, 1]) - row_indices, col_indices = tf.meshgrid(tf.range(H), tf.range(W), indexing="ij") - index_tensor = tf.stack([row_indices, col_indices], axis=-1) - index_tensor = tf.expand_dims(index_tensor, axis=0) - index_tensor = tf.expand_dims(index_tensor, axis=-2) - index_tensor = tf.repeat(index_tensor, repeats=3, axis=-2) - index_repeated = tf.expand_dims(index_repeated, axis=-1) - indices = tf.concat([tf.cast(index_tensor, tf.int64), index_repeated], axis=-1) - rgb = tf.gather_nd(planes, indices, batch_dims=1) - - if self.max_val == 255.0: - # Make value range valid again - rgb = tf.round(rgb) - - return rgb - - -# ================================================================================================== - - -def bayer_resize(img, size): - """Resize a Bayer image by splitting color channels""" - - # Split the image into 4 channels - r = img[:, 0::2, 0::2, 0] - g1 = img[:, 0::2, 1::2, 0] - g2 = img[:, 1::2, 0::2, 0] - b = img[:, 1::2, 1::2, 0] - bsplit = tf.stack([r, g1, g2, b], axis=-1) - - # Resize the image - # Make sure the target size is divisible by 2 - size = (size[0] // 2, size[1] // 2) - bsized = tf.image.resize(bsplit, size=size, method="bilinear") - - # Create a bayer image again - img = tf.nn.depth_to_space(bsized, block_size=2) - - return img - - -# ================================================================================================== - - -class Letterbox(tf.keras.layers.Layer): - def __init__(self, target_size, fill_value=128): - """Resize and pad image while keeping aspect ratio""" - super(Letterbox, self).__init__() - - self.b2rgb = BayerToRGB() - self.target_size = target_size - self.fill_value = fill_value - - def calc_params(self, ishape): - img_h, img_w = ishape[1], ishape[2] - target_h, target_w = self.target_size - - scale = tf.minimum(target_w / img_w, target_h / img_h) - new_w = tf.round(tf.cast(img_w, scale.dtype) * scale) - new_h = tf.round(tf.cast(img_h, scale.dtype) * scale) - new_w = tf.cast(new_w, tf.int32) - new_h = tf.cast(new_h, tf.int32) - new_w = new_w - (new_w % 2) - new_h = new_h - (new_h % 2) - - pad_w = target_w - new_w - pad_h = target_h - new_h - pad_left = tf.cast(tf.floor(tf.cast(pad_w, tf.float32) / 2.0), tf.int32) - pad_top = tf.cast(tf.floor(tf.cast(pad_h, tf.float32) / 2.0), tf.int32) - pad_right = pad_w - pad_left - pad_bottom = pad_h - pad_top - paddings = [pad_top, pad_bottom, pad_left, pad_right] - - return paddings, scale, (new_w, new_h) - - def call(self, img): - paddings, _, (nw, nh) = self.calc_params(tf.shape(img)) - - # Resize the image and convert to RGB - img = bayer_resize(img, (nh, nw)) - img = self.b2rgb(img) - - # Pad the image - pad_top, pad_bottom, pad_left, pad_right = paddings - img = tf.pad( - img, - paddings=[[0, 0], [pad_top, pad_bottom], [pad_left, pad_right], [0, 0]], - mode="CONSTANT", - constant_values=self.fill_value, - ) - - return img - - -# ================================================================================================== - - -class DetPreprocess(tf.keras.layers.Layer): - def __init__(self, target_size, fill_value=114): - super(DetPreprocess, self).__init__() - self.letterbox = Letterbox(target_size, fill_value) - - def call(self, img): - """img: tf.Tensor of shape [batch, H, W, C], dtype=tf.uint8""" - - # Cast to float32 since TensorRT does not support uint8 layers - img = tf.cast(img, tf.float32) - - img = self.letterbox(img) - return img - - -# ================================================================================================== - - -def rgb2bayer(img): - bayer = np.zeros((img.shape[0], img.shape[1]), dtype=img.dtype) - bayer[0::2, 0::2] = img[0::2, 0::2, 0] - bayer[0::2, 1::2] = img[0::2, 1::2, 1] - bayer[1::2, 0::2] = img[1::2, 0::2, 1] - bayer[1::2, 1::2] = img[1::2, 1::2, 2] - return bayer - - -# ================================================================================================== - - -def main(): - - img_path = "/RapidPoseTriangulation/scripts/../data/h1/54138969-img_003201.jpg" - image = cv2.imread(img_path, 3) - image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) - image = rgb2bayer(image) - image = np.expand_dims(image, axis=-1) - image = np.asarray(image, dtype=np.uint8) - - # Initialize the DetPreprocess module - preprocess_model = tf.keras.Sequential() - preprocess_model.add(DetPreprocess(target_size=det_target_size)) - det_dummy_input_a0 = tf.convert_to_tensor( - np.expand_dims(image, axis=0), dtype=tf.uint8 - ) - det_dummy_output_a0 = preprocess_model(det_dummy_input_a0) - print("\n", det_dummy_output_a0.shape, "\n") - - output_a0 = det_dummy_output_a0.numpy() - output_a0 = np.squeeze(output_a0, axis=0) - output_a0 = np.asarray(output_a0, dtype=np.uint8) - output_a0 = cv2.cvtColor(output_a0, cv2.COLOR_RGB2BGR) - cv2.imwrite(base_path + "det_preprocess.jpg", output_a0) - - # Export to ONNX - input_signature = [tf.TensorSpec([None, None, None, 1], tf.uint8, name="x")] - _, _ = tf2onnx.convert.from_keras( - preprocess_model, - input_signature, - opset=11, - output_path=base_path + "det_preprocess.onnx", - target=["tensorrt"], - ) - - -# ================================================================================================== - -if __name__ == "__main__": - main() diff --git a/extras/mmdeploy/run_container.sh b/extras/mmdeploy/run_container.sh deleted file mode 100755 index 7353774..0000000 --- a/extras/mmdeploy/run_container.sh +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/bash - -xhost + -docker run --privileged --rm --network host -it \ - --gpus all --shm-size=16g --ulimit memlock=-1 --ulimit stack=67108864 \ - --volume "$(pwd)"/:/RapidPoseTriangulation/ \ - --volume /tmp/.X11-unix:/tmp/.X11-unix \ - --env DISPLAY --env QT_X11_NO_MITSHM=1 \ - rpt_mmdeploy diff --git a/extras/mmdeploy/testimages/human-pose.jpeg b/extras/mmdeploy/testimages/human-pose.jpeg deleted file mode 100644 index 8de401563e83541f016fa475bfcbf44fda25a38c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39020 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 L=95W3&7=R>P-1Ef diff --git a/extras/mmpose/README.md b/extras/mmpose/README.md deleted file mode 100644 index c731136..0000000 --- a/extras/mmpose/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Finetuning MMPose models - -See: - -
- -```bash -docker build --progress=plain -f extras/mmpose/dockerfile -t rpt_mmpose . - -./extras/mmpose/run_container.sh -``` - -```bash -cd /mmpose/ -export CUDA_VISIBLE_DEVICES=0 - -python3 ./tools/train.py \ - /RapidPoseTriangulation/extras/mmpose/configs/rtmpose-l_8xb32-270e_coco-wholebody-384x288.py \ - --amp \ - --cfg-options \ - load_from=https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmpose-l_simcc-coco-wholebody_pt-aic-coco_270e-384x288-eaeb96c8_20230125.pth \ - base_lr=0.00004 -``` diff --git a/extras/mmpose/configs/rtmpose-l_8xb32-270e_coco-wholebody-384x288.py b/extras/mmpose/configs/rtmpose-l_8xb32-270e_coco-wholebody-384x288.py deleted file mode 100644 index 9af4ae2..0000000 --- a/extras/mmpose/configs/rtmpose-l_8xb32-270e_coco-wholebody-384x288.py +++ /dev/null @@ -1,235 +0,0 @@ -_base_ = ['mmpose::_base_/default_runtime.py'] - -val_interval=1 -max_epochs = 3 - -# common setting -num_keypoints = 133 -input_size = (288, 384) - -# runtime -stage2_num_epochs = 30 -base_lr = 4e-3 -train_batch_size = 32 -val_batch_size = 32 - -train_cfg = dict(max_epochs=max_epochs, val_interval=val_interval) -randomness = dict(seed=21) - -# optimizer -optim_wrapper = dict( - type='OptimWrapper', - optimizer=dict(type='AdamW', lr=base_lr, weight_decay=0.05), - clip_grad=dict(max_norm=35, norm_type=2), - paramwise_cfg=dict( - norm_decay_mult=0, bias_decay_mult=0, bypass_duplicate=True)) - -# learning rate -param_scheduler = [ - dict( - type='LinearLR', - start_factor=1.0e-5, - by_epoch=False, - begin=0, - end=1000), - dict( - type='CosineAnnealingLR', - eta_min=base_lr * 0.05, - begin=max_epochs // 2, - end=max_epochs, - T_max=max_epochs // 2, - by_epoch=True, - convert_to_iter_based=True), -] - -# automatically scaling LR based on the actual training batch size -auto_scale_lr = dict(base_batch_size=512) - -# codec settings -codec = dict( - type='SimCCLabel', - input_size=input_size, - sigma=(6., 6.93), - simcc_split_ratio=2.0, - normalize=False, - use_dark=False) - -# model settings -model = dict( - type='TopdownPoseEstimator', - data_preprocessor=dict( - type='PoseDataPreprocessor', - mean=[123.675, 116.28, 103.53], - std=[58.395, 57.12, 57.375], - bgr_to_rgb=True), - backbone=dict( - _scope_='mmdet', - type='CSPNeXt', - arch='P5', - expand_ratio=0.5, - deepen_factor=1., - widen_factor=1., - out_indices=(4, ), - channel_attention=True, - norm_cfg=dict(type='SyncBN'), - act_cfg=dict(type='SiLU'), - init_cfg=dict( - type='Pretrained', - prefix='backbone.', - checkpoint='https://download.openmmlab.com/mmpose/v1/projects/' - 'rtmposev1/cspnext-l_udp-aic-coco_210e-256x192-273b7631_20230130.pth' # noqa - )), - head=dict( - type='RTMCCHead', - in_channels=1024, - out_channels=num_keypoints, - input_size=codec['input_size'], - in_featuremap_size=tuple([s // 32 for s in codec['input_size']]), - simcc_split_ratio=codec['simcc_split_ratio'], - final_layer_kernel_size=7, - gau_cfg=dict( - hidden_dims=256, - s=128, - expansion_factor=2, - dropout_rate=0., - drop_path=0., - act_fn='SiLU', - use_rel_bias=False, - pos_enc=False), - loss=dict( - type='KLDiscretLoss', - use_target_weight=True, - beta=10., - label_softmax=True), - decoder=codec), - test_cfg=dict(flip_test=True, )) - -# base dataset settings -dataset_type = 'CocoWholeBodyDataset' -data_mode = 'topdown' -data_root = 'data/coco/' - -backend_args = dict(backend='local') - -# pipelines -train_pipeline = [ - dict(type='LoadImage', backend_args=backend_args), - dict(type='GetBBoxCenterScale'), - dict(type='RandomFlip', direction='horizontal'), - dict(type='RandomHalfBody'), - dict( - type='RandomBBoxTransform', scale_factor=[0.6, 1.4], rotate_factor=80), - dict(type='TopdownAffine', input_size=codec['input_size']), - dict(type='mmdet.YOLOXHSVRandomAug'), - dict( - type='Albumentation', - transforms=[ - dict(type='Blur', p=0.1), - dict(type='MedianBlur', p=0.1), - dict( - type='CoarseDropout', - max_holes=1, - max_height=0.4, - max_width=0.4, - min_holes=1, - min_height=0.2, - min_width=0.2, - p=1.0), - ]), - dict(type='GenerateTarget', encoder=codec), - dict(type='PackPoseInputs') -] -val_pipeline = [ - dict(type='LoadImage', backend_args=backend_args), - dict(type='GetBBoxCenterScale'), - dict(type='TopdownAffine', input_size=codec['input_size']), - dict(type='PackPoseInputs') -] - -train_pipeline_stage2 = [ - dict(type='LoadImage', backend_args=backend_args), - dict(type='GetBBoxCenterScale'), - dict(type='RandomFlip', direction='horizontal'), - dict(type='RandomHalfBody'), - dict( - type='RandomBBoxTransform', - shift_factor=0., - scale_factor=[0.75, 1.25], - rotate_factor=60), - dict(type='TopdownAffine', input_size=codec['input_size']), - dict(type='mmdet.YOLOXHSVRandomAug'), - dict( - type='Albumentation', - transforms=[ - dict(type='Blur', p=0.1), - dict(type='MedianBlur', p=0.1), - dict( - type='CoarseDropout', - max_holes=1, - max_height=0.4, - max_width=0.4, - min_holes=1, - min_height=0.2, - min_width=0.2, - p=0.5), - ]), - dict(type='GenerateTarget', encoder=codec), - dict(type='PackPoseInputs') -] - -# data loaders -train_dataloader = dict( - batch_size=train_batch_size, - num_workers=10, - persistent_workers=True, - sampler=dict(type='DefaultSampler', shuffle=True), - dataset=dict( - type=dataset_type, - data_root=data_root, - data_mode=data_mode, - ann_file='annotations/coco_wholebody_train_v1.0.json', - data_prefix=dict(img='train2017/'), - pipeline=train_pipeline, - )) -val_dataloader = dict( - batch_size=val_batch_size, - num_workers=10, - persistent_workers=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False, round_up=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - data_mode=data_mode, - ann_file='annotations/coco_wholebody_val_v1.0.json', - data_prefix=dict(img='val2017/'), - test_mode=True, - # bbox_file='data/coco/person_detection_results/' - # 'COCO_val2017_detections_AP_H_56_person.json', - pipeline=val_pipeline, - )) -test_dataloader = val_dataloader - -# hooks -default_hooks = dict( - checkpoint=dict( - save_best='coco-wholebody/AP', rule='greater', max_keep_ckpts=1)) - -custom_hooks = [ - dict( - type='EMAHook', - ema_type='ExpMomentumEMA', - momentum=0.0002, - update_buffers=True, - priority=49), - dict( - type='mmdet.PipelineSwitchHook', - switch_epoch=max_epochs - stage2_num_epochs, - switch_pipeline=train_pipeline_stage2) -] - -# evaluators -val_evaluator = dict( - type='CocoWholeBodyMetric', - ann_file=data_root + 'annotations/coco_wholebody_val_v1.0.json') -test_evaluator = val_evaluator diff --git a/extras/mmpose/dockerfile b/extras/mmpose/dockerfile deleted file mode 100644 index 455ba87..0000000 --- a/extras/mmpose/dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM rpt_mmdeploy - -RUN apt-get update && apt-get install -y --no-install-recommends nano -RUN pip3 install --upgrade --no-cache-dir "albumentations<1.4" - -RUN sed -i '94i\ self.runner.val_loop.run()' /usr/local/lib/python3.8/dist-packages/mmengine/runner/loops.py - -WORKDIR /mmpose/ -CMD ["/bin/bash"] diff --git a/extras/mmpose/run_container.sh b/extras/mmpose/run_container.sh deleted file mode 100755 index 2b0ed92..0000000 --- a/extras/mmpose/run_container.sh +++ /dev/null @@ -1,11 +0,0 @@ -#! /bin/bash - -xhost + -docker run --privileged --rm --network host -it \ - --gpus all --shm-size=16g --ulimit memlock=-1 --ulimit stack=67108864 \ - --volume "$(pwd)"/:/RapidPoseTriangulation/ \ - --volume "$(pwd)"/../datasets/coco2017/annotations/:/mmpose/data/coco/annotations/ \ - --volume "$(pwd)"/../datasets/coco2017/images/:/mmpose/data/coco/ \ - --volume /tmp/.X11-unix:/tmp/.X11-unix \ - --env DISPLAY --env QT_X11_NO_MITSHM=1 \ - rpt_mmpose diff --git a/extras/ros/README.md b/extras/ros/README.md index 757fbdd..e1b3d11 100644 --- a/extras/ros/README.md +++ b/extras/ros/README.md @@ -1,13 +1,12 @@ -# ROS-Wrapper +# ROS Wrapper -Run pose estimator with ros topics as inputs and publish detected poses. +Run the 3D triangulator with ROS topics as inputs and publish detected poses.
- Build container: ```bash - docker build --progress=plain -t rapidposetriangulation_ros2d -f extras/ros/dockerfile_2d . docker build --progress=plain -t rapidposetriangulation_ros3d -f extras/ros/dockerfile_3d . ``` @@ -16,7 +15,6 @@ Run pose estimator with ros topics as inputs and publish detected poses. - Run and test: ```bash - xhost + && export CAMID="camera01" && docker compose -f extras/ros/docker-compose-2d.yml up xhost + && docker compose -f extras/ros/docker-compose-3d.yml up docker exec -it ros-test_node-1 bash diff --git a/extras/ros/docker-compose-2d.yml b/extras/ros/docker-compose-2d.yml deleted file mode 100644 index ba79b9f..0000000 --- a/extras/ros/docker-compose-2d.yml +++ /dev/null @@ -1,71 +0,0 @@ -services: - - test_node: - image: rapidposetriangulation_ros2d - network_mode: "host" - ipc: "host" - runtime: nvidia - privileged: true - volumes: - - ../../:/RapidPoseTriangulation/ - - ../../skelda/:/skelda/ - - /tmp/.X11-unix:/tmp/.X11-unix - - /dev/shm:/dev/shm - environment: - - CAMID - - DISPLAY - - QT_X11_NO_MITSHM=1 - - "PYTHONUNBUFFERED=1" - command: /bin/bash -i -c 'sleep infinity' - - estimator: - image: rapidposetriangulation_ros2d - network_mode: "host" - ipc: "host" - runtime: nvidia - privileged: true - volumes: - - ../../:/RapidPoseTriangulation/ - - ../../skelda/:/skelda/ - - /tmp/.X11-unix:/tmp/.X11-unix - - /dev/shm:/dev/shm - environment: - - CAMID - - DISPLAY - - QT_X11_NO_MITSHM=1 - - "PYTHONUNBUFFERED=1" - command: /bin/bash -i -c 'export ROS_DOMAIN_ID=18 && ros2 run rpt2d_wrapper_cpp rpt2d_wrapper' - - pose_visualizer: - image: rapidposetriangulation_ros2d - network_mode: "host" - ipc: "host" - runtime: nvidia - privileged: true - volumes: - - ../../:/RapidPoseTriangulation/ - - ../../skelda/:/skelda/ - - /tmp/.X11-unix:/tmp/.X11-unix - - /dev/shm:/dev/shm - environment: - - CAMID - - DISPLAY - - QT_X11_NO_MITSHM=1 - - "PYTHONUNBUFFERED=1" - command: /bin/bash -i -c 'sleep 2 && export ROS_DOMAIN_ID=18 && ros2 run pose2d_visualizer pose2d_visualizer $CAMID' - - pose_viewer: - image: rapidposetriangulation_ros2d - network_mode: "host" - ipc: "host" - runtime: nvidia - privileged: true - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - - /dev/shm:/dev/shm - environment: - - CAMID - - DISPLAY - - QT_X11_NO_MITSHM=1 - - "PYTHONUNBUFFERED=1" - command: /bin/bash -i -c 'sleep 2 && export ROS_DOMAIN_ID=18 && ros2 run image_view image_view --ros-args --remap image:=/$CAMID/img_with_pose -p autosize:=True -p window_name:=MyPoseImage' diff --git a/extras/ros/docker-compose-3d.yml b/extras/ros/docker-compose-3d.yml index b6266bf..7989b27 100644 --- a/extras/ros/docker-compose-3d.yml +++ b/extras/ros/docker-compose-3d.yml @@ -7,7 +7,6 @@ services: privileged: true volumes: - ../../:/RapidPoseTriangulation/ - - ../../skelda/:/skelda/ - /tmp/.X11-unix:/tmp/.X11-unix - /dev/shm:/dev/shm environment: @@ -23,7 +22,6 @@ services: privileged: true volumes: - ../../:/RapidPoseTriangulation/ - - ../../skelda/:/skelda/ - /tmp/.X11-unix:/tmp/.X11-unix - /dev/shm:/dev/shm environment: diff --git a/extras/ros/dockerfile_2d b/extras/ros/dockerfile_2d deleted file mode 100644 index fe72c48..0000000 --- a/extras/ros/dockerfile_2d +++ /dev/null @@ -1,67 +0,0 @@ -FROM rapidposetriangulation -WORKDIR / - -# Install ROS2 -# https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debians.html -RUN apt-get update && apt-get install -y --no-install-recommends locales -RUN locale-gen en_US en_US.UTF-8 && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 -RUN apt-get update && apt-get install -y --no-install-recommends software-properties-common -RUN add-apt-repository universe -RUN apt-get update && apt-get install -y --no-install-recommends curl -RUN curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg -RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" > /etc/apt/sources.list.d/ros2.list -RUN apt-get update && apt-get install -y --no-install-recommends ros-humble-ros-base python3-argcomplete -RUN apt-get update && apt-get install -y --no-install-recommends ros-dev-tools -RUN echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc - -# Fix ros package building error -RUN pip3 install --no-cache-dir "setuptools<=58.2.0" - -# Create ROS2 workspace for basic packages -RUN mkdir -p /project/base/src/ -RUN cd /project/base/; colcon build -RUN echo "source /project/base/install/setup.bash" >> ~/.bashrc - -# Install opencv and cv_bridge -RUN apt-get update && apt-get install -y --no-install-recommends libboost-dev -RUN apt-get update && apt-get install -y --no-install-recommends libboost-python-dev -RUN apt-get update && apt-get install -y --no-install-recommends libopencv-dev -RUN cd /project/base/src/; git clone --branch humble --depth=1 https://github.com/ros-perception/vision_opencv.git -RUN /bin/bash -i -c 'cd /project/base/; colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release' - -# Install ROS2 image viewer -RUN cd /project/base/src/; git clone --branch=humble --depth=1 https://github.com/ros-perception/image_pipeline.git -RUN cd /project/base/src/; git clone --branch=humble --depth=1 https://github.com/ros-perception/image_common.git -RUN /bin/bash -i -c 'cd /project/base/; colcon build --symlink-install --packages-select camera_calibration_parsers image_transport image_view --cmake-args -DCMAKE_BUILD_TYPE=Release' - -# Fix module not found error when displaying images -RUN apt-get update && apt-get install -y --no-install-recommends libcanberra-gtk-module libcanberra-gtk3-module - -# Create ROS2 workspace for project packages -RUN mkdir -p /project/dev_ws/src/ -RUN cd /project/dev_ws/; colcon build -RUN echo "source /project/dev_ws/install/setup.bash" >> ~/.bashrc - -# Copy modules -COPY ./extras/include/ /RapidPoseTriangulation/extras/include/ -COPY ./scripts/ /RapidPoseTriangulation/scripts/ -COPY ./extras/ros/rpt_msgs/ /RapidPoseTriangulation/extras/ros/rpt_msgs/ -COPY ./extras/ros/pose2d_visualizer/ /RapidPoseTriangulation/extras/ros/pose2d_visualizer/ -COPY ./extras/ros/rpt2d_wrapper_cpp/ /RapidPoseTriangulation/extras/ros/rpt2d_wrapper_cpp/ - -# Link and build as ros package -RUN ln -s /RapidPoseTriangulation/extras/ros/rpt_msgs/ /project/dev_ws/src/ -RUN ln -s /RapidPoseTriangulation/extras/ros/pose2d_visualizer/ /project/dev_ws/src/ -RUN ln -s /RapidPoseTriangulation/extras/ros/rpt2d_wrapper_cpp/ /project/dev_ws/src/ -RUN /bin/bash -i -c 'cd /project/dev_ws/; colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release' - -# Update ros packages -> autocompletion and check -RUN /bin/bash -i -c 'ros2 pkg list' - -# Clear cache to save space, only has an effect if image is squashed -RUN apt-get autoremove -y \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /RapidPoseTriangulation/ -CMD ["/bin/bash"] diff --git a/extras/ros/pose2d_visualizer/package.xml b/extras/ros/pose2d_visualizer/package.xml deleted file mode 100644 index 99c3f33..0000000 --- a/extras/ros/pose2d_visualizer/package.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - pose2d_visualizer - 0.0.0 - TODO: Package description - root - TODO: License declaration - - rpt_msgs - - ament_copyright - ament_flake8 - ament_pep257 - python3-pytest - - - ament_python - - diff --git a/extras/ros/pose2d_visualizer/pose2d_visualizer/__init__.py b/extras/ros/pose2d_visualizer/pose2d_visualizer/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/extras/ros/pose2d_visualizer/pose2d_visualizer/pose2d_visualizer.py b/extras/ros/pose2d_visualizer/pose2d_visualizer/pose2d_visualizer.py deleted file mode 100644 index 7c717b2..0000000 --- a/extras/ros/pose2d_visualizer/pose2d_visualizer/pose2d_visualizer.py +++ /dev/null @@ -1,158 +0,0 @@ -import sys -import threading -import time - -import cv2 -from matplotlib import pyplot as plt -import numpy as np -import rclpy -from cv_bridge import CvBridge -from rclpy.qos import QoSHistoryPolicy, QoSProfile, QoSReliabilityPolicy -from sensor_msgs.msg import Image - -from rpt_msgs.msg import Poses -from skelda import utils_view - -# ================================================================================================== - -bridge = CvBridge() -node = None -publisher_img = None - -img_input_topic = "/{}/pylon_ros2_camera_node/image_raw" -pose_input_topic = "/poses/{}" -img_output_topic = "/{}/img_with_pose" - -last_input_image = None -lock = threading.Lock() - -# ================================================================================================== - - -def bayer2rgb(bayer): - img = cv2.cvtColor(bayer, cv2.COLOR_BayerBG2RGB) - return img - - -# ================================================================================================== - - -def callback_images(image_data): - global last_input_image, lock - - # Convert into cv images from image string - if image_data.encoding == "bayer_rggb8": - bayer_image = bridge.imgmsg_to_cv2(image_data, "bayer_rggb8") - color_image = bayer2rgb(bayer_image) - elif image_data.encoding == "mono8": - gray_image = bridge.imgmsg_to_cv2(image_data, "mono8") - color_image = cv2.cvtColor(gray_image, cv2.COLOR_GRAY2RGB) - elif image_data.encoding == "rgb8": - color_image = bridge.imgmsg_to_cv2(image_data, "rgb8") - else: - raise ValueError("Unknown image encoding:", image_data.encoding) - - time_stamp = image_data.header.stamp.sec + image_data.header.stamp.nanosec / 1.0e9 - - with lock: - last_input_image = (color_image, time_stamp) - - -# ================================================================================================== - - -def callback_poses(pose_data): - global last_input_image, lock - - ptime = time.time() - if last_input_image is None: - return - - # Extract pose data - joint_names = pose_data.joint_names - bodies2D = np.array(pose_data.bodies_flat).reshape(pose_data.bodies_shape).tolist() - - # Collect inputs - images_2d = [] - timestamps = [] - with lock: - img = np.copy(last_input_image[0]) - ts = last_input_image[1] - images_2d.append(img) - timestamps.append(ts) - - # Visualize 2D poses - colors = plt.cm.hsv(np.linspace(0, 1, len(bodies2D), endpoint=False)).tolist() - colors = [[int(c[0] * 255), int(c[1] * 255), int(c[2] * 255)] for c in colors] - for i, body in enumerate(bodies2D): - color = list(reversed(colors[i])) - img = utils_view.draw_body_in_image(img, body, joint_names, color) - - # Publish image with poses - publish(img) - - msg = "Visualization time: {:.3f}s" - print(msg.format(time.time() - ptime)) - - -# ================================================================================================== - - -def publish(img): - # Publish image data - msg = bridge.cv2_to_imgmsg(img, "rgb8") - publisher_img.publish(msg) - - -# ================================================================================================== - - -def main(): - global node, publisher_img - - # Start node - rclpy.init(args=sys.argv) - cam_id = sys.argv[1] - node = rclpy.create_node("pose2d_visualizer") - - # Quality of service settings - qos_profile = QoSProfile( - reliability=QoSReliabilityPolicy.RELIABLE, - history=QoSHistoryPolicy.KEEP_LAST, - depth=1, - ) - - # Create subscribers - _ = node.create_subscription( - Image, - img_input_topic.format(cam_id), - callback_images, - qos_profile, - ) - _ = node.create_subscription( - Poses, - pose_input_topic.format(cam_id), - callback_poses, - qos_profile, - ) - - # Create publishers - publisher_img = node.create_publisher( - Image, - img_output_topic.format(cam_id), - qos_profile, - ) - - node.get_logger().info("Finished initialization of pose visualizer") - - # Run ros update thread - rclpy.spin(node) - - node.destroy_node() - rclpy.shutdown() - - -# ================================================================================================== - -if __name__ == "__main__": - main() diff --git a/extras/ros/pose2d_visualizer/resource/pose2d_visualizer b/extras/ros/pose2d_visualizer/resource/pose2d_visualizer deleted file mode 100644 index e69de29..0000000 diff --git a/extras/ros/pose2d_visualizer/setup.cfg b/extras/ros/pose2d_visualizer/setup.cfg deleted file mode 100644 index 5522611..0000000 --- a/extras/ros/pose2d_visualizer/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[develop] -script_dir=$base/lib/pose2d_visualizer -[install] -install_scripts=$base/lib/pose2d_visualizer diff --git a/extras/ros/pose2d_visualizer/setup.py b/extras/ros/pose2d_visualizer/setup.py deleted file mode 100644 index ea0f365..0000000 --- a/extras/ros/pose2d_visualizer/setup.py +++ /dev/null @@ -1,23 +0,0 @@ -from setuptools import setup - -package_name = "pose2d_visualizer" - -setup( - name=package_name, - version="0.0.0", - packages=[package_name], - data_files=[ - ("share/ament_index/resource_index/packages", ["resource/" + package_name]), - ("share/" + package_name, ["package.xml"]), - ], - install_requires=["setuptools"], - zip_safe=True, - maintainer="root", - maintainer_email="root@todo.todo", - description="TODO: Package description", - license="TODO: License declaration", - tests_require=["pytest"], - entry_points={ - "console_scripts": ["pose2d_visualizer = pose2d_visualizer.pose2d_visualizer:main"], - }, -) diff --git a/extras/ros/pose2d_visualizer/test/test_copyright.py b/extras/ros/pose2d_visualizer/test/test_copyright.py deleted file mode 100644 index 8f18fa4..0000000 --- a/extras/ros/pose2d_visualizer/test/test_copyright.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2015 Open Source Robotics Foundation, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from ament_copyright.main import main - - -# Remove the `skip` decorator once the source file(s) have a copyright header -@pytest.mark.skip( - reason="No copyright header has been placed in the generated source file." -) -@pytest.mark.copyright -@pytest.mark.linter -def test_copyright(): - rc = main(argv=[".", "test"]) - assert rc == 0, "Found errors" diff --git a/extras/ros/pose2d_visualizer/test/test_flake8.py b/extras/ros/pose2d_visualizer/test/test_flake8.py deleted file mode 100644 index f494570..0000000 --- a/extras/ros/pose2d_visualizer/test/test_flake8.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2017 Open Source Robotics Foundation, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from ament_flake8.main import main_with_errors - - -@pytest.mark.flake8 -@pytest.mark.linter -def test_flake8(): - rc, errors = main_with_errors(argv=[]) - assert rc == 0, "Found %d code style errors / warnings:\n" % len( - errors - ) + "\n".join(errors) diff --git a/extras/ros/pose2d_visualizer/test/test_pep257.py b/extras/ros/pose2d_visualizer/test/test_pep257.py deleted file mode 100644 index 4eddb46..0000000 --- a/extras/ros/pose2d_visualizer/test/test_pep257.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2015 Open Source Robotics Foundation, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from ament_pep257.main import main - - -@pytest.mark.linter -@pytest.mark.pep257 -def test_pep257(): - rc = main(argv=[".", "test"]) - assert rc == 0, "Found code style errors / warnings" diff --git a/extras/ros/rpt2d_wrapper_cpp/CMakeLists.txt b/extras/ros/rpt2d_wrapper_cpp/CMakeLists.txt deleted file mode 100644 index 6b4f8c3..0000000 --- a/extras/ros/rpt2d_wrapper_cpp/CMakeLists.txt +++ /dev/null @@ -1,69 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(rpt2d_wrapper_cpp) - -# Default to C99 -if(NOT CMAKE_C_STANDARD) - set(CMAKE_C_STANDARD 99) -endif() - -# Default to C++17 -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 17) -endif() - -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-Wall -Wextra -Wpedantic) -endif() - -# find dependencies -find_package(ament_cmake REQUIRED) -find_package(rclcpp REQUIRED) -find_package(rpt_msgs REQUIRED) -find_package(sensor_msgs REQUIRED) -find_package(cv_bridge REQUIRED) -find_package(OpenCV REQUIRED) - -### 3) ONNX Runtime -# for desktop -include_directories(/onnxruntime/include/ - /onnxruntime/include/onnxruntime/core/session/ - /onnxruntime/include/onnxruntime/core/providers/tensorrt/) -link_directories(/onnxruntime/build/Linux/Release/) -# for jetson -include_directories(/usr/local/include/onnxruntime/) -link_directories(/usr/local/lib/) - -add_executable(rpt2d_wrapper src/rpt2d_wrapper.cpp) -ament_target_dependencies(rpt2d_wrapper rclcpp sensor_msgs rpt_msgs cv_bridge) -target_include_directories(rpt2d_wrapper PUBLIC - $ - $) - -target_link_libraries(rpt2d_wrapper - ${OpenCV_LIBS} - onnxruntime_providers_tensorrt - onnxruntime_providers_shared - onnxruntime_providers_cuda - onnxruntime -) - -set_target_properties(rpt2d_wrapper PROPERTIES - BUILD_WITH_INSTALL_RPATH TRUE - INSTALL_RPATH "/onnxruntime/build/Linux/Release" -) - -install(TARGETS rpt2d_wrapper - DESTINATION lib/${PROJECT_NAME}) - -if(BUILD_TESTING) - find_package(ament_lint_auto REQUIRED) - # the following line skips the linter which checks for copyrights - # uncomment the line when a copyright and license is not present in all source files - #set(ament_cmake_copyright_FOUND TRUE) - # the following line skips cpplint (only works in a git repo) - # uncomment the line when this package is not in a git repo - #set(ament_cmake_cpplint_FOUND TRUE) - ament_lint_auto_find_test_dependencies() -endif() - -ament_package() diff --git a/extras/ros/rpt2d_wrapper_cpp/package.xml b/extras/ros/rpt2d_wrapper_cpp/package.xml deleted file mode 100644 index 7a40658..0000000 --- a/extras/ros/rpt2d_wrapper_cpp/package.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - rpt2d_wrapper_cpp - 0.0.0 - TODO: Package description - root - TODO: License declaration - - ament_cmake - - rclcpp - rpt_msgs - sensor_msgs - - cv_bridge - OpenCV - - ament_lint_auto - ament_lint_common - - - ament_cmake - - diff --git a/extras/ros/rpt2d_wrapper_cpp/src/rpt2d_wrapper.cpp b/extras/ros/rpt2d_wrapper_cpp/src/rpt2d_wrapper.cpp deleted file mode 100644 index 0d2ed12..0000000 --- a/extras/ros/rpt2d_wrapper_cpp/src/rpt2d_wrapper.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include -#include -#include -#include -#include - -// ROS2 -#include -#include - -// OpenCV / cv_bridge -#include -#include - -// JSON library -#include "/RapidPoseTriangulation/extras/include/nlohmann/json.hpp" -using json = nlohmann::json; - -#include "rpt_msgs/msg/poses.hpp" -#include "/RapidPoseTriangulation/scripts/utils_2d_pose.hpp" -#include "/RapidPoseTriangulation/scripts/utils_pipeline.hpp" - -// ================================================================================================= - -static const std::string img_input_topic = "/{}/pylon_ros2_camera_node/image_raw"; -static const std::string pose_out_topic = "/poses/{}"; - -static const float min_bbox_score = 0.4; -static const float min_bbox_area = 0.1 * 0.1; -static const bool batch_poses = true; - -static const std::map whole_body = { - {"foots", true}, - {"face", true}, - {"hands", true}, -}; - -// ================================================================================================= -// ================================================================================================= - -class Rpt2DWrapperNode : public rclcpp::Node -{ -public: - Rpt2DWrapperNode(const std::string &cam_id) - : Node("rpt2d_wrapper_" + cam_id) - { - this->is_busy = false; - std::string img_topic = std::string(img_input_topic) - .replace(img_input_topic.find("{}"), 2, cam_id); - std::string pose_topic = std::string(pose_out_topic) - .replace(pose_out_topic.find("{}"), 2, cam_id); - - // QoS - rclcpp::QoS qos_profile(1); - qos_profile.reliable(); - qos_profile.keep_last(1); - - // Setup subscriber - image_sub_ = this->create_subscription( - img_topic, qos_profile, - std::bind(&Rpt2DWrapperNode::callback_images, this, std::placeholders::_1)); - - // Setup publisher - pose_pub_ = this->create_publisher(pose_topic, qos_profile); - - // Load model - bool use_wb = utils_pipeline::use_whole_body(whole_body); - this->kpt_model = std::make_unique( - use_wb, min_bbox_score, min_bbox_area, batch_poses); - - RCLCPP_INFO(this->get_logger(), "Finished initialization of pose estimator."); - } - -private: - rclcpp::Subscription::SharedPtr image_sub_; - rclcpp::Publisher::SharedPtr pose_pub_; - std::atomic is_busy; - - // Pose model pointer - std::unique_ptr kpt_model; - const std::vector joint_names_2d = utils_pipeline::get_joint_names(whole_body); - - void callback_images(const sensor_msgs::msg::Image::SharedPtr msg); - - std::vector>> call_model(const cv::Mat &image); -}; - -// ================================================================================================= - -void Rpt2DWrapperNode::callback_images(const sensor_msgs::msg::Image::SharedPtr msg) -{ - if (this->is_busy) - { - RCLCPP_WARN(this->get_logger(), "Skipping frame, still processing..."); - return; - } - this->is_busy = true; - auto ts_image = std::chrono::high_resolution_clock::now(); - - // Load or convert image to Bayer format - cv::Mat bayer_image; - try - { - if (msg->encoding == "mono8") - { - cv_bridge::CvImageConstPtr cv_ptr = cv_bridge::toCvShare(msg, msg->encoding); - bayer_image = cv_ptr->image; - } - else if (msg->encoding == "bayer_rggb8") - { - cv_bridge::CvImageConstPtr cv_ptr = cv_bridge::toCvShare(msg, msg->encoding); - bayer_image = cv_ptr->image; - } - else if (msg->encoding == "rgb8") - { - cv_bridge::CvImageConstPtr cv_ptr = cv_bridge::toCvShare(msg, "rgb8"); - cv::Mat color_image = cv_ptr->image; - bayer_image = utils_pipeline::rgb2bayer(color_image); - } - else - { - throw std::runtime_error("Unknown image encoding: " + msg->encoding); - } - } - catch (const std::exception &e) - { - RCLCPP_ERROR(this->get_logger(), "cv_bridge exception: %s", e.what()); - return; - } - - // Call model - const auto &valid_poses = this->call_model(bayer_image); - - // Calculate timings - double time_stamp = msg->header.stamp.sec + msg->header.stamp.nanosec / 1.0e9; - auto ts_image_sec = std::chrono::duration(ts_image.time_since_epoch()).count(); - auto ts_pose = std::chrono::high_resolution_clock::now(); - double ts_pose_sec = std::chrono::duration(ts_pose.time_since_epoch()).count(); - double z_trigger_image = ts_image_sec - time_stamp; - double z_trigger_pose = ts_pose_sec - time_stamp; - double z_image_pose = ts_pose_sec - ts_image_sec; - json jdata; - jdata["timestamps"] = { - {"trigger", time_stamp}, - {"image", ts_image_sec}, - {"pose2d", ts_pose_sec}, - {"z-trigger-image", z_trigger_image}, - {"z-image-pose2d", z_image_pose}, - {"z-trigger-pose2d", z_trigger_pose}}; - - // Publish message - auto pose_msg = rpt_msgs::msg::Poses(); - pose_msg.header = msg->header; - std::vector bshape = {(int)valid_poses.size(), (int)joint_names_2d.size(), 3}; - pose_msg.bodies_shape = bshape; - pose_msg.bodies_flat.reserve(bshape[0] * bshape[1] * bshape[2]); - for (int32_t i = 0; i < bshape[0]; i++) - { - for (int32_t j = 0; j < bshape[1]; j++) - { - for (int32_t k = 0; k < bshape[2]; k++) - { - pose_msg.bodies_flat.push_back(valid_poses[i][j][k]); - } - } - } - pose_msg.joint_names = joint_names_2d; - pose_msg.extra_data = jdata.dump(); - pose_pub_->publish(pose_msg); - - // Print info - double elapsed_time = std::chrono::duration( - std::chrono::high_resolution_clock::now() - ts_image) - .count(); - std::cout << "Detected persons: " << valid_poses.size() - << " - Prediction time: " << elapsed_time << "s" << std::endl; - - this->is_busy = false; -} - -// ================================================================================================= - -std::vector>> Rpt2DWrapperNode::call_model(const cv::Mat &image) -{ - // Create image vector - cv::Mat rgb_image = utils_pipeline::bayer2rgb(image); - std::vector images_2d = {rgb_image}; - - // Predict 2D poses - auto poses_2d_all = kpt_model->predict(images_2d); - auto poses_2d_upd = utils_pipeline::update_keypoints( - poses_2d_all, joint_names_2d, whole_body); - auto &poses_2d = poses_2d_upd[0]; - - // Drop persons with no joints - std::vector>> valid_poses; - for (auto &person : poses_2d) - { - float sum_conf = 0.0; - for (auto &kp : person) - { - sum_conf += kp[2]; - } - if (sum_conf > 0.0) - { - valid_poses.push_back(person); - } - } - - // Round poses to 3 decimal places - for (auto &person : valid_poses) - { - for (auto &kp : person) - { - kp[0] = std::round(kp[0] * 1000.0) / 1000.0; - kp[1] = std::round(kp[1] * 1000.0) / 1000.0; - kp[2] = std::round(kp[2] * 1000.0) / 1000.0; - } - } - - return valid_poses; -} - -// ================================================================================================= -// ================================================================================================= - -int main(int argc, char **argv) -{ - rclcpp::init(argc, argv); - const std::string cam_id = std::getenv("CAMID"); - - auto node = std::make_shared(cam_id); - rclcpp::spin(node); - - rclcpp::shutdown(); - return 0; -} diff --git a/pyproject.toml b/pyproject.toml index ec652a3..c940f7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,4 +22,4 @@ wheel.packages = ["src/rpt"] [tool.isort] profile = "black" -known_first_party = "rpt,draw_utils,test_triangulate,utils_2d_pose,triangulate_poses" +known_first_party = "rpt" diff --git a/run_container.sh b/run_container.sh index 15f2fb9..c72bbab 100755 --- a/run_container.sh +++ b/run_container.sh @@ -5,7 +5,6 @@ docker run --privileged --rm --network host -it \ --runtime nvidia --shm-size=16g --ulimit memlock=-1 --ulimit stack=67108864 \ --volume "$(pwd)"/:/RapidPoseTriangulation/ \ --volume "$(pwd)"/../datasets/:/datasets/ \ - --volume "$(pwd)"/skelda/:/skelda/ \ --volume /tmp/.X11-unix:/tmp/.X11-unix \ --env DISPLAY --env QT_X11_NO_MITSHM=1 \ rapidposetriangulation diff --git a/scripts/test_skelda_dataset.cpp b/scripts/test_skelda_dataset.cpp deleted file mode 100644 index 99c4602..0000000 --- a/scripts/test_skelda_dataset.cpp +++ /dev/null @@ -1,327 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// OpenCV -#include - -// JSON library -#include "/RapidPoseTriangulation/extras/include/nlohmann/json.hpp" -using json = nlohmann::json; - -#include "/RapidPoseTriangulation/rpt/camera.hpp" -#include "/RapidPoseTriangulation/rpt/interface.hpp" -#include "/RapidPoseTriangulation/rpt/tracker.hpp" -#include "/RapidPoseTriangulation/scripts/utils_2d_pose.hpp" -#include "/RapidPoseTriangulation/scripts/utils_pipeline.hpp" - -// ================================================================================================= - -static const std::string path_data = "/tmp/rpt/all.json"; -static const std::string path_cfg = "/tmp/rpt/config.json"; - -// ================================================================================================= - -std::vector load_images(json &item) -{ - // Load images - std::vector images; - for (size_t j = 0; j < item["imgpaths"].size(); j++) - { - auto ipath = item["imgpaths"][j].get(); - cv::Mat image = cv::imread(ipath, cv::IMREAD_COLOR); - cv::cvtColor(image, image, cv::COLOR_BGR2RGB); - images.push_back(image); - } - - if (item["dataset_name"] == "human36m") - { - // Since the images don't have the same shape, rescale some of them - for (size_t i = 0; i < images.size(); i++) - { - cv::Mat &img = images[i]; - cv::Size ishape = img.size(); - if (ishape != cv::Size(1000, 1000)) - { - auto cam = item["cameras"][i]; - cam["K"][1][1] = cam["K"][1][1].get() * (1000.0 / ishape.height); - cam["K"][1][2] = cam["K"][1][2].get() * (1000.0 / ishape.height); - cam["K"][0][0] = cam["K"][0][0].get() * (1000.0 / ishape.width); - cam["K"][0][2] = cam["K"][0][2].get() * (1000.0 / ishape.width); - cv::resize(img, img, cv::Size(1000, 1000)); - images[i] = img; - } - } - } - - // Convert image format to Bayer encoding to simulate real camera input - // This also resulted in notably better MPJPE results in most cases, presumably since the - // demosaicing algorithm from OpenCV is better than the default one from the cameras - for (size_t i = 0; i < images.size(); i++) - { - cv::Mat &img = images[i]; - cv::Mat bayer_image = utils_pipeline::rgb2bayer(img); - images[i] = std::move(bayer_image); - } - - return images; -} - -// ================================================================================================= - -std::string read_file(const std::string &path) -{ - std::ifstream file_stream(path); - if (!file_stream.is_open()) - { - throw std::runtime_error("Unable to open file: " + path); - } - - std::stringstream buffer; - buffer << file_stream.rdbuf(); - return buffer.str(); -} - -void write_file(const std::string &path, const std::string &content) -{ - std::ofstream file_stream(path, std::ios::out | std::ios::binary); - if (!file_stream.is_open()) - { - throw std::runtime_error("Unable to open file for writing: " + path); - } - - file_stream << content; - - if (!file_stream) - { - throw std::runtime_error("Error occurred while writing to file: " + path); - } - file_stream.close(); -} - -// ================================================================================================= - -int main(int argc, char **argv) -{ - // Load the files - auto dataset = json::parse(read_file(path_data)); - auto config = json::parse(read_file(path_cfg)); - - // Load the configuration - const std::map whole_body = config["whole_body"]; - const float min_bbox_score = config["min_bbox_score"]; - const float min_bbox_area = config["min_bbox_area"]; - const bool batch_poses = config["batch_poses"]; - const std::vector joint_names_2d = utils_pipeline::get_joint_names(whole_body); - const float min_match_score = config["min_match_score"]; - const size_t min_group_size = config["min_group_size"]; - const int take_interval = config["take_interval"]; - const float ifps = config["fps"]; - const float max_movement_speed = config["max_movement_speed"]; - const float max_track_distance = config["max_track_distance"]; - - // Load 2D model - bool use_wb = utils_pipeline::use_whole_body(whole_body); - std::unique_ptr kpt_model = - std::make_unique( - use_wb, min_bbox_score, min_bbox_area, batch_poses); - - // Load 3D models - std::unique_ptr tri_model = std::make_unique( - min_match_score, min_group_size); - std::unique_ptr pose_tracker = std::make_unique( - max_movement_speed, max_track_distance); - - // Timers - size_t time_count = dataset.size(); - std::vector times_image; - std::vector times_debayer; - std::vector times_pose2d; - std::vector times_pose3d; - std::vector times_tracks; - times_image.reserve(time_count); - times_debayer.reserve(time_count); - times_pose2d.reserve(time_count); - times_pose3d.reserve(time_count); - times_tracks.reserve(time_count); - size_t print_steps = (size_t)std::floor((float)time_count / 100.0f); - print_steps = std::max((size_t)1, print_steps); - - std::cout << "Running predictions: |"; - size_t bar_width = (size_t)std::ceil((float)time_count / (float)print_steps); - for (size_t i = 0; i < bar_width; i++) - { - std::cout << "-"; - } - std::cout << "|" << std::endl; - - // Calculate 2D poses [items, views, persons, joints, 3] - std::vector>>>> all_poses_2d; - std::cout << "Calculating 2D poses: |"; - for (size_t i = 0; i < dataset.size(); i++) - { - if (i % print_steps == 0) - { - std::cout << "#" << std::flush; - } - std::chrono::duration elapsed; - auto &item = dataset[i]; - - // Load images - auto stime = std::chrono::high_resolution_clock::now(); - std::vector images = load_images(item); - elapsed = std::chrono::high_resolution_clock::now() - stime; - times_image.push_back(elapsed.count()); - - // Demosaic images - stime = std::chrono::high_resolution_clock::now(); - for (size_t i = 0; i < images.size(); i++) - { - cv::Mat &img = images[i]; - cv::Mat rgb = utils_pipeline::bayer2rgb(img); - images[i] = std::move(rgb); - } - elapsed = std::chrono::high_resolution_clock::now() - stime; - times_debayer.push_back(elapsed.count()); - - // Predict 2D poses - stime = std::chrono::high_resolution_clock::now(); - auto poses_2d_all = kpt_model->predict(images); - auto poses_2d_upd = utils_pipeline::update_keypoints( - poses_2d_all, joint_names_2d, whole_body); - elapsed = std::chrono::high_resolution_clock::now() - stime; - times_pose2d.push_back(elapsed.count()); - - all_poses_2d.push_back(std::move(poses_2d_upd)); - } - std::cout << "|" << std::endl; - - // Calculate 3D poses [items, persons, joints, 4] - std::vector>>> all_poses_3d; - std::vector all_ids; - std::string old_scene = ""; - int old_id = -1; - std::cout << "Calculating 3D poses: |"; - for (size_t i = 0; i < dataset.size(); i++) - { - if (i % print_steps == 0) - { - std::cout << "#" << std::flush; - } - std::chrono::duration elapsed; - auto &item = dataset[i]; - auto &poses_2d = all_poses_2d[i]; - - if (old_scene != item["scene"] || old_id + take_interval != item["index"]) - { - // Reset last poses if scene changes - tri_model->reset(); - pose_tracker->reset(); - old_scene = item["scene"]; - } - - auto stime = std::chrono::high_resolution_clock::now(); - std::vector cameras; - for (size_t j = 0; j < item["cameras"].size(); j++) - { - auto &cam = item["cameras"][j]; - Camera camera; - camera.name = cam["name"].get(); - camera.K = cam["K"].get, 3>>(); - camera.DC = cam["DC"].get>(); - camera.R = cam["R"].get, 3>>(); - camera.T = cam["T"].get, 3>>(); - camera.width = cam["width"].get(); - camera.height = cam["height"].get(); - camera.type = cam["type"].get(); - cameras.push_back(camera); - } - std::array, 2> roomparams = { - item["room_size"].get>(), - item["room_center"].get>()}; - - PoseBatch2D pose_batch_2d = PoseBatch2D::from_nested(poses_2d); - auto poses_3d = tri_model->triangulate_poses( - pose_batch_2d, cameras, roomparams, joint_names_2d).to_nested(); - elapsed = std::chrono::high_resolution_clock::now() - stime; - times_pose3d.push_back(elapsed.count()); - - if (ifps <= 0) - { - // Disable pose tracking if frame rate is too low - times_tracks.push_back(0.0); - } - else - { - stime = std::chrono::high_resolution_clock::now(); - double ts = ((int)item["index"]) / ifps; - auto pose_tracks = pose_tracker->track_poses(poses_3d, joint_names_2d, ts); - std::vector>> poses_3d_refined; - for (size_t j = 0; j < pose_tracks.size(); j++) - { - auto &pose = std::get<1>(pose_tracks[j]); - poses_3d_refined.push_back(pose); - } - poses_3d = poses_3d_refined; - elapsed = std::chrono::high_resolution_clock::now() - stime; - times_tracks.push_back(elapsed.count()); - } - - all_poses_3d.push_back(std::move(poses_3d)); - all_ids.push_back(item["id"].get()); - old_id = item["index"]; - } - std::cout << "|" << std::endl; - - // Print timing stats - std::cout << "\nMetrics:" << std::endl; - size_t warmup = std::min((size_t)10, time_count - 1); - double time_image = 0.0; - double time_debayer = 0.0; - double time_pose2d = 0.0; - double time_pose3d = 0.0; - double time_tracks = 0.0; - for (size_t i = warmup; i < time_count; i++) - { - time_image += times_image[i]; - time_debayer += times_debayer[i]; - time_pose2d += times_pose2d[i]; - time_pose3d += times_pose3d[i]; - time_tracks += times_tracks[i]; - } - double avg_time_image = time_image / (time_count - warmup); - double avg_time_debayer = time_debayer / (time_count - warmup); - double avg_time_pose2d = time_pose2d / (time_count - warmup); - double avg_time_pose3d = time_pose3d / (time_count - warmup); - double avg_time_tracks = time_tracks / (time_count - warmup); - double fps = 1.0 / (avg_time_debayer + avg_time_pose2d + avg_time_pose3d + avg_time_tracks); - std::cout << "{\n" - << " \"img_loading\": " << avg_time_image << ",\n" - << " \"demosaicing\": " << avg_time_debayer << ",\n" - << " \"avg_time_2d\": " << avg_time_pose2d << ",\n" - << " \"avg_time_3d\": " << avg_time_pose3d << ",\n" - << " \"time_tracks\": " << avg_time_tracks << ",\n" - << " \"fps\": " << fps << "\n" - << "}" << std::endl; - tri_model->print_stats(); - - // Store the results as json - json all_results; - all_results["all_ids"] = all_ids; - all_results["all_poses_2d"] = all_poses_2d; - all_results["all_poses_3d"] = all_poses_3d; - all_results["joint_names_2d"] = joint_names_2d; - all_results["joint_names_3d"] = joint_names_2d; - - // Save the results - std::string path_results = "/tmp/rpt/results.json"; - write_file(path_results, all_results.dump(0)); - - return 0; -} diff --git a/scripts/test_skelda_dataset.py b/scripts/test_skelda_dataset.py deleted file mode 100644 index 00381c3..0000000 --- a/scripts/test_skelda_dataset.py +++ /dev/null @@ -1,513 +0,0 @@ -import json -import os - -import numpy as np - -import utils_pipeline -from skelda import evals -from skelda.writers import json_writer - -# ================================================================================================== - -whole_body = { - "foots": False, - "face": False, - "hands": False, -} - -dataset_use = "human36m" -# dataset_use = "panoptic" -# dataset_use = "mvor" -# dataset_use = "shelf" -# dataset_use = "campus" -# dataset_use = "ikeaasm" -# dataset_use = "chi3d" -# dataset_use = "tsinghua" -# dataset_use = "human36m_wb" -# dataset_use = "egohumans_tagging" -# dataset_use = "egohumans_legoassemble" -# dataset_use = "egohumans_fencing" -# dataset_use = "egohumans_basketball" -# dataset_use = "egohumans_volleyball" -# dataset_use = "egohumans_badminton" -# dataset_use = "egohumans_tennis" - - -# Describes the minimum area as fraction of the image size for a 2D bounding box to be considered -# If the persons are small in the image, use a lower value -default_min_bbox_area = 0.1 * 0.1 - -# Describes how confident a 2D bounding box needs to be to be considered -# If the persons are small in the image, or poorly recognizable, use a lower value -default_min_bbox_score = 0.3 - -# Describes how good two 2D poses need to match each other to create a valid triangulation -# If the quality of the 2D detections is poor, use a lower value -default_min_match_score = 0.94 - -# Describes the minimum number of camera pairs that need to detect the same person -# If the number of cameras is high, and the views are not occluded, use a higher value -default_min_group_size = 1 - -# Batch poses per image for faster processing -# If most of the time only one person is in a image, disable it, because it is slightly slower then -default_batch_poses = True - -# Approach speed of EN ISO 13855 with 2000 mm/sec for hand speed -# and an additional factor to compensate for noise-based jumps -default_max_movement_speed = 2.0 * 1.5 - -# The size of an A4 sheet of paper which is assumed to fit between two different persons -# and additionally the distance a person can move between two frames (here at 10 fps) -default_max_track_distance = 0.3 + default_max_movement_speed / 10 - - -datasets = { - "human36m": { - "path": "/datasets/human36m/skelda/pose_test.json", - "take_interval": 5, - "fps": 50, - "min_match_score": 0.95, - "min_group_size": 1, - "min_bbox_score": 0.4, - "min_bbox_area": 0.1 * 0.1, - "batch_poses": False, - "max_movement_speed": 2.0 * 1.5, - "max_track_distance": 0.3 + default_max_movement_speed / (50 / 5), - }, - "panoptic": { - "path": "/datasets/panoptic/skelda/test.json", - "cams": ["00_03", "00_06", "00_12", "00_13", "00_23"], - # "cams": ["00_03", "00_06", "00_12"], - # "cams": ["00_03", "00_06", "00_12", "00_13", "00_23", "00_15", "00_10"], - # "cams": ["00_03", "00_06", "00_12", "00_13", "00_23", "00_15", "00_10", "00_21", "00_09", "00_01"], - # "cams": [], - "take_interval": 3, - "fps": 30, - "min_match_score": 0.95, - "use_scenes": ["160906_pizza1", "160422_haggling1", "160906_ian5"], - "min_group_size": 1, - # "min_group_size": 1, - # "min_group_size": 1, - # "min_group_size": 2, - # "min_group_size": 11, - "min_bbox_area": 0.05 * 0.05, - "max_track_distance": 0.3 + default_max_movement_speed / (30 / 3), - }, - "mvor": { - "path": "/datasets/mvor/skelda/all.json", - "take_interval": 1, - "fps": -1, - "min_match_score": 0.81, - "min_bbox_score": 0.25, - }, - "campus": { - "path": "/datasets/campus/skelda/test.json", - "fps": 25, - "take_interval": 1, - "min_match_score": 0.91, - "min_bbox_score": 0.5, - "max_track_distance": 0.3 + default_max_movement_speed / 25, - }, - "shelf": { - "path": "/datasets/shelf/skelda/test.json", - "take_interval": 1, - "fps": 25, - "min_match_score": 0.95, - "min_group_size": 3, - "max_track_distance": 0.3 + default_max_movement_speed / 25, - }, - "ikeaasm": { - "path": "/datasets/ikeaasm/skelda/test.json", - "take_interval": 2, - "fps": -1, - "min_match_score": 0.81, - "min_bbox_score": 0.20, - }, - "chi3d": { - "path": "/datasets/chi3d/skelda/all.json", - "take_interval": 5, - "fps": 50, - "min_match_score": 0.92, - "min_bbox_area": 0.2 * 0.2, - "max_track_distance": 0.3 + default_max_movement_speed / (50 / 5), - }, - "tsinghua": { - "path": "/datasets/tsinghua/skelda/test.json", - "take_interval": 3, - "fps": 30, - "min_match_score": 0.95, - "min_group_size": 2, - "max_track_distance": 0.3 + default_max_movement_speed / (30 / 3), - }, - "human36m_wb": { - "path": "/datasets/human36m/skelda/wb/test.json", - "take_interval": 100, - "fps": -1, - "min_bbox_score": 0.4, - "batch_poses": False, - }, - "egohumans_tagging": { - "path": "/datasets/egohumans/skelda/all.json", - "take_interval": 2, - "fps": 20, - "subset": "tagging", - "min_match_score": 0.89, - "min_group_size": 1, - "min_bbox_score": 0.2, - "min_bbox_area": 0.05 * 0.05, - "max_movement_speed": 4.0 * 1.5, - "max_track_distance": 0.3 + (4.0 * 1.5) / (20 / 2), - }, - "egohumans_legoassemble": { - "path": "/datasets/egohumans/skelda/all.json", - "take_interval": 2, - "fps": 20, - "subset": "legoassemble", - "min_group_size": 2, - "max_track_distance": 0.3 + default_max_movement_speed / (20 / 2), - }, - "egohumans_fencing": { - "path": "/datasets/egohumans/skelda/all.json", - "take_interval": 2, - "fps": 20, - "subset": "fencing", - "min_group_size": 7, - "min_bbox_score": 0.5, - "min_bbox_area": 0.05 * 0.05, - "max_track_distance": 0.3 + default_max_movement_speed / (20 / 2), - }, - "egohumans_basketball": { - "path": "/datasets/egohumans/skelda/all.json", - "take_interval": 2, - "fps": 20, - "subset": "basketball", - "min_group_size": 4, - "min_bbox_score": 0.25, - "min_bbox_area": 0.025 * 0.025, - "max_movement_speed": 4.0 * 1.5, - "max_track_distance": 0.3 + (4.0 * 1.5) / (20 / 2), - }, - "egohumans_volleyball": { - "path": "/datasets/egohumans/skelda/all.json", - "take_interval": 2, - "fps": 20, - "subset": "volleyball", - "min_match_score": 0.95, - "min_group_size": 7, - "min_bbox_score": 0.20, - "min_bbox_area": 0.05 * 0.05, - "max_movement_speed": 4.0 * 1.5, - "max_track_distance": 0.3 + (4.0 * 1.5) / (20 / 2), - }, - "egohumans_badminton": { - "path": "/datasets/egohumans/skelda/all.json", - "take_interval": 2, - "fps": 20, - "subset": "badminton", - "min_group_size": 7, - "min_bbox_score": 0.25, - "min_bbox_area": 0.05 * 0.05, - "max_movement_speed": 4.0 * 1.5, - "max_track_distance": 0.3 + (4.0 * 1.5) / (20 / 2), - }, - "egohumans_tennis": { - "path": "/datasets/egohumans/skelda/all.json", - "take_interval": 2, - "fps": 20, - "subset": "tennis", - "min_group_size": 11, - "min_bbox_area": 0.025 * 0.025, - "max_movement_speed": 4.0 * 1.5, - "max_track_distance": 0.3 + (4.0 * 1.5) / (20 / 2), - }, -} - -joint_names_2d = utils_pipeline.get_joint_names(whole_body) -joint_names_3d = list(joint_names_2d) -eval_joints = [ - "head", - "shoulder_left", - "shoulder_right", - "elbow_left", - "elbow_right", - "wrist_left", - "wrist_right", - "hip_left", - "hip_right", - "knee_left", - "knee_right", - "ankle_left", - "ankle_right", -] -if dataset_use == "human36m": - eval_joints[eval_joints.index("head")] = "nose" -if dataset_use == "panoptic": - eval_joints[eval_joints.index("head")] = "nose" -if dataset_use == "human36m_wb": - if utils_pipeline.use_whole_body(whole_body): - eval_joints = list(joint_names_2d) - else: - eval_joints[eval_joints.index("head")] = "nose" - -# output_dir = "/RapidPoseTriangulation/data/testoutput/" -output_dir = "" - -# pred_export_path = f"/datasets/predictions/{dataset_use}/RapidPoseTriangulation.json" -pred_export_path = "" - - -# ================================================================================================== - - -def load_labels(dataset: dict): - """Load labels by dataset description""" - - if "panoptic" in dataset: - labels = utils_pipeline.load_json(dataset["panoptic"]["path"]) - labels = [lb for i, lb in enumerate(labels) if i % 1500 < 90] - - # Filter by maximum number of persons - labels = [l for l in labels if len(l["bodies3D"]) <= 10] - - # Filter scenes - if "use_scenes" in dataset["panoptic"]: - labels = [ - l for l in labels if l["scene"] in dataset["panoptic"]["use_scenes"] - ] - - # Filter cameras - if not "cameras_depth" in labels[0] and len(dataset["panoptic"]["cams"]) > 0: - for label in labels: - for i, cam in reversed(list(enumerate(label["cameras"]))): - if cam["name"] not in dataset["panoptic"]["cams"]: - label["cameras"].pop(i) - label["imgpaths"].pop(i) - - elif "human36m" in dataset: - labels = utils_pipeline.load_json(dataset["human36m"]["path"]) - labels = [lb for lb in labels if lb["subject"] == "S9"] - labels = [lb for i, lb in enumerate(labels) if i % 4000 < 150] - - elif "mvor" in dataset: - labels = utils_pipeline.load_json(dataset["mvor"]["path"]) - - # Rename keys - for label in labels: - label["cameras_color"] = label["cameras"] - label["imgpaths_color"] = label["imgpaths"] - - elif "ikeaasm" in dataset: - labels = utils_pipeline.load_json(dataset["ikeaasm"]["path"]) - cams0 = str(labels[0]["cameras"]) - labels = [lb for lb in labels if str(lb["cameras"]) == cams0] - - elif "shelf" in dataset: - labels = utils_pipeline.load_json(dataset["shelf"]["path"]) - labels = [lb for lb in labels if "test" in lb["splits"]] - - elif "campus" in dataset: - labels = utils_pipeline.load_json(dataset["campus"]["path"]) - labels = [lb for lb in labels if "test" in lb["splits"]] - - elif "tsinghua" in dataset: - labels = utils_pipeline.load_json(dataset["tsinghua"]["path"]) - labels = [lb for lb in labels if "test" in lb["splits"]] - labels = [lb for lb in labels if lb["seq"] == "seq_1"] - labels = [lb for i, lb in enumerate(labels) if i % 300 < 90] - - for label in labels: - label["bodyids"] = list(range(len(label["bodies3D"]))) - - elif "chi3d" in dataset: - labels = utils_pipeline.load_json(dataset["chi3d"]["path"]) - labels = [lb for lb in labels if lb["setup"] == "s03"] - labels = [lb for i, lb in enumerate(labels) if i % 2000 < 150] - - elif "human36m_wb" in dataset: - labels = utils_pipeline.load_json(dataset["human36m_wb"]["path"]) - - elif any(("egohumans" in key for key in dataset)): - labels = utils_pipeline.load_json(dataset[dataset_use]["path"]) - labels = [lb for lb in labels if "test" in lb["splits"]] - labels = [lb for lb in labels if dataset[dataset_use]["subset"] in lb["seq"]] - if dataset[dataset_use]["subset"] in ["volleyball", "tennis"]: - labels = [lb for i, lb in enumerate(labels) if i % 150 < 60] - - else: - raise ValueError("Dataset not available") - - # Optionally drop samples to speed up train/eval - if "take_interval" in dataset: - take_interval = dataset["take_interval"] - if take_interval > 1: - labels = [l for i, l in enumerate(labels) if i % take_interval == 0] - - # Add default values - for label in labels: - if "scene" not in label: - label["scene"] = "default" - for cam in label["cameras"]: - if not "type" in cam: - cam["type"] = "pinhole" - - return labels - - -# ================================================================================================== - - -def main(): - global joint_names_3d, eval_joints - - print("Loading dataset ...") - labels = load_labels( - { - dataset_use: datasets[dataset_use], - "take_interval": datasets[dataset_use]["take_interval"], - } - ) - - # Print a dataset sample for debugging - print("Amount of samples:", len(labels)) - print(labels[0]) - - # Save dataset - tmp_export_dir = "/tmp/rpt/" - for label in labels: - if "splits" in label: - label.pop("splits") - json_writer.save_dataset(labels, tmp_export_dir) - - # Load dataset specific parameters - min_match_score = datasets[dataset_use].get( - "min_match_score", default_min_match_score - ) - min_group_size = datasets[dataset_use].get("min_group_size", default_min_group_size) - min_bbox_score = datasets[dataset_use].get("min_bbox_score", default_min_bbox_score) - min_bbox_area = datasets[dataset_use].get("min_bbox_area", default_min_bbox_area) - batch_poses = datasets[dataset_use].get("batch_poses", default_batch_poses) - max_movement_speed = datasets[dataset_use].get( - "max_movement_speed", default_max_movement_speed - ) - max_track_distance = datasets[dataset_use].get( - "max_track_distance", default_max_track_distance - ) - - # Save config - config_path = tmp_export_dir + "config.json" - config = { - "min_match_score": min_match_score, - "min_group_size": min_group_size, - "min_bbox_score": min_bbox_score, - "min_bbox_area": min_bbox_area, - "batch_poses": batch_poses, - "max_movement_speed": max_movement_speed, - "max_track_distance": max_track_distance, - "whole_body": whole_body, - "take_interval": datasets[dataset_use]["take_interval"], - "fps": datasets[dataset_use]["fps"], - } - utils_pipeline.save_json(config, config_path) - - # Call the CPP binary - os.system("/RapidPoseTriangulation/scripts/test_skelda_dataset.bin") - - # Load the results - print("Loading exports ...") - res_path = tmp_export_dir + "results.json" - results = utils_pipeline.load_json(res_path) - all_poses_3d = results["all_poses_3d"] - all_poses_2d = results["all_poses_2d"] - all_ids = results["all_ids"] - joint_names_3d = results["joint_names_3d"] - - # # Visualize labels and predictions - # from skelda import utils_view - # for i in range(0, len(labels), 1): - # posesL = [] - # posesR = [] - # jnames = [] - # for j in labels[i]["joints"]: - # if "->" in j: - # jnames.append(j.split("->")[-1]) - # else: - # jnames.append(j) - # for j in range(len(labels[i]["bodies3D"])): - # pose = [] - # for k in range(len(eval_joints)): - # n = eval_joints[k] - # pose.append(labels[i]["bodies3D"][j][jnames.index(n)]) - # posesL.append(pose) - # for j in range(len(all_poses_3d[i])): - # pose = [] - # for k in range(len(eval_joints)): - # n = eval_joints[k] - # pose.append(all_poses_3d[i][j][joint_names_3d.index(n)]) - # posesR.append(pose) - # poses_3d = posesL + posesR - # sample = labels[i] - # sample["bodies3D"] = np.array(poses_3d).round(3).tolist() - # sample["joints"] = eval_joints - # sample["num_persons"] = len(poses_3d) - # print(sample) - # utils_view.draw_sample_3d(sample) - # utils_view.draw_many_images( - # sample["imgpaths"], - # sample["cameras"], - # [], - # all_poses_2d[i], - # joint_names_3d, - # "2D detections", - # ) - # utils_view.show_plots() - - if pred_export_path != "": - # Export predictions - print("\nExporting predictions ...") - all_poses_3d = [np.array(poses).round(3).tolist() for poses in all_poses_3d] - data = { - "poses3D": all_poses_3d, - "ids": all_ids, - "joint_names": joint_names_3d, - } - os.makedirs(os.path.dirname(pred_export_path), exist_ok=True) - with open(pred_export_path, "w") as file: - json.dump(data, file, indent=0) - - # Run evaluation - _ = evals.mpjpe.run_eval( - labels, - all_poses_3d, - all_ids, - joint_names_net=joint_names_3d, - joint_names_use=eval_joints, - save_error_imgs=output_dir, - debug_2D_preds=all_poses_2d, - ) - _ = evals.pcp.run_eval( - labels, - all_poses_3d, - all_ids, - joint_names_net=joint_names_3d, - joint_names_use=eval_joints, - replace_head_with_nose=True, - ) - - if dataset_use == "shelf": - # Also run old-style evaluation for shelf dataset - odir = os.path.join(output_dir, "pcp/") if output_dir != "" else "" - _ = evals.campus_shelf.run_eval( - labels, - all_poses_3d, - all_ids, - joint_names_net=joint_names_3d, - save_error_imgs=odir, - debug_2D_preds=all_poses_2d, - ) - - -# ================================================================================================== - -if __name__ == "__main__": - main() diff --git a/scripts/test_triangulate.py b/scripts/test_triangulate.py deleted file mode 100644 index 207ac84..0000000 --- a/scripts/test_triangulate.py +++ /dev/null @@ -1,144 +0,0 @@ -import copy -import json -import os - -import numpy as np - -import utils_pipeline -from skelda import utils_pose, utils_view -from skelda.writers import json_writer - -# ================================================================================================== - -filepath = os.path.dirname(os.path.realpath(__file__)) + "/" -test_img_dir = filepath + "../data/" - -whole_body = { - "foots": False, - "face": False, - "hands": False, -} -config = { - "min_match_score": 0.94, - "min_group_size": 1, - "min_bbox_score": 0.3, - "min_bbox_area": 0.1 * 0.1, - "batch_poses": True, - "whole_body": whole_body, - "take_interval": 1, - "fps": -1, - "max_movement_speed": 0, - "max_track_distance": 0, -} - -joint_names_2d = utils_pipeline.get_joint_names(whole_body) -joint_names_3d = list(joint_names_2d) - - -# ================================================================================================== - - -def update_sample(sample, new_dir=""): - sample = copy.deepcopy(sample) - - # Rename image paths - sample["imgpaths"] = [ - os.path.join(new_dir, os.path.basename(v)) for v in sample["imgpaths"] - ] - - # Add placeholders for missing keys - if not "scene" in sample: - sample["scene"] = "default" - if not "id" in sample: - sample["id"] = "0" - if not "index" in sample: - sample["index"] = 0 - for cam in sample["cameras"]: - if not "type" in cam: - cam["type"] = "pinhole" - - return sample - - -# ================================================================================================== - - -def main(): - - for dirname in sorted(os.listdir(test_img_dir)): - dirpath = os.path.join(test_img_dir, dirname) - - if not os.path.isdir(dirpath): - continue - - if (dirname[0] not in ["p", "h", "e", "q"]) or len(dirname) != 2: - continue - - # Load sample infos - print("\n" + dirpath) - with open(os.path.join(dirpath, "sample.json"), "r", encoding="utf-8") as file: - sample = json.load(file) - sample = update_sample(sample, dirpath) - - if len(sample["imgpaths"]) == 1: - # At least two images are required - continue - - # Save dataset - labels = [sample] - tmp_export_dir = "/tmp/rpt/" - for label in labels: - if "splits" in label: - label.pop("splits") - json_writer.save_dataset(labels, tmp_export_dir) - - # Save config - config_path = tmp_export_dir + "config.json" - utils_pipeline.save_json(config, config_path) - - # Call the CPP binary - os.system("/RapidPoseTriangulation/scripts/test_skelda_dataset.bin") - - # Load the results - print("Loading exports ...") - res_path = tmp_export_dir + "results.json" - results = utils_pipeline.load_json(res_path) - poses_3d = results["all_poses_3d"][0] - poses_2d = results["all_poses_2d"][0] - joint_names_3d = results["joint_names_3d"] - - # Visualize the 2D results - fig1 = utils_view.draw_many_images( - sample["imgpaths"], [], [], poses_2d, joint_names_2d, "2D detections" - ) - fig1.savefig(os.path.join(dirpath, "2d-k.png"), dpi=fig1.dpi) - - # Visualize the 3D results - print("Detected 3D poses:") - poses_3d = np.array(poses_3d) - print(poses_3d.round(3)) - if len(poses_3d) == 0: - utils_view.show_plots() - continue - camparams = sample["cameras"] - roomparams = { - "room_size": sample["room_size"], - "room_center": sample["room_center"], - } - poses_2d_proj = [] - for cam in camparams: - poses_2d_cam, _ = utils_pose.project_poses(poses_3d, cam) - poses_2d_proj.append(poses_2d_cam) - fig2 = utils_view.draw_poses3d(poses_3d, joint_names_3d, roomparams, camparams) - fig3 = utils_view.draw_many_images( - sample["imgpaths"], [], [], poses_2d_proj, joint_names_3d, "2D projections" - ) - fig2.savefig(os.path.join(dirpath, "3d-p.png"), dpi=fig2.dpi) - fig3.savefig(os.path.join(dirpath, "2d-p.png"), dpi=fig3.dpi) - utils_view.show_plots() - - -# ================================================================================================== - -if __name__ == "__main__": - main() diff --git a/scripts/utils_2d_pose.hpp b/scripts/utils_2d_pose.hpp deleted file mode 100644 index 85b66a8..0000000 --- a/scripts/utils_2d_pose.hpp +++ /dev/null @@ -1,1161 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include - -// ================================================================================================= -// ================================================================================================= - -namespace utils_2d_pose -{ - - class BaseModel - { - public: - explicit BaseModel(const std::string &model_path, int warmup_iterations); - virtual ~BaseModel() = default; - - std::vector>> call_by_image( - const std::vector &images); - - protected: - static Ort::Env &get_env() - { - // Static method to create or retrieve a single global Ort::Env - static Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "tensorrt_model_env"); - // static Ort::Env env(ORT_LOGGING_LEVEL_VERBOSE, "tensorrt_model_env"); - return env; - } - - // Initialize ONNX Runtime session with TensorRT provider - void init_onnx_runtime(const std::string &model_path); - - // Generate random input data, run forward pass - void warmup(int epoch); - - // Actually run the session with given input tensors - std::vector call_model(const std::vector &inputs); - - // Utility to check if a string ends with a given suffix - bool ends_with(const std::string &str, const std::string &suffix) - { - if (str.size() < suffix.size()) - return false; - return (str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0); - } - - protected: - Ort::SessionOptions session_options; - std::unique_ptr session; - Ort::AllocatorWithDefaultOptions allocator; - - std::vector input_names; - std::vector> input_shapes; - std::vector input_types; - std::vector output_names; - std::vector input_name_ptrs; - std::vector output_name_ptrs; - }; - - // ============================================================================================= - - BaseModel::BaseModel(const std::string &model_path, int warmup_iterations) - { - if (!std::filesystem::exists(model_path)) - { - throw std::runtime_error("File not found: " + model_path); - } - - if (!ends_with(model_path, ".onnx")) - { - throw std::runtime_error("Only .onnx models are supported: " + model_path); - } - - std::cout << "Loading model: " << model_path << std::endl; - init_onnx_runtime(model_path); - - if (warmup_iterations > 0) - { - std::cout << "Running warmup ...\n"; - warmup((int)warmup_iterations / 2); - warmup((int)warmup_iterations / 2); - } - } - - // ============================================================================================= - - void BaseModel::init_onnx_runtime(const std::string &model_path) - { - // 0) Create env - Ort::Env &env = get_env(); - - session_options = Ort::SessionOptions(); - session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED); - - // 1) Create TensorRT provider options - OrtTensorRTProviderOptionsV2 *tensorrt_options = nullptr; - Ort::ThrowOnError(Ort::GetApi().CreateTensorRTProviderOptions(&tensorrt_options)); - - // 2) Configure runtime options - tensorrt_options->trt_max_workspace_size = 2147483647; - tensorrt_options->trt_engine_cache_enable = 1; - tensorrt_options->trt_engine_cache_path = "/RapidPoseTriangulation/data/trt_cache/"; - if (model_path.find("_fp16") != std::string::npos) - { - tensorrt_options->trt_fp16_enable = 1; - } - else - { - tensorrt_options->trt_fp16_enable = 0; - } - if (model_path.find("_Bx") != std::string::npos) - { - tensorrt_options->trt_profile_min_shapes = "image_input:1x384x288x3"; - tensorrt_options->trt_profile_max_shapes = "image_input:10x384x288x3"; - tensorrt_options->trt_profile_opt_shapes = "image_input:3x384x288x3"; - } - - // 3) Append to session options - Ort::ThrowOnError( - Ort::GetApi().SessionOptionsAppendExecutionProvider_TensorRT_V2( - static_cast(session_options), - tensorrt_options)); - - // 4) Create the session - session = std::make_unique(env, model_path.c_str(), session_options); - - // 5) Gather input info - size_t num_inputs = session->GetInputCount(); - for (size_t i = 0; i < num_inputs; i++) - { - Ort::AllocatedStringPtr iname = session->GetInputNameAllocated(i, allocator); - input_names.push_back(iname.get()); - - Ort::TypeInfo type_info = session->GetInputTypeInfo(i); - auto tensor_info = type_info.GetTensorTypeAndShapeInfo(); - - input_shapes.push_back(tensor_info.GetShape()); - input_types.push_back(tensor_info.GetElementType()); - } - - // Gather output info to avoid "At least one output should be requested." - size_t num_outputs = session->GetOutputCount(); - for (size_t i = 0; i < num_outputs; i++) - { - Ort::AllocatedStringPtr oname = session->GetOutputNameAllocated(i, allocator); - output_names.push_back(oname.get()); - } - - // Build array of input name C-strings - input_name_ptrs.resize(input_names.size()); - for (size_t i = 0; i < input_names.size(); i++) - { - input_name_ptrs[i] = input_names[i].c_str(); - } - - // Build array of output name C-strings - output_name_ptrs.resize(output_names.size()); - for (size_t i = 0; i < output_names.size(); i++) - { - output_name_ptrs[i] = output_names[i].c_str(); - } - } - - // ============================================================================================= - - void BaseModel::warmup(int epoch) - { - std::vector use_batch_sizes = {4, 10, 5, 1, 9, 3, 7, 2, 8, 4, 6, 1, 3, 7, 2}; - - std::cout << "Warmup: "; - for (int e = 0; e < epoch; e++) - { - // Extract the 4D shape. - auto shape = input_shapes[0]; - int batch_size = static_cast(shape[0]); - int height = static_cast(shape[1]); - int width = static_cast(shape[2]); - int channels = static_cast(shape[3]); - - // We'll only handle batch=1 in this example - if (batch_size != 1) - { - batch_size = use_batch_sizes[e % use_batch_sizes.size()]; - } - - std::vector images; - for (int i = 0; i < batch_size; i++) - { - // Create a multi-channel Mat of shape (Height, Width, Channels) - cv::Mat img(height, width, CV_8UC(channels)); - if (channels == 1) - { - cv::randu(img, 0, 256); - } - else if (channels == 3) - { - cv::randu(img, cv::Scalar(0, 0, 0), cv::Scalar(256, 256, 256)); - } - else - { - throw std::runtime_error("Unsupported number of channels for warmup."); - } - - images.push_back(std::move(img)); - } - - // Call the model - auto outputs = call_by_image(images); - std::cout << "#"; - } - - std::cout << std::endl; - } - - // ============================================================================================= - - std::vector>> BaseModel::call_by_image( - const std::vector &images) - { - size_t batch_size = images.size(); - int height = images[0].rows; - int width = images[0].cols; - int channels = images[0].channels(); - - // Flatten into single vector - size_t single_img_size = height * width * channels; - size_t total_elems = batch_size * single_img_size; - std::vector batch_data(total_elems); - for (size_t b = 0; b < batch_size; b++) - { - auto &img = images[b]; - if (img.rows != height || img.cols != width || img.channels() != channels) - { - throw std::runtime_error("All images must have the same dimensions!"); - } - std::memcpy(batch_data.data() + b * single_img_size, img.data, single_img_size); - } - - // Create an onnx tensor - auto mem_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); - std::vector shape = { - static_cast(batch_size), - static_cast(height), - static_cast(width), - static_cast(channels), - }; - Ort::Value input_tensor = Ort::Value::CreateTensor( - mem_info, - batch_data.data(), - total_elems, - shape.data(), - shape.size()); - - // Call model - std::vector input_tensors; - input_tensors.push_back(std::move(input_tensor)); - auto outputs = call_model(input_tensors); - - // Get pointer to ouput tensor - const float *tensor_data = outputs[0].GetTensorData(); - auto data_info = outputs[0].GetTensorTypeAndShapeInfo(); - auto shape0 = data_info.GetShape(); - size_t B = (size_t)shape0[0]; - size_t N = (size_t)shape0[1]; - size_t C = (size_t)shape0[2]; - - // Convert to vector of values - std::vector>> data; - data.reserve(B); - for (size_t i = 0; i < B; i++) - { - std::vector> item; - item.reserve(N); - for (size_t j = 0; j < N; j++) - { - std::vector values; - values.reserve(C); - for (size_t k = 0; k < C; k++) - { - values.push_back(tensor_data[i * N * C + j * C + k]); - } - item.push_back(values); - } - data.push_back(item); - } - - return data; - } - - // ============================================================================================= - - std::vector BaseModel::call_model(const std::vector &inputs) - { - if (inputs.size() != input_names.size()) - { - throw std::runtime_error("Number of input tensors does not match model's input count."); - } - - // Actually run the model, requesting all known outputs - return session->Run( - Ort::RunOptions{nullptr}, - input_name_ptrs.data(), - inputs.data(), - inputs.size(), - output_name_ptrs.data(), - output_name_ptrs.size()); - } - - // ============================================================================================= - // ============================================================================================= - - class LetterBox - { - public: - LetterBox(const std::array &target_size, int fill_value = 0) - { - this->target_size = target_size; - this->fill_value = fill_value; - } - - std::tuple, double, std::array> calc_params( - const cv::Mat &image) const; - cv::Mat resize_image(const cv::Mat &image) const; - - private: - std::array target_size; - int fill_value; - }; - - // ============================================================================================= - - std::tuple, double, std::array> LetterBox::calc_params( - const cv::Mat &image) const - { - // Original dimensions - int img_h = image.rows; - int img_w = image.cols; - - // Target dimensions - int target_h = static_cast(target_size[0]); - int target_w = static_cast(target_size[1]); - - // Scale factor - double scale = std::min( - static_cast(target_w) / static_cast(img_w), - static_cast(target_h) / static_cast(img_h)); - - // Compute new width/height (round to nearest int) - int new_w = static_cast(std::round(img_w * scale)); - int new_h = static_cast(std::round(img_h * scale)); - - // Calculate padding - int pad_w = target_w - new_w; - int pad_h = target_h - new_h; - - int pad_left = pad_w / 2; - int pad_top = pad_h / 2; - int pad_right = pad_w - pad_left; - int pad_bottom = pad_h - pad_top; - - std::array paddings = {pad_left, pad_right, pad_top, pad_bottom}; - std::array new_size = {(size_t)new_w, (size_t)new_h}; - - return {paddings, scale, new_size}; - } - - // ============================================================================================= - - cv::Mat LetterBox::resize_image(const cv::Mat &image) const - { - // 1) Compute letterbox params - auto [paddings, scale, new_sz] = calc_params(image); - int pad_left = paddings[0]; - int pad_right = paddings[1]; - int pad_top = paddings[2]; - int pad_bottom = paddings[3]; - - // 2) Resize using nearest-neighbor interpolation - cv::Mat resized_img; - cv::Size cv_new_sz = cv::Size(new_sz[0], new_sz[1]); - cv::resize(image, resized_img, cv_new_sz, /*fx=*/0, /*fy=*/0, cv::INTER_NEAREST); - - // 3) Pad if needed - if (pad_left == 0 && pad_right == 0 && pad_top == 0 && pad_bottom == 0) - { - // No padding required - return resized_img; - } - else - { - cv::Mat final_img; - cv::Scalar fill_color(fill_value, fill_value, fill_value); - - cv::copyMakeBorder( - resized_img, final_img, - pad_top, pad_bottom, - pad_left, pad_right, - cv::BORDER_CONSTANT, - fill_color); - return final_img; - } - } - - // ============================================================================================= - // ============================================================================================= - - class BoxCrop - { - public: - BoxCrop(const std::array &target_size, - float padding_scale = 1.0f, - int fill_value = 0) - { - this->target_size = target_size; - this->padding_scale = padding_scale; - this->fill_value = fill_value; - } - - std::tuple, float, std::array, std::array> - calc_params(const cv::Mat &image, const std::array &bbox) const; - - cv::Mat crop_resize_box(const cv::Mat &image, - const std::array &bbox) const; - - private: - std::array target_size; - float padding_scale; - int fill_value; - }; - - // ============================================================================================= - - std::tuple, float, std::array, std::array> - BoxCrop::calc_params(const cv::Mat &image, const std::array &bbox) const - { - // Extract some params - int img_h = image.rows; - int img_w = image.cols; - int target_h = target_size[0]; - int target_w = target_size[1]; - - // Round the bounding box coordinates - float start_x = std::floor(bbox[0]); - float start_y = std::floor(bbox[1]); - float end_x = std::ceil(bbox[2]); - float end_y = std::ceil(bbox[3]); - - // Calculate original bounding box center - float center_x = 0.5f * (start_x + end_x); - float center_y = 0.5f * (start_y + end_y); - - // Scale the bounding box by the padding_scale - float bbox_w = end_x - start_x; - float bbox_h = end_y - start_y; - float scaled_w = bbox_w * padding_scale; - float scaled_h = bbox_h * padding_scale; - - // Calculate the aspect ratios - float bbox_aspect = scaled_w / scaled_h; - float target_aspect = static_cast(target_w) / static_cast(target_h); - - // Adjust the scaled bounding box to match the target aspect ratio - float adjusted_w, adjusted_h; - if (bbox_aspect > target_aspect) - { - adjusted_w = scaled_w; - adjusted_h = scaled_w / target_aspect; - } - else - { - adjusted_h = scaled_h; - adjusted_w = scaled_h * target_aspect; - } - - // Calculate scaled bounding box coordinates - float new_start_xf = center_x - 0.5f * adjusted_w; - float new_start_yf = center_y - 0.5f * adjusted_h; - float new_end_xf = center_x + 0.5f * adjusted_w; - float new_end_yf = center_y + 0.5f * adjusted_h; - - // Round the box coordinates - int start_xi = static_cast(std::floor(new_start_xf)); - int start_yi = static_cast(std::floor(new_start_yf)); - int end_xi = static_cast(std::ceil(new_end_xf)); - int end_yi = static_cast(std::ceil(new_end_yf)); - - // Define the new box coordinates - int new_start_x = std::max(0, start_xi); - int new_start_y = std::max(0, start_yi); - int new_end_x = std::min(img_w - 1, end_xi); - int new_end_y = std::min(img_h - 1, end_yi); - std::array new_box{new_start_x, new_start_y, new_end_x, new_end_y}; - - // Calculate resized crop size - int clipped_w = new_box[2] - new_box[0]; - int clipped_h = new_box[3] - new_box[1]; - float scale_w = static_cast(target_w) / static_cast(clipped_w); - float scale_h = static_cast(target_h) / static_cast(clipped_h); - float scale = std::min(scale_w, scale_h); - int new_w = static_cast(std::round(clipped_w * scale)); - int new_h = static_cast(std::round(clipped_h * scale)); - - // Calculate paddings - int pad_w = target_w - new_w; - int pad_h = target_h - new_h; - int pad_left = 0; - int pad_right = 0; - int pad_top = 0; - int pad_bottom = 0; - if (pad_w > 0) - { - if (start_xi < 0) - { - pad_left = pad_w; - pad_right = 0; - } - else if (end_xi > img_w) - { - pad_left = 0; - pad_right = pad_w; - } - else - { - // Can be caused by bbox rounding - pad_left = pad_w / 2; - pad_right = pad_w - pad_left; - } - } - if (pad_h > 0) - { - if (start_yi < 0) - { - pad_top = pad_h; - pad_bottom = 0; - } - else if (end_yi > img_h) - { - pad_top = 0; - pad_bottom = pad_h; - } - else - { - // Can be caused by bbox rounding - pad_top = pad_h / 2; - pad_bottom = pad_h - pad_top; - } - } - std::array paddings{pad_left, pad_right, pad_top, pad_bottom}; - - return {paddings, scale, new_box, {(size_t)new_w, (size_t)new_h}}; - } - - // ============================================================================================= - - cv::Mat BoxCrop::crop_resize_box(const cv::Mat &image, - const std::array &bbox) const - { - auto [paddings, _, new_box, new_size] = calc_params(image, bbox); - - // Extract the bounding box - int x1 = new_box[0]; - int y1 = new_box[1]; - int x2 = new_box[2]; - int y2 = new_box[3]; - cv::Rect roi(x1, y1, x2 - x1, y2 - y1); - cv::Mat cropped_img = image(roi); - - // Resize the image - cv::Size new_size_cv = cv::Size(new_size[0], new_size[1]); - cv::Mat resized_img; - cv::resize(cropped_img, resized_img, new_size_cv, 0, 0, cv::INTER_NEAREST); - - // Optionally pad the image - int pad_left = paddings[0]; - int pad_right = paddings[1]; - int pad_top = paddings[2]; - int pad_bottom = paddings[3]; - if (pad_left == 0 && pad_right == 0 && pad_top == 0 && pad_bottom == 0) - { - // No padding - return resized_img; - } - else - { - cv::Mat final_img; - cv::Scalar fill_color(fill_value, fill_value, fill_value); - cv::copyMakeBorder( - resized_img, - final_img, - pad_top, - pad_bottom, - pad_left, - pad_right, - cv::BORDER_CONSTANT, - fill_color); - return final_img; - } - } - - // ============================================================================================= - // ============================================================================================= - - class RTMDet : public BaseModel - { - public: - RTMDet(const std::string &model_path, - float conf_threshold, - float min_area_fraction, - int warmup = 30) - : BaseModel(model_path, warmup) - { - this->target_size = {320, 320}; - this->conf_threshold = conf_threshold; - - float img_area = target_size[0] * target_size[1]; - this->min_area = img_area * min_area_fraction; - - this->letterbox = std::make_unique(target_size, 114); - } - - std::vector> call(const cv::Mat &image); - - private: - std::array target_size; - float conf_threshold; - float min_area; - std::unique_ptr letterbox; - - cv::Mat preprocess(const cv::Mat &image); - std::vector> postprocess( - const std::vector>> &result, - const cv::Mat &image); - - void clip_boxes(std::vector> &boxes, - const cv::Mat &image) const; - }; - - // ============================================================================================= - - std::vector> RTMDet::call(const cv::Mat &image) - { - cv::Mat preprocessed = preprocess(image); - std::vector inputs = {preprocessed}; - auto results = call_by_image(inputs); - auto outputs = postprocess(results, image); - return outputs; - } - - // ============================================================================================= - - cv::Mat RTMDet::preprocess(const cv::Mat &image) - { - cv::Mat resized = letterbox->resize_image(image); - return resized; - } - - // ============================================================================================= - - std::vector> RTMDet::postprocess( - const std::vector>> &result, - const cv::Mat &image) - { - // Expected result shape: [B, N, 6] => (x1,y1,x2,y2,score,class) - - // Convert to vector of boxes - std::vector> boxes; - for (auto &item : result[0]) - { - float x1 = item[0]; - float y1 = item[1]; - float x2 = item[2]; - float y2 = item[3]; - float score = item[4]; - float cls = item[5]; - boxes.push_back({x1, y1, x2, y2, score, cls}); - } - - if (boxes.empty()) - { - return {}; - } - - // Filter by confidence + area - for (int i = boxes.size() - 1; i >= 0; i--) - { - auto &b = boxes[i]; - if (b[4] < conf_threshold) - { - boxes.erase(boxes.begin() + i); - continue; - } - - float area = (b[2] - b[0]) * (b[3] - b[1]); - if (area < min_area) - { - boxes.erase(boxes.begin() + i); - } - } - - if (boxes.empty()) - { - return {}; - } - - // Shift by letterbox padding and scale back - clip_boxes(boxes, image); - - return boxes; - } - - // ============================================================================================= - - void RTMDet::clip_boxes(std::vector> &boxes, - const cv::Mat &image) const - { - // Get paddings, scale from letterbox - auto [paddings, scale, _] = letterbox->calc_params(image); - int pad_left = paddings[0]; - int pad_right = paddings[1]; - int pad_top = paddings[2]; - int pad_bottom = paddings[3]; - - // The effective region in the letterboxed image - float letter_w = static_cast(target_size[1] - (pad_left + pad_right)); - float letter_h = static_cast(target_size[0] - (pad_top + pad_bottom)); - - for (auto &b : boxes) - { - float &x1 = b[0]; - float &y1 = b[1]; - float &x2 = b[2]; - float &y2 = b[3]; - - // Shift by left/top - x1 -= pad_left; - x2 -= pad_left; - y1 -= pad_top; - y2 -= pad_top; - - // Clamp to letterbox region - x1 = std::max(0.f, std::min(x1, letter_w - 1.0f)); - x2 = std::max(0.f, std::min(x2, letter_w - 1.0f)); - y1 = std::max(0.f, std::min(y1, letter_h - 1.0f)); - y2 = std::max(0.f, std::min(y2, letter_h - 1.0f)); - - // Scale back to original resolution - x1 /= scale; - x2 /= scale; - y1 /= scale; - y2 /= scale; - } - } - - // ============================================================================================= - // ============================================================================================= - - class RTMPose : public BaseModel - { - public: - RTMPose(const std::string &model_path, - int warmup = 30) - : BaseModel(model_path, warmup) - { - this->target_size = {384, 288}; - this->boxcrop = std::make_unique(target_size, 1.25f, 114); - } - - std::vector>> call(const cv::Mat &image, - const std::vector> &bboxes); - - private: - std::array target_size; - std::unique_ptr boxcrop; - - std::vector preprocess( - const cv::Mat &image, - const std::vector> &bboxes); - std::vector>> postprocess( - const std::vector>> &result, - const cv::Mat &image, - const std::vector> &bboxes); - - void clip_keypoints(std::vector> &kp, - const cv::Mat &image, - const std::array &bbox) const; - }; - - // ============================================================================================= - - std::vector>> RTMPose::call( - const cv::Mat &image, const std::vector> &bboxes) - { - std::vector inputs = preprocess(image, bboxes); - auto results = call_by_image(inputs); - auto outputs = postprocess(results, image, bboxes); - return outputs; - } - - // ============================================================================================= - - std::vector RTMPose::preprocess( - const cv::Mat &image, const std::vector> &bboxes) - { - std::vector crops; - for (auto &bbox : bboxes) - { - cv::Mat cropped = boxcrop->crop_resize_box(image, bbox); - crops.push_back(std::move(cropped)); - } - - return crops; - } - - // ============================================================================================= - - std::vector>> RTMPose::postprocess( - const std::vector>> &result, - const cv::Mat &image, - const std::vector> &bboxes) - { - // Expected result shape: [B, N, 3] => (x,y,score) - std::vector>> keypoints; - for (size_t i = 0; i < result.size(); i++) - { - // Convert to vector of keypoints - std::vector> kpts; - for (auto &item : result[i]) - { - float x = item[0]; - float y = item[1]; - float score = item[2]; - - kpts.push_back({x, y, score}); - } - - if (!kpts.empty()) - { - // Shift by boxcrop padding and scale back - clip_keypoints(kpts, image, bboxes[i]); - keypoints.push_back(std::move(kpts)); - } - } - - return keypoints; - } - - // ============================================================================================= - - void RTMPose::clip_keypoints(std::vector> &kpts, - const cv::Mat &image, - const std::array &bbox) const - { - // Get paddings, scale from boxcrop - auto [paddings, scale, box, _] = boxcrop->calc_params(image, bbox); - int pad_left = paddings[0]; - int pad_top = paddings[2]; - int box_left = box[0]; - int box_top = box[1]; - - for (auto &kp : kpts) - { - float &x = kp[0]; - float &y = kp[1]; - - // Shift by left/top - x -= pad_left; - y -= pad_top; - - x /= scale; - y /= scale; - - x += box_left; - y += box_top; - } - } - - // ============================================================================================= - // ============================================================================================= - - class TopDown - { - public: - TopDown(const std::string &det_model_path, - const std::string &pose_model_path, - float box_conf_threshold, - float box_min_area, - bool batch_poses = false, - int warmup = 30) - { - this->batch_poses = batch_poses; - - this->det_model = std::make_unique( - det_model_path, box_conf_threshold, box_min_area, warmup); - this->pose_model = std::make_unique(pose_model_path, warmup); - } - - std::vector>> predict(const cv::Mat &image); - - private: - std::unique_ptr det_model; - std::unique_ptr pose_model; - bool batch_poses; - - void merge_close_poses( - std::vector>> &poses, - std::array image_size); - }; - - // ============================================================================================= - - std::vector>> TopDown::predict(const cv::Mat &image) - { - auto bboxes = det_model->call(image); - - // Drop boxes with non-person class - for (int i = bboxes.size() - 1; i >= 0; i--) - { - if (static_cast(bboxes[i][5]) != 0) - { - bboxes.erase(bboxes.begin() + i); - } - } - - std::vector>> poses; - if (this->batch_poses) - { - if (!bboxes.empty()) - { - poses = std::move(pose_model->call(image, bboxes)); - } - } - else - { - for (size_t i = 0; i < bboxes.size(); i++) - { - auto kpts = pose_model->call(image, {bboxes[i]}); - poses.push_back(std::move(kpts[0])); - } - } - - // Sometimes the detection model predicts multiple boxes with different shapes for the same - // person. They then result in strongly overlapping poses, which are merged here. - merge_close_poses(poses, {(size_t)image.cols, (size_t)image.rows}); - - // Clip keypoints far outside the image - float mask_offset = (image.cols + image.rows) / 10.0; - for (size_t i = 0; i < poses.size(); ++i) - { - for (size_t j = 0; j < poses[i].size(); ++j) - { - auto &kp = poses[i][j]; - if (kp[0] < -mask_offset) - { - kp[0] = -mask_offset; - kp[2] = 0.001; - } - if (kp[1] < -mask_offset) - { - kp[1] = -mask_offset; - kp[2] = 0.001; - } - if (kp[0] >= image.cols + mask_offset) - { - kp[0] = image.cols + mask_offset; - kp[2] = 0.001; - } - if (kp[1] >= image.rows + mask_offset) - { - kp[1] = image.rows + mask_offset; - kp[2] = 0.001; - } - } - } - - return poses; - } - - // ============================================================================================= - - void TopDown::merge_close_poses( - std::vector>> &poses, - std::array image_size) - { - // Joint ids in COCO order - const size_t num_overlaps = 5; - const std::map joint_indices = { - {"nose", 0}, - {"left_shoulder", 5}, - {"right_shoulder", 6}, - {"left_hip", 11}, - {"right_hip", 12}, - {"left_elbow", 7}, - {"right_elbow", 8}, - }; - - if (poses.size() < 2) - { - return; - } - - // Calculate pixel threshold based on image size - size_t min_dim = std::min(image_size[0], image_size[1]); - float pixel_threshold = 0.025f * min_dim; - - // Merge poses if enough joints are close - std::vector>> merged_poses; - merged_poses.reserve(poses.size()); - for (auto &opose : poses) - { - bool merged = false; - for (auto &mpose : merged_poses) - { - size_t close_count = 0; - for (auto &kv : joint_indices) - { - size_t joint_id = kv.second; - float x1 = opose[joint_id][0]; - float y1 = opose[joint_id][1]; - float c1 = opose[joint_id][2]; - - float x2 = mpose[joint_id][0]; - float y2 = mpose[joint_id][1]; - float c2 = mpose[joint_id][2]; - - if (c1 > 0.0f && c2 > 0.0f) - { - float dx = x1 - x2; - float dy = y1 - y2; - float dist_sq = dx * dx + dy * dy; - if (dist_sq <= pixel_threshold * pixel_threshold) - { - ++close_count; - } - } - } - - if (close_count >= num_overlaps) - { - // Merge new pose with existing one - for (size_t j = 0; j < mpose.size(); ++j) - { - if (opose[j][2] > mpose[j][2]) - { - mpose[j] = std::move(opose[j]); - } - } - merged = true; - break; - } - } - if (!merged) - { - // Not mergeable, add as new pose - merged_poses.push_back(std::move(opose)); - } - } - - // Replace original poses with merged ones - poses = std::move(merged_poses); - } - - // ============================================================================================= - // ============================================================================================= - - class PosePredictor - { - public: - PosePredictor(bool whole_body, - float min_bbox_score, - float min_bbox_area, - bool batch_poses = false) - { - std::string base_path = "/RapidPoseTriangulation/extras/mmdeploy/exports/"; - - std::string path_det_n1 = base_path + "rtmdet-nano_1x320x320x3_fp16_extra-steps.onnx"; - std::string path_det_m1 = base_path + "rtmdet-m_1x320x320x3_fp16_extra-steps.onnx"; - - std::string path_pose_m1 = base_path + "rtmpose-m_1x384x288x3_fp16_extra-steps.onnx"; - std::string path_pose_mb = base_path + "rtmpose-m_Bx384x288x3_fp16_extra-steps.onnx"; - std::string path_pose_w1 = base_path + "rtmpose-l_wb_1x384x288x3_fp16_extra-steps.onnx"; - std::string path_pose_wb = base_path + "rtmpose-l_wb_Bx384x288x3_fp16_extra-steps.onnx"; - - this->num_joints = whole_body ? 133 : 17; - std::string path_det; - std::string path_pose; - if (!whole_body) - { - path_det = path_det_n1; - if (!batch_poses) - { - path_pose = path_pose_m1; - } - else - { - path_pose = path_pose_mb; - } - } - else - { - path_det = path_det_m1; - if (!batch_poses) - { - path_pose = path_pose_w1; - } - else - { - path_pose = path_pose_wb; - } - } - - std::cout << "Loading 2D" + std::string(whole_body ? "-WB" : "") + " models ..." - << std::endl; - - this->topdown_model = std::make_unique( - path_det, path_pose, min_bbox_score, min_bbox_area, batch_poses, 30); - - std::cout << "Loaded models \n" - << std::endl; - } - - std::vector>>> predict( - const std::vector &images); - - private: - std::unique_ptr topdown_model; - size_t num_joints; - }; - - // ============================================================================================= - - std::vector>>> PosePredictor::predict( - const std::vector &images) - { - std::vector>>> keypoints; - for (auto &img : images) - { - auto kpts = topdown_model->predict(img); - - if (!kpts.empty()) - { - keypoints.push_back(std::move(kpts)); - } - else - { - // create zero keypoints - std::vector>> zero_keypoints; - zero_keypoints.resize(1); - zero_keypoints[0].resize(num_joints, {0.0f, 0.0f, 0.0f}); - keypoints.push_back(std::move(zero_keypoints)); - } - } - return keypoints; - } -} diff --git a/scripts/utils_pipeline.hpp b/scripts/utils_pipeline.hpp deleted file mode 100644 index 8744c2a..0000000 --- a/scripts/utils_pipeline.hpp +++ /dev/null @@ -1,316 +0,0 @@ -#pragma once - -#include -#include - -#include - -// ================================================================================================= - -namespace utils_pipeline -{ - bool use_whole_body(const std::map &whole_body) - { - for (const auto &pair : whole_body) - { - if (pair.second) - { - return true; - } - } - return false; - } - - // ============================================================================================= - - std::vector get_joint_names(const std::map &whole_body) - { - std::vector joint_names_2d = { - "nose", - "eye_left", - "eye_right", - "ear_left", - "ear_right", - "shoulder_left", - "shoulder_right", - "elbow_left", - "elbow_right", - "wrist_left", - "wrist_right", - "hip_left", - "hip_right", - "knee_left", - "knee_right", - "ankle_left", - "ankle_right", - }; - - if (whole_body.at("foots")) - { - joint_names_2d.insert( - joint_names_2d.end(), - { - "foot_toe_big_left", - "foot_toe_small_left", - "foot_heel_left", - "foot_toe_big_right", - "foot_toe_small_right", - "foot_heel_right", - }); - } - if (whole_body.at("face")) - { - joint_names_2d.insert( - joint_names_2d.end(), - { - "face_jaw_right_1", - "face_jaw_right_2", - "face_jaw_right_3", - "face_jaw_right_4", - "face_jaw_right_5", - "face_jaw_right_6", - "face_jaw_right_7", - "face_jaw_right_8", - "face_jaw_middle", - "face_jaw_left_1", - "face_jaw_left_2", - "face_jaw_left_3", - "face_jaw_left_4", - "face_jaw_left_5", - "face_jaw_left_6", - "face_jaw_left_7", - "face_jaw_left_8", - "face_eyebrow_right_1", - "face_eyebrow_right_2", - "face_eyebrow_right_3", - "face_eyebrow_right_4", - "face_eyebrow_right_5", - "face_eyebrow_left_1", - "face_eyebrow_left_2", - "face_eyebrow_left_3", - "face_eyebrow_left_4", - "face_eyebrow_left_5", - "face_nose_1", - "face_nose_2", - "face_nose_3", - "face_nose_4", - "face_nose_5", - "face_nose_6", - "face_nose_7", - "face_nose_8", - "face_nose_9", - "face_eye_right_1", - "face_eye_right_2", - "face_eye_right_3", - "face_eye_right_4", - "face_eye_right_5", - "face_eye_right_6", - "face_eye_left_1", - "face_eye_left_2", - "face_eye_left_3", - "face_eye_left_4", - "face_eye_left_5", - "face_eye_left_6", - "face_mouth_1", - "face_mouth_2", - "face_mouth_3", - "face_mouth_4", - "face_mouth_5", - "face_mouth_6", - "face_mouth_7", - "face_mouth_8", - "face_mouth_9", - "face_mouth_10", - "face_mouth_11", - "face_mouth_12", - "face_mouth_13", - "face_mouth_14", - "face_mouth_15", - "face_mouth_16", - "face_mouth_17", - "face_mouth_18", - "face_mouth_19", - "face_mouth_20", - }); - } - if (whole_body.at("hands")) - { - joint_names_2d.insert( - joint_names_2d.end(), - { - "hand_wrist_left", - "hand_finger_thumb_left_1", - "hand_finger_thumb_left_2", - "hand_finger_thumb_left_3", - "hand_finger_thumb_left_4", - "hand_finger_index_left_1", - "hand_finger_index_left_2", - "hand_finger_index_left_3", - "hand_finger_index_left_4", - "hand_finger_middle_left_1", - "hand_finger_middle_left_2", - "hand_finger_middle_left_3", - "hand_finger_middle_left_4", - "hand_finger_ring_left_1", - "hand_finger_ring_left_2", - "hand_finger_ring_left_3", - "hand_finger_ring_left_4", - "hand_finger_pinky_left_1", - "hand_finger_pinky_left_2", - "hand_finger_pinky_left_3", - "hand_finger_pinky_left_4", - "hand_wrist_right", - "hand_finger_thumb_right_1", - "hand_finger_thumb_right_2", - "hand_finger_thumb_right_3", - "hand_finger_thumb_right_4", - "hand_finger_index_right_1", - "hand_finger_index_right_2", - "hand_finger_index_right_3", - "hand_finger_index_right_4", - "hand_finger_middle_right_1", - "hand_finger_middle_right_2", - "hand_finger_middle_right_3", - "hand_finger_middle_right_4", - "hand_finger_ring_right_1", - "hand_finger_ring_right_2", - "hand_finger_ring_right_3", - "hand_finger_ring_right_4", - "hand_finger_pinky_right_1", - "hand_finger_pinky_right_2", - "hand_finger_pinky_right_3", - "hand_finger_pinky_right_4", - }); - } - - joint_names_2d.insert( - joint_names_2d.end(), - { - "hip_middle", - "shoulder_middle", - "head", - }); - - return joint_names_2d; - } - - // ============================================================================================= - - cv::Mat bayer2rgb(const cv::Mat &bayer) - { - cv::Mat rgb; - cv::cvtColor(bayer, rgb, cv::COLOR_BayerBG2RGB); - return rgb; - } - - cv::Mat rgb2bayer(const cv::Mat &img) - { - CV_Assert(img.type() == CV_8UC3); - cv::Mat bayer(img.rows, img.cols, CV_8UC1); - - for (int r = 0; r < img.rows; ++r) - { - const uchar *imgData = img.ptr(r); - uchar *bayerRowPtr = bayer.ptr(r); - - for (int c = 0; c < img.cols; ++c) - { - int pixelIndex = 3 * c; - - // Use faster bit operation instead of modulo+if - // Even row, even col => R = 0 - // Even row, odd col => G = 1 - // Odd row, even col => G = 1 - // Odd row, odd col => B = 2 - int row_mod = r & 1; - int col_mod = c & 1; - int component = row_mod + col_mod; - - bayerRowPtr[c] = imgData[pixelIndex + component]; - } - } - - return bayer; - } - - // ============================================================================================= - - inline int find_index(const std::vector &vec, const std::string &key) - { - auto it = std::find(vec.begin(), vec.end(), key); - if (it == vec.end()) - { - throw std::runtime_error("Cannot find \"" + key + "\" in joint_names."); - } - return static_cast(std::distance(vec.begin(), it)); - } - - std::vector>>> update_keypoints( - const std::vector>>> &poses_2d, - const std::vector &joint_names, - const std::map &whole_body) - { - std::vector>>> new_views; - new_views.reserve(poses_2d.size()); - - for (const auto &view : poses_2d) - { - // "view" is a list of bodies => each body is Nx3 - std::vector>> new_bodies; - new_bodies.reserve(view.size()); - - for (const auto &body : view) - { - // 1) Copy first 17 keypoints - std::vector> new_body; - new_body.insert(new_body.end(), body.begin(), body.begin() + 17); - - // 2) Optionally append extra keypoints - if (whole_body.at("foots")) - { - new_body.insert(new_body.end(), body.begin() + 17, body.begin() + 23); - } - if (whole_body.at("face")) - { - new_body.insert(new_body.end(), body.begin() + 23, body.begin() + 91); - } - if (whole_body.at("hands")) - { - new_body.insert(new_body.end(), body.begin() + 91, body.end()); - } - - // 3) Compute mid_hip - int hlid = find_index(joint_names, "hip_left"); - int hrid = find_index(joint_names, "hip_right"); - float mid_hip_x = 0.5 * (new_body[hlid][0] + new_body[hrid][0]); - float mid_hip_y = 0.5 * (new_body[hlid][1] + new_body[hrid][1]); - float mid_hip_c = std::min(new_body[hlid][2], new_body[hrid][2]); - new_body.push_back({mid_hip_x, mid_hip_y, mid_hip_c}); - - // 4) Compute mid_shoulder - int slid = find_index(joint_names, "shoulder_left"); - int srid = find_index(joint_names, "shoulder_right"); - float mid_shoulder_x = 0.5 * (new_body[slid][0] + new_body[srid][0]); - float mid_shoulder_y = 0.5 * (new_body[slid][1] + new_body[srid][1]); - float mid_shoulder_c = std::min(new_body[slid][2], new_body[srid][2]); - new_body.push_back({mid_shoulder_x, mid_shoulder_y, mid_shoulder_c}); - - // 5) Compute head - int elid = find_index(joint_names, "ear_left"); - int erid = find_index(joint_names, "ear_right"); - float head_x = 0.5 * (new_body[elid][0] + new_body[erid][0]); - float head_y = 0.5 * (new_body[elid][1] + new_body[erid][1]); - float head_c = std::min(new_body[elid][2], new_body[erid][2]); - new_body.push_back({head_x, head_y, head_c}); - - // Add this updated body into new_bodies - new_bodies.push_back(new_body); - } - - // Add all updated bodies for this view - new_views.push_back(new_bodies); - } - - return new_views; - } -} diff --git a/scripts/utils_pipeline.py b/scripts/utils_pipeline.py deleted file mode 100644 index 8e6c0cf..0000000 --- a/scripts/utils_pipeline.py +++ /dev/null @@ -1,189 +0,0 @@ -import json - -# ================================================================================================== - - -def load_json(path: str): - with open(path, "r", encoding="utf-8") as file: - data = json.load(file) - return data - - -def save_json(data: dict, path: str): - with open(path, "w+", encoding="utf-8") as file: - json.dump(data, file, indent=0) - - -# ================================================================================================== - - -def use_whole_body(whole_body: dict) -> bool: - return any((whole_body[k] for k in whole_body)) - - -# ================================================================================================== - - -def get_joint_names(whole_body: dict): - - joint_names_2d = [ - "nose", - "eye_left", - "eye_right", - "ear_left", - "ear_right", - "shoulder_left", - "shoulder_right", - "elbow_left", - "elbow_right", - "wrist_left", - "wrist_right", - "hip_left", - "hip_right", - "knee_left", - "knee_right", - "ankle_left", - "ankle_right", - ] - - if whole_body["foots"]: - joint_names_2d.extend( - [ - "foot_toe_big_left", - "foot_toe_small_left", - "foot_heel_left", - "foot_toe_big_right", - "foot_toe_small_right", - "foot_heel_right", - ] - ) - if whole_body["face"]: - joint_names_2d.extend( - [ - "face_jaw_right_1", - "face_jaw_right_2", - "face_jaw_right_3", - "face_jaw_right_4", - "face_jaw_right_5", - "face_jaw_right_6", - "face_jaw_right_7", - "face_jaw_right_8", - "face_jaw_middle", - "face_jaw_left_1", - "face_jaw_left_2", - "face_jaw_left_3", - "face_jaw_left_4", - "face_jaw_left_5", - "face_jaw_left_6", - "face_jaw_left_7", - "face_jaw_left_8", - "face_eyebrow_right_1", - "face_eyebrow_right_2", - "face_eyebrow_right_3", - "face_eyebrow_right_4", - "face_eyebrow_right_5", - "face_eyebrow_left_1", - "face_eyebrow_left_2", - "face_eyebrow_left_3", - "face_eyebrow_left_4", - "face_eyebrow_left_5", - "face_nose_1", - "face_nose_2", - "face_nose_3", - "face_nose_4", - "face_nose_5", - "face_nose_6", - "face_nose_7", - "face_nose_8", - "face_nose_9", - "face_eye_right_1", - "face_eye_right_2", - "face_eye_right_3", - "face_eye_right_4", - "face_eye_right_5", - "face_eye_right_6", - "face_eye_left_1", - "face_eye_left_2", - "face_eye_left_3", - "face_eye_left_4", - "face_eye_left_5", - "face_eye_left_6", - "face_mouth_1", - "face_mouth_2", - "face_mouth_3", - "face_mouth_4", - "face_mouth_5", - "face_mouth_6", - "face_mouth_7", - "face_mouth_8", - "face_mouth_9", - "face_mouth_10", - "face_mouth_11", - "face_mouth_12", - "face_mouth_13", - "face_mouth_14", - "face_mouth_15", - "face_mouth_16", - "face_mouth_17", - "face_mouth_18", - "face_mouth_19", - "face_mouth_20", - ] - ) - if whole_body["hands"]: - joint_names_2d.extend( - [ - "hand_wrist_left", - "hand_finger_thumb_left_1", - "hand_finger_thumb_left_2", - "hand_finger_thumb_left_3", - "hand_finger_thumb_left_4", - "hand_finger_index_left_1", - "hand_finger_index_left_2", - "hand_finger_index_left_3", - "hand_finger_index_left_4", - "hand_finger_middle_left_1", - "hand_finger_middle_left_2", - "hand_finger_middle_left_3", - "hand_finger_middle_left_4", - "hand_finger_ring_left_1", - "hand_finger_ring_left_2", - "hand_finger_ring_left_3", - "hand_finger_ring_left_4", - "hand_finger_pinky_left_1", - "hand_finger_pinky_left_2", - "hand_finger_pinky_left_3", - "hand_finger_pinky_left_4", - "hand_wrist_right", - "hand_finger_thumb_right_1", - "hand_finger_thumb_right_2", - "hand_finger_thumb_right_3", - "hand_finger_thumb_right_4", - "hand_finger_index_right_1", - "hand_finger_index_right_2", - "hand_finger_index_right_3", - "hand_finger_index_right_4", - "hand_finger_middle_right_1", - "hand_finger_middle_right_2", - "hand_finger_middle_right_3", - "hand_finger_middle_right_4", - "hand_finger_ring_right_1", - "hand_finger_ring_right_2", - "hand_finger_ring_right_3", - "hand_finger_ring_right_4", - "hand_finger_pinky_right_1", - "hand_finger_pinky_right_2", - "hand_finger_pinky_right_3", - "hand_finger_pinky_right_4", - ] - ) - - joint_names_2d.extend( - [ - "hip_middle", - "shoulder_middle", - "head", - ] - ) - - return joint_names_2d diff --git a/skelda b/skelda deleted file mode 160000 index d48d65b..0000000 --- a/skelda +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d48d65b9614ddf6fb3e45d05efdbf2a90cadfc21 diff --git a/tests/README.md b/tests/README.md index 2e092fb..e25d5c4 100644 --- a/tests/README.md +++ b/tests/README.md @@ -9,28 +9,3 @@ Various module tests uv sync --group dev uv run pytest tests/test_interface.py ``` - -### Onnx C++ Interface - -```bash -cd /RapidPoseTriangulation/tests/ - -g++ -std=c++17 -O3 -march=native -Wall \ - $(pkg-config --cflags opencv4) \ - -I /onnxruntime/include \ - -I /onnxruntime/include/onnxruntime/core/session \ - -I /onnxruntime/include/onnxruntime/core/providers/tensorrt \ - -L /onnxruntime/build/Linux/Release \ - test_utils2d.cpp \ - -o my_app.bin \ - -Wl,--start-group \ - -lonnxruntime_providers_tensorrt \ - -lonnxruntime_providers_shared \ - -lonnxruntime_providers_cuda \ - -lonnxruntime \ - -Wl,--end-group \ - $(pkg-config --libs opencv4) \ - -Wl,-rpath,/onnxruntime/build/Linux/Release - -./my_app.bin -``` diff --git a/tests/test_utils2d.cpp b/tests/test_utils2d.cpp deleted file mode 100644 index f7d83ad..0000000 --- a/tests/test_utils2d.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "/RapidPoseTriangulation/scripts/utils_2d_pose.hpp" - -// ================================================================================================= -// ================================================================================================= - -int main(int argc, char **argv) -{ - using namespace utils_2d_pose; - - std::string base_path = "/RapidPoseTriangulation/extras/mmdeploy/exports/"; - std::string model_path1 = base_path + "rtmdet-nano_1x320x320x3_fp16_extra-steps.onnx"; - std::string model_path2 = base_path + "rtmpose-m_1x384x288x3_fp16_extra-steps.onnx"; - - std::vector img_paths = { - "/RapidPoseTriangulation/data/h1/54138969-img_003201.jpg", - "/RapidPoseTriangulation/data/h1/55011271-img_003201.jpg", - "/RapidPoseTriangulation/data/h1/58860488-img_003201.jpg", - "/RapidPoseTriangulation/data/h1/60457274-img_003201.jpg", - }; - - // { - // std::cout << "\nTesting RTMDet and RTMPose" << std::endl; - // RTMDet model1(model_path1, 0.3, 0.1 * 0.1, 30); - // RTMPose model2(model_path2, 30); - // for (size_t i = 0; i < img_paths.size(); i++) - // { - // cv::Mat img = cv::imread(img_paths[i]); - // cv::cvtColor(img, img, cv::COLOR_BGR2RGB); - - // auto outputs1 = model1.call(img); - // std::cout << "Model1 outputs: " << outputs1[0][0] << " " << outputs1[0][1] << " " - // << outputs1[0][2] << " " << outputs1[0][3] << " " << outputs1[0][4] << " " - // << std::endl; - - // for (size_t j = 0; j < outputs1.size(); j++) - // { - // std::vector> bboxes = {outputs1[j]}; - // auto outputs2 = model2.call(img, bboxes); - // std::cout << "Model2 outputs: " << outputs2[0][0][0] << " " - // << outputs2[0][0][1] << " " << outputs2[0][0][2] << " " << std::endl; - // } - // } - // } - - // { - // std::cout << "\nTesting TopDown" << std::endl; - // TopDown model3(model_path1, model_path2, 0.3, 0.1 * 0.1, false, 30); - // for (size_t i = 0; i < img_paths.size(); i++) - // { - // cv::Mat img = cv::imread(img_paths[i]); - // cv::cvtColor(img, img, cv::COLOR_BGR2RGB); - - // auto outputs3 = model3.predict(img); - // std::cout << "Model3 outputs: " << outputs3[0][0][0] << " " - // << outputs3[0][0][1] << " " << outputs3[0][0][2] << " " << std::endl; - // } - // } - - { - std::cout << "\nTesting PosePredictor 1" << std::endl; - PosePredictor model4(false, 0.3, 0.1 * 0.1, false); - std::vector images; - for (size_t i = 0; i < img_paths.size(); i++) - { - cv::Mat img = cv::imread(img_paths[i]); - cv::cvtColor(img, img, cv::COLOR_BGR2RGB); - images.push_back(img); - } - auto outputs4 = model4.predict(images); - std::cout << "Model4 outputs: " << outputs4[0][0][0][0] << " " - << outputs4[0][0][0][1] << " " << outputs4[0][0][0][2] << " " << std::endl; - } - - { - std::cout << "\nTesting PosePredictor 2" << std::endl; - PosePredictor model5(false, 0.3, 0.1 * 0.1, true); - std::vector images; - for (size_t i = 0; i < img_paths.size(); i++) - { - cv::Mat img = cv::imread(img_paths[i]); - cv::cvtColor(img, img, cv::COLOR_BGR2RGB); - images.push_back(img); - } - auto outputs5 = model5.predict(images); - std::cout << "Model5 outputs: " << outputs5[0][0][0][0] << " " - << outputs5[0][0][0][1] << " " << outputs5[0][0][0][2] << " " << std::endl; - } - return 0; -}