OpenVINO を使用したライブ 3D 人物の姿勢推定#

この Jupyter ノートブックはオンラインで起動でき、ブラウザーのウィンドウで対話型環境を開きます。ローカルにインストールすることもできます。次のオプションのいずれかを選択します:

BinderGitHub

このノートブックは、ウェブカメラ経由で OpenVINO を使用したライブ 3D 人物の姿勢推定を示します。Open Model Zoohuman-pose-estimation-3d-0001 モデルを使用します。このノートブックの最後では、ウェブカメラからのライブ推論結果が表示されます (利用可能な場合)。あるいは、ビデオファイルをアップロードしてアルゴリズムをテストすることもできます。Jupyter 拡張機能適切にインストールされていることおよび ``README.md`` で提案されているように JupyterLab を使用してデモを実行していることを確認してください。

: ウェブカメラを使用するには、ウェブカメラを備えたコンピューター上でこの Jupyter ノートブックを実行する必要があります。リモートサーバー上で実行すると、ウェブカメラは機能しなくなります。ただし、最終ステップではビデオに対して推論を行うことができます。このデモでは、WebGL と統合された ``Three.js`` の Python インターフェイスを利用して、モデル推論からのデータを処理します。これらの結果は処理されノートブックに表示されます。

結果が正しく表示されることを確認するには、次のオペレーティング・システムのいずれかの推奨ブラウザーでコードを実行します: Ubuntu*、Windows*: Chrome macOS*: Safari

目次:

必要条件#

最新の Jupyter ノートブックのリリースを使用すると、``pythreejs`` 拡張子が正しく表示されない場合があります。したがって、代わりに Jupyter Lab を使用することを推奨します。

%pip install pythreejs "openvino-dev>=2024.0.0" "opencv-python" "torch" "onnx" --extra-index-url https://download.pytorch.org/whl/cpu
Looking in indexes: https://pypi.org/simple, https://download.pytorch.org/whl/cpu 
Collecting pythreejs 
  Using cached pythreejs-2.4.2-py3-none-any.whl.metadata (5.4 kB) 
Collecting openvino-dev>=2024.0.0 
  Using cached openvino_dev-2024.2.0-15519-py3-none-any.whl.metadata (16 kB) 
Collecting opencv-python 
  Using cached opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (20 kB) 
Collecting torch 
  Using cached https://download.pytorch.org/whl/cpu/torch-2.3.1%2Bcpu-cp38-cp38-linux_x86_64.whl (190.4 MB) 
Collecting onnx 
  Using cached onnx-1.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (16 kB) 
Requirement already satisfied: ipywidgets>=7.2.1 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from pythreejs) (8.1.3) 
Collecting ipydatawidgets>=1.1.1 (from pythreejs) 
  Using cached ipydatawidgets-4.3.5-py2.py3-none-any.whl.metadata (1.4 kB) 
Collecting numpy (from pythreejs) 
  Using cached numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (5.6 kB) 
Requirement already satisfied: traitlets in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from pythreejs) (5.14.3) 
Requirement already satisfied: defusedxml>=0.7.1 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from openvino-dev>=2024.0.0) (0.7.1) 
Collecting networkx<=3.1.0 (from openvino-dev>=2024.0.0) 
  Using cached networkx-3.1-py3-none-any.whl.metadata (5.3 kB) 
Collecting openvino-telemetry>=2023.2.1 (from openvino-dev>=2024.0.0) 
  Using cached openvino_telemetry-2024.1.0-py3-none-any.whl.metadata (2.3 kB) 
Requirement already satisfied: packaging in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from openvino-dev>=2024.0.0) (24.1) 
Requirement already satisfied: pyyaml>=5.4.1 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from openvino-dev>=2024.0.0) (6.0.1) 
Requirement already satisfied: requests>=2.25.1 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from openvino-dev>=2024.0.0) (2.32.0) 
Collecting openvino==2024.2.0 (from openvino-dev>=2024.0.0) 
  Using cached openvino-2024.2.0-15519-cp38-cp38-manylinux2014_x86_64.whl.metadata (8.9 kB) 
