Skip to content

Commit 0010e4e

Browse files
Jetson dockerfiles: OpenCV with GStreamer on JP62, add BuildKit cache mounts on JP62/JP71, bump JP51 OpenCV (#2321)
* Build OpenCV from source with GStreamer + bake NVIDIA gst plugins into JP62 The pip opencv-python wheel ships without GStreamer, and the l4t-cuda runtime base used for the JP62 image has no multimedia stack, so cv2.VideoCapture on Jetson silently falls through to the plain v4l2 ioctl path -- pulling raw 4K YUYV, doing CPU YUV->BGR, and never reaching NVIDIA's hardware engines. Three pieces, all needed together: 1. Compile OpenCV from source in the builder stage with WITH_GSTREAMER=ON, WITH_FFMPEG=ON, WITH_LIBV4L=ON, WITH_CUDA=ON. Replaces the pip-installed opencv-python/opencv-contrib-python with a wheel built from python_loader. Adds a build-time cv2.getBuildInformation() check so a regression in the detected flags fails the build instead of silently shipping CPU-only cv2. 2. Add the GStreamer runtime + PyGObject stack to the runtime stage (libgstreamer1.0-0, plugins-base/good, python3-gi, gir typelibs) so that downstream Python code using "from gi.repository import Gst" to construct hardware capture/encode pipelines can import and run inside the container. 3. Copy NVIDIA's GStreamer plugins (libgstnv*.so) plus the tegra runtime libs from the JetPack builder stage into the runtime image, instead of relying on nvidia-container-passthrough host mounts. Self-contained -- works without any host-side changes deployed. Branched off v1.2.7 to keep the diff minimal. * Pass CMAKE_POLICY_VERSION_MINIMUM=3.5 to OpenCV cmake CMake 4.x removed compatibility with cmake_minimum_required(VERSION <3.5). OpenCV 4.10.0's OpenCVGenPkgconfig.cmake still uses the old declaration, so the configure step aborts with: CMake Error at .../OpenCVGenPkgconfig.cmake:113 (cmake_minimum_required): Compatibility with CMake < 3.5 has been removed from CMake. Same workaround the existing onnxruntime build step uses. * Bump OpenCV to 4.13.0 and reorder JP62 build OpenCV 4.13.0 (released 2025-12-31) is the current latest stable; it builds cleanly under CMake 4.x without the cmake_minimum_required workaround the 4.10.0 source needed. Aligns both Jetson images. JP62-specific: - Move the from-source OpenCV step to AFTER the inference packages install so the cv2 we install (built with GStreamer + FFmpeg) is the last thing to touch /usr/local/lib/python3.10/dist-packages/cv2/. Previously the inference_core/cli/sdk pip install would refetch opencv-python (as a transitive dep) and clobber our from-source cv2. - Drop CMAKE_POLICY_VERSION_MINIMUM=3.5 -- not needed on 4.13.0. * Drop the bake-in attempt for NVIDIA gst plugins The l4t-jetpack:r36.4.0 builder image does not ship the tegra runtime libs as a regular populated /usr/lib/aarch64-linux-gnu/tegra directory. Those libs are tied to the host JetPack BSP and only appear inside the container at run time via nvidia-container-runtime's CSV-driven host mounts. So `COPY --from=builder /usr/lib/aarch64-linux-gnu/tegra ...` errors during the build with "not found". This image now scopes to enabling the cv2 side of the GStreamer stack (WITH_GSTREAMER=ON OpenCV, python-gi, base gstreamer runtime + GIR typelibs). The host system has to provide the NVIDIA plugins for any nv* pipeline element to resolve at run time. * mkdir tegra/ at end of builder so libnvdla glob COPY can lstat parent BuildKit needs to lstat the source parent dir before evaluating a glob COPY pattern, even when "no match" is allowed. The libnvdla fix in PR #2201 copies tegra/libnvdla_compiler.so*, but l4t-jetpack:r36.4.0 doesn't always ship /usr/lib/aarch64-linux-gnu/tegra/ as a real directory -- that path is normally populated by nvidia-container-runtime at container start. Recent builds intermittently fail at this step depending on layer cache state. mkdir -p the path so BuildKit can process the COPY. The glob will copy the real file if it's there, or silently no-op if not. Doesn't paper over the underlying flakiness of the libnvdla source path, but lets this PR's build progress. * Correct libnvdla_compiler.so source path from tegra/ to nvidia/ The actual location in nvcr.io/nvidia/l4t-jetpack:r36.4.0 is /usr/lib/aarch64-linux-gnu/nvidia/libnvdla_compiler.so, not tegra/. The original COPY in PR #2201 used the wrong path; the glob silently matched zero files, so the libnvdla fix shipped an empty layer and ONNX Runtime kept falling back to CPU on Jetson 6.x. Same one-line correction as PR #2306. Bundling it here so this PR's build can also actually ship libnvdla_compiler.so. * Set OpenCV python install path so the loader stub finds cv2 at runtime Without OPENCV_PYTHON3_INSTALL_PATH / PYTHON3_INCLUDE_DIR / PYTHON_VERSION the python_loader config.py bakes the BUILDER path (/build/opencv/<ver>/release/lib/python3) into sys.path. That path only exists in the builder stage, so in the runtime image the loader stub fails to find the native cv2 .so, falls back to importlib.import_module("cv2") which re-enters the same stub package, and Python raises: ImportError: ERROR: recursion is detected during loading of "cv2" binary extensions. Check OpenCV installation. JP51's PR #2100 passes these flags explicitly; replicating the same set here so the loader references /usr/local/lib/python3.10/dist-packages. * Rescue install-tree cv2 config files; parameterize for porting Two related changes to make the from-source OpenCV build correct and reusable across jp5.1 / jp6.2 / jp7.1: 1. Rescue the install-tree config files. After ninja install, the files at ${INSTALL_PATH}/cv2/config*.py have install-tree paths (correct). The pip install of the wheel built from /build/.../python_loader then overwrites them with BUILD-TREE config files (paths like /build/opencv/opencv-X.Y.Z/release/lib/python3) which only exist in the builder stage. In a multi-stage runtime image those paths are dead, so the loader's bootstrap can't find the native cv2 .so, re-imports the stub package, and Python raises: ImportError: recursion is detected during loading of "cv2" Fix by saving config*.py between ninja install and pip wheel/install, restoring after. 2. Pull per-platform variables out of the cmake invocation into ARGs at the top of the builder (OPENCV_CUDA_ARCH, OPENCV_PYTHON_INSTALL_PATH, OPENCV_PYTHON_INCLUDE_DIR, OPENCV_PYTHON_VERSION). Replicating this OpenCV block in jp5.1.1 / jp7.1.0 dockerfiles is now a copy-paste plus four ARG value changes; no surgery inside the cmake invocation. Adds a build-time sanity check that simulates the runtime stage by stripping /build/ from sys.path before importing cv2. If the install- tree rescue ever regresses, the docker build fails here -- not 90 minutes later on the device. * Add BuildKit cache mounts to Jetson builds and split Depot projects Pins syntax to dockerfile:1.7 on both jp62 and jp71 so ARG interpolation in cache-mount ids works. Adds apt cache mounts (sharing=locked, id scoped per JetPack + builder/runtime + arch) to every apt RUN so .debs and lists persist across builds. Disables docker-clean and turns on Keep-Downloaded-Packages so the apt cache is actually used. Drops the trailing rm of /var/lib/apt/lists/* since that path is now the cache mount. Adds per-build-tree cache mounts on the expensive C++ compiles: PyTorch, torchvision, onnxruntime (build/cuda12 on jp62, build/Linux on jp71), OpenCV (release/ on jp62), with the TRT .deb download also cached on jp62. Each id is scoped by version + arch + SM so a version bump starts clean instead of reusing a stale build tree. The OpenCV step on jp62 stays positioned last in the builder because the inference wheel installs upstream are not --no-deps and an earlier opencv-python pull would clobber the from-source build. The build-dir cache mount makes the forced rebuild after COPY-invalidation a fast ninja no-op rather than a 30-60 min recompile. Points jp62 and jp71 workflows at the new dedicated Depot projects (2rp7mfjw7q for jp62, v1xzfwkc4b for jp71) so they no longer share an NVMe cache pool and evict each other. * Split git-clone out of cache-mounted build RUNs The cache mount on build/ auto-creates its parent directory before the RUN body executes, so a combined 'git clone ... && cd ... && make' fails with 'destination path already exists and is not an empty directory'. First jp62 attempt with these cache mounts failed at the PyTorch step: fatal: destination path 'pytorch' already exists and is not an empty directory. Split each of PyTorch / torchvision / onnxruntime on jp62 and jp71 into two RUNs: a clone RUN with no mount, then a build RUN whose cache mount overlays build/ on the cloned source layer. * Bump all GitHub Actions to latest major versions Bumps: - actions/cache v3 -> v5 - actions/checkout v2/v3/v4 -> v6 - actions/create-github-app-token v1 -> v3 - actions/setup-node v3 -> v6 - actions/setup-python v2/v5 -> v6 - actions/upload-artifact v4 -> v7 - aws-actions/amazon-ecr-login v1 -> v2 - aws-actions/configure-aws-credentials v2 -> v6 - dcarbone/install-jq-action v2.1.0 -> v3.2.0 - digicert/ssm-code-signing v1.0.0 -> v1.2.1 - docker/login-action v2/v3 -> v4 - docker/setup-buildx-action v2 -> v4 - docker/setup-qemu-action v2 -> v4 - google-github-actions/auth v2 -> v3 - google-github-actions/setup-gcloud v2 -> v3 depot/* already pinned to floating v1 (latest major). actions/upload-release-asset and pypa/gh-action-pypi-publish kept on their existing pins (v1 already latest; pypi-publish uses the release/v1 stable-channel ref recommended by upstream). * Bump jp71 flash-attn MAX_JOBS from 1 to 2 PR #2068 reduced this to 1 because MAX_JOBS=4 was starving the Depot BuildKit gRPC connection during flash-attn CUDA kernel compilation. 2 is a middle ground: flash-attn's nvcc instances peak ~3-4 GB each against PyTorch 2.10 headers, so 2 concurrent fits comfortably on a 32 GB arm builder while halving the wall time vs serial. With the cache mounts from c5701f2 in place, the cost of a regression here is ~30-60 min of flash-attn rebuild, not the full 4hr build. jp62 stays at MAX_JOBS=4 because it's on PyTorch 2.6 (lighter headers) and that build is already known-good. --------- Co-authored-by: Paweł Pęczek <146137186+PawelPeczek-Roboflow@users.noreply.github.com>
1 parent 16ba9a0 commit 0010e4e

62 files changed

Lines changed: 398 additions & 230 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-osx-bundle.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ jobs:
2020
NOTARY_PROFILE_NAME: "roboflow-notary"
2121
steps:
2222
- name: Checkout repository
23-
uses: actions/checkout@v4
23+
uses: actions/checkout@v6
2424
# No 'path' means checkout to the root
2525

2626
- name: Set up Python
27-
uses: actions/setup-python@v5
27+
uses: actions/setup-python@v6
2828
with:
2929
python-version: '3.12'
3030

@@ -180,7 +180,7 @@ jobs:
180180
run: python build.py
181181

182182
- name: Upload macOS DMG Artifact
183-
uses: actions/upload-artifact@v4
183+
uses: actions/upload-artifact@v7
184184
with:
185185
name: Roboflow-Inference-DMG-${{ steps.determine_version.outputs.BUILD_VERSION_ENV || 'dev' }} # Add version to artifact name
186186
path: app_bundles/osx/Roboflow-Inference-${{ steps.determine_version.outputs.BUILD_VERSION_ENV || 'unknown' }}.dmg

.github/workflows/build-windows-installer-cpu.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ jobs:
1616
contents: write
1717
steps:
1818
- name: Checkout repository
19-
uses: actions/checkout@v4
19+
uses: actions/checkout@v6
2020
# No 'path' means checkout to the root
2121

2222
- name: Set up Python
23-
uses: actions/setup-python@v5
23+
uses: actions/setup-python@v6
2424
with:
2525
python-version: '3.12'
2626

@@ -79,7 +79,7 @@ jobs:
7979
8080
- name: Install DigiCert Client tools
8181
id: install_digicert_tools # Renamed for clarity
82-
uses: digicert/ssm-code-signing@v1.0.0
82+
uses: digicert/ssm-code-signing@v1.2.1
8383

8484

8585
- name: Set DigiCert certificate
@@ -328,7 +328,7 @@ jobs:
328328
echo "Installer signed successfully (based on smctl exit code and absence of 'FAILED' in CLI output). Review logs carefully."
329329
330330
- name: Upload Windows Installer
331-
uses: actions/upload-artifact@v4
331+
uses: actions/upload-artifact@v7
332332
with:
333333
name: Roboflow-Inference-Windows-Installer
334334
# The build.py script creates the installer in installer/inference-{version}-installer.exe

.github/workflows/build-windows-installer-gpu.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ jobs:
1616
contents: write
1717
steps:
1818
- name: Checkout repository
19-
uses: actions/checkout@v4
19+
uses: actions/checkout@v6
2020
# No 'path' means checkout to the root
2121

2222
- name: Set up Python
23-
uses: actions/setup-python@v5
23+
uses: actions/setup-python@v6
2424
with:
2525
python-version: '3.12'
2626

@@ -132,7 +132,7 @@ jobs:
132132
133133
- name: Install DigiCert Client tools
134134
id: install_digicert_tools # Renamed for clarity
135-
uses: digicert/ssm-code-signing@v1.0.0
135+
uses: digicert/ssm-code-signing@v1.2.1
136136

137137

138138
- name: Set DigiCert certificate
@@ -381,7 +381,7 @@ jobs:
381381
echo "Installer signed successfully (based on smctl exit code and absence of 'FAILED' in CLI output). Review logs carefully."
382382
383383
- name: Upload Windows Installer
384-
uses: actions/upload-artifact@v4
384+
uses: actions/upload-artifact@v7
385385
with:
386386
name: Roboflow-Inference-Windows-Installer
387387
# The build.py script creates the installer in installer/inference-{version}-installer.exe

.github/workflows/check_model_licenses.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
steps:
1515
- name: "Checkout repository"
16-
uses: actions/checkout@v3
16+
uses: actions/checkout@v6
1717

1818
- name: "Validate model directories"
1919
run: |

.github/workflows/codeflash.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ jobs:
2020
CODEFLASH_PR_NUMBER: ${{ github.event.number }}
2121
steps:
2222
- name: 🛎️ Checkout
23-
uses: actions/checkout@v4
23+
uses: actions/checkout@v6
2424
with:
2525
fetch-depth: 0
2626
- name: 📦 Cache Python packages
27-
uses: actions/cache@v3
27+
uses: actions/cache@v5
2828
with:
2929
path: ~/.cache/pip
3030
key: ${{ runner.os }}-pip-3.12-${{ hashFiles('requirements/**') }}
3131
restore-keys: |
3232
${{ runner.os }}-pip-3.12-
3333
- name: 🐍 Set up Python ${{ matrix.python-version }}
34-
uses: actions/setup-python@v5
34+
uses: actions/setup-python@v6
3535
with:
3636
python-version: "3.12"
3737
check-latest: true

.github/workflows/docker.cpu.parallel.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ jobs:
2626
contents: read
2727
steps:
2828
- name: Login to Docker Hub
29-
uses: docker/login-action@v3
29+
uses: docker/login-action@v4
3030
with:
3131
username: ${{ secrets.DOCKERHUB_USERNAME }}
3232
password: ${{ secrets.DOCKERHUB_TOKEN }}
3333
- name: 🛎️ Checkout
34-
uses: actions/checkout@v4
34+
uses: actions/checkout@v6
3535
- name: Read version from file
3636
run: echo "VERSION=$(DISABLE_VERSION_CHECK=true python ./inference/core/version.py)" >> $GITHUB_ENV
3737
- name: Set up Depot CLI

.github/workflows/docker.cpu.slim.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ jobs:
2727
packages: write
2828
steps:
2929
- name: Login to Docker Hub
30-
uses: docker/login-action@v3
30+
uses: docker/login-action@v4
3131
with:
3232
username: ${{ secrets.DOCKERHUB_USERNAME }}
3333
password: ${{ secrets.DOCKERHUB_TOKEN }}
3434
- name: 🛎️ Checkout
35-
uses: actions/checkout@v4
35+
uses: actions/checkout@v6
3636
- name: Read version from file
3737
run: echo "VERSION=$(DISABLE_VERSION_CHECK=true python ./inference/core/version.py)" >> $GITHUB_ENV
3838
- name: Set up Depot CLI

.github/workflows/docker.cpu.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
contents: read
3333
steps:
3434
- name: Checkout
35-
uses: actions/checkout@v4
35+
uses: actions/checkout@v6
3636
- name: Read version from file
3737
run: echo "VERSION=$(DISABLE_VERSION_CHECK=true python ./inference/core/version.py)" >> $GITHUB_ENV
3838
- name: Determine Image Tags
@@ -52,14 +52,14 @@ jobs:
5252
username: ${{ secrets.DOCKERHUB_USERNAME }}
5353
password: ${{ secrets.DOCKERHUB_TOKEN }}
5454
- name: Authenticate gcloud
55-
uses: google-github-actions/auth@v2
55+
uses: 'google-github-actions/auth@v3'
5656
id: auth
5757
with:
5858
workload_identity_provider: "projects/878913763597/locations/global/workloadIdentityPools/github-actions/providers/github"
5959
service_account: "gha-inference@roboflow-staging.iam.gserviceaccount.com"
6060
token_format: access_token
6161
- name: Login to Google Artifact Registry
62-
uses: docker/login-action@v3
62+
uses: docker/login-action@v4
6363
with:
6464
registry: us-docker.pkg.dev
6565
username: oauth2accesstoken

.github/workflows/docker.gpu.parallel.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ jobs:
2626
contents: read
2727
steps:
2828
- name: Login to Docker Hub
29-
uses: docker/login-action@v3
29+
uses: docker/login-action@v4
3030
with:
3131
username: ${{ secrets.DOCKERHUB_USERNAME }}
3232
password: ${{ secrets.DOCKERHUB_TOKEN }}
3333
- name: 🛎️ Checkout
34-
uses: actions/checkout@v4
34+
uses: actions/checkout@v6
3535
- name: Read version from file
3636
run: echo "VERSION=$(DISABLE_VERSION_CHECK=true python ./inference/core/version.py)" >> $GITHUB_ENV
3737
- name: Set up Depot CLI

.github/workflows/docker.gpu.slim.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ jobs:
2626
contents: read
2727
steps:
2828
- name: Login to Docker Hub
29-
uses: docker/login-action@v3
29+
uses: docker/login-action@v4
3030
with:
3131
username: ${{ secrets.DOCKERHUB_USERNAME }}
3232
password: ${{ secrets.DOCKERHUB_TOKEN }}
3333
- name: 🛎️ Checkout
34-
uses: actions/checkout@v4
34+
uses: actions/checkout@v6
3535
- name: Read version from file
3636
run: echo "VERSION=$(DISABLE_VERSION_CHECK=true python ./inference/core/version.py)" >> $GITHUB_ENV
3737
- name: Set up Depot CLI

0 commit comments

Comments
 (0)