Windows CUDA x64 #89
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # GitHub Actions Workflow: Build opencv-python with CUDA support on Windows | |
| # | |
| # This workflow compiles opencv-python from source with CUDA enabled on a | |
| # GitHub-hosted Windows runner. The resulting Python wheel is then uploaded | |
| # as a build artifact. | |
| # | |
| # This is a complex and long-running process. It is configured to run only | |
| # on manual trigger (workflow_dispatch). | |
| name: Windows CUDA x64 | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| # TODO: Add cuda_version input to allow running only CUDA 12 or 13 builds for debugging | |
| # TODO: Right now we don't correctly determine when things have changed and we require a | |
| # rebuild, so for now we just manually trigger rebuilds between runs. | |
| restore_build_cache: | |
| description: "Restore build cache. Uncheck to force re-build." | |
| required: false | |
| type: boolean | |
| default: true | |
| save_build_cache: | |
| description: "Save build cache." | |
| required: false | |
| type: boolean | |
| default: true | |
| rolling_build: | |
| description: "Use latest commit from upstream OpenCV repo. Cache settings will be ignored." | |
| required: false | |
| type: boolean | |
| default: false | |
| jobs: | |
| Build: | |
| runs-on: ${{ matrix.runs-on }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ['3.13'] | |
| platform: [x64] | |
| cuda-version: ['12'] | |
| include: | |
| # CUDA 12: Supports Maxwell (5.0) through Blackwell (10.0) | |
| - cuda-version: '12' | |
| runs-on: 'windows-2025' | |
| cuda-installer: 'cuda_12.9.1_windows_network.exe' | |
| cuda-path-version: 'v12.9' | |
| cudnn-archive: 'cudnn-windows-x86_64-9.18.1.3_cuda12-archive.zip' | |
| cudnn-folder: 'cudnn-windows-x86_64-9.18.1.3_cuda12-archive' | |
| video-codec-sdk-archive: 'Video_Codec_SDK_13.0.37.zip' | |
| video-codec-sdk-folder: 'Video_Codec_SDK_13.0.37' | |
| cuda-arch-bin: '5.0;5.2;6.0;6.1;7.0;7.5;8.0;8.6;8.9;9.0;10.0' | |
| cuda-arch-ptx: '10.0' | |
| cache-key: 'nvidia-deps-cuda-12.9.1-cudnn-9.18.1.3' | |
| # TODO: Re-enable CUDA 13 after updating to OpenCV 4.13+ | |
| # CUDA 13: Supports Turing (7.5) through Blackwell (12.0) | |
| # - cuda-version: '13' | |
| # runs-on: 'windows-2025' | |
| # cuda-installer: 'cuda_13.1.1_windows_network.exe' | |
| # cuda-path-version: 'v13.1' | |
| # cudnn-archive: 'cudnn-windows-x86_64-9.18.1.3_cuda13-archive.zip' | |
| # cudnn-folder: 'cudnn-windows-x86_64-9.18.1.3_cuda13-archive' | |
| # video-codec-sdk-archive: 'Video_Codec_SDK_13.0.37.zip' | |
| # video-codec-sdk-folder: 'Video_Codec_SDK_13.0.37' | |
| # cuda-arch-bin: '7.5;8.0;8.6;8.9;9.0;10.0;12.0' | |
| # cuda-arch-ptx: '12.0' | |
| # cache-key: 'nvidia-deps-cuda-13.1.1-cudnn-9.18.1.3' | |
| env: | |
| ACTIONS_ALLOW_UNSECURE_COMMANDS: true | |
| SDIST: 0 | |
| ENABLE_HEADLESS: 0 | |
| ENABLE_CONTRIB: 1 | |
| ENABLE_ROLLING: ${{ inputs.rolling_build && 1 || 0 }} | |
| OPENCV_TEST_DATA_PATH: ${{ github.workspace }}\opencv_extra\testdata | |
| CUDA_ARCH_BIN: ${{ matrix.cuda-arch-bin }} | |
| CUDA_ARCH_PTX: ${{ matrix.cuda-arch-ptx }} | |
| steps: | |
| - name: Cleanup | |
| shell: bash | |
| run: | | |
| rm -rf ./* || true | |
| rm -rf ./.??* || true | |
| working-directory: ${{ github.workspace }} | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| fetch-depth: 0 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| architecture: ${{ matrix.platform }} | |
| - name: Setup MSBuild.exe | |
| uses: microsoft/[email protected] | |
| - name: Setup NASM | |
| uses: ilammy/setup-nasm@v1 | |
| - name: Cache NVIDIA dependencies | |
| id: cache-nvidia-deps | |
| uses: actions/cache@v3 | |
| with: | |
| path: .deps/Nvidia | |
| key: ${{ matrix.cache-key }} | |
| - name: Clone NVIDIA dependencies | |
| if: steps.cache-nvidia-deps.outputs.cache-hit != 'true' | |
| env: | |
| SSH_PRIVATE_KEY: ${{ secrets.DEPS_REPO_SSH_KEY }} | |
| run: | | |
| eval "$(ssh-agent -s)" | |
| ssh-add - <<< "${SSH_PRIVATE_KEY}" | |
| ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null | |
| git clone [email protected]:Breakthrough/opencv-python-cuda-deps.git .deps | |
| shell: bash | |
| - name: 🔧 Install NVIDIA CUDA Toolkit | |
| run: | | |
| $installer_path = ".deps/Nvidia/${{ matrix.cuda-installer }}" | |
| if (-not (Test-Path $installer_path)) { | |
| throw "CUDA Toolkit installer not found at $installer_path" | |
| } | |
| echo "Installing CUDA ${{ matrix.cuda-version }} Toolkit silently..." | |
| Start-Process -FilePath $installer_path -ArgumentList "-s" -Wait -NoNewWindow | |
| echo "Adding CUDA to PATH..." | |
| $CUDA_PATH = "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\${{ matrix.cuda-path-version }}" | |
| echo "CUDA_PATH=$CUDA_PATH" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| Copy-Item -Path "$CUDA_PATH\bin\*.dll" -Destination . | |
| shell: pwsh | |
| - name: 🔧 Install NVIDIA CuDNN | |
| run: | | |
| $cudnn_path = ".deps/Nvidia/${{ matrix.cudnn-archive }}" | |
| if (-not (Test-Path $cudnn_path)) { | |
| throw "CuDNN archive not found at $cudnn_path" | |
| } | |
| echo "Installing CuDNN for CUDA ${{ matrix.cuda-version }}..." | |
| 7z x $cudnn_path | |
| $CUDNN_PATH = "D:/a/opencv-python-cuda/opencv-python-cuda/${{ matrix.cudnn-folder }}" | |
| echo "CUDNN_LIBRARY=$CUDNN_PATH/lib/x64/cudnn.lib" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| echo "CUDNN_INCLUDE_DIR=$CUDNN_PATH/include" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| Copy-Item -Path "$CUDNN_PATH/bin/x64/*" -Destination . -Include "*.dll" | |
| shell: pwsh | |
| - name: 🔧 Install NVIDIA Video Codec SDK | |
| run: | | |
| $sdk_path = ".deps/Nvidia/${{ matrix.video-codec-sdk-archive }}" | |
| if (-not (Test-Path $sdk_path)) { | |
| throw "Video Codec SDK archive not found at $sdk_path" | |
| } | |
| echo "Installing Video Codec SDK..." | |
| 7z x $sdk_path | |
| $SDK_PATH = "D:/a/opencv-python-cuda/opencv-python-cuda/${{ matrix.video-codec-sdk-folder }}" | |
| echo "CUDA_nvcuvid_LIBRARY=$SDK_PATH/Lib/win/x64/nvcuvid.lib" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| echo "CUDA_nvencodeapi_LIBRARY=$SDK_PATH/Lib/win/x64/nvencodeapi.lib" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| echo "NVCUVID_INCLUDE_DIR=$SDK_PATH/Interface" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| shell: pwsh | |
| - name: Restore build artifacts | |
| uses: actions/cache/restore@v3 | |
| if: ${{ inputs.restore_build_cache && !inputs.rolling_build }} | |
| with: | |
| path: _skbuild | |
| key: ${{ runner.os }}-${{ matrix.python-version }}-cuda${{ matrix.cuda-version }}-${{ hashFiles('**/CMakeLists.txt') }} | |
| restore-keys: | | |
| ${{ runner.os }}-${{ matrix.python-version }}-cuda${{ matrix.cuda-version }}- | |
| - name: Remove unused CUDA DLLs | |
| # Only bundle CUDA DLLs that cv2.pyd actually needs (determined via PE import analysis). | |
| # This removes ~1.3 GB of unused libraries (cusparse, cusolver, curand, nvrtc, etc.) | |
| run: | | |
| $keep = @( | |
| "cublas64_*", "cublasLt64_*", "cudart64_*", "cufft64_*", | |
| "nppc64_*", "nppial64_*", "nppicc64_*", "nppidei64_*", "nppif64_*", | |
| "nppig64_*", "nppim64_*", "nppist64_*", "nppitc64_*" | |
| ) | |
| Get-ChildItem -Path . -Filter "*.dll" | Where-Object { | |
| $name = $_.Name | |
| $is_cuda = $name -match '^\w+64_\d' | |
| $is_kept = $false | |
| foreach ($pattern in $keep) { | |
| if ($name -like $pattern) { $is_kept = $true; break } | |
| } | |
| $is_cuda -and -not $is_kept | |
| } | ForEach-Object { | |
| $size = [math]::Round($_.Length / 1MB) | |
| echo "Removing $($_.Name) (${size} MB)" | |
| Remove-Item $_.FullName | |
| } | |
| shell: pwsh | |
| - name: Build a package | |
| # CMake 3.25 regression fix. See https://stackoverflow.com/questions/74162633/problem-compiling-from-source-opencv-with-mvsc2019-in-64-bit-version | |
| run: | | |
| python --version | |
| python -m pip install --upgrade pip | |
| python -m pip install --upgrade setuptools | |
| python -m pip install cmake==3.24.2 | |
| python -m pip install toml && python -c "import toml; c = toml.load('pyproject.toml'); print('\n'.join(c['build-system']['requires']))" >> requirements.txt | python -m pip install -r requirements.txt | |
| $CI_BUILD = 1 | |
| if (Test-Path _skbuild) { | |
| $SKIP_CMAKE = "--skip-cmake" | |
| } | |
| python setup.py $SKIP_CMAKE bdist_wheel --py-limited-api=cp37 --dist-dir="$PWD\wheelhouse" -v | |
| shell: pwsh | |
| - name: Repack wheel with LZMA compression | |
| run: | | |
| for whl in wheelhouse/opencv*.whl; do | |
| src_size=$(stat -c%s "$whl") | |
| echo "Repacking $(basename "$whl") ($(numfmt --to=iec $src_size))..." | |
| tmpdir=$(mktemp -d) | |
| 7z x -o"$tmpdir" "$whl" -y > /dev/null | |
| rm "$whl" | |
| (cd "$tmpdir" && 7z a -tzip -mm=LZMA -mx=5 "$OLDPWD/$whl" . > /dev/null) | |
| rm -rf "$tmpdir" | |
| dst_size=$(stat -c%s "$whl") | |
| saved=$((src_size - dst_size)) | |
| echo "Done: $(numfmt --to=iec $dst_size) (saved $(numfmt --to=iec $saved))" | |
| done | |
| shell: bash | |
| - name: Save build artifacts to cache | |
| uses: actions/cache/save@v3 | |
| if: ${{ inputs.save_build_cache && !inputs.rolling_build }} | |
| with: | |
| path: _skbuild | |
| key: ${{ runner.os }}-${{ matrix.python-version }}-cuda${{ matrix.cuda-version }}-${{ hashFiles('**/CMakeLists.txt') }} | |
| - name: Saving all wheels | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheel-cuda${{ matrix.cuda-version }}-${{ matrix.platform }} | |
| path: wheelhouse/opencv* | |
| Test: | |
| needs: [Build] | |
| runs-on: windows-2025 | |
| defaults: | |
| run: | |
| shell: cmd | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ['3.12'] | |
| platform: [x64] | |
| cuda-version: ['12'] # TODO: Re-enable '13' after updating to OpenCV 4.13+ | |
| env: | |
| ACTIONS_ALLOW_UNSECURE_COMMANDS: true | |
| OPENCV_TEST_DATA_PATH: ${{ github.workspace }}\opencv_extra\testdata | |
| PlatformToolset: v143 | |
| steps: | |
| - name: Cleanup | |
| shell: bash | |
| run: | | |
| rm -rf ./* || true | |
| rm -rf ./.??* || true | |
| working-directory: ${{ github.workspace }} | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| fetch-depth: 0 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| architecture: ${{ matrix.platform }} | |
| - name: Download a wheel accordingly to matrix | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wheel-cuda${{ matrix.cuda-version }}-${{ matrix.platform }} | |
| path: wheelhouse/ | |
| - name: Package installation | |
| run: | | |
| cd ${{ github.workspace }}/tests | |
| &python -m pip install --user --no-warn-script-location (ls "../wheelhouse/opencv*.whl") | |
| if ($LastExitCode -ne 0) {throw $LastExitCode} | |
| python get_build_info.py | |
| shell: powershell | |
| - name: Run tests | |
| run: | | |
| cd ${{ github.workspace }}/tests | |
| python test.py |