Collecting filelock (from torch) 
  Using cached filelock-3.15.4-py3-none-any.whl.metadata (2.9 kB) 
Requirement already satisfied: typing-extensions>=4.8.0 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from torch) (4.12.2) 
Collecting sympy (from torch) 
  Using cached sympy-1.13.0-py3-none-any.whl.metadata (12 kB) 
Requirement already satisfied: jinja2 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from torch) (3.1.4) 
Collecting fsspec (from torch) 
  Using cached fsspec-2024.6.1-py3-none-any.whl.metadata (11 kB) 
Collecting protobuf>=3.20.2 (from onnx) 
  Using cached protobuf-5.27.2-cp38-abi3-manylinux2014_x86_64.whl.metadata (592 bytes) 
Collecting traittypes>=0.2.0 (from ipydatawidgets>=1.1.1->pythreejs) 
  Using cached traittypes-0.2.1-py2.py3-none-any.whl.metadata (1.0 kB) 
Requirement already satisfied: comm>=0.1.3 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipywidgets>=7.2.1->pythreejs) (0.2.2) 
Requirement already satisfied: ipython>=6.1.0 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipywidgets>=7.2.1->pythreejs) (8.12.3) 
Requirement already satisfied: widgetsnbextension~=4.0.11 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipywidgets>=7.2.1->pythreejs) (4.0.11) 
Requirement already satisfied: jupyterlab-widgets~=3.0.11 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipywidgets>=7.2.1->pythreejs) (3.0.11) 
Requirement already satisfied: charset-normalizer<4,>=2 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from requests>=2.25.1->openvino-dev>=2024.0.0) (3.3.2) 
Requirement already satisfied: idna<4,>=2.5 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from requests>=2.25.1->openvino-dev>=2024.0.0) (3.7) 
Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from requests>=2.25.1->openvino-dev>=2024.0.0) (2.2.2) 
Requirement already satisfied: certifi>=2017.4.17 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from requests>=2.25.1->openvino-dev>=2024.0.0) (2024.7.4) 
Requirement already satisfied: MarkupSafe>=2.0 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from jinja2->torch) (2.1.5) 
Collecting mpmath<1.4,>=1.1.0 (from sympy->torch) 
  Using cached https://download.pytorch.org/whl/mpmath-1.3.0-py3-none-any.whl (536 kB) 
