diff --git a/.circleci/config.yml b/.circleci/config.yml index 505c75e76..304f75d0f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -98,7 +98,7 @@ workflows: - deploy: matrix: parameters: - docker-variant: ['', '-cuda', '-cuda-tf2', '-cuda-torch'] + docker-variant: ['', '-cuda', '-cuda-tf2', '-cuda-torch', '-cuda-onnx'] filters: branches: only: master diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 01c635731..52ed3411a 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -54,4 +54,4 @@ jobs: username: ${{ secrets.DOCKERIO_USERNAME }} password: ${{ secrets.DOCKERIO_PASSWORD }} - name: Build the Docker images - run: make docker docker-cuda docker-cuda-tf2 docker-cuda-torch + run: make docker docker-cuda docker-cuda-tf2 docker-cuda-torch docker-cuda-onnx diff --git a/Dockerfile.cuda-onnx b/Dockerfile.cuda-onnx new file mode 100644 index 000000000..7c9a59020 --- /dev/null +++ b/Dockerfile.cuda-onnx @@ -0,0 +1,15 @@ +ARG BASE_IMAGE=docker.io/ocrd/core-cuda +FROM $BASE_IMAGE AS ocrd_core_base + +WORKDIR /build/core + +COPY Makefile . + +RUN make deps-onnx +# Smoke Test +RUN ocrd --version + +WORKDIR /data + +CMD ["/usr/local/bin/ocrd", "--help"] + diff --git a/Makefile b/Makefile index d6762f393..e35f7e020 100644 --- a/Makefile +++ b/Makefile @@ -84,21 +84,32 @@ get-conda: ; endif # Dependencies for CUDA installation via Conda -deps-cuda: PYTHON_PREFIX != $(PYTHON) -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])' deps-cuda: get-conda # Get CUDA toolkit, including compiler and libraries with dev from NVIDIA channels -# Get CUDNN (needed for Torch, TF etc) from conda-forge. -# CUDA runtime libs will be pulled by `pip` for TF and Torch differently anyway, +# CUDA runtime libs will be pulled by `pip` for ONNX, TF and Torch differently anyway, # so do _not_ install them here to avoid wasting space. conda install -c nvidia/label/cuda-12.4.0 cuda-minimal-build \ && conda clean -a && ldconfig +deps-tf2: CUDA_RPATH := '$$ORIGIN:$$ORIGIN/../nvidia/cufft/lib:$$ORIGIN/../nvidia/cudnn/lib:$$ORIGIN/../nvidia/cusolver/lib:$$ORIGIN/../nvidia/cublas/lib:$$ORIGIN/../nvidia/cusparse/lib:$$ORIGIN/../nvidia/cuda_cupti/lib:$$ORIGIN/../nvidia/cuda_runtime/lib:$$ORIGIN/../tensorrt_libs' deps-tf2: $(PIP) install "tensorflow[and-cuda]" -r requirements.txt + # fix missing search paths for CUDA runtime libs + cd $$(dirname `$(PYTHON) -c 'print(__import__("tensorflow").__file__)'`) && \ + patchelf --set-rpath $(CUDA_RPATH) libtensorflow_framework.so.2 && \ + patchelf --set-rpath $(CUDA_RPATH) libtensorflow_cc.so.2 deps-torch: $(PIP) install torch==2.5.1 torchvision==0.20.1 --extra-index-url https://download.pytorch.org/whl/cu124 -r requirements.txt +deps-onnx: CUDA_RPATH := '$$ORIGIN/../../nvidia/cudnn/lib:$$ORIGIN/../../nvidia/cusolver/lib:$$ORIGIN/../../nvidia/cublas/lib:$$ORIGIN/../../nvidia/cufft/lib:$$ORIGIN/../../nvidia/curand/lib:$$ORIGIN/../../nvidia/cusparse/lib:$$ORIGIN/../../nvidia/cuda_cupti/lib:$$ORIGIN/../../nvidia/cuda_runtime/lib:$$ORIGIN/../../tensorrt_libs' +deps-onnx: + $(PIP) install "onnxruntime-gpu[cuda,cudnn]" "tensorrt_cu12<11" -r requirements.txt + # fix missing search paths for CUDA runtime libs + cd $$(dirname `$(PYTHON) -c 'print(__import__("onnxruntime").__file__)'`)/capi && \ + patchelf --set-rpath $(CUDA_RPATH) libonnxruntime_providers_cuda.so && \ + patchelf --set-rpath $(CUDA_RPATH) libonnxruntime_providers_tensorrt.so + # deps-*: always mix core's requirements.txt with additional deps, # so pip does not ignore the older version reqs, # but instead tries to find a mutually compatible set. @@ -106,7 +117,7 @@ deps-torch: # Dependencies for deployment in an ubuntu/debian linux deps-ubuntu: apt-get update - apt-get install -y bzip2 python3 imagemagick libgeos-dev libxml2-dev libxslt-dev libssl-dev + apt-get install -y bzip2 python3 imagemagick libgeos-dev libxml2-dev libxslt-dev libssl-dev patchelf # Dependencies for deployment via Conda deps-conda: get-conda @@ -343,9 +354,15 @@ docker-cuda-torch: DOCKER_FILE = Dockerfile.cuda-torch docker-cuda-torch: docker-cuda +docker-cuda-onnx: DOCKER_BASE_IMAGE = $(DOCKER_BASE_TAG)/core-cuda +docker-cuda-onnx: DOCKER_TAG = $(DOCKER_BASE_TAG:%=%/core-cuda-onnx) +docker-cuda-onnx: DOCKER_FILE = Dockerfile.cuda-onnx + +docker-cuda-onnx: docker-cuda + # if the current ref is a release, then use it as tag instead of :latest -docker docker-cuda docker-cuda-tf2 docker-cuda-torch: GIT_TAG := $(strip $(shell git describe --tags | grep -x "v[0-9]\.[0-9][[0-9]\.[0-9]")) -docker docker-cuda docker-cuda-tf2 docker-cuda-torch: +docker docker-cuda docker-cuda-tf2 docker-cuda-torch docker-cuda-onnx: GIT_TAG := $(strip $(shell git describe --tags | grep -x "v[0-9]\.[0-9][[0-9]\.[0-9]")) +docker docker-cuda docker-cuda-tf2 docker-cuda-torch docker-cuda-onnx: $(DOCKER_BUILD) -f $(DOCKER_FILE) $(DOCKER_TAG:%=-t %) \ $(if $(GIT_TAG),$(DOCKER_TAG:%=-t %:$(GIT_TAG))) \ --target ocrd_core_base \ diff --git a/src/ocrd_utils/logging.py b/src/ocrd_utils/logging.py index f703f7f4c..7363cd745 100644 --- a/src/ocrd_utils/logging.py +++ b/src/ocrd_utils/logging.py @@ -83,10 +83,15 @@ def tf_disable_interactive_logs(): from os import environ # pylint: disable=import-outside-toplevel # This env variable must be set before importing from Keras environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + environ['GLOG_minloglevel'] = '3' + environ['GRPC_VERBOSITY'] = 'ERROR' + environ['KMP_AFFINITY'] = 'noverbose' from tensorflow.keras.utils import disable_interactive_logging # pylint: disable=import-outside-toplevel # Enabled interactive logging throws an exception # due to a call of sys.stdout.flush() disable_interactive_logging() + logging.getLogger('tensorflow').setLevel(LOGGING_DEFAULTS['tensorflow']) + logging.getLogger('absl').setLevel(LOGGING_DEFAULTS['tensorflow']) except ImportError: # Nothing should be handled here if TF is not available pass diff --git a/src/ocrd_utils/os.py b/src/ocrd_utils/os.py index 58dadc22d..caaf9466c 100644 --- a/src/ocrd_utils/os.py +++ b/src/ocrd_utils/os.py @@ -83,7 +83,7 @@ def unzip_file_to_dir(path_to_zip : Union[str, PathLike], output_directory : str @lru_cache() def is_git_url(url: str) -> bool: try: - run(['git', 'ls-remote', '--exit-code', '-q', '-h', url], check=True) + run(['git', 'ls-remote', '--exit-code', '-q', '-h', url], check=True, capture_output=True) except CalledProcessError: return False return True