diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 165c5f51..006b761e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,16 +5,70 @@ on: [push, pull_request] jobs: test: runs-on: ubuntu-latest + defaults: + run: + shell: bash -el {0} strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9"] steps: - - uses: actions/checkout@v3 - - run: pipx install poetry - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 with: + submodules: 'recursive' + - name: Install EGL mesa - required for Panda3D renderer + run: | + sudo apt-get update -y -qq + sudo apt-get install -y -qq libegl1-mesa libegl1-mesa-dev + + - name: Setup conda + uses: conda-incubator/setup-miniconda@v2 + with: + activate-environment: happypose python-version: ${{ matrix.python-version }} - cache: poetry - - run: poetry install --with dev -E render - - run: poetry run coverage run -m unittest + miniforge-variant: Mambaforge + miniforge-version: latest + use-mamba: true + + - name: Get date for caching, reset cache every day + id: get-date + run: echo "::set-output name=today::$(/bin/date -u '+%Y%m%d')" + shell: bash + + - name: Caching of the happypose installation + uses: actions/cache@v3 + with: + path: ${{ env.CONDA }}/envs + key: + conda-${{ runner.os }}--${{ runner.arch }}--${{steps.get-date.outputs.today }}-${{hashFiles('environment.yml') }}-${{env.CACHE_NUMBER }} + env: + # Increase this value to reset cache manually + CACHE_NUMBER: 0 + id: cache + + - name: Update conda environment with happypose dependencies + run: + mamba env update -n happypose -f environment.yml + if: steps.cache.outputs.cache-hit != 'true' + + - name: Install bop toolkit (temporal fix) + run: | + cd happypose/pose_estimators/megapose/deps/bop_toolkit_challenge/ + sed 's/==.*$//' requirements.txt > req_nover.txt + pip install -r req_nover.txt -e . + if: steps.cache.outputs.cache-hit != 'true' + + - name: Install happypose + run: | + cd happypose/pose_estimators/cosypose + pip install . + cd ../../.. + pip install -e . + + - name: Run tests + run: | + pip install pytest coverage + coverage run --source=happypose -m pytest tests + coverage xml - uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.vscode/settings.json b/.vscode/settings.json index 19055a87..cb32fdad 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,5 @@ }, "python.linting.enabled": true, "python.linting.flake8Enabled": true, - "python.linting.mypyEnabled": true, -} \ No newline at end of file + "python.linting.mypyEnabled": true +} diff --git a/environment.yml b/environment.yml index dd8fec7f..c6e08482 100644 --- a/environment.yml +++ b/environment.yml @@ -16,9 +16,9 @@ dependencies: - ipython - ipykernel - jupyterlab - - notebook - - nb_conda_kernels - - jupyter_contrib_nbextensions +# - notebook +# - nb_conda_kernels +# - jupyter_contrib_nbextensions - pinocchio - rclone - pillow diff --git a/happypose/toolbox/renderer/panda3d_scene_renderer.py b/happypose/toolbox/renderer/panda3d_scene_renderer.py index 98536600..bcde01cb 100644 --- a/happypose/toolbox/renderer/panda3d_scene_renderer.py +++ b/happypose/toolbox/renderer/panda3d_scene_renderer.py @@ -29,6 +29,7 @@ from typing import Dict, List, Optional, Set # Third Party +import torch import numpy as np import panda3d as p3d from direct.showbase.ShowBase import ShowBase @@ -62,6 +63,7 @@ def __init__(self) -> None: p3d.core.load_prc_file_data( __file__, "load-display pandagl\n" + "gl-version 3 2\n" "notify-level-assimp fatal\n" "notify-level-egldisplay fatal\n" "notify-level-glgsg fatal\n" @@ -80,20 +82,21 @@ def __init__(self) -> None: "audio-library-name null\n" "model-cache-dir\n", ) - assert "CUDA_VISIBLE_DEVICES" in os.environ - devices = os.environ["CUDA_VISIBLE_DEVICES"].split(",") - assert len(devices) == 1 - if "EGL_VISIBLE_DEVICES" not in os.environ: - out = subprocess.check_output( - ["nvidia-smi", "--id=" + str(devices[0]), "-q", "--xml-format"] - ) - tree = ET.fromstring(out) - gpu = tree.findall("gpu")[0] - assert gpu is not None - minor_number_el = gpu.find("minor_number") - assert minor_number_el is not None - dev_id = minor_number_el.text - os.environ["EGL_VISIBLE_DEVICES"] = str(dev_id) + if torch.cuda.is_available(): + assert "CUDA_VISIBLE_DEVICES" in os.environ + devices = os.environ["CUDA_VISIBLE_DEVICES"].split(",") + assert len(devices) == 1 + if "EGL_VISIBLE_DEVICES" not in os.environ: + out = subprocess.check_output( + ["nvidia-smi", "--id=" + str(devices[0]), "-q", "--xml-format"] + ) + tree = ET.fromstring(out) + gpu = tree.findall("gpu")[0] + assert gpu is not None + minor_number_el = gpu.find("minor_number") + assert minor_number_el is not None + dev_id = minor_number_el.text + os.environ["EGL_VISIBLE_DEVICES"] = str(dev_id) super().__init__(windowType="offscreen") self.render.set_shader_auto() diff --git a/tests/data/obj_000001.ply b/tests/data/obj_000001.ply new file mode 100644 index 00000000..60c98f76 Binary files /dev/null and b/tests/data/obj_000001.ply differ diff --git a/tests/tests_cosy.py b/tests/test_cosy.py similarity index 100% rename from tests/tests_cosy.py rename to tests/test_cosy.py diff --git a/tests/tests_docs.py b/tests/test_docs.py similarity index 100% rename from tests/tests_docs.py rename to tests/test_docs.py diff --git a/tests/test_renderer_panda3d.py b/tests/test_renderer_panda3d.py new file mode 100644 index 00000000..82a1ba09 --- /dev/null +++ b/tests/test_renderer_panda3d.py @@ -0,0 +1,72 @@ +"""Set of unit tests for Panda3D renderer.""" +import unittest +from pathlib import Path +import numpy as np +from numpy.testing import assert_equal + +from happypose.toolbox.datasets.object_dataset import RigidObjectDataset, RigidObject +from happypose.toolbox.lib3d.transform import Transform +from happypose.toolbox.renderer.panda3d_scene_renderer import Panda3dSceneRenderer +from happypose.toolbox.renderer.types import ( + Panda3dObjectData, + Panda3dCameraData, + Panda3dLightData, +) + + +class TestRendererPanda3D(unittest.TestCase): + """Unit tests for Panda3D renderer.""" + + def test_simple_render(self): + """Render an example object and check that output image match expectation.""" + renderer = Panda3dSceneRenderer( + asset_dataset=RigidObjectDataset( + objects=[ + RigidObject( + label="obj", + mesh_path=Path(__file__).parent.joinpath("data/obj_000001.ply"), + mesh_units="mm", + ) + ] + ) + ) + + object_datas = [ + Panda3dObjectData( + label="obj", TWO=Transform((0, 0, 0, 1), (0, 0, 1)), color=(1, 0, 0, 1) + ) + ] + camera_datas = [ + Panda3dCameraData( + K=np.array( + [ + [600.0, 0.0, 160.0], + [0.0, 600.0, 160.0], + [0.0, 0.0, 1.0], + ] + ), + resolution=(320, 320), + ) + ] + + light_datas = [ + Panda3dLightData( + light_type="ambient", + color=(1.0, 1, 1, 1), + ), + ] + renderings = renderer.render_scene(object_datas, camera_datas, light_datas) + # import matplotlib.pyplot as plt + # fig, ax = plt.subplots(1, 1, squeeze=True) # type: plt.Figure, plt.Axes + # ax.imshow(renderings[0].rgb) + # plt.show() + + self.assertEqual(len(renderings), 1) + rgb = renderings[0].rgb + + assert_equal(rgb[rgb.shape[0] // 2, rgb.shape[1] // 2], (255, 0, 0)) + assert_equal(rgb[0, 0], (0, 0, 0)) + + +if __name__ == "__main__": + unittest.main()