Requirement already satisfied: backcall in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (0.2.0) 
Requirement already satisfied: decorator in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (5.1.1) 
Requirement already satisfied: jedi>=0.16 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (0.19.1) 
Requirement already satisfied: matplotlib-inline in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (0.1.7) 
Requirement already satisfied: pickleshare in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (0.7.5) 
Requirement already satisfied: prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (3.0.47) 
Requirement already satisfied: pygments>=2.4.0 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (2.18.0) 
Requirement already satisfied: stack-data in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (0.6.3) 
Requirement already satisfied: pexpect>4.3 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (4.9.0) 
Requirement already satisfied: parso<0.9.0,>=0.8.3 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from jedi>=0.16->ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (0.8.4) 
Requirement already satisfied: ptyprocess>=0.5 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from pexpect>4.3->ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (0.7.0) 
Requirement already satisfied: wcwidth in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30->ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (0.2.13) 
Requirement already satisfied: executing>=1.2.0 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from stack-data->ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (2.0.1) 
Requirement already satisfied: asttokens>=2.1.0 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from stack-data->ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (2.4.1) 
Requirement already satisfied: pure-eval in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from stack-data->ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (0.2.2) 
Requirement already satisfied: six>=1.12.0 in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (from asttokens>=2.1.0->stack-data->ipython>=6.1.0->ipywidgets>=7.2.1->pythreejs) (1.16.0) 
Using cached pythreejs-2.4.2-py3-none-any.whl (3.4 MB) 
Using cached openvino_dev-2024.2.0-15519-py3-none-any.whl (4.7 MB) 
Using cached openvino-2024.2.0-15519-cp38-cp38-manylinux2014_x86_64.whl (38.7 MB) 
Using cached opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (62.5 MB) 
Using cached onnx-1.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (15.9 MB) 
Using cached ipydatawidgets-4.3.5-py2.py3-none-any.whl (271 kB) 
Using cached networkx-3.1-py3-none-any.whl (2.1 MB) 
Using cached numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (17.3 MB) 
Using cached openvino_telemetry-2024.1.0-py3-none-any.whl (23 kB) 
Using cached protobuf-5.27.2-cp38-abi3-manylinux2014_x86_64.whl (309 kB) 
Using cached filelock-3.15.4-py3-none-any.whl (16 kB) 
Using cached fsspec-2024.6.1-py3-none-any.whl (177 kB) 
Using cached sympy-1.13.0-py3-none-any.whl (6.2 MB) 
Using cached traittypes-0.2.1-py2.py3-none-any.whl (8.6 kB) 
Installing collected packages: openvino-telemetry, mpmath, traittypes, sympy, protobuf, numpy, networkx, fsspec, filelock, torch, openvino, opencv-python, onnx, openvino-dev, ipydatawidgets, pythreejs 
Successfully installed filelock-3.15.4 fsspec-2024.6.1 ipydatawidgets-4.3.5 mpmath-1.3.0 networkx-3.1 numpy-1.24.4 onnx-1.16.1 opencv-python-4.10.0.84 openvino-2024.2.0 openvino-dev-2024.2.0 openvino-telemetry-2024.1.0 protobuf-5.27.2 pythreejs-2.4.2 sympy-1.13.0 torch-2.3.1+cpu traittypes-0.2.1 
Note: you may need to restart the kernel to use updated packages.

インポート#

import collections 
import time 
from pathlib import Path 

import cv2 
import ipywidgets as widgets 
import numpy as np 
from IPython.display import clear_output, display 
import openvino as ov 

# `notebook_utils` モジュールを取得 
import requests 

r = requests.get( 

url="https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py", 
) 
with open("notebook_utils.py", "w") as f: 
    f.write(r.text) 

r = requests.get( 

url="https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/engine3js.py", 
) 
with open("engine3js.py", "w") as f: 
    f.write(r.text) 

import notebook_utils as utils 
import engine3js as engine

モデル#

モデルのダウンロード#

ここでは、openvino-dev パッケージのコマンドライン・ツールである omz_downloader を使用します。omz_downloader は、自動的にディレクトリー構造を作成し、選択したモデルをダウンロードします。

# モデルがダウンロードされるディレクトリー 
base_model_dir = "model" 

# Open Model Zoo で命名されたモデル名 
model_name = "human-pose-estimation-3d-0001" 
# 選択された精度 (FP32, FP16) 
precision = "FP32" 

BASE_MODEL_NAME = f"{base_model_dir}/public/{model_name}/{model_name}" 
model_path = Path(BASE_MODEL_NAME).with_suffix(".pth") 
onnx_path = Path(BASE_MODEL_NAME).with_suffix(".onnx") 

ir_model_path = f"model/public/{model_name}/{precision}/{model_name}.xml" 
model_weights_path = f"model/public/{model_name}/{precision}/{model_name}.bin" 

if not model_path.exists(): 
    download_command = f"omz_downloader " f"--name {model_name} " f"--output_dir {base_model_dir}" 
    ! $download_command
################|| Downloading human-pose-estimation-3d-0001 ||################ 
========== Downloading model/public/human-pose-estimation-3d-0001/human-pose-estimation-3d-0001.tar.gz 

========== Unpacking model/public/human-pose-estimation-3d-0001/human-pose-estimation-3d-0001.tar.gz

モデルを OpenVINO IR 形式に変換#

選択されたモデルはパブリック・ディレクトリーから取得されます。つまり、OpenVINO 中間表現 (OpenVINO IR) に変換する必要があります。omz_converter を使用して、ONNX 形式モデルを OpenVINO IR 形式に変換します。

