No java so (shared object) with docker build of custom modules

Attempting to add the stitcher module from contrib to opencv build for android within a docker container. Everything builds except the java#.so file. I’ve attempted to force it thru env variables (genai suggestions) to force it, but the approach feels like a hack. Including the docker file below. Any advice is appreciated from humans. Thanks in advance!

# Use a lean base image suitable for compilation
FROM ubuntu:24.04

# Define build arguments for version control
ARG OPENCV_VERSION="4.12.0"
ARG NDK_VERSION="r29"

# Define SHA256 checksums
ARG OPENCV_SHA256="FA3FAF7581F1FA943C9E670CF57DD6BA1C5B4178F363A188A2C8BFF1EB28B7E4"
ARG CONTRIB_SHA256="52BEF961072E464604F162010E71DF769AA42794C95CE064A183056C4D5868E3"
ARG NDK_SHA256="4ABBBCDC842F3D4879206E9695D52709603E52DD68D3C1FFF04B3B5E7A308ECF"

# Set non-interactive mode for installation
ENV DEBIAN_FRONTEND=noninteractive

# Install essential dependencies: build tools, CMake, and Java
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    wget \
    unzip \
    git \
    cmake \
    ninja-build \
    build-essential \
    python3 \
    python3-pip \
    openjdk-17-jdk \
    coreutils \
    && rm -rf /var/lib/apt/lists/*

# Set environment variables for the build
ENV OPENCV_HOME=/opencv
ENV NDK_HOME=/opt/android-ndk
ENV ANDROID_NATIVE_API_LEVEL=24
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV PATH=$JAVA_HOME/bin:$PATH

# Create directories
RUN mkdir -p $OPENCV_HOME/src $OPENCV_HOME/contrib $OPENCV_HOME/build 

WORKDIR /tmp

# Download and extract OpenCV and Contrib sources
RUN wget -q https://github.com/opencv/opencv/archive/$OPENCV_VERSION.zip -O opencv.zip && \
    echo "$OPENCV_SHA256 opencv.zip" | sha256sum -c - && \
    unzip -q opencv.zip -d $OPENCV_HOME/src && \
    mv $OPENCV_HOME/src/opencv-$OPENCV_VERSION $OPENCV_HOME/src/opencv_main && \
    rm opencv.zip

RUN wget -q https://github.com/opencv/opencv_contrib/archive/$OPENCV_VERSION.zip -O contrib.zip && \
    echo "$CONTRIB_SHA256 contrib.zip" | sha256sum -c - && \
    unzip -q contrib.zip -d $OPENCV_HOME/contrib && \
    mv $OPENCV_HOME/contrib/opencv_contrib-$OPENCV_VERSION $OPENCV_HOME/contrib/opencv_contrib_main && \
    rm contrib.zip

# Download and extract Android NDK
RUN wget -q https://dl.google.com/android/repository/android-ndk-$NDK_VERSION-linux.zip -O ndk.zip && \
    echo "$NDK_SHA256 ndk.zip" | sha256sum -c - && \
    unzip -q ndk.zip -d /opt && \
    mv /opt/android-ndk-$NDK_VERSION $NDK_HOME && \
    rm ndk.zip

WORKDIR $OPENCV_HOME/build

# CMake command to configure a minimal Android build targeting arm64-v8a.
# BUILD_LIST explicitly includes 'stitching' and its essential dependencies.
RUN cmake \
    -GNinja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=$NDK_HOME/build/cmake/android.toolchain.cmake \
# --- PATH RELATED
    -DOPENCV_EXTRA_MODULES_PATH=$OPENCV_HOME/contrib/opencv_contrib_main/modules \
    -DBUILD_LIST="core,imgproc,imgcodecs,features2d,calib3d,flann,stitching,java" \
# ---ANDROID RELATED---
    -DANDROID_ABI="arm64-v8a" \
    -DANDROID_NDK=$NDK_HOME \
    -DANDROID_NATIVE_API_LEVEL=$ANDROID_NATIVE_API_LEVEL \
    -DANDROID_SUPPRESS_SDK_BUILD=ON \
    -DANDROID_SDK_HOME=OFF \
    -DANDROID_SDK_BUILD_TOOLS_VERSION=OFF \
    -DBUILD_JAVA_PROJECT=OFF \
    -DBUILD_ANDROID_EXAMPLES=OFF \
    -DINSTALL_ANDROID_EXAMPLES=OFF \
    -DBUILD_ANDROID_PROJECTS=OFF \
    -DBUILD_ANDROID_SERVICE=OFF \
# ---JAVA RELATED---
    -DBUILD_JAVA=ON \
    -DBUILD_FAT_JAVA_LIB=OFF \
    -DJAVA_HOME=$JAVA_HOME \ 
    # FORCE BUILD OF JAVA SO - HACK
    -DJava_FOUND=TRUE \
    -DJava_AWT_FOUND=TRUE \
    -DJava_INCLUDE_PATH=$JAVA_HOME/include \
    -DJava_AWT_INCLUDE_PATH=$JAVA_HOME/include \
    -DJava_JNI_INCLUDE_PATH=$JAVA_HOME/include \
    -DJava_JNI_INCLUDE_PATH2=$JAVA_HOME/include/linux \
    -DJava_JNI_INCLUDE_PATH_HOST=$JAVA_HOME/include/x64 \
    -DBUILD_opencv_java=ON \
# OTHER OPTIONS
    -DCPACK_GENERATOR=None \
    -DBUILD_TESTS=OFF \
    -DBUILD_PERF_TESTS=OFF \
    -DBUILD_DOCS=OFF \
    -DBUILD_DATA_FILES=OFF \
    -DINSTALL_CREATE_DISTRIB=OFF \
    -DBUILD_SDK=OFF \
    -DBUILD_SHARED_LIBS=ON \
    -DWITH_TBB=OFF \
    -DWITH_CUDA=OFF \
    -DWITH_IPP=OFF \
    $OPENCV_HOME/src/opencv_main

# Compile and install the custom SDK
RUN ninja -j$(nproc)
RUN ninja install

# Set the entry point to keep the container running if needed, or just exit cleanly
CMD ["echo", "Custom OpenCV build complete. Output is in /opencv/build/install/sdk"]

The Errors are:

43.36 CMake Error: File /opencv/src/opencv_main/modules/java/android_sdk/build.gradle.in does not exist.
43.36 CMake Error at modules/java/android_sdk/CMakeLists.txt:179 (configure_file):
43.36   configure_file Problem configuring file
43.36
43.36
43.36 CMake Error: File /opencv/src/opencv_main/modules/java/android_sdk/build.gradle.in does not exist.
43.36 CMake Error at modules/java/android_sdk/CMakeLists.txt:206 (configure_file):
43.36   configure_file Problem configuring file
43.36
43.36
43.37 CMake Error at CMakeLists.txt:1099 (add_subdirectory):
43.37   add_subdirectory given source "doc" which is not an existing directory.
43.37
43.37
43.37 CMake Error at CMakeLists.txt:1102 (add_subdirectory):
43.37   add_subdirectory given source "data" which is not an existing directory.
43.37
43.37
43.37 CMake Error at CMakeLists.txt:1121 (include):
43.37   include could not find requested file:
43.37
43.37     cmake/OpenCVGenHeaders.cmake
43.37
43.37
43.37 CMake Error at CMakeLists.txt:1129 (include):
43.37   include could not find requested file:
43.37
43.37     cmake/OpenCVGenAndroidMK.cmake
43.37
43.37
43.37 CMake Error at CMakeLists.txt:1132 (include):
43.37   include could not find requested file:
43.37
43.37     cmake/OpenCVGenConfig.cmake
43.37
43.37
43.37 CMake Error at CMakeLists.txt:1140 (include):
43.37   include could not find requested file:
43.37
43.37     cmake/OpenCVGenABI.cmake
43.37

The cmake directory contents do exist. The android sdk does not. Only the NDK is present.

A few things I’ve learned about Stitcher:

  • It is in the main OpenCV repo not contrib
  • It is not included in the standard android build
  • The java wrappers are not built for stitcher at this time.
  • Implementing stitcher-like functionality with OpenCV core is potentially too slow to be useful with limited resources on Android.

The java so never built as desired with the hacks in my previous example. I think the android sdk may be necessary, and I found the build-sdk.py script in the main opencv repo was easy to use once that was in place. Was successful (genai assisted) with this dockerfile:

FROM ubuntu:24.04

# Define build arguments for version control
ARG OPENCV_VERSION="4.12.0"
ARG NDK_VERSION="29.0.14206865"
ARG ANDROID_VERSION="35"

# Define SHA256 checksums
ARG CMDTOOLS_SHA256="7ec965280a073311c339e571cd5de778b9975026cfcbe79f2b1cdcb1e15317ee"

# Set non-interactive mode for installation
ENV DEBIAN_FRONTEND=noninteractive

# Install essential dependencies: build tools, CMake, and Java
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    wget \
    unzip \
    git \
    cmake \
    ninja-build \
    build-essential \
    python3 \
    python3-pip \
    openjdk-17-jdk \
    coreutils \
    && rm -rf /var/lib/apt/lists/*

# Set environment variables for the build
ENV OPENCV_BUILD_HOME=/opt/opencv_build
ENV ANDROID_SDK_ROOT=/opt/android-sdk
ENV ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDK_VERSION
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV PATH="$JAVA_HOME/bin:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools:$PATH"

# Create directories
RUN mkdir -p $OPENCV_BUILD_HOME $ANDROID_SDK_ROOT/cmdline-tools

WORKDIR /tmp

# Download and extract Android Command Line Tools
RUN wget -q https://dl.google.com/android/repository/commandlinetools-linux-13114758_latest.zip -O android-clt.zip && \
    echo "$CMDTOOLS_SHA256 android-clt.zip" | sha256sum -c - && \
    unzip -q android-clt.zip -d $ANDROID_SDK_ROOT/cmdline-tools && \
    mv $ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools $ANDROID_SDK_ROOT/cmdline-tools/latest && \
    rm android-clt.zip


# Accept licenses and install specific SDK and NDK version
RUN yes | sdkmanager --licenses \
    && sdkmanager "platforms;android-$ANDROID_VERSION" "ndk;$NDK_VERSION" "build-tools;$ANDROID_VERSION.0.0"

WORKDIR /opt/
RUN git clone https://github.com/opencv/opencv.git --depth 1 -b $OPENCV_VERSION \
    && git clone https://github.com/opencv/opencv_contrib.git --depth 1 -b $OPENCV_VERSION

WORKDIR /opt/opencv/platforms/android


COPY custom-config.py.txt /opt/opencv/platforms/android/custom-config.py

# We use --contrib_path to point to the modules directory in the cloned repo.
# We explicitly list all standard modules + 'stitching' using --enable_modules
# to guarantee the desired functionality is included.
RUN python3 build_sdk.py \
    --sdk_path $ANDROID_SDK_ROOT \
    --ndk_path $ANDROID_NDK_ROOT \
    --config "/opt/opencv/platforms/android/custom-config.py" \
#    --extra_modules_path "/opt/opencv_contrib/modules" \
    --modules_list "core,imgproc,imgcodecs,videoio,features2d,calib3d,flann,stitching,java" \
    --no_samples_build \
    $OPENCV_BUILD_HOME \
    /opt/opencv

# Set the entry point to keep the container running if needed, or just exit cleanly
CMD ["echo", "Custom OpenCV build complete. Output is in /opt/opencv_build"]

There was a custom config for the build copied from the local file system. I am not certain about the optimal contents. Found these in a github issue:

ANDROID_NATIVE_API_LEVEL = int(os.environ.get('ANDROID_NATIVE_API_LEVEL', 29))
cmake_common_vars = {
'ANDROID_COMPILE_SDK_VERSION': os.environ.get('ANDROID_COMPILE_SDK_VERSION', 35),
'ANDROID_TARGET_SDK_VERSION': os.environ.get('ANDROID_TARGET_SDK_VERSION', 35),
'ANDROID_MIN_SDK_VERSION': os.environ.get('ANDROID_MIN_SDK_VERSION', ANDROID_NATIVE_API_LEVEL),

'ANDROID_GRADLE_PLUGIN_VERSION': '8.1.2',
'ANDROID_STL': 'c++_shared',
'GRADLE_VERSION': '8.7',
'KOTLIN_PLUGIN_VERSION': '1.9.0',
'ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES':'ON',

}
ABIs = [
ABI("2", "armeabi-v7a", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars),
ABI("3", "arm64-v8a", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars),
ABI("5", "x86_64", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars),
ABI("4", "x86", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars),
]

The commands for the build are as follows:

docker build -t opencv-stitcher-android .

# For debugging
docker run -it --rm opencv-stitcher-android /bin/bash

# Create a temporary container from the image
docker create --name opencv_extract opencv-stitcher-android

# Copy the resulting SDK to your local machine (e.g., to a folder named 'custom_sdk')
docker cp opencv_extract:/opt/opencv_build/sdk/OpenCV-android-sdk/sdk/ ./custom_sdk

# Clean up the temporary container
docker rm opencv_extract

Stitching was not included as mentioned earlier, but the java so was present. Seems this would work for contrib as well. Just uncomment that line.