diff --git a/docker/Dockerfile.multi b/docker/Dockerfile.multi index e724eb4cc629..65282c2ae406 100644 --- a/docker/Dockerfile.multi +++ b/docker/Dockerfile.multi @@ -49,7 +49,7 @@ RUN --mount=type=bind,source=docker/common,target=/opt/docker/common \ NCCL_VER=${NCCL_VER} CUBLAS_VER=${CUBLAS_VER} \ TORCH_INSTALL_TYPE=${TORCH_INSTALL_TYPE} \ bash /opt/docker/common/install.sh --base --cmake --ccache --cuda_toolkit \ - --tensorrt --polygraphy --mpi4py --pytorch --opencv + --tensorrt --polygraphy --mpi4py --pytorch # Install constraints after install.sh so cleanup() doesn't delete the file mid-RUN COPY constraints.txt /tmp/constraints.txt diff --git a/docker/common/install.sh b/docker/common/install.sh index 0de962c9afcb..0d12d812a354 100755 --- a/docker/common/install.sh +++ b/docker/common/install.sh @@ -15,7 +15,6 @@ tensorrt=0 polygraphy=0 mpi4py=0 pytorch=0 -opencv=0 while [[ $# -gt 0 ]]; do case $1 in @@ -51,10 +50,6 @@ while [[ $# -gt 0 ]]; do pytorch=1 shift 1 ;; - --opencv) - opencv=1 - shift 1 - ;; --all) base=1 cmake=1 @@ -64,7 +59,6 @@ while [[ $# -gt 0 ]]; do polygraphy=1 mpi4py=1 pytorch=1 - opencv=1 shift 1 ;; *) @@ -122,10 +116,3 @@ if [ $pytorch -eq 1 ]; then echo "Installing PyTorch..." bash $SCRIPT_DIR/install_pytorch.sh $TORCH_INSTALL_TYPE fi - -if [ $opencv -eq 1 ]; then - echo "Installing OpenCV..." - bash -c "pip3 uninstall -y opencv" - rm -rf /usr/local/lib/python3*/dist-packages/cv2/ - bash -c "pip3 install opencv-python-headless --force-reinstall --no-deps --no-cache-dir" -fi diff --git a/docs/source/features/multi-modality.md b/docs/source/features/multi-modality.md index e2be649bf44a..6d6512a38d17 100644 --- a/docs/source/features/multi-modality.md +++ b/docs/source/features/multi-modality.md @@ -26,6 +26,16 @@ Further optimizations are under development and will be updated as they become a Please refer to the latest multimodal [support matrix](../models/supported-models.md#multimodal-feature-support-matrix-pytorch-backend). +## Optional dependencies + +Video input decoding uses OpenCV (`cv2`) and is not installed by default. Install it only for models with video inputs: + +```bash +pip install opencv-python-headless +``` + +Image-only and text-only workflows do not require this package. + ## Examples The following examples demonstrate how to use TensorRT LLM's multimodal support in various scenarios, including quick run examples, serving endpoints, and performance benchmarking. diff --git a/jenkins/L0_Test.groovy b/jenkins/L0_Test.groovy index 301e6f693fdf..0f7a960ee9e3 100644 --- a/jenkins/L0_Test.groovy +++ b/jenkins/L0_Test.groovy @@ -3591,6 +3591,7 @@ def runLLMTestlistOnPlatformImpl(pipeline, platform, testList, config=VANILLA_CO sh "cd ${llmSrc} && sed -i 's#tensorrt~=.*\$#tensorrt#g' requirements.txt && cat requirements.txt" } trtllm_utils.llmExecStepWithRetry(pipeline, script: "cd ${llmSrc} && pip3 install -r requirements-dev.txt") + trtllm_utils.llmExecStepWithRetry(pipeline, script: "pip3 install opencv-python-headless") if (stageName.contains("-Ray-")) { trtllm_utils.llmExecStepWithRetry(pipeline, script: "pip3 install ray[default]==2.55.1") } diff --git a/jenkins/current_image_tags.properties b/jenkins/current_image_tags.properties index b4648ceb7aed..07aa75063b77 100644 --- a/jenkins/current_image_tags.properties +++ b/jenkins/current_image_tags.properties @@ -13,8 +13,8 @@ # images are adopted from PostMerge pipelines, the abbreviated commit hash is used instead. IMAGE_NAME=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm -LLM_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:pytorch-26.04-py3-x86_64-ubuntu24.04-trt10.16.1.11-skip-tritondevel-202607072201-15907 -LLM_SBSA_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:pytorch-26.04-py3-aarch64-ubuntu24.04-trt10.16.1.11-skip-tritondevel-202607072201-15907 -LLM_ROCKYLINUX8_PY310_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:cuda-13.2.1-devel-rocky8-x86_64-rocky8-py310-trt10.16.1.11-skip-tritondevel-202607072201-15907 -LLM_ROCKYLINUX8_PY312_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:cuda-13.2.1-devel-rocky8-x86_64-rocky8-py312-trt10.16.1.11-skip-tritondevel-202607072201-15907 -LLM_SBSA_WHEEL_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:cuda-13.2.1-devel-ubuntu24.04-sbsa-ubuntu24.04-py312-trt10.16.1.11-skip-tritondevel-202607072201-15907 +LLM_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:pytorch-26.04-py3-x86_64-ubuntu24.04-trt10.16.1.11-skip-tritondevel-202607100544-16206 +LLM_SBSA_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:pytorch-26.04-py3-aarch64-ubuntu24.04-trt10.16.1.11-skip-tritondevel-202607100544-16206 +LLM_ROCKYLINUX8_PY310_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:cuda-13.2.1-devel-rocky8-x86_64-rocky8-py310-trt10.16.1.11-skip-tritondevel-202607100544-16206 +LLM_ROCKYLINUX8_PY312_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:cuda-13.2.1-devel-rocky8-x86_64-rocky8-py312-trt10.16.1.11-skip-tritondevel-202607100544-16206 +LLM_SBSA_WHEEL_DOCKER_IMAGE=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm:cuda-13.2.1-devel-ubuntu24.04-sbsa-ubuntu24.04-py312-trt10.16.1.11-skip-tritondevel-202607100544-16206 diff --git a/jenkins/scripts/perf/local/slurm_install.sh b/jenkins/scripts/perf/local/slurm_install.sh index 8c03385edf41..91bb7d664e62 100755 --- a/jenkins/scripts/perf/local/slurm_install.sh +++ b/jenkins/scripts/perf/local/slurm_install.sh @@ -46,6 +46,8 @@ slurm_install_setup() { echo "(Installing TensorRT-LLM and requirements) Install mode: ${INSTALL_MODE:-source}" + retry_command pip install --retries 10 opencv-python-headless + # Support two installation modes: source (default) and wheel if [ "${INSTALL_MODE:-source}" = "wheel" ]; then # Wheel installation mode diff --git a/jenkins/scripts/slurm_install.sh b/jenkins/scripts/slurm_install.sh index d1302beb956a..27c591760f5c 100644 --- a/jenkins/scripts/slurm_install.sh +++ b/jenkins/scripts/slurm_install.sh @@ -29,6 +29,7 @@ slurm_install_setup() { if [[ $pytestCommand == *--run-ray* ]]; then retry_command --timeout 2700 pip3 install --retries 10 "ray[default]==2.55.1" fi + retry_command --timeout 2700 bash -c "pip3 install --retries 10 opencv-python-headless" retry_command --timeout 2700 bash -c "cd $llmSrcNode && pip3 install --retries 10 -r requirements-dev.txt" retry_command --timeout 2700 bash -c "cd $resourcePathNode && pip3 install --retries 10 --force-reinstall --no-deps TensorRT-LLM/tensorrt_llm-*.whl" gpuUuids=$(nvidia-smi -q | grep "GPU UUID" | awk '{print $4}' | tr '\n' ',' || true) diff --git a/requirements.txt b/requirements.txt index d14251c5c423..0b0d123d4ced 100644 --- a/requirements.txt +++ b/requirements.txt @@ -56,7 +56,6 @@ peft>=0.18.1,<0.19.0 patchelf einops flashinfer-python==0.6.14 -opencv-python-headless xgrammar==0.1.32 llguidance==0.7.29 jsonschema diff --git a/tensorrt_llm/inputs/media_io.py b/tensorrt_llm/inputs/media_io.py index 8e93784ab650..9d456b23e4f1 100644 --- a/tensorrt_llm/inputs/media_io.py +++ b/tensorrt_llm/inputs/media_io.py @@ -32,7 +32,6 @@ from urllib.parse import unquote, urljoin, urlparse import aiohttp -import lazy_loader as lazy import numpy as np import requests import soundfile @@ -43,11 +42,6 @@ from tensorrt_llm.inputs.multimodal_data import AudioData, VideoData from tensorrt_llm.logger import logger -# Lazy import: OpenCV is large and only needed when the cv2-backed video -# decode path is exercised. The proxy triggers the actual `import cv2` on -# first attribute access (e.g. `cv2.VideoCapture`). -cv2 = lazy.load("cv2") - def rgba_to_rgb( image: Image.Image, background_color: Union[tuple[int, int, int], list[int]] = (255, 255, 255) @@ -338,6 +332,18 @@ def extract_audio_from_video( return audio, target_sr +def _get_cv2(): + """Import OpenCV on demand for the optional cv2-backed video decode path.""" + try: + import cv2 + except ImportError as exc: + raise ImportError( + "OpenCV (cv2) is required for video decoding but is not installed. " + "Install it with `pip install opencv-python-headless`." + ) from exc + return cv2 + + def _select_cv2_stream_buffered_backend() -> Optional[int]: """Return a VideoCapture backend that can read from a Python `BytesIO`. @@ -351,6 +357,8 @@ def _select_cv2_stream_buffered_backend() -> Optional[int]: open. Built-in backends (the FFMPEG path in the PyPI wheels) are always safe to use. """ + cv2 = _get_cv2() + # The stream-buffered API was introduced in OpenCV 4.13.0. Older builds # don't have `cv2.videoio_registry.getStreamBufferedBackends`, so signal # "no usable backend" and let the caller fall back to the tempfile path. @@ -420,6 +428,8 @@ def _load_video_by_cv2( """ assert format in ("pt", "np", "pil"), "format must be one of 'pt', 'np', 'pil'" + cv2 = _get_cv2() + # Open the source. Two cases: # (a) `video` is a file path / URL str -> hand it straight to cv2. # (b) `video` is mp4 bytes -> feed cv2 from an in-memory BytesIO via