├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── pom.xml ├── src ├── main │ ├── java │ │ └── nu │ │ │ └── pattern │ │ │ ├── OpenCV.java │ │ │ └── PrintVersion.java │ └── resources │ │ └── nu │ │ └── pattern │ │ └── opencv │ │ ├── linux │ │ ├── ARMv7 │ │ │ └── README.md │ │ ├── ARMv8 │ │ │ └── README.md │ │ ├── x86_32 │ │ │ └── README.md │ │ └── x86_64 │ │ │ └── README.md │ │ ├── osx │ │ ├── ARMv8 │ │ │ └── README.md │ │ └── x86_64 │ │ │ └── README.md │ │ └── windows │ │ ├── x86_32 │ │ └── README.md │ │ └── x86_64 │ │ └── README.md └── test │ ├── java │ └── nu │ │ └── pattern │ │ ├── LibraryLoadingTest.java │ │ ├── LoadLibraryRunListener.java │ │ └── MserTest.java │ └── resources │ └── logging.properties └── upstream └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: vonnieda 4 | ko_fi: vonnieda 5 | custom: "https://www.buymeacoffee.com/vonnieda" 6 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/marketplace/actions/run-on-architecture 2 | # https://github.com/openpnp/opencv/pull/56 3 | # https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix 4 | # https://github.com/openpnp/opencv/actions 5 | # https://github.com/openpnp/opencv/pull/59/files 6 | # https://stackoverflow.com/questions/57498605/github-actions-share-workspace-artifacts-between-jobs?rq=1 7 | # https://github.com/actions/upload-artifact 8 | # https://github.com/actions/download-artifact 9 | 10 | # TODO 11 | # - maybe consider doing an arm64 test with run on? 12 | # - see if I can remove more hardcoded paths and such using the matrix variables. 13 | 14 | name: Build OpenPnP OpenCV Distribution 15 | 16 | on: 17 | push: 18 | pull_request: 19 | 20 | jobs: 21 | build_linux_arm: 22 | strategy: 23 | matrix: 24 | os: [ubuntu-22.04] 25 | java: [8] 26 | 27 | runs-on: ${{ matrix.os }} 28 | 29 | steps: 30 | - name: Checkout Repo 31 | uses: actions/checkout@v2 32 | 33 | - name: Setup JDK ${{ matrix.java }} 34 | uses: actions/setup-java@v1 35 | with: 36 | java-version: ${{ matrix.java }} 37 | 38 | - name: Get Version Info 39 | run: | 40 | echo "POM_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV 41 | echo "OPENCV_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.long -q -DforceStdout)" >> $GITHUB_ENV 42 | echo "OPENCV_VERSION_SHORT=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.short -q -DforceStdout)" >> $GITHUB_ENV 43 | 44 | - name: Build OpenCV on Arm 45 | uses: uraimo/run-on-arch-action@v2.0.9 46 | with: 47 | arch: armv7 48 | distro: ubuntu18.04 49 | 50 | # Not required, but speeds up builds 51 | githubToken: ${{ github.token }} 52 | 53 | # Create an artifacts directory 54 | setup: | 55 | mkdir -p "${PWD}/artifacts" 56 | 57 | # Mount the artifacts directory as /artifacts in the container 58 | # Also mount the checked out repo as /host_repo so we can do a test build. 59 | dockerRunArgs: | 60 | --volume "${PWD}/artifacts:/artifacts" 61 | --volume "${PWD}:/host_repo" 62 | 63 | run: | 64 | apt-get update -q -y 65 | apt-get install -q -y git wget cmake unzip build-essential default-jre default-jdk ant maven python3 66 | 67 | # TODO: I'd like to get this path automatically somehow. I think it might be 68 | # in /etc/environment but sourcing it didn't seem to work. Need to figure out 69 | # where it would be after starting a new shell. 70 | export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-armhf 71 | 72 | wget https://github.com/opencv/opencv/archive/${{ env.OPENCV_VERSION }}.zip > /dev/null 73 | unzip ${{ env.OPENCV_VERSION }} > /dev/null 74 | 75 | cd opencv-${{ env.OPENCV_VERSION }} 76 | mkdir build 77 | cd build 78 | cmake \ 79 | -D OPENCV_FORCE_3RDPARTY_BUILD=ON \ 80 | -D BUILD_JAVA=ON \ 81 | -D BUILD_FAT_JAVA_LIB=ON \ 82 | -D OPENCV_ENABLE_NONFREE=ON \ 83 | -D BUILD_SHARED_LIBS=OFF \ 84 | -D BUILD_PERF_TESTS=OFF \ 85 | -D BUILD_TESTS=OFF \ 86 | -D BUILD_EXAMPLES=OFF \ 87 | -D BUILD_DOCS=OFF \ 88 | -D BUILD_PACKAGE=OFF \ 89 | -D BUILD_opencv_python2=OFF \ 90 | -D BUILD_opencv_python3=OFF \ 91 | -D BUILD_opencv_apps=OFF \ 92 | -D BUILD_opencv_gapi=OFF \ 93 | -D CMAKE_BUILD_TYPE=RELEASE \ 94 | .. 95 | make -j4 96 | 97 | # Copy the build artifacts to the /artifacts directory, which will be 98 | # used by the host to upload them. 99 | mkdir -p /artifacts/bin 100 | mkdir -p /artifacts/lib 101 | cp bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar /artifacts/bin 102 | cp lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so /artifacts/lib 103 | 104 | # And also copy the build artifacts to the repo, so we can do a test build. 105 | # Note that we use the repo checked out on the host so that we can use 106 | # actions/checkout for the Git stuff, rather than having to mess with 107 | # the authentication in this container. 108 | cp bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar /host_repo/upstream 109 | cp lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so /host_repo/src/main/resources/nu/pattern/opencv/linux/ARMv7 110 | 111 | # Peform a test build in the host repo, which now contains all of the 112 | # build artifacts. 113 | cd /host_repo 114 | mvn -B test 115 | 116 | - name: Upload Artifacts 117 | uses: actions/upload-artifact@v2 118 | with: 119 | name: ubuntu-18.04-arm 120 | path: | 121 | artifacts/bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar 122 | artifacts/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so 123 | 124 | 125 | 126 | 127 | build_linux_arm64: 128 | strategy: 129 | matrix: 130 | os: [ubuntu-22.04] 131 | java: [15] 132 | 133 | runs-on: ${{ matrix.os }} 134 | 135 | steps: 136 | - name: Checkout Repo 137 | uses: actions/checkout@v2 138 | 139 | - name: Setup JDK ${{ matrix.java }} 140 | uses: actions/setup-java@v1 141 | with: 142 | java-version: ${{ matrix.java }} 143 | 144 | - name: Get Version Info 145 | run: | 146 | echo "POM_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV 147 | echo "OPENCV_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.long -q -DforceStdout)" >> $GITHUB_ENV 148 | echo "OPENCV_VERSION_SHORT=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.short -q -DforceStdout)" >> $GITHUB_ENV 149 | 150 | - name: Build OpenCV on Arm64 151 | uses: uraimo/run-on-arch-action@v2.0.9 152 | with: 153 | arch: aarch64 154 | distro: ubuntu18.04 155 | 156 | # Not required, but speeds up builds 157 | githubToken: ${{ github.token }} 158 | 159 | # Create an artifacts directory 160 | setup: | 161 | mkdir -p "${PWD}/artifacts" 162 | 163 | # Mount the artifacts directory as /artifacts in the container 164 | # Also mount the checked out repo as /host_repo so we can do a test build. 165 | dockerRunArgs: | 166 | --volume "${PWD}/artifacts:/artifacts" 167 | --volume "${PWD}:/host_repo" 168 | 169 | run: | 170 | apt-get update -q -y 171 | apt-get install -q -y git wget cmake unzip build-essential default-jre default-jdk ant maven python3 172 | 173 | # TODO: I'd like to get this path automatically somehow. I think it might be 174 | # in /etc/environment but sourcing it didn't seem to work. Need to figure out 175 | # where it would be after starting a new shell. 176 | export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-arm64 177 | 178 | wget https://github.com/opencv/opencv/archive/${{ env.OPENCV_VERSION }}.zip > /dev/null 179 | unzip ${{ env.OPENCV_VERSION }} > /dev/null 180 | 181 | cd opencv-${{ env.OPENCV_VERSION }} 182 | mkdir build 183 | cd build 184 | cmake \ 185 | -D OPENCV_FORCE_3RDPARTY_BUILD=ON \ 186 | -D BUILD_JAVA=ON \ 187 | -D BUILD_FAT_JAVA_LIB=ON \ 188 | -D OPENCV_ENABLE_NONFREE=ON \ 189 | -D BUILD_SHARED_LIBS=OFF \ 190 | -D BUILD_PERF_TESTS=OFF \ 191 | -D BUILD_TESTS=OFF \ 192 | -D BUILD_EXAMPLES=OFF \ 193 | -D BUILD_DOCS=OFF \ 194 | -D BUILD_PACKAGE=OFF \ 195 | -D BUILD_opencv_python2=OFF \ 196 | -D BUILD_opencv_python3=OFF \ 197 | -D BUILD_opencv_apps=OFF \ 198 | -D BUILD_opencv_gapi=OFF \ 199 | -D CMAKE_BUILD_TYPE=RELEASE \ 200 | .. 201 | make -j4 202 | 203 | # Copy the build artifacts to the /artifacts directory, which will be 204 | # used by the host to upload them. 205 | mkdir -p /artifacts/bin 206 | mkdir -p /artifacts/lib 207 | cp bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar /artifacts/bin 208 | cp lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so /artifacts/lib 209 | 210 | # And also copy the build artifacts to the repo, so we can do a test build. 211 | # Note that we use the repo checked out on the host so that we can use 212 | # actions/checkout for the Git stuff, rather than having to mess with 213 | # the authentication in this container. 214 | cp bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar /host_repo/upstream 215 | cp lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so /host_repo/src/main/resources/nu/pattern/opencv/linux/ARMv8 216 | 217 | # Peform a test build in the host repo, which now contains all of the 218 | # build artifacts. 219 | cd /host_repo 220 | mvn -B test 221 | 222 | - name: Upload Artifacts 223 | uses: actions/upload-artifact@v2 224 | with: 225 | name: ubuntu-18.04-arm64 226 | path: | 227 | artifacts/bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar 228 | artifacts/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so 229 | 230 | 231 | 232 | 233 | # Build native libraries for Linux and Mac. Uploads the libraries 234 | # as artifacts which are used in subsequent jobs. 235 | build_mac_linux_x64: 236 | strategy: 237 | matrix: 238 | os: [ubuntu-20.04, macos-11] 239 | java: [8] 240 | 241 | runs-on: ${{ matrix.os }} 242 | 243 | steps: 244 | - name: Checkout Repo 245 | uses: actions/checkout@v2 246 | 247 | - name: Setup JDK ${{ matrix.java }} 248 | uses: actions/setup-java@v1 249 | with: 250 | java-version: ${{ matrix.java }} 251 | 252 | - name: Get Version Info 253 | run: | 254 | echo "POM_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV 255 | echo "OPENCV_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.long -q -DforceStdout)" >> $GITHUB_ENV 256 | echo "OPENCV_VERSION_SHORT=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.short -q -DforceStdout)" >> $GITHUB_ENV 257 | 258 | - name: Build OpenCV 259 | run: | 260 | wget https://github.com/opencv/opencv/archive/${{ env.OPENCV_VERSION }}.zip > /dev/null 261 | unzip ${{ env.OPENCV_VERSION }} > /dev/null 262 | cd opencv-${{ env.OPENCV_VERSION }} 263 | mkdir build 264 | cd build 265 | cmake \ 266 | -D OPENCV_FORCE_3RDPARTY_BUILD=ON \ 267 | -D BUILD_JAVA=ON \ 268 | -D BUILD_FAT_JAVA_LIB=ON \ 269 | -D OPENCV_ENABLE_NONFREE=ON \ 270 | -D BUILD_SHARED_LIBS=OFF \ 271 | -D BUILD_PERF_TESTS=OFF \ 272 | -D BUILD_TESTS=OFF \ 273 | -D BUILD_EXAMPLES=OFF \ 274 | -D BUILD_DOCS=OFF \ 275 | -D BUILD_PACKAGE=OFF \ 276 | -D BUILD_opencv_python2=OFF \ 277 | -D BUILD_opencv_python3=OFF \ 278 | -D BUILD_opencv_apps=OFF \ 279 | -D BUILD_opencv_gapi=OFF \ 280 | -D CMAKE_BUILD_TYPE=RELEASE \ 281 | .. 282 | make -j8 283 | 284 | - name: Copy Libraries 285 | run: | 286 | cp opencv-${{ env.OPENCV_VERSION }}/build/bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar upstream || : 287 | cp opencv-${{ env.OPENCV_VERSION }}/build/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so src/main/resources/nu/pattern/opencv/linux/x86_64 || : 288 | cp opencv-${{ env.OPENCV_VERSION }}/build/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.dylib src/main/resources/nu/pattern/opencv/osx/x86_64 || : 289 | 290 | - name: Build with Maven 291 | run: mvn -B test 292 | 293 | - name: Upload Libraries 294 | uses: actions/upload-artifact@v2 295 | with: 296 | name: ${{ matrix.os }} 297 | path: | 298 | opencv-${{ env.OPENCV_VERSION }}/build/bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar 299 | opencv-${{ env.OPENCV_VERSION }}/build/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so 300 | opencv-${{ env.OPENCV_VERSION }}/build/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.dylib 301 | 302 | 303 | 304 | 305 | # Build Apple Silicon Mac native binary. 306 | # See https://cmake.org/cmake/help/v3.23/variable/CMAKE_APPLE_SILICON_PROCESSOR.html 307 | build_mac_aarch64: 308 | strategy: 309 | matrix: 310 | java: [18] 311 | runs-on: [macos-13-xlarge] 312 | 313 | steps: 314 | - name: Checkout Repo 315 | uses: actions/checkout@v2 316 | 317 | - name: Setup JDK ${{ matrix.java }} 318 | uses: actions/setup-java@v3 319 | with: 320 | distribution: 'temurin' 321 | architecture: aarch64 322 | java-version: ${{ matrix.java }} 323 | 324 | - name: Get Version Info 325 | run: | 326 | echo "POM_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV 327 | echo "OPENCV_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.long -q -DforceStdout)" >> $GITHUB_ENV 328 | echo "OPENCV_VERSION_SHORT=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.short -q -DforceStdout)" >> $GITHUB_ENV 329 | 330 | - name: Build OpenCV 331 | run: | 332 | wget https://github.com/opencv/opencv/archive/${{ env.OPENCV_VERSION }}.zip > /dev/null 333 | unzip ${{ env.OPENCV_VERSION }} > /dev/null 334 | cd opencv-${{ env.OPENCV_VERSION }} 335 | mkdir build 336 | cd build 337 | cmake \ 338 | -D OPENCV_FORCE_3RDPARTY_BUILD=ON \ 339 | -D BUILD_JAVA=ON \ 340 | -D BUILD_FAT_JAVA_LIB=ON \ 341 | -D OPENCV_ENABLE_NONFREE=ON \ 342 | -D BUILD_SHARED_LIBS=OFF \ 343 | -D BUILD_PERF_TESTS=OFF \ 344 | -D BUILD_TESTS=OFF \ 345 | -D BUILD_EXAMPLES=OFF \ 346 | -D BUILD_DOCS=OFF \ 347 | -D BUILD_PACKAGE=OFF \ 348 | -D BUILD_opencv_python2=OFF \ 349 | -D BUILD_opencv_python3=OFF \ 350 | -D BUILD_opencv_apps=OFF \ 351 | -D BUILD_opencv_gapi=OFF \ 352 | -D CMAKE_BUILD_TYPE=RELEASE \ 353 | -D CMAKE_APPLE_SILICON_PROCESSOR=arm64 \ 354 | -D CMAKE_CXX_STANDARD=17 \ 355 | .. 356 | make -j8 357 | 358 | - name: Copy Libraries 359 | run: | 360 | cp opencv-${{ env.OPENCV_VERSION }}/build/bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar upstream || : 361 | cp opencv-${{ env.OPENCV_VERSION }}/build/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.dylib src/main/resources/nu/pattern/opencv/osx/ARMv8 || : 362 | 363 | - name: Build with Maven 364 | run: mvn -B test 365 | 366 | - name: Upload Libraries 367 | uses: actions/upload-artifact@v2 368 | with: 369 | name: macos-aarch64 370 | path: | 371 | opencv-${{ env.OPENCV_VERSION }}/build/bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar 372 | opencv-${{ env.OPENCV_VERSION }}/build/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.dylib 373 | 374 | 375 | 376 | 377 | # Downloads the Windows distribution from OpenCV, extracts it and uploads 378 | # the native libraries as artifacts for use by subsequent jobs. This is 379 | # in leiu of building the native libraries on Windows. 380 | build_windows: 381 | strategy: 382 | matrix: 383 | os: [ubuntu-20.04] 384 | java: [15] 385 | 386 | runs-on: ${{ matrix.os }} 387 | 388 | steps: 389 | - name: Checkout Repo 390 | uses: actions/checkout@v2 391 | 392 | - name: Setup JDK ${{ matrix.java }} 393 | uses: actions/setup-java@v1 394 | with: 395 | java-version: ${{ matrix.java }} 396 | 397 | - name: Get Version Info 398 | run: | 399 | echo "POM_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV 400 | echo "OPENCV_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.long -q -DforceStdout)" >> $GITHUB_ENV 401 | echo "OPENCV_VERSION_SHORT=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.short -q -DforceStdout)" >> $GITHUB_ENV 402 | 403 | - name: Download Windows Distribution 404 | run: wget -O opencv-${{ env.OPENCV_VERSION }}.exe https://github.com/opencv/opencv/releases/download/${{ env.OPENCV_VERSION }}/opencv-${{ env.OPENCV_VERSION }}-windows.exe 405 | 406 | - name: Extract Windows Distribution 407 | run: 7z x opencv-${{ env.OPENCV_VERSION }}.exe 408 | 409 | - name: Upload Libraries 410 | uses: actions/upload-artifact@v2 411 | with: 412 | name: windows-2016 413 | path: opencv/build/java 414 | 415 | 416 | 417 | 418 | # Download and combine the artifacts from the above jobs and build the 419 | # distribution jar. Uploads it as an artifact for subsequent steps. 420 | build_dist: 421 | needs: [build_mac_aarch64, build_linux_arm, build_linux_arm64, build_mac_linux_x64, build_windows] 422 | 423 | strategy: 424 | matrix: 425 | os: [ubuntu-20.04] 426 | java: [8] 427 | 428 | runs-on: ${{ matrix.os }} 429 | 430 | steps: 431 | - name: Checkout Repo 432 | uses: actions/checkout@v2 433 | 434 | - name: Setup JDK ${{ matrix.java }} 435 | uses: actions/setup-java@v1 436 | with: 437 | java-version: ${{ matrix.java }} 438 | 439 | - name: Get Version Info 440 | run: | 441 | echo "POM_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV 442 | echo "OPENCV_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.long -q -DforceStdout)" >> $GITHUB_ENV 443 | echo "OPENCV_VERSION_SHORT=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.short -q -DforceStdout)" >> $GITHUB_ENV 444 | 445 | - name: Download Libraries 446 | uses: actions/download-artifact@v2 447 | 448 | - name: Copy Binaries 449 | run: | 450 | cp macos-11/bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar upstream 451 | cp macos-11/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.dylib src/main/resources/nu/pattern/opencv/osx/x86_64 452 | cp macos-aarch64/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.dylib src/main/resources/nu/pattern/opencv/osx/ARMv8 453 | cp ubuntu-20.04/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so src/main/resources/nu/pattern/opencv/linux/x86_64 454 | cp ubuntu-18.04-arm/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so src/main/resources/nu/pattern/opencv/linux/ARMv7 455 | cp ubuntu-18.04-arm64/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so src/main/resources/nu/pattern/opencv/linux/ARMv8 456 | cp windows-2016/x86/opencv_java${{ env.OPENCV_VERSION_SHORT }}.dll src/main/resources/nu/pattern/opencv/windows/x86_32 457 | cp windows-2016/x64/opencv_java${{ env.OPENCV_VERSION_SHORT }}.dll src/main/resources/nu/pattern/opencv/windows/x86_64 458 | 459 | - name: Build with Maven 460 | run: mvn -B install 461 | 462 | - name: Upload Distribution 463 | uses: actions/upload-artifact@v2 464 | with: 465 | name: dist 466 | path: target/opencv* 467 | 468 | 469 | 470 | 471 | # Downloads the distribution jar that was built above and runs 472 | # a short smoke test on it on many platforms and versions of 473 | # Java. This is intended to ensure that the built jar 474 | # runs on all of the supported targets. 475 | test: 476 | needs: build_dist 477 | strategy: 478 | matrix: 479 | os: [macos-11, windows-2019, ubuntu-20.04, ubuntu-22.04] 480 | java: [8, 9, 10, 11, 12, 13, 14, 15] 481 | 482 | runs-on: ${{ matrix.os }} 483 | 484 | steps: 485 | - name: Checkout Repo 486 | uses: actions/checkout@v2 487 | 488 | - name: Setup JDK ${{ matrix.java }} 489 | uses: actions/setup-java@v1 490 | with: 491 | java-version: ${{ matrix.java }} 492 | 493 | - name: Get Version Info 494 | shell: bash 495 | run: | 496 | echo "POM_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV 497 | echo "OPENCV_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.long -q -DforceStdout)" >> $GITHUB_ENV 498 | echo "OPENCV_VERSION_SHORT=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.short -q -DforceStdout)" >> $GITHUB_ENV 499 | 500 | - name: Download Distribution 501 | uses: actions/download-artifact@v2 502 | with: 503 | name: dist 504 | 505 | - name: Run Test 506 | shell: bash 507 | run: java -cp opencv-${{ env.POM_VERSION }}.jar nu.pattern.PrintVersion 508 | 509 | 510 | 511 | 512 | publish: 513 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 514 | 515 | needs: test 516 | 517 | strategy: 518 | matrix: 519 | os: [ubuntu-20.04] 520 | java: [8] 521 | 522 | runs-on: ${{ matrix.os }} 523 | 524 | steps: 525 | - name: Checkout Repo 526 | uses: actions/checkout@v2 527 | 528 | - name: Setup JDK ${{ matrix.java }} 529 | uses: actions/setup-java@v1 530 | with: 531 | java-version: ${{ matrix.java }} 532 | server-id: ossrh 533 | server-username: OSSRH_USERNAME 534 | server-password: OSSRH_PASSWORD 535 | gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} 536 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 537 | 538 | - name: Get Version Info 539 | run: | 540 | echo "POM_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV 541 | echo "OPENCV_VERSION=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.long -q -DforceStdout)" >> $GITHUB_ENV 542 | echo "OPENCV_VERSION_SHORT=$(mvn build-helper:parse-version org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=opencv.version.short -q -DforceStdout)" >> $GITHUB_ENV 543 | 544 | - name: Download Libraries 545 | uses: actions/download-artifact@v2 546 | 547 | - name: Copy Binaries 548 | run: | 549 | cp macos-11/bin/opencv-${{ env.OPENCV_VERSION_SHORT }}.jar upstream 550 | cp macos-11/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.dylib src/main/resources/nu/pattern/opencv/osx/x86_64 551 | cp macos-aarch64/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.dylib src/main/resources/nu/pattern/opencv/osx/ARMv8 552 | cp ubuntu-20.04/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so src/main/resources/nu/pattern/opencv/linux/x86_64 553 | cp ubuntu-18.04-arm/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so src/main/resources/nu/pattern/opencv/linux/ARMv7 554 | cp ubuntu-18.04-arm64/lib/libopencv_java${{ env.OPENCV_VERSION_SHORT }}.so src/main/resources/nu/pattern/opencv/linux/ARMv8 555 | cp windows-2016/x86/opencv_java${{ env.OPENCV_VERSION_SHORT }}.dll src/main/resources/nu/pattern/opencv/windows/x86_32 556 | cp windows-2016/x64/opencv_java${{ env.OPENCV_VERSION_SHORT }}.dll src/main/resources/nu/pattern/opencv/windows/x86_64 557 | 558 | - name: Publish to Apache Maven Central 559 | run: mvn -B -e clean deploy -P release-sign-artifacts 560 | env: 561 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} 562 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 563 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} 564 | 565 | 566 | 567 | 568 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.sc 3 | .DS_Store 4 | .idea 5 | settings.xml 6 | target 7 | .classpath 8 | .project 9 | .settings/org.eclipse.core.resources.prefs 10 | .settings/org.eclipse.jdt.core.prefs 11 | .settings/org.eclipse.m2e.core.prefs 12 | opencv/** 13 | .metadata/ 14 | .vscode 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Issues, Bug Reports, and Feature Requests 2 | 3 | * When creating a new Issue, first search to see if it has already been filed. 4 | * Bug reports must include the following or will be deleted: 5 | * Steps to reproduce. 6 | * Expected result. 7 | * Actual result. 8 | * A Short, Self Contained, Correct, Example: http://sscce.org/ 9 | * Notes, log files, screen captures, videos, etc. to show the problem. 10 | 11 | # Code Contributions and Pull Requests 12 | 13 | Before starting work on a pull request, please read: https://github.com/openpnp/openpnp/wiki/Developers-Guide#contributing 14 | 15 | Summary of guidelines: 16 | 17 | * One pull request per issue, bug or feature. 18 | * Describe the change in detail and why it is needed. 19 | * Follow the coding style. 20 | * Include tests and documentation. 21 | * Run and pass the tests. 22 | * Think of how this affects other users. 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | By downloading, copying, installing or using the software you agree to this license. 2 | If you do not agree to this license, do not download, install, 3 | copy or use the software. 4 | 5 | 6 | License Agreement 7 | For Open Source Computer Vision Library 8 | (3-clause BSD License) 9 | 10 | Redistribution and use in source and binary forms, with or without modification, 11 | are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, 14 | this list of conditions and the following disclaimer. 15 | 16 | * Redistributions in binary form must reproduce the above copyright notice, 17 | this list of conditions and the following disclaimer in the documentation 18 | and/or other materials provided with the distribution. 19 | 20 | * Neither the names of the copyright holders nor the names of the contributors 21 | may be used to endorse or promote products derived from this software 22 | without specific prior written permission. 23 | 24 | This software is provided by the copyright holders and contributors "as is" and 25 | any express or implied warranties, including, but not limited to, the implied 26 | warranties of merchantability and fitness for a particular purpose are disclaimed. 27 | In no event shall copyright holders or contributors be liable for any direct, 28 | indirect, incidental, special, exemplary, or consequential damages 29 | (including, but not limited to, procurement of substitute goods or services; 30 | loss of use, data, or profits; or business interruption) however caused 31 | and on any theory of liability, whether in contract, strict liability, 32 | or tort (including negligence or otherwise) arising in any way out of 33 | the use of this software, even if advised of the possibility of such damage. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenCV (packaged by [OpenPnP](http://openpnp.org)) 2 | 3 | [OpenCV](http://opencv.org) Java bindings packaged with native libraries, seamlessly delivered as a turn-key Maven dependency. 4 | 5 | ## Fork Notes 6 | 7 | ### Soft Fork 8 | 9 | This is a soft fork of Pattern's OpenCV package at https://github.com/PatternConsulting/opencv. 10 | That package has not been maintained in quite some time and I needed updated OpenCV 11 | binaries for OpenPnP. I intend to maintain this fork for the foreseeable future 12 | or until Pattern resumes maintenance of their package. 13 | 14 | ### Backwards Compatibility 15 | 16 | I have left the source code packages and directories the same (nu.pattern) 17 | and only changed the Maven coordinates in the pom.xml. This way the package 18 | remains backwards compatible and it is very easy to switch between the 19 | OpenPnP version and the Pattern version. 20 | 21 | ### Maven 22 | 23 | To use this fork in your project, instead of the Pattern one, simply add 24 | the same dependency but with the groupId org.openpnp instead of nu.pattern. 25 | 26 | ### Scala 27 | 28 | I'm not uploading Scala artifacts as I don't know or use Scala. If someone 29 | wants to maintain that portion of the package, let me know. 30 | 31 | ## Usage 32 | 33 | ### Project 34 | 35 | OpenPnP's OpenCV package is added to your project as any other dependency. 36 | 37 | #### [Maven](http://maven.apache.org/) 38 | 39 | ```xml 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.openpnp 50 | opencv 51 | [4.3.0,) 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | A list of all published versions can be found at https://mvnrepository.com/artifact/org.openpnp/opencv. 64 | 65 | ### API 66 | 67 | Typically, using the upstream [OpenCV Java bindings involves loading the native library](http://docs.opencv.org/doc/tutorials/introduction/desktop_java/java_dev_intro.html#java-sample-with-ant) as follows: 68 | 69 | ```java 70 | static { 71 | System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME); 72 | } 73 | ``` 74 | 75 | Fortunately, this is unchanged except for one caveat. To use the native libraries included with this package, first call [`nu.pattern.OpenCV.loadShared()`](https://github.com/PatternConsulting/opencv/blob/master/src/main/java/nu/pattern/OpenCV.java). 76 | 77 | **Note: In Java 12+ loadShared() is not available. Use loadLocally() instead, and see notes below.** 78 | 79 | This call will—exactly once per class loader—first attempt to load from the system-wide installation (exactly as if `System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);` were called without any preceding steps). If that fails, the loader will select a binary from the package appropriate for the runtime environment's operating system and architecture. It will write that native library to a temporary directory (also defined by the environment), add that directory to `java.library.path`. _This involves writing to disk_, so consider the implications. Temporary files will be garbage-collected on clean shutdown. 80 | 81 | This approach keeps most clients decoupled from Pattern's package and loader. As long as this is done sufficiently early in execution, any library using the OpenCV Java bindings can use the usual load call as documented by the OpenCV project. 82 | 83 | There are, however, cases where Java class loaders are frequently changing (_e.g._, application servers, SBT projects, Scala worksheets), and [spurious attempts to load the native library will result in JNI errors](https://github.com/PatternConsulting/opencv/issues/7). As a partial work-around, this package offers an alternative API, [`nu.pattern.OpenCV.loadLocally()`](https://github.com/PatternConsulting/opencv/blob/master/src/main/java/nu/pattern/OpenCV.java), which—also exactly once per class loader—extracts the binary appropriate for the runtime platform, and passes it to `System#load(String)`. Ultimately, this may eventually load the library redundantly in the same JVM, which could be unsafe in production. Use with caution and understand the implications. 84 | 85 | It's recommended developers using any JNI library read further: 86 | 87 | - [JNI 1.2 Specifications: Library and Version Management](http://docs.oracle.com/javase/7/docs/technotes/guides/jni/jni-12.html#libmanage) 88 | - [Holger Hoffstätte's Comments on Native Libraries, Class Loaders, and Garbage Collection](https://groups.google.com/forum/#!msg/ospl-developer/J4i6cF6yPk0/-3Jm3Qs_HDwJ) 89 | 90 | ## Debugging 91 | 92 | [Java logging](http://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html) is used to produce log messages from `nu.pattern.OpenCV`. 93 | 94 | ## Rationale 95 | 96 | Developers wishing to use the Java API for OpenCV would typically go through the process of building the project, and building it for each platform they wished to support (_e.g._, 32-bit Linux, OS X). This project provides those binaries for inclusion as a typical dependency in Maven, Ivy, and SBT projects. 97 | 98 | Apart from testing, this package deliberately specifies no external dependencies. It does, however, make use of modern Java APIs (such as [Java NIO](http://docs.oracle.com/javase/tutorial/essential/io/fileio.html)). 99 | 100 | ## Contributing 101 | 102 | Producing native binaries is the most cumbersome process in maintaining this package. If you can contribute binaries _for the current version_, please make a pull request including the build artifacts and any platform definitions in `nu.pattern.OpenCV`. 103 | 104 | ## Support 105 | 106 | The following platforms are supported by this package: 107 | 108 | OS | Architecture 109 | --- | --- 110 | macOS | Intel 111 | macOS | Apple Silicon (arm64) 112 | Linux | x86_64 113 | Linux | ARMv7 (arm) 114 | Linux | ARMv8 (arm64 / aarch64) 115 | Windows | x86_32 116 | Windows | x86_64 117 | 118 | If you can help create binaries for additional platforms, please see notes under [_Contributing_](#contributing). 119 | 120 | ## Credits 121 | 122 | This package is maintained by [Jason von Nieda](http://github.com/vonnieda). If you find it useful, please 123 | consider [sponsoring me](https://github.com/sponsors/vonnieda). 124 | 125 | ## Acknowledgements 126 | 127 | - [Michael Ahlers](http://github.com/michaelahlers), for originally creating and maintaining this project. 128 | - [Greg Borenstein](https://github.com/atduskgreg), whose advice and [OpenCV for Processing](https://github.com/atduskgreg/opencv-processing) project informed this package's development. 129 | - [Alex Osborne](https://github.com/ato), for helpful [utility class producing temporary directories with Java NIO that are properly garbage-collected on shutdown](https://gist.github.com/ato/6774390). 130 | 131 | ## Builds 132 | 133 | Builds are automatically created and published to Maven Central by Github Actions triggered by new tags. Builds are also created for branches and pull requests, and the artifacts can be downloaded from the Actions tab. These are not automatically released to Maven Central. 134 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | bundle 8 | org.openpnp 9 | opencv 10 | 4.9.0-0 11 | OpenPnP OpenCV 12 | OpenCV packaged with native libraries and loader for multiple platforms. 13 | http://github.com/openpnp/opencv 14 | 15 | 16 | UTF-8 17 | ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion} 18 | ${parsedVersion.majorVersion}${parsedVersion.minorVersion}${parsedVersion.incrementalVersion} 19 | 20 | 21 | 22 | 23 | jdk-8 24 | 25 | 1.8 26 | 27 | 28 | 29 | -Xdoclint:none 30 | 31 | 32 | 33 | 34 | release-sign-artifacts 35 | 36 | 37 | gpg.sign 38 | true 39 | 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-gpg-plugin 46 | 1.6 47 | 48 | 49 | sign-artifacts 50 | verify 51 | 52 | sign 53 | 54 | 55 | 56 | 57 | --pinentry-mode 58 | loopback 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | BSD License 72 | http://github.com/Itseez/opencv/raw/master/LICENSE 73 | 74 | 75 | 76 | 77 | http://github.com/openpnp/opencv 78 | scm:git:http://github.com/openpnp/opencv.git 79 | scm:git:http://github.com/openpnp/opencv.git 80 | 81 | 82 | 83 | GitHub 84 | http://github.com/openpnp/opencv/issues 85 | 86 | 87 | 88 | 89 | vonnieda 90 | Jason von Nieda 91 | jason@vonnieda.org 92 | OpenPnP 93 | http://openpnp.org 94 | 95 | maintainer 96 | 97 | 98 | 99 | michaelahlers 100 | Michael Ahlers 101 | michael.ahlers@pattern.nu 102 | Pattern 103 | http://pattern.nu 104 | 105 | maintainer 106 | 107 | 110 | 111 | 112 | 113 | 114 | 115 | ossrh 116 | https://oss.sonatype.org/content/repositories/snapshots 117 | 118 | 119 | 120 | 121 | 122 | 123 | org.codehaus.mojo 124 | build-helper-maven-plugin 125 | 3.2.0 126 | 127 | 128 | parse-version 129 | 130 | parse-version 131 | 132 | 133 | 134 | 135 | 136 | 137 | org.sonatype.plugins 138 | nexus-staging-maven-plugin 139 | 1.6.7 140 | true 141 | 142 | ossrh 143 | https://oss.sonatype.org/ 144 | true 145 | 146 | 147 | 148 | 149 | org.apache.felix 150 | maven-bundle-plugin 151 | true 152 | 153 | 154 | nu.pattern.*, org.opencv.* 155 | !sun.reflect.* 156 | 157 | 158 | 159 | 160 | 161 | 162 | maven-compiler-plugin 163 | 164 | 1.7 165 | 1.7 166 | 167 | 168 | 169 | 170 | 171 | maven-antrun-plugin 172 | 1.3 173 | 174 | 175 | generate-sources 176 | 177 | 178 | Extracting Java classes. 179 | 180 | 181 | 182 | 183 | 184 | 185 | Extracting Java sources. 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | run 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | org.codehaus.mojo 203 | build-helper-maven-plugin 204 | 1.8 205 | 206 | 207 | prepare-package 208 | 209 | add-source 210 | 211 | 212 | 213 | ${project.build.directory}/upstream-sources 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | org.apache.maven.plugins 223 | maven-source-plugin 224 | 2.2.1 225 | 226 | 227 | 228 | jar 229 | 230 | 231 | 232 | 233 | 234 | 235 | maven-surefire-plugin 236 | 2.12.4 237 | 238 | 239 | 240 | java.util.logging.config.file 241 | ${project.build.directory}/test-classes/logging.properties 242 | 243 | 244 | 245 | 246 | 247 | listener 248 | nu.pattern.LoadLibraryRunListener 249 | 250 | 251 | 252 | ${project.basedir}/upstream 253 | 254 | 255 | **/OpenCVTestCase.* 256 | 257 | 258 | 259 | 260 | 261 | org.apache.maven.plugins 262 | maven-javadoc-plugin 263 | 2.9.1 264 | 265 | 266 | attach-javadocs 267 | 268 | jar 269 | 270 | 271 | 272 | 273 | 274 | http://docs.opencv.org/java/ 275 | http://docs.oracle.com/javase/8/docs/api/ 276 | 277 | ${javadoc.parameters} 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | org.eclipse.m2e 286 | lifecycle-mapping 287 | 1.0.0 288 | 289 | 290 | 291 | 292 | 293 | 294 | org.apache.maven.plugins 295 | 296 | 297 | maven-antrun-plugin 298 | 299 | [1.3,) 300 | 301 | run 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | junit 319 | junit 320 | 4.11 321 | test 322 | 323 | 324 | 325 | 326 | -------------------------------------------------------------------------------- /src/main/java/nu/pattern/OpenCV.java: -------------------------------------------------------------------------------- 1 | package nu.pattern; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.lang.reflect.Field; 7 | import java.nio.file.AccessDeniedException; 8 | import java.nio.file.FileVisitResult; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.SimpleFileVisitor; 12 | import java.nio.file.attribute.BasicFileAttributes; 13 | import java.util.Arrays; 14 | import java.util.HashSet; 15 | import java.util.List; 16 | import java.util.Set; 17 | import java.util.logging.Level; 18 | import java.util.logging.Logger; 19 | import java.util.regex.Pattern; 20 | 21 | import org.opencv.core.Core; 22 | 23 | public class OpenCV { 24 | 25 | private final static Logger logger = Logger.getLogger(OpenCV.class.getName()); 26 | 27 | 28 | 29 | static enum OS { 30 | OSX("^[Mm]ac OS X$"), 31 | LINUX("^[Ll]inux$"), 32 | WINDOWS("^[Ww]indows.*"); 33 | 34 | private final Set patterns; 35 | 36 | private OS(final String... patterns) { 37 | this.patterns = new HashSet(); 38 | 39 | for (final String pattern : patterns) { 40 | this.patterns.add(Pattern.compile(pattern)); 41 | } 42 | } 43 | 44 | private boolean is(final String id) { 45 | for (final Pattern pattern : patterns) { 46 | if (pattern.matcher(id).matches()) { 47 | return true; 48 | } 49 | } 50 | return false; 51 | } 52 | 53 | public static OS getCurrent() { 54 | final String osName = System.getProperty("os.name"); 55 | 56 | for (final OS os : OS.values()) { 57 | if (os.is(osName)) { 58 | logger.log(Level.FINEST, "Current environment matches operating system descriptor \"{0}\".", os); 59 | return os; 60 | } 61 | } 62 | 63 | throw new UnsupportedOperationException(String.format("Operating system \"%s\" is not supported.", osName)); 64 | } 65 | } 66 | 67 | static enum Arch { 68 | X86_32("i386", "i686", "x86"), 69 | X86_64("amd64", "x86_64"), 70 | ARMv7("arm"), 71 | ARMv8("aarch64", "arm64"); 72 | 73 | private final Set patterns; 74 | 75 | private Arch(final String... patterns) { 76 | this.patterns = new HashSet(Arrays.asList(patterns)); 77 | } 78 | 79 | private boolean is(final String id) { 80 | return patterns.contains(id); 81 | } 82 | 83 | public static Arch getCurrent() { 84 | final String osArch = System.getProperty("os.arch"); 85 | 86 | for (final Arch arch : Arch.values()) { 87 | if (arch.is(osArch)) { 88 | logger.log(Level.FINEST, "Current environment matches architecture descriptor \"{0}\".", arch); 89 | return arch; 90 | } 91 | } 92 | 93 | throw new UnsupportedOperationException(String.format("Architecture \"%s\" is not supported.", osArch)); 94 | } 95 | } 96 | 97 | private static class UnsupportedPlatformException extends RuntimeException { 98 | private UnsupportedPlatformException(final OS os, final Arch arch) { 99 | super(String.format("Operating system \"%s\" and architecture \"%s\" are not supported.", os, arch)); 100 | } 101 | } 102 | 103 | private static class TemporaryDirectory { 104 | static final String OPENCV_PREFIX = "opencv_openpnp"; 105 | final Path path; 106 | 107 | public TemporaryDirectory() { 108 | try { 109 | path = Files.createTempDirectory(OPENCV_PREFIX); 110 | } catch (IOException e) { 111 | throw new RuntimeException(e); 112 | } 113 | } 114 | 115 | public Path getPath() { 116 | return path; 117 | } 118 | 119 | public TemporaryDirectory deleteOldInstancesOnStart() { 120 | Path tempDirectory = path.getParent(); 121 | 122 | for (File file : tempDirectory.toFile().listFiles()) { 123 | if (file.isDirectory() && file.getName().startsWith(OPENCV_PREFIX)) { 124 | try { 125 | delete(file.toPath()); 126 | } catch (RuntimeException e) { 127 | if (e.getCause() instanceof AccessDeniedException) { 128 | logger.fine("Failed delete a previous instance of the OpenCV binaries, " 129 | + "likely in use by another program: "); 130 | } 131 | } 132 | } 133 | } 134 | 135 | return this; 136 | } 137 | 138 | public TemporaryDirectory markDeleteOnExit() { 139 | Runtime.getRuntime().addShutdownHook(new Thread() { 140 | @Override 141 | public void run() { 142 | delete(); 143 | } 144 | }); 145 | 146 | return this; 147 | } 148 | 149 | private void delete(Path path) { 150 | if (!Files.exists(path)) { 151 | return; 152 | } 153 | 154 | try { 155 | Files.walkFileTree(path, new SimpleFileVisitor() { 156 | @Override 157 | public FileVisitResult postVisitDirectory(final Path dir, final IOException e) throws IOException { 158 | Files.deleteIfExists(dir); 159 | return super.postVisitDirectory(dir, e); 160 | } 161 | 162 | @Override 163 | public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) 164 | throws IOException { 165 | Files.deleteIfExists(file); 166 | return super.visitFile(file, attrs); 167 | } 168 | }); 169 | } catch (IOException e) { 170 | throw new RuntimeException(e); 171 | } 172 | } 173 | 174 | public void delete() { 175 | delete(path); 176 | } 177 | } 178 | 179 | /** 180 | * Exactly once per {@link ClassLoader}, attempt to load the native library (via {@link System#loadLibrary(String)} with {@link Core#NATIVE_LIBRARY_NAME}). If the first attempt fails, the native binary will be extracted from the classpath to a temporary location (which gets cleaned up on shutdown), that location is added to the {@code java.library.path} system property and {@link ClassLoader#usr_paths}, and then another call to load the library is made. Note this method uses reflection to gain access to private memory in {@link ClassLoader} as there's no documented method to augment the library path at runtime. Spurious calls are safe. 181 | */ 182 | public static void loadShared() { 183 | SharedLoader.getInstance(); 184 | } 185 | 186 | /** 187 | * @see Initialization-on-demand holder idiom 188 | */ 189 | private static class SharedLoader { 190 | // Class loader error messages indicating OpenCV is not found on java.library.path 191 | private static final List errorMessages = Arrays.asList( 192 | String.format("no %s in java.library.path", Core.NATIVE_LIBRARY_NAME), 193 | String.format("%s (Not found in java.library.path)", Core.NATIVE_LIBRARY_NAME) 194 | ); 195 | 196 | private Path libraryPath; 197 | 198 | private SharedLoader() { 199 | try { 200 | System.loadLibrary(Core.NATIVE_LIBRARY_NAME); 201 | logger.log(Level.FINEST, "Loaded existing OpenCV library \"{0}\" from library path.", Core.NATIVE_LIBRARY_NAME); 202 | } catch (final UnsatisfiedLinkError ule) { 203 | 204 | /* Only update the library path and load if the original error indicates it's missing from the library path. */ 205 | if (ule == null || !openCVNotFoundInJavaLibraryPath(ule.getMessage())) { 206 | logger.log(Level.FINEST, String.format("Encountered unexpected loading error."), ule); 207 | throw ule; 208 | } 209 | 210 | /** 211 | * In Java >= 12 it is no longer possible to use addLibraryPath, which modifies the 212 | * ClassLoader's static usr_paths field. There does not seem to be any way around this 213 | * so we fall back to loadLocally() and return. 214 | */ 215 | if (Double.parseDouble(System.getProperty("java.specification.version")) >= 12) { 216 | logger.log(Level.SEVERE, "OpenCV.loadShared() is not supported in Java >= 12. Falling back to OpenCV.loadLocally()."); 217 | OpenCV.loadLocally(); 218 | return; 219 | } 220 | 221 | /* Retain this path for cleaning up the library path later. */ 222 | this.libraryPath = extractNativeBinary(); 223 | 224 | addLibraryPath(libraryPath.getParent()); 225 | System.loadLibrary(Core.NATIVE_LIBRARY_NAME); 226 | 227 | logger.log(Level.FINEST, "OpenCV library \"{0}\" loaded from extracted copy at \"{1}\".", new Object[]{Core.NATIVE_LIBRARY_NAME, System.mapLibraryName(Core.NATIVE_LIBRARY_NAME)}); 228 | } 229 | } 230 | 231 | /** 232 | * Check if any error fragment is contained in the errorMessage 233 | * @param errorMessage the message to check 234 | * @return true if any error fragment matches, false otherwise 235 | */ 236 | private boolean openCVNotFoundInJavaLibraryPath(String errorMessage) { 237 | for (String errorFragment : errorMessages) { 238 | if (errorMessage.contains(errorFragment)) { 239 | return true; 240 | } 241 | } 242 | 243 | return false; 244 | } 245 | 246 | /** 247 | * Cleans up patches done to the environment. 248 | */ 249 | @Override 250 | protected void finalize() throws Throwable { 251 | super.finalize(); 252 | 253 | if (null == libraryPath) { 254 | return; 255 | } 256 | 257 | removeLibraryPath(libraryPath.getParent()); 258 | } 259 | 260 | private static class Holder { 261 | private static final SharedLoader INSTANCE = new SharedLoader(); 262 | } 263 | 264 | public static SharedLoader getInstance() { 265 | return Holder.INSTANCE; 266 | } 267 | 268 | /** 269 | * Adds the provided {@link Path}, normalized, to the {@link ClassLoader#usr_paths} array, as well as to the {@code java.library.path} system property. Uses the reflection API to make the field accessible, and may be unsafe in environments with a security policy. 270 | * 271 | * @see Adding new paths for native libraries at runtime in Java 272 | */ 273 | private static void addLibraryPath(final Path path) { 274 | final String normalizedPath = path.normalize().toString(); 275 | 276 | try { 277 | final Field field = ClassLoader.class.getDeclaredField("usr_paths"); 278 | field.setAccessible(true); 279 | 280 | final Set userPaths = new HashSet<>(Arrays.asList((String[]) field.get(null))); 281 | userPaths.add(normalizedPath); 282 | 283 | field.set(null, userPaths.toArray(new String[userPaths.size()])); 284 | 285 | System.setProperty("java.library.path", System.getProperty("java.library.path") + File.pathSeparator + normalizedPath); 286 | 287 | logger.log(Level.FINEST, "System library path now \"{0}\".", System.getProperty("java.library.path")); 288 | } catch (IllegalAccessException e) { 289 | throw new RuntimeException("Failed to get permissions to set library path"); 290 | } catch (NoSuchFieldException e) { 291 | throw new RuntimeException("Failed to get field handle to set library path"); 292 | } 293 | } 294 | 295 | /** 296 | * Removes the provided {@link Path}, normalized, from the {@link ClassLoader#usr_paths} array, as well as to the {@code java.library.path} system property. Uses the reflection API to make the field accessible, and may be unsafe in environments with a security policy. 297 | */ 298 | private static void removeLibraryPath(final Path path) { 299 | final String normalizedPath = path.normalize().toString(); 300 | 301 | try { 302 | final Field field = ClassLoader.class.getDeclaredField("usr_paths"); 303 | field.setAccessible(true); 304 | 305 | final Set userPaths = new HashSet<>(Arrays.asList((String[]) field.get(null))); 306 | userPaths.remove(normalizedPath); 307 | 308 | field.set(null, userPaths.toArray(new String[userPaths.size()])); 309 | 310 | System.setProperty("java.library.path", System.getProperty("java.library.path").replace(File.pathSeparator + path.normalize().toString(), "")); 311 | } catch (IllegalAccessException e) { 312 | throw new RuntimeException("Failed to get permissions to set library path"); 313 | } catch (NoSuchFieldException e) { 314 | throw new RuntimeException("Failed to get field handle to set library path"); 315 | } 316 | } 317 | } 318 | 319 | /** 320 | * Exactly once per {@link ClassLoader}, extract the native binary from the classpath to a temporary location (which gets cleaned up on shutdown), and load that binary (via {@link System#load(String)}). Spurious calls are safe. 321 | */ 322 | public static void loadLocally() { 323 | LocalLoader.getInstance(); 324 | } 325 | 326 | private static class LocalLoader { 327 | private LocalLoader() { 328 | /* Retain this path for cleaning up later. */ 329 | final Path libraryPath = extractNativeBinary(); 330 | System.load(libraryPath.normalize().toString()); 331 | 332 | logger.log(Level.FINEST, "OpenCV library \"{0}\" loaded from extracted copy at \"{1}\".", new Object[]{Core.NATIVE_LIBRARY_NAME, System.mapLibraryName(Core.NATIVE_LIBRARY_NAME)}); 333 | } 334 | 335 | private static class Holder { 336 | private static final LocalLoader INSTANCE = new LocalLoader(); 337 | } 338 | 339 | public static LocalLoader getInstance() { 340 | return Holder.INSTANCE; 341 | } 342 | } 343 | 344 | /** 345 | * Selects the appropriate packaged binary, extracts it to a temporary location (which gets deleted when the JVM shuts down), and returns a {@link Path} to that file. 346 | */ 347 | private static Path extractNativeBinary() { 348 | final OS os = OS.getCurrent(); 349 | final Arch arch = Arch.getCurrent(); 350 | return extractNativeBinary(os, arch); 351 | } 352 | 353 | /** 354 | * Extracts the packaged binary for the specified platform to a temporary location (which gets deleted when the JVM shuts down), and returns a {@link Path} to that file. 355 | */ 356 | private static Path extractNativeBinary(final OS os, final Arch arch) { 357 | final String location; 358 | 359 | switch (os) { 360 | case LINUX: 361 | switch (arch) { 362 | case X86_64: 363 | location = "/nu/pattern/opencv/linux/x86_64/libopencv_java490.so"; 364 | break; 365 | case ARMv7: 366 | location = "/nu/pattern/opencv/linux/ARMv7/libopencv_java490.so"; 367 | break; 368 | case ARMv8: 369 | location = "/nu/pattern/opencv/linux/ARMv8/libopencv_java490.so"; 370 | break; 371 | default: 372 | throw new UnsupportedPlatformException(os, arch); 373 | } 374 | break; 375 | case OSX: 376 | switch (arch) { 377 | case X86_64: 378 | location = "/nu/pattern/opencv/osx/x86_64/libopencv_java490.dylib"; 379 | break; 380 | case ARMv8: 381 | location = "/nu/pattern/opencv/osx/ARMv8/libopencv_java490.dylib"; 382 | break; 383 | default: 384 | throw new UnsupportedPlatformException(os, arch); 385 | } 386 | break; 387 | case WINDOWS: 388 | switch (arch) { 389 | case X86_32: 390 | location = "/nu/pattern/opencv/windows/x86_32/opencv_java490.dll"; 391 | break; 392 | case X86_64: 393 | location = "/nu/pattern/opencv/windows/x86_64/opencv_java490.dll"; 394 | break; 395 | default: 396 | throw new UnsupportedPlatformException(os, arch); 397 | } 398 | break; 399 | default: 400 | throw new UnsupportedPlatformException(os, arch); 401 | } 402 | 403 | logger.log(Level.FINEST, "Selected native binary \"{0}\".", location); 404 | 405 | final InputStream binary = OpenCV.class.getResourceAsStream(location); 406 | final Path destination; 407 | 408 | // Do not try to delete the temporary directory on the close if Windows 409 | // because there will be a write lock on the file which will cause an 410 | // AccessDeniedException. Instead, try to delete existing instances of 411 | // the temporary directory before extracting. 412 | if (OS.WINDOWS.equals(os)) { 413 | destination = new TemporaryDirectory().deleteOldInstancesOnStart().getPath().resolve("./" + location).normalize(); 414 | } else { 415 | destination = new TemporaryDirectory().markDeleteOnExit().getPath().resolve("./" + location).normalize(); 416 | } 417 | 418 | try { 419 | logger.log(Level.FINEST, "Copying native binary to \"{0}\".", destination); 420 | Files.createDirectories(destination.getParent()); 421 | Files.copy(binary, destination); 422 | binary.close(); 423 | } catch (final IOException ioe) { 424 | throw new IllegalStateException(String.format("Error writing native library to \"%s\".", destination), ioe); 425 | } 426 | 427 | logger.log(Level.FINEST, "Extracted native binary to \"{0}\".", destination); 428 | 429 | return destination; 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /src/main/java/nu/pattern/PrintVersion.java: -------------------------------------------------------------------------------- 1 | package nu.pattern; 2 | 3 | import org.opencv.core.Core; 4 | 5 | public class PrintVersion { 6 | static { 7 | OpenCV.loadLocally(); 8 | } 9 | 10 | public static void main(String[] args) { 11 | System.out.println(Core.getVersionString()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/resources/nu/pattern/opencv/linux/ARMv7/README.md: -------------------------------------------------------------------------------- 1 | Placeholder -------------------------------------------------------------------------------- /src/main/resources/nu/pattern/opencv/linux/ARMv8/README.md: -------------------------------------------------------------------------------- 1 | # Placeholder 2 | -------------------------------------------------------------------------------- /src/main/resources/nu/pattern/opencv/linux/x86_32/README.md: -------------------------------------------------------------------------------- 1 | # Linux x86_32 2 | 3 | ## Preparation 4 | 5 | ```shell 6 | cmake -DBUILD_SHARED_LIBS=OFF .. 7 | ``` 8 | 9 | ## Build 10 | 11 | ```shell 12 | make -j8 13 | ``` 14 | -------------------------------------------------------------------------------- /src/main/resources/nu/pattern/opencv/linux/x86_64/README.md: -------------------------------------------------------------------------------- 1 | # Linux x86_64 2 | 3 | ## Preparation 4 | 5 | ```shell 6 | cmake -DBUILD_SHARED_LIBS=OFF .. 7 | ``` 8 | 9 | ## Build 10 | 11 | ```shell 12 | make -j8 13 | ``` 14 | -------------------------------------------------------------------------------- /src/main/resources/nu/pattern/opencv/osx/ARMv8/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openpnp/opencv/098e4996bc304eb3e5e81f0e3def2ff6af28ccc1/src/main/resources/nu/pattern/opencv/osx/ARMv8/README.md -------------------------------------------------------------------------------- /src/main/resources/nu/pattern/opencv/osx/x86_64/README.md: -------------------------------------------------------------------------------- 1 | # OS X x86_64 2 | 3 | ```shell 4 | cmake -DBUILD_SHARED_LIBS=OFF -DEIGEN_INCLUDE_PATH=/usr/local/include/eigen3 .. 5 | ``` 6 | 7 | ```shell 8 | git clone git://github.com/Itseez/opencv.git itseez-opencv 9 | cd itseez-opencv 10 | git checkout 2.4 11 | mkdir build 12 | cd build 13 | cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_C_FLAGS=-m64 -DCMAKE_CXX_FLAGS=-m64 .. > cmake.log 14 | make -j8 15 | ``` 16 | -------------------------------------------------------------------------------- /src/main/resources/nu/pattern/opencv/windows/x86_32/README.md: -------------------------------------------------------------------------------- 1 | # Placeholder 2 | -------------------------------------------------------------------------------- /src/main/resources/nu/pattern/opencv/windows/x86_64/README.md: -------------------------------------------------------------------------------- 1 | # Placeholder 2 | -------------------------------------------------------------------------------- /src/test/java/nu/pattern/LibraryLoadingTest.java: -------------------------------------------------------------------------------- 1 | package nu.pattern; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.junit.runners.JUnit4; 9 | import org.opencv.core.CvType; 10 | import org.opencv.core.Mat; 11 | import org.opencv.core.Scalar; 12 | 13 | /** 14 | * @see Issue 7 15 | */ 16 | @RunWith(JUnit4.class) 17 | public class LibraryLoadingTest { 18 | private final static Logger logger = Logger.getLogger(LibraryLoadingTest.class.getName()); 19 | 20 | public static class Client { 21 | static { 22 | OpenCV.loadLocally(); 23 | } 24 | 25 | /** 26 | * Run interesting tests on instantiation to make reflection chores simpler. 27 | */ 28 | public Client() { 29 | final Mat m = new Mat(5, 10, CvType.CV_8UC1, new Scalar(0)); 30 | logger.log(Level.FINEST, "Initial matrix: {0}.", m.dump()); 31 | 32 | final Mat mr1 = m.row(1); 33 | mr1.setTo(new Scalar(1)); 34 | 35 | final Mat mc5 = m.col(5); 36 | mc5.setTo(new Scalar(5)); 37 | 38 | logger.log(Level.FINEST, "Final matrix: {0}.", m.dump()); 39 | } 40 | } 41 | 42 | /** 43 | * {@link OpenCV#loadLocally()} is safe to call repeatedly within a single {@link ClassLoader} context. 44 | */ 45 | @Test 46 | public void spuriousLoads() { 47 | OpenCV.loadLocally(); 48 | OpenCV.loadLocally(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/nu/pattern/LoadLibraryRunListener.java: -------------------------------------------------------------------------------- 1 | package nu.pattern; 2 | 3 | import org.junit.runner.Description; 4 | import org.junit.runner.notification.RunListener; 5 | 6 | public class LoadLibraryRunListener extends RunListener { 7 | @Override 8 | public void testRunStarted(final Description description) throws Exception { 9 | super.testRunStarted(description); 10 | OpenCV.loadShared(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/nu/pattern/MserTest.java: -------------------------------------------------------------------------------- 1 | package nu.pattern; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.junit.runners.JUnit4; 10 | import org.opencv.core.CvType; 11 | import org.opencv.core.Mat; 12 | import org.opencv.core.MatOfPoint; 13 | import org.opencv.core.MatOfRect; 14 | import org.opencv.core.Point; 15 | import org.opencv.core.Scalar; 16 | import org.opencv.features2d.MSER; 17 | import org.opencv.imgproc.Imgproc; 18 | 19 | @RunWith(JUnit4.class) 20 | public class MserTest { 21 | static { 22 | OpenCV.loadLocally(); 23 | } 24 | 25 | /** 26 | * Draws a filled circle in an image and then uses MSER to attempt to 27 | * find it. Expects to find one result. 28 | */ 29 | @Test 30 | public void testMser() { 31 | Mat mat = new Mat(400, 400, CvType.CV_8U); 32 | mat.setTo(new Scalar(0)); 33 | Imgproc.circle( 34 | mat, 35 | new Point(200, 200), 36 | 20, 37 | new Scalar(100), 38 | -1); 39 | MSER mser = MSER.create(); 40 | List msers = new ArrayList<>(); 41 | MatOfRect bboxes = new MatOfRect(); 42 | mser.detectRegions(mat, msers, bboxes); 43 | Assert.assertEquals(1, msers.size()); 44 | } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/test/resources/logging.properties: -------------------------------------------------------------------------------- 1 | handlers=java.util.logging.ConsoleHandler 2 | 3 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 4 | java.util.logging.ConsoleHandler.level=ALL 5 | 6 | nu.pattern.level=ALL 7 | -------------------------------------------------------------------------------- /upstream/README.md: -------------------------------------------------------------------------------- 1 | # Upstream 2 | 3 | Java resources produced by the OpenCV build process. This project extracts them to standard locations. 4 | 5 | Aritfact | Origin | Comments 6 | --- | --- | --- 7 | `res/` | `build/modules/java/test/.build/res/` | Test fixtures. 8 | `opencv-249.jar` | `build/modules/java/test/.build/bin/opencv-249.jar` | Java API with sources. 9 | `opencv-test.jar` | `build/modules/java/test/.build/jar/opencv-test.jar` | Compiled test classes. 10 | 11 | A primary goal of this package is to avoid making any modifications to the artifacts produced by the upstream OpenCV project. The listed resources will be incorporated into standard Maven conventions. 12 | --------------------------------------------------------------------------------