if not onnx_path.exists(): 
    convert_command = ( 
        f"omz_converter " f"--name {model_name} " f"--precisions {precision} " f"--download_dir {base_model_dir} " f"--output_dir {base_model_dir}" 
    ) 
    ! $convert_command

========== Converting human-pose-estimation-3d-0001 to ONNX 
Conversion to ONNX command: /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/bin/python -- /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages/omz_tools/internal_scripts/pytorch_to_onnx.py --model-path=model/public/human-pose-estimation-3d-0001 --model-name=PoseEstimationWithMobileNet --model-param=is_convertible_by_mo=True --import-module=model --weights=model/public/human-pose-estimation-3d-0001/human-pose-estimation-3d-0001.pth --input-shape=1,3,256,448 --input-names=data --output-names=features,heatmaps,pafs --output-file=model/public/human-pose-estimation-3d-0001/human-pose-estimation-3d-0001.onnx 

ONNX check passed successfully.

========== Converting human-pose-estimation-3d-0001 to IR (FP32) 
Conversion command: /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/bin/python -- /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/bin/mo --framework=onnx --output_dir=model/public/human-pose-estimation-3d-0001/FP32 --model_name=human-pose-estimation-3d-0001 --input=data '--mean_values=data[128.0,128.0,128.0]' '--scale_values=data[255.0,255.0,255.0]' --output=features,heatmaps,pafs --input_model=model/public/human-pose-estimation-3d-0001/human-pose-estimation-3d-0001.onnx '--layout=data(NCHW)' '--input_shape=[1, 3, 256, 448]' --compress_to_fp16=False 

[ INFO ] MO command line tool is considered as the legacy conversion API as of OpenVINO 2023.2 release. In 2025.0 MO command line tool and openvino.tools.mo.convert_model() will be removed. Please use OpenVINO Model Converter (OVC) or openvino.convert_model(). OVC represents a lightweight alternative of MO and provides simplified model conversion API. Find more information about transition from MO to OVC at https://docs.openvino.ai/2023.2/openvino_docs_OV_Converter_UG_prepare_model_convert_model_MO_OVC_transition.html 
[ SUCCESS ] Generated IR version 11 model.
[ SUCCESS ] XML file: /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/notebooks/3D-pose-estimation-webcam/model/public/human-pose-estimation-3d-0001/FP32/human-pose-estimation-3d-0001.xml 
[ SUCCESS ] BIN file: /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/notebooks/3D-pose-estimation-webcam/model/public/human-pose-estimation-3d-0001/FP32/human-pose-estimation-3d-0001.bin

推論デバイスの選択#

OpenVINO を使用して推論を実行するためにドロップダウン・リストからデバイスを選択します

core = ov.Core() 

device = widgets.Dropdown( 
    options=core.available_devices + ["AUTO"], 
    value="AUTO", 
    description="Device:", 
    disabled=False, 
) 

device
Dropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')

モデルのロード#

変換されたモデルは、ベンダー、モデル名、精度を示す固定構造に配置されます。

まず、推論エンジンである OpenVINO ランタイムを初期化します。次に、.bin および .xml ファイルからネットワーク・アーキテクチャーとモデルの重みを読み取り、目的のデバイス用にコンパイルします。次に、コンパイルされたモデルを推論するため推論要求が作成されます。

# 推論エンジンを初期化 
core = ov.Core() 
# ファイルからネットワークと対応する重みを読み取り 
model = core.read_model(model=ir_model_path, weights=model_weights_path) 
# 指定されたデバイスにモデルをロード 
compiled_model = core.compile_model(model=model, device_name=device.value) 
infer_request = compiled_model.create_infer_request() 
input_tensor_name = model.inputs[0].get_any_name() 

# ノードの入力名と出力名を取得 
input_layer = compiled_model.input(0) 
output_layers = list(compiled_model.outputs)

モデルの入力は入力画像からのデータであり、出力はヒートマップ、PAF (パーツ・アフィニティー・フィールド)、およびフィーチャーです。

input_layer.any_name, [o.any_name for o in output_layers]
('data', ['features', 'heatmaps', 'pafs'])

処理#

モデル推論#

ビデオファイルまたはライブ Web カメラからキャプチャーされたフレームは、3D モデルの入力として使用されます。これは、出力ヒートマップ、PAF (パーツ・アフィニティー・フィールド)、およびフィーチャーを取得する方法です。

def model_infer(scaled_img, stride): 
    """ 
    Run model inference on the input image 

    Parameters: 
        scaled_img: resized image according to the input size of the model 
        stride: int, the stride of the window 
    """ 

    # 画像から余分なスペースを削除 
    img = scaled_img[ 
        0 : scaled_img.shape[0] - (scaled_img.shape[0] % stride), 
        0 : scaled_img.shape[1] - (scaled_img.shape[1] % stride), 
    ] 

    img = np.transpose(img, (2, 0, 1))[None,] 
    infer_request.infer({input_tensor_name: img}) 
    # 3 つの推論結果のセットを取得 
    results = {name: infer_request.get_tensor(name).data[:] for name in {"features", "heatmaps", "pafs"}} 
    # 結果を取得 
    results = (results["features"][0], results["heatmaps"][0], results["pafs"][0]) 

    return results

2D ポーズ・オーバーレイの描画#

推論結果を取得した後、結果の画像に人体の構造を描画できるように、関節の接続を事前に定義する必要があります。関節は円として描画され、手足は線で描画されます。このコードは、Open Model Zoo の 3D Human Pose Estimation Demo をベースにしています。

# 3D エッジ・インデックス配列 
body_edges = np.array( 
    [ 
        [0, 1], 
        [0, 9], 
        [9, 10], 
        [10, 11], # neck - r_shoulder - r_elbow - r_wrist 
        [0, 3], 
        [3, 4], 
        [4, 5], # neck - l_shoulder - l_elbow - l_wrist 
        [1, 15], 
        [15, 16], # nose - l_eye - l_ear 
        [1, 17], 
        [17, 18], # nose - r_eye - r_ear 
        [0, 6], 
        [6, 7], 
        [7, 8], # neck - l_hip - l_knee - l_ankle 
        [0, 12], 
        [12, 13], 
        [13, 14], # neck - r_hip - r_knee - r_ankle 
    ] 
) 

body_edges_2d = np.array( 
    [ 
        [0, 1], # neck - nose 
        [1, 16], 
        [16, 18], # nose - l_eye - l_ear 
        [1, 15], 
        [15, 17], # nose - r_eye - r_ear 
        [0, 3], 
        [3, 4], 
        [4, 5], # neck - l_shoulder - l_elbow - l_wrist 
        [0, 9], 
        [9, 10], 
        [10, 11], # neck - r_shoulder - r_elbow - r_wrist 
        [0, 6], 
        [6, 7], 
        [7, 8], # neck - l_hip - l_knee - l_ankle 
        [0, 12], 
        [12, 13], 
        [13, 14], # neck - r_hip - r_knee - r_ankle 
    ] 
) 

def draw_poses(frame, poses_2d, scaled_img, use_popup): 
    """ 
    Draw 2D pose overlays on the image to visualize estimated poses. 
    Joints are drawn as circles and limbs are drawn as lines.
    :param frame: the input image 
    :param poses_2d: array of human joint pairs 
    """ 
    for pose in poses_2d: 
        pose = np.array(pose[0:-1]).reshape((-1, 3)).transpose() 
        was_found = pose[2] > 0 

        pose[0], pose[1] = ( 
            pose[0] * frame.shape[1] / scaled_img.shape[1], 
            pose[1] * frame.shape[0] / scaled_img.shape[0], 
        ) 

        # Draw joints. 
        for edge in body_edges_2d: 
            if was_found[edge[0]] and was_found[edge[1]]: 
                cv2.line( 
                    frame, 
                    tuple(pose[0:2, edge[0]].astype(np.int32)), 
                    tuple(pose[0:2, edge[1]].astype(np.int32)), 
                    (255, 255, 0), 
                    4, 
                    cv2.LINE_AA, 
                ) 
        # Draw limbs. 
        for kpt_id in range(pose.shape[1]): 
            if pose[2, kpt_id] != -1: 
                cv2.circle( 
                    frame, 
                    tuple(pose[0:2, kpt_id].astype(np.int32)), 
                    3, 
                    (0, 255, 255), 
                    -1, 
                    cv2.LINE_AA,
                ) 

    return frame

メイン処理関数#

指定されたソースで 3D 姿勢推定を実行します。ウェブカメラのフィードまたはビデオファイルのどちらかである可能性があります。

def run_pose_estimation(source=0, flip=False, use_popup=False, skip_frames=0): 
    """ 
    2D image as input, using OpenVINO as inference backend, 
    get joints 3D coordinates, and draw 3D human skeleton in the scene 

    :param source:      The webcam number to feed the video stream with primary webcam set to "0", or the video path. 
    :param flip:        To be used by VideoPlayer function for flipping capture image. 
    :param use_popup:   False for showing encoded frames over this notebook, True for creating a popup window. 
    :param skip_frames: Number of frames to skip at the beginning of the video.
    """ 

    focal_length = -1 # デフォルト 
    stride = 8 
    player = None 
    skeleton_set = None 

    try:
        # ターゲット fps で再生するビデオプレーヤーを作成 
        # video_path はカメラからフレームを取得 
        # 最初の N フレームをスキップしてビデオを早送り。'skip_first_frames’ を変更 
        player = utils.VideoPlayer(source, flip=flip, fps=30, skip_first_frames=skip_frames) 
        # キャプチャーを開始 
        player.start() 

        input_image = player.next() 
        # ウィンドウサイズを設定 
        resize_scale = 450 / input_image.shape[1] 
        windows_width = int(input_image.shape[1] * resize_scale) 
        windows_height = int(input_image.shape[0] * resize_scale) 

        # 視覚化ライブラリーを使用 
        engine3D = engine.Engine3js(grid=True, axis=True, view_width=windows_width, view_height=windows_height) 

        if use_popup:
            # このノートブックに 3D の人間のポーズを表示し、ポップアップ・ウィンドウに原点フレームを表示 
            display(engine3D.renderer) 
            title = "Press ESC to Exit" 
            cv2.namedWindow(title, cv2.WINDOW_KEEPRATIO | cv2.WINDOW_AUTOSIZE) 
        else:
            # 2D 画像ボックスを設定し、ノートブックに人間のポーズと画像の両方を表示 
            imgbox = widgets.Image(format="jpg", height=windows_height, width=windows_width) 
            display(widgets.HBox([engine3D.renderer, imgbox])) 

        skeleton = engine.Skeleton(body_edges=body_edges) 

        processing_times = collections.deque() 

        while True:
            # フレームをグラブ 
            frame = player.next() 
            if frame is None: 
                print("Source ended") 
                break 

            # ニューラル・ネットワークの入力に合わせて画像のサイズを変更して暗さを変更 
            # (https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001 を参照) 
            scaled_img = cv2.resize(frame, dsize=(model.inputs[0].shape[3], model.inputs[0].shape[2])) 

            if focal_length < 0: # 焦点距離は不明 
                focal_length = np.float32(0.8 * scaled_img.shape[1]) 

            # 推論を開始 
            start_time = time.time() 
            # 結果を取得 
            inference_result = model_infer(scaled_img, stride) 

            # 推論を停止 
            stop_time = time.time() 
            processing_times.append(stop_time - start_time) 
            # データのポイントツーポイント座標を処理 
            poses_3d, poses_2d = engine.parse_poses(inference_result, 1, stride, focal_length, True) 

            # 最後の 200 フレームの処理時間を使用 
            if len(processing_times) > 200: 
                processing_times.popleft() 

            processing_time = np.mean(processing_times) * 1000 
            fps = 1000 / processing_time 

            if len(poses_3d) > 0:
                # ここから、関数 "draw_poses" を使用して 3D ポイントの位置を回転、 
                # または、以下の正しいマッピングを直接行うことで、画面上にオブジェクト画像を適切に表示 
                poses_3d_copy = poses_3d.copy() 
                x = poses_3d_copy[:, 0::4] 
                y = poses_3d_copy[:, 1::4] 
                z = poses_3d_copy[:, 2::4] 
                poses_3d[:, 0::4], poses_3d[:, 1::4], poses_3d[:, 2::4] = ( 
                    -z + np.ones(poses_3d[:, 2::4].shape) * 200, 
                    -y + np.ones(poses_3d[:, 2::4].shape) * 100, 
                    -x, 
                ) 

                poses_3d = poses_3d.reshape(poses_3d.shape[0], 19, -1)[:, :, 0:3] 
                people = skeleton(poses_3d=poses_3d) 

                try: 
                    engine3D.scene_remove(skeleton_set) 
                except Exception: 
                    pass 

                engine3D.scene_add(people) 
                skeleton_set = people 

                # 2D 描画 
                frame = draw_poses(frame, poses_2d, scaled_img, use_popup) 

            else: 
                try: 
                    engine3D.scene_remove(skeleton_set) skeleton_set = None 
                except Exception: 
                    pass 

            cv2.putText( 
                frame, 
                f"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)", 
                (10, 30), 
                cv2.FONT_HERSHEY_COMPLEX, 
                0.7, 
                (0, 0, 255), 
                1, 
                cv2.LINE_AA, 
            ) 

            if use_popup: 
                cv2.imshow(title, frame) 
                key = cv2.waitKey(1) 
                # escape = 27、ESC キーを押して終了 
                if key == 27: 
                    break 
                else:
                    # numpy 配列を jpg にエンコード 
                    imgbox.value = cv2.imencode( 
                        ".jpg", 
                        frame, 
                        params=[cv2.IMWRITE_JPEG_QUALITY, 90], 
                    )[1].tobytes() 

                engine3D.renderer.render(engine3D.scene, engine3D.cam) 

    except KeyboardInterrupt: 
        print("Interrupted") 
    except RuntimeError as e: 
        print(e) 
    finally: 
        clear_output() 
        if player is not None:
            # キャプチャーを停止 
            player.stop() 
        if use_popup: 
            cv2.destroyAllWindows() 
        if skeleton_set: 
            engine3D.scene_remove(skeleton_set)

実行#

Web カメラをビデオ入力として使用して実行します。デフォルトでは、プライマリー Web カメラは source=0 に設定されます。複数のウェブカメラがある場合、0 から始まる連続した番号が割り当てられます。前面カメラを使用する場合は、flip=True を設定します。一部のウェブブラウザー、特に Mozilla Firefox ではちらつきが発生する場合があります。ちらつきが発生する場合、use_popup=True を設定してください。

注:

  1. このノートブックをウェブカメラで使用するには、ウェブカメラを備えたコンピューター上でノートブックを実行する必要があります。ノートブックをサーバー (Binder など) 上で実行する場合、ウェブカメラは機能しません。
  2. このノートブックをリモート・コンピューター (Binder など) で実行する場合、ポップアップ・モードは機能しない可能性があります。

ウェブカメラがない場合でも、ビデオファイルを使用してこのデモを実行できます。OpenCV でサポートされている形式であればどれでも機能します。

次の方法を使用すると、左側の画像をクリックしてマウスを移動して操作できます。

USE_WEBCAM = False 

cam_id = 0 
video_path = "https://github.com/intel-iot-devkit/sample-videos/raw/master/face-demographics-walking.mp4" 

source = cam_id if USE_WEBCAM else video_path 

run_pose_estimation(source=source, flip=isinstance(source, int), use_popup=False)