[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: bytedeco\n"
  },
  {
    "path": ".github/workflows/javacv.yml",
    "content": "name: javacv\non: [push, pull_request, workflow_dispatch]\nenv:\n  CI_DEPLOY_MODULE: .\n  CI_DEPLOY_PLATFORM: ${{ github.job }}\n  CI_DEPLOY_SETTINGS: ${{ secrets.CI_DEPLOY_SETTINGS }}\n  CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }}\n  CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }}\n  STAGING_REPOSITORY: ${{ secrets.STAGING_REPOSITORY }}\njobs:\n  linux-arm64:\n    runs-on: ubuntu-22.04-arm\n    steps:\n      - uses: bytedeco/javacpp-presets/.github/actions/deploy-ubuntu@actions\n      - uses: mxschmitt/action-tmate@v3\n        if: ${{ github.event_name == 'workflow_dispatch' }}\n  linux-x86_64:\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: bytedeco/javacpp-presets/.github/actions/deploy-ubuntu@actions\n      - uses: mxschmitt/action-tmate@v3\n        if: ${{ github.event_name == 'workflow_dispatch' }}\n  macosx-arm64:\n    runs-on: macos-15\n    steps:\n      - uses: bytedeco/javacpp-presets/.github/actions/deploy-macosx@actions\n      - uses: mxschmitt/action-tmate@v3\n        if: ${{ github.event_name == 'workflow_dispatch' }}\n  macosx-x86_64:\n    runs-on: macos-15-intel\n    steps:\n      - uses: bytedeco/javacpp-presets/.github/actions/deploy-macosx@actions\n      - uses: mxschmitt/action-tmate@v3\n        if: ${{ github.event_name == 'workflow_dispatch' }}\n  windows-x86_64:\n    runs-on: windows-2022\n    steps:\n      - uses: bytedeco/javacpp-presets/.github/actions/deploy-windows@actions\n      - uses: mxschmitt/action-tmate@v3\n        if: ${{ github.event_name == 'workflow_dispatch' }}\n  platform:\n    needs: [linux-x86_64, macosx-arm64, macosx-x86_64, windows-x86_64]\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: bytedeco/javacpp-presets/.github/actions/redeploy@actions\n"
  },
  {
    "path": ".gitignore",
    "content": "**/target/**\n\n# Mac\n.DS_Store\n\n# Eclipse\n.classpath\n.project\n.settings\n\n# IntelliJ\n*.iml\n.idea\n"
  },
  {
    "path": ".travis.yml",
    "content": "dist: xenial\ncache:\n  directories:\n    - $HOME/.m2/repository\n\nbefore_install:\n  - \"echo '<settings><localRepository>${env.HOME}/.m2/repository</localRepository><servers><server><id>sonatype-nexus-snapshots</id><username>${env.CI_DEPLOY_USERNAME}</username><password>${env.CI_DEPLOY_PASSWORD}</password></server></servers></settings>' > $HOME/settings.xml\"\n  - \"[[ $TRAVIS_PULL_REQUEST == 'false' ]] && export MAVEN_PHASE=deploy || export MAVEN_PHASE=install\"\n\njobs:\n  include:\n    - os: linux\n      arch: arm64\n      language: java\n      addons:\n        apt:\n          packages: openjdk-8-jdk openjfx maven\n      env: PLATFORMS=\"linux-arm64\"\n      install:\n        - export JAVA_HOME=\"/usr/lib/jvm/java-8-openjdk-arm64\"\n      script:\n        - mvn clean $MAVEN_PHASE -B -V -U -s $HOME/settings.xml\n        - mvn clean $MAVEN_PHASE -B -V -U -s $HOME/settings.xml -f platform/pom.xml -Djavacpp.platform=linux-arm64 '-Dtest=!FrameGrabberTest#testFFmpegFrameGrabber'\n    - os: linux\n      arch: ppc64le\n      language: java\n      addons:\n        apt:\n          packages: openjdk-8-jdk openjfx maven\n      env: PLATFORMS=\"linux-ppc64le\"\n      install:\n        - export JAVA_HOME=\"/usr/lib/jvm/java-8-openjdk-ppc64el\"\n      script:\n        - mvn clean $MAVEN_PHASE -B -V -U -s $HOME/settings.xml\n        - mvn clean $MAVEN_PHASE -B -V -U -s $HOME/settings.xml -f platform/pom.xml -Djavacpp.platform=linux-ppc64le\n    - os: linux\n      arch: amd64\n      language: java\n      addons:\n        apt:\n          packages: openjdk-8-jdk openjfx maven\n      env: PLATFORMS=\"linux-x86_64\"\n      install:\n        - export JAVA_HOME=\"/usr/lib/jvm/java-8-openjdk-amd64\"\n      script:\n        - mvn clean $MAVEN_PHASE -B -V -U -s $HOME/settings.xml\n        - mvn clean $MAVEN_PHASE -B -V -U -s $HOME/settings.xml -f platform/pom.xml -Djavacpp.platform=linux-x86_64\n    - os: osx\n      osx_image: xcode12.2\n      language: java\n      env: PLATFORMS=\"macosx-x86_64\"\n      install:\n        - brew update\n        - brew install gpg1\n        - brew tap AdoptOpenJDK/openjdk\n        - brew install --cask adoptopenjdk8\n        - export JAVA_HOME=$(/usr/libexec/java_home -v1.8)\n      script:\n        - mvn clean $MAVEN_PHASE -B -V -U -s $HOME/settings.xml\n        - mvn clean $MAVEN_PHASE -B -V -U -s $HOME/settings.xml -f platform/pom.xml -Djavacpp.platform=macosx-x86_64 '-Dtest=!FrameGrabberTest#testFFmpegFrameGrabber'\n    - os: windows\n      language: bash\n      env: PLATFORMS=\"windows-x86_64\"\n      install:\n        - powershell Install-WindowsFeature Server-Media-Foundation\n        - choco install jdk8 --params 'installdir=c:\\\\jdk8'\n        - choco install maven --version=3.6.3\n        - export JAVA_HOME=\"/c/jdk8/\"\n        - export PATH=\"$PATH:/c/ProgramData/chocolatey/lib/maven/apache-maven-3.6.3/bin/:/c/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/VC/Auxiliary/Build/\"\n      script:\n        - cmd.exe //C 'vcvarsall.bat amd64 && mvn clean %MAVEN_PHASE% -B -V -U -s %HOME%/settings.xml'\n        - cmd.exe //C 'vcvarsall.bat amd64 && mvn clean %MAVEN_PHASE% -B -V -U -s %HOME%/settings.xml -f platform/pom.xml -Djavacpp.platform=windows-x86_64 -Dtest=!FrameConverterTest#testOpenCV*'\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "\r\n * Compile classes with `parameters` bumping minimum requirements to Java SE 8 and Android 7.0 ([issue bytedeco/javacpp-presets#1739](https://github.com/bytedeco/javacpp-presets/issues/1739))\r\n\r\n### February 22, 2026 version 1.5.13\r\n * Call `Pointer.trimMemory()` on `FFmpegFrameGrabber/Filter/Recorder` close to reclaim system memory on Linux ([issue #2334](https://github.com/bytedeco/javacv/issues/2334))\r\n * Add `FrameRecorder.videoProfile` property ([pull #2361](https://github.com/bytedeco/javacv/pull/2361))\r\n * Upgrade dependencies for OpenBLAS 0.3.31, OpenCV 4.13.0, FFmpeg 8.0.1, Leptonica 1.87.0, Tesseract 5.5.2\r\n\r\n### June 30, 2025 version 1.5.12\r\n * Add `LibgdxFrameConverter` to convert `Frame` to Libgdx `Pixmap` ([pull #2315](https://github.com/bytedeco/javacv/pull/2315))\r\n * Fix `FFmpegFrameRecorder` dropped frame issues with audio samples ([pull #2307](https://github.com/bytedeco/javacv/pull/2307))\r\n * Add `FrameFilter.videoFilterArgs/audioFilterArgs` properties to support multiple different inputs ([pull #2304](https://github.com/bytedeco/javacv/pull/2304))\r\n * Ensure `FFmpegFrameGrabber.start()` skips over streams with no codecs ([issue #2299](https://github.com/bytedeco/javacv/issues/2299))\r\n * Add `FFmpegLogCallback.logRejectedOptions()` for debugging purposes ([pull #2301](https://github.com/bytedeco/javacv/pull/2301))\r\n * Upgrade dependencies for OpenBLAS 0.3.30, OpenCV 4.11.0, FFmpeg 7.1.1, Tesseract 5.5.1\r\n\r\n### November 16, 2024 version 1.5.11\r\n * Fix memory leak in `FFmpegFrameGrabber` when decoding from `InputStream` ([pull #2214](https://github.com/bytedeco/javacv/pull/2214))\r\n * Upgrade dependencies for OpenBLAS 0.3.28, OpenCV 4.10.0, FFmpeg 7.1, Leptonica 1.85.0, Tesseract 5.5.0\r\n\r\n### January 29, 2024 version 1.5.10\r\n * Work around `swscale` bug in `FFmpegFrameGrabber` for images with unaligned width ([issue #1960](https://github.com/bytedeco/javacv/issues/1960))\r\n * Improve `FFmpegFrameGrabber.setTimestamp()` further for MPEG-TS streams ([pull #2144](https://github.com/bytedeco/javacv/pull/2144))\r\n * Fix `module-info.java` broken since last release ([issue bytedeco/javacpp-presets#1414](https://github.com/bytedeco/javacpp-presets/issues/1414))\r\n * Add new `AudioSplitMergeHelper` sample for processing raw audio frames ([pull #2052](https://github.com/bytedeco/javacv/pull/2052))\r\n * Upgrade dependencies for OpenBLAS 0.3.26, OpenCV 4.9.0, FFmpeg 6.1.1, Leptonica 1.84.1, Tesseract 5.3.4\r\n\r\n### June 6, 2023 version 1.5.9\r\n * Add `FrameRecorder.videoSideData/audioSideData` properties and `FFmpegFrameRecorder.setDisplayRotation()` for convenience ([issue #1976](https://github.com/bytedeco/javacv/issues/1976))\r\n * Fix `FFmpegFrameGrabber.grab()` not returning audio frames buffered by the codec ([issue #1971](https://github.com/bytedeco/javacv/issues/1971))\r\n * Upgrade dependencies for OpenBLAS 0.3.23, OpenCV 4.7.0, FFmpeg 6.0 ([issue #1693](https://github.com/bytedeco/javacv/issues/1693)), librealsense2 2.53.1, Leptonica 1.83.0, Tesseract 5.3.1\r\n\r\n### November 2, 2022 version 1.5.8\r\n * Override `FFmpegFrameGrabber.getVideoCodecName()/getAudioCodecName()` to return names of opened codecs ([pull #1901](https://github.com/bytedeco/javacv/pull/1901))\r\n * Add `FrameGrabber.videoDisposition/audioDisposition` properties to select streams by disposition ([pull #1879](https://github.com/bytedeco/javacv/pull/1879))\r\n * Work around `OpenKinect2FrameGrabber` failing when provided with a pipeline on some system ([pull #1886](https://github.com/bytedeco/javacv/pull/1886))\r\n * Fix `FFmpegFrameRecorder.record()` incorrectly flushing the video codec on data frames ([issue #1858](https://github.com/bytedeco/javacv/issues/1858))\r\n * Improve accuracy of `FFmpegFrameGrabber.setFrameNumber()` ([pull #1851](https://github.com/bytedeco/javacv/pull/1851))\r\n * Add `FrameGrabber.resetStartTime()` to allow `grabAtFrameRate()` after operations such as seeking ([pull #1846](https://github.com/bytedeco/javacv/pull/1846))\r\n * Add `FrameGrabber.videoSideData/audioSideData` properties and `FFmpegFrameGrabber.getDisplayRotation()` for convenience ([issue #1361](https://github.com/bytedeco/javacv/issues/1361))\r\n * Add to `FFmpegFrameGrabber` and `FFmpegFrameRecorder` constructors taking a `URL` for convenience and clarity\r\n * Fix incorrect call to `opencv_calib3d.stereoRectify()` in `ProjectiveDevice` ([issue #1802](https://github.com/bytedeco/javacv/issues/1802))\r\n * Retry after 10 ms when `av_read_frame()` returns `EAGAIN` in `FFmpegFrameGrabber.grabFrame()` ([issue #1784](https://github.com/bytedeco/javacv/issues/1784))\r\n * Append `frame_rate=%d/%d` input parameter in `FFmpegFrameFilter` as required by `xfade` ([issue #1776](https://github.com/bytedeco/javacv/issues/1776))\r\n * Update `FFmpegStreamingTimeout` sample to use `timeout` instead of `stimeout` for RTSP ([pull #1758](https://github.com/bytedeco/javacv/pull/1758))\r\n * Restore static calls to `FFmpegFrameGrabber.tryLoad()` and `FFmpegFrameRecorder.tryLoad()` ([issue #1756](https://github.com/bytedeco/javacv/issues/1756))\r\n * Enable by default on `RealSense2FrameGrabber.start()` all color, depth, and IR streams as `videoStream` ([pull #1750](https://github.com/bytedeco/javacv/pull/1750))\r\n * Upgrade dependencies for OpenBLAS 0.3.21, OpenCV 4.6.0, FFmpeg 5.1.2, Leptonica 1.82.0 ([pull #1791](https://github.com/bytedeco/javacv/pull/1791)), Tesseract 5.2.0\r\n\r\n### February 11, 2022 version 1.5.7\r\n * Fix accuracy and latency issues with `FFmpegFrameGrabber.setVideoFrameNumber()` ([pull #1734](https://github.com/bytedeco/javacv/pull/1734))\r\n * Add new `Frame.pictType` field set to `I`, `P`, `B`, etc by `FFmpegFrameGrabber` ([pull #1730](https://github.com/bytedeco/javacv/pull/1730))\r\n * Set metadata for `AVFrame.opaque` in `FFmpegFrameGrabber` with call to `av_frame_copy_props()` ([issue #1729](https://github.com/bytedeco/javacv/issues/1729))\r\n * Add `charset` property to `FrameGrabber` and `FrameRecorder` to use for metadata from FFmpeg ([pull #1720](https://github.com/bytedeco/javacv/pull/1720))\r\n * Call `Frame.close()` on temporary clones in `Java2DFrameUtils` to prevent premature deallocations ([issue #1716](https://github.com/bytedeco/javacv/issues/1716))\r\n * Ignore errors from `avcodec_send_packet()` and `avcodec_receive_frame()` to emulate old API in `FFmpegFrameGrabber` ([issue #1679](https://github.com/bytedeco/javacv/issues/1679))\r\n * Upgrade dependencies for OpenBLAS 0.3.19, OpenCV 4.5.5, FFmpeg 5.0, librealsense2 2.50.0, Leptonica 1.82.0, Tesseract 5.0.1\r\n\r\n### August 2, 2021 version 1.5.6\r\n * Enhance audio and video synchronization of `JavaFxPlayVideoAndAudio` sample ([pull #1662](https://github.com/bytedeco/javacv/pull/1662))\r\n * Add `FrameGrabber.grabAtFrameRate()` to simulate a device or stream when reading from files ([pull #1659](https://github.com/bytedeco/javacv/pull/1659))\r\n * Update `FFmpegFrameGrabber` and `FFmpegFrameRecorder` with new `avcodec` API ([issue #1498](https://github.com/bytedeco/javacv/issues/1498))\r\n * Add new `Similarity` sample with PSNR and MSSIM ([pull #1622](https://github.com/bytedeco/javacv/pull/1622))\r\n * Avoid crash in `FFmpegFrameRecorder.stop()` by moving `av_write_trailer()` out of `flush()` ([issue #1616](https://github.com/bytedeco/javacv/issues/1616))\r\n * Upgrade dependencies for OpenBLAS 0.3.17, OpenCV 4.5.3, FFmpeg 4.4, librealsense2 2.44.0, Leptonica 1.81.1\r\n\r\n### March 8, 2021 version 1.5.5\r\n * Have `Frame` and `FrameConverter` implement `AutoCloseable` to release memory explicitly ([issue #1574](https://github.com/bytedeco/javacv/issues/1574))\r\n * Add new `YOLONet` sample for object detection ([pull #1595](https://github.com/bytedeco/javacv/pull/1595))\r\n * Fix crash on `FFmpegFrameGrabber.stop()` when in `ImageMode.RAW` ([issue #1568](https://github.com/bytedeco/javacv/issues/1568))\r\n * Let `FFmpegFrameRecorder.flush()` ignore errors from the encoder ([issue #1563](https://github.com/bytedeco/javacv/issues/1563))\r\n * Improve `FFmpegFrameGrabber.setTimestamp()` and fix `getAudioFrameRate()` ([pull #1559](https://github.com/bytedeco/javacv/pull/1559))\r\n * Fix frame rate and aspect ratio on `FFmpegFrameRecorder.start(AVFormatContext)` ([pull #1535](https://github.com/bytedeco/javacv/pull/1535))\r\n * Upgrade dependencies for OpenBLAS 0.3.13, OpenCV 4.5.1, FFmpeg 4.3.2, librealsense2 2.40.0\r\n * Update unit tests to use codecs available in FFmpeg under LGPL v3 ([pull bytedeco/javacpp-presets#950](https://github.com/bytedeco/javacpp-presets/pull/950))\r\n * Add `RealSense2FrameGrabber.tryLoad()` method and missing entries for librealsense2 ([issue bytedeco/procamcalib#25](https://github.com/bytedeco/procamcalib/issues/25))\r\n\r\n### September 9, 2020 version 1.5.4\r\n * Fix error message thrown from `FFmpegFrameRecorder.start()` not containing filename ([pull #1492](https://github.com/bytedeco/javacv/pull/1492))\r\n * Fix `FFmpegFrameFilter.pull()` not returning audio/video frames without audio/video filtergraph ([issue #1466](https://github.com/bytedeco/javacv/issues/1466))\r\n * Update `OpenCVFrameConverter.convertToOrgOpenCvCoreMat()` with new API to set the stride ([issue #1460](https://github.com/bytedeco/javacv/issues/1460))\r\n * Fix memory leaks and reduce memory fragmentation in `FFmpegFrameGrabber` and `FFmpegFrameRecorder` ([issue #1366](https://github.com/bytedeco/javacv/issues/1366))\r\n * Use `PointerScope` in `FFmpegFrameFilter`, `FFmpegFrameGrabber`, and `FFmpegFrameRecorder` to deallocate quickly temporary buffers ([issue #1383](https://github.com/bytedeco/javacv/issues/1383))\r\n * Fix `FFmpegFrameFilter` by calling `String.format()` with `Locale.ROOT` ([pull #1441](https://github.com/bytedeco/javacv/pull/1441))\r\n * Increase thread safety of `FFmpegFrameFilter`, `FFmpegFrameGrabber`, and `FFmpegFrameRecorder` with `synchronized` methods ([issue #1434](https://github.com/bytedeco/javacv/issues/1434))\r\n * Upgrade dependencies for OpenBLAS 0.3.10, OpenCV 4.4.0, FFmpeg 4.3.1, and Leptonica 1.80.0\r\n\r\n### April 14, 2020 version 1.5.3\r\n * Add `FFmpegFrameGrabber.start(boolean findStreamInfo)` parameter to minimize startup time ([issue #1376](https://github.com/bytedeco/javacv/issues/1376))\r\n * Let `FFmpegFrameGrabber.grab()` return non-audio/video streams as new `Frame.DATA` type ([pull #1378](https://github.com/bytedeco/javacv/pull/1378))\r\n * Fix crash in `FFmpegFrameRecorder.flush()` for HLS format and possibly others ([pull #1374](https://github.com/bytedeco/javacv/pull/1374))\r\n * Fix \"Resetting to invalid mark\" `IOException` thrown on `FFmpegFrameGrabber.release()` ([issue #911](https://github.com/bytedeco/javacv/issues/911))\r\n * Upgrade dependencies for OpenBLAS 0.3.9, OpenCV 4.3.0, FFmpeg 4.2.2, Leptonica 1.79.0, and Tesseract 4.1.1\r\n * Add `Seekable` and `SeekableByteArrayOutputStream` to be used with `FFmpegFrameRecorder` ([pull #1350](https://github.com/bytedeco/javacv/pull/1350))\r\n * Update `RealSense2FrameGrabber` with support for sensor options and fix for multiple devices ([pull #1348](https://github.com/bytedeco/javacv/pull/1348))\r\n\r\n### November 5, 2019 version 1.5.2\r\n * Increase thread safety of `FFmpegFrameFilter`, `FFmpegFrameGrabber`, and `FFmpegFrameRecorder` with `volatile boolean started` flag ([pull #1325](https://github.com/bytedeco/javacv/pull/1325))\r\n * Let `FFmpegFrameFilter.push(null)` indicate EOF to audio filters as well ([issue #1315](https://github.com/bytedeco/javacv/issues/1315))\r\n * Add `RealSense2FrameGrabber` to capture images with librealsense2 ([pull #1316](https://github.com/bytedeco/javacv/pull/1316))\r\n * Disable seek function in `FFmpegFrameGrabber` when `maximumSize <= 0` ([issue #1304](https://github.com/bytedeco/javacv/issues/1304))\r\n * Use `Pointer.retainReference()` to prevent `PointerScope` from deallocating globally shared callback objects for FFmpeg\r\n * Fix `FFmpegFrameRecorder` failing to encode `float` samples in MP3 ([issue #1294](https://github.com/bytedeco/javacv/issues/1294))\r\n * Fix `OpenCVFrameConverter` error in `IPCameraFrameGrabber` ([pull #1278](https://github.com/bytedeco/javacv/pull/1278))\r\n * Allow setting properties for `OpenCVFrameGrabber` and `OpenCVFrameRecorder` with `setOption()` ([issue #1269](https://github.com/bytedeco/javacv/issues/1269))\r\n * Add missing `requires java.desktop` to `module-info.java` ([issue #1265](https://github.com/bytedeco/javacv/issues/1265))\r\n * Upgrade dependencies for OpenBLAS 0.3.7, OpenCV 4.1.2, FFmpeg 4.2.1, librealsense 1.12.4, and librealsense2 2.29.0\r\n\r\n### July 9, 2019 version 1.5.1\r\n * Work around `swscale` bug in `FFmpegFrameGrabber` for images with unaligned width ([issue #845](https://github.com/bytedeco/javacv/issues/845))\r\n * Add support for `AVSEEK_SIZE` to `FFmpegFrameGrabber` as required by MPEG-TS ([issue #1234](https://github.com/bytedeco/javacv/issues/1234))\r\n * Throw exception on `start()` for already started `FFmpegFrameFilter`, `FFmpegFrameGrabber`, or `FFmpegFrameRecorder` ([issue #1233](https://github.com/bytedeco/javacv/issues/1233))\r\n * Add dependency on OpenBLAS/MKL, now used by OpenCV to accelerate some matrix operations\r\n * Upgrade dependencies for OpenCV 4.1.0, libdc1394 2.2.6, and Tesseract 4.1.0\r\n * Add support for `Frame.timestamp` to `FFmpegFrameFilter` ([issue #1177](https://github.com/bytedeco/javacv/issues/1177))\r\n\r\n### April 11, 2019 version 1.5\r\n * Override methods in `FFmpegFrameGrabber` to get all metadata from streams ([issue #1180](https://github.com/bytedeco/javacv/issues/1180))\r\n * Fix sample rate in output of `FFmpegFrameRecorder` by setting deprecated `AVStream.codec.time_base` ([issue #1179](https://github.com/bytedeco/javacv/issues/1179))\r\n * Add `asetpts=N` to input of `FFmpegFrameFilter` to make filters like `afade` behave as expected ([issue #1171](https://github.com/bytedeco/javacv/issues/1171))\r\n * Use `AVFormat.format()` from `Frame.opaque` when available in `FFmpegFrameFilter` and `FFmpegFrameRecorder` ([issue #1173](https://github.com/bytedeco/javacv/issues/1173))\r\n * Enable multithreading for all codecs by default in `FFmpegFrameGrabber` and `FFmpegFrameRecorder` ([issue #1163](https://github.com/bytedeco/javacv/issues/1163))\r\n * Improve thread safety of `FFmpegFrameRecorder` and `Java2DFrameConverter` by relying less on `Buffer.position` ([pull #1166](https://github.com/bytedeco/javacv/pull/1166))\r\n * Use ModiTect to compile `module-info.java` with JDK 8 and preserve backward compatibility\r\n * Add `FFmpegFrameRecorder.closeOutputStream` and `FFmpegFrameGrabber.closeInputStream` properties to leave streams opened ([issue #1149](https://github.com/bytedeco/javacv/issues/1149))\r\n * Add `FFmpegFrameRecorder.flush()` method that does not release the stream ([issue #1149](https://github.com/bytedeco/javacv/issues/1149))\r\n * Readd `synchronized` blocks for `FFmpegFrameGrabber` and `FFmpegFrameRecorder`, but make unsafe methods public ([issue #1139](https://github.com/bytedeco/javacv/issues/1139))\r\n * Allocate native memory for `Frame` using `Pointer` to allow deallocation with `PointerScope` ([issue #1152](https://github.com/bytedeco/javacv/issues/1152))\r\n * Add `module-info.java` and depend on modularized JavaCPP Presets to comply with JPMS\r\n * Upgrade dependencies for FFmpeg 4.1.3, libfreenect 0.5.7, and Leptonica 1.78.0\r\n * Allow allocation of `Frame` images with custom strides\r\n * Take into account `Bitmap.getRowBytes()` in `AndroidFrameConverter.convert(Bitmap)` ([issue #1143](https://github.com/bytedeco/javacv/issues/1143))\r\n * Add `static { Loader.load(); }` in `LeptonicaFrameConverter` and `OpenCVFrameConverter` to prevent link errors ([issue #1128](https://github.com/bytedeco/javacv/issues/1128))\r\n\r\n### January 11, 2019 version 1.4.4\r\n * Add `FFmpegFrameGrabber(InputStream, int)` constructor to set the maximum cache size used for seeking\r\n * Set `pts` and `dts` for `AVPacket` in `FFmpegFrameRecorder.recordPacket()` ([pull #1097](https://github.com/bytedeco/javacv/pull/1097))\r\n * Prevent premature deallocations with `LeptonicaFrameConverter` ([issue bytedeco/javacpp#272](https://github.com/bytedeco/javacpp/issues/272)) and `OpenCVFrameConverter.IplImage` ([issue #1101](https://github.com/bytedeco/javacv/issues/1101))\r\n * Fix `OpenCVFrameGrabber` from crashing when in `ImageMode.GRAY`\r\n * Add support for multiple inputs to `FFmpegFrameFilter` ([issue #955](https://github.com/bytedeco/javacv/issues/955))\r\n * Fix fps in output of `FFmpegFrameRecorder` by setting deprecated `AVStream.codec.time_base` ([issue #1069](https://github.com/bytedeco/javacv/issues/1069))\r\n * Fix memory leak in `FFmpegFrameRecorder` on `writePacket()` ([issue #1068](https://github.com/bytedeco/javacv/issues/1068))\r\n * Upgrade dependencies for OpenCV 4.0.1, FFmpeg 4.1, FlyCapture 2.13.3.31, Leptonica 1.77.0, and Tesseract 4.0.0\r\n\r\n### October 15, 2018 version 1.4.3\r\n * Add `imageScalingFlags` property to `FrameGrabber` and `FrameRecorder`, with `SWS_BILINEAR` as default for FFmpeg ([issue #845](https://github.com/bytedeco/javacv/issues/845))\r\n * Add `OpenCVFrameConverter.ToOrgOpenCvCoreMat` to easily but efficiently get image data from official Java API of OpenCV ([issue bytedeco/javacpp#38](https://github.com/bytedeco/javacpp/issues/38))\r\n * Keep globally shared callback objects for FFmpeg out of `PointerScope` ([issue #911](https://github.com/bytedeco/javacv/issues/911))\r\n * Upgrade dependencies for OpenCV 3.4.3, FFmpeg 4.0.2, and Tesseract 4.0.0-rc2\r\n * Update the `Demo` class to use the C++ API of OpenCV ([issue #1042](https://github.com/bytedeco/javacv/issues/1042))\r\n * Add new `DeepLearningFaceDetection` ([pull #1041](https://github.com/bytedeco/javacv/pull/1041)) and `PerspectiveWarpDemo` ([pull #1066](https://github.com/bytedeco/javacv/pull/1066)) samples\r\n\r\n### July 17, 2018 version 1.4.2\r\n * Allow `FFmpegFrameGrabber` to use accelerated decoders with `videoCodecName` and `audioCodecName` properties ([pull #948](https://github.com/bytedeco/javacv/pull/948))\r\n * Add new `KazemiFacemarkExample` and `LBFFacemarkExampleWithVideo` samples ([pull #1030](https://github.com/bytedeco/javacv/pull/1030))\r\n * Expose `apiPreference` constructor argument of `VideoCapture` to `OpenCVFrameGrabber` ([pull #1025](https://github.com/bytedeco/javacv/pull/1025))\r\n * Add `LeptonicaFrameConverter` to easily but efficiently pass image data to Tesseract ([issue bytedeco/javacpp-presets#224](https://github.com/bytedeco/javacpp-presets/issues/224))\r\n * Update `RecordActivity` to fix issue with pixel formats ([issue #979](https://github.com/bytedeco/javacv/issues/979))\r\n * Fix `FFmpegFrameFilter` to support negative strides returned by \"vflip\" ([pull #977](https://github.com/bytedeco/javacv/pull/977))\r\n * Fix `FFmpegFrameFilter` on Mac throwing \"Resource temporarily unavailable\" ([issue #974](https://github.com/bytedeco/javacv/issues/974))\r\n * Upgrade dependencies for OpenCV 3.4.2, FFmpeg 4.0.1 and Tesseract 4.0.0-beta.3\r\n * Add initial limited version of `JavaFXFrameConverter` ([pull #969](https://github.com/bytedeco/javacv/pull/969))\r\n * Revert default behavior of `FFmpegFrameGrabber.setTimestamp()` to previous version ([pull #949](https://github.com/bytedeco/javacv/pull/949))\r\n * Add support for audio frames to `FFmpegFrameFilter` ([issue #492](https://github.com/bytedeco/javacv/issues/492))\r\n * Add `setpts=N` to input of `FFmpegFrameFilter` to make `fade` and `overlay` filters behave as expected ([issue #667](https://github.com/bytedeco/javacv/issues/667))\r\n * Fix crash on `FFmpegFrameRecorder.stop()` when no audio samples are left to write\r\n\r\n### March 29, 2018 version 1.4.1\r\n * Improve seeking and frame number estimates in `FFmpegFrameGrabber` ([pull #908](https://github.com/bytedeco/javacv/pull/908))\r\n * Add `maxBFrames`, `trellis`, and `maxDelay` properties to `FFmpegFrameRecorder` ([pull #939](https://github.com/bytedeco/javacv/pull/939))\r\n * Introduce `FFmpegFrameGrabber.maxDelay` property ([pull #938](https://github.com/bytedeco/javacv/pull/938))\r\n * Upgrade dependencies for OpenCV 3.4.1, FFmpeg 3.4.2\r\n * Allow enabling streams with `RealSenseFrameGrabber.setFormat()` for compatibility ([pull #922](https://github.com/bytedeco/javacv/pull/922))\r\n * Process audio frames after `FFmpegFrameGrabber.setTimestamp()` to avoid corrupted images ([issue #896](https://github.com/bytedeco/javacv/issues/896))\r\n * Fix `FFmpegFrameRecorder` not flushing all audio samples properly ([pull #886](https://github.com/bytedeco/javacv/pull/886))\r\n * Give access to pixel format, etc for images returned by `FFmpegFrameFilter.pull()` ([issue #887](https://github.com/bytedeco/javacv/issues/887))\r\n\r\n### January 16, 2018 version 1.4\r\n * Decode audio frames on `FFmpegFrameGrabber.setTimestamp()` to avoid sync issues ([pull #871](https://github.com/bytedeco/javacv/pull/871))\r\n * Give access to options and metadata `Map` from `FrameGrabber` and `FrameRecorder` ([issue #858](https://github.com/bytedeco/javacv/issues/858))\r\n * Make `FFmpegFrameGrabber(InputStream)` and `FFmpegFrameRecorder(OutputStream)` thread-safe\r\n * Add new `OpenCVFeatures2dSerialization` sample ([pull #842](https://github.com/bytedeco/javacv/pull/842))\r\n * Upgrade dependencies for OpenCV 3.4.0, FFmpeg 3.4.1, librealsense 1.12.1\r\n * Fix potential audio read issue in `WebcamAndMicrophoneCapture` sample ([issue #826](https://github.com/bytedeco/javacv/issues/826))\r\n * Update `JavaFxPlayVideoAndAudio` sample to support `FrameGrabber.sampleMode` property ([issue #820](https://github.com/bytedeco/javacv/issues/820))\r\n * Set the `Frame.timestamp` field on `FFmpegFrameGrabber.grab()` ([pull #810](https://github.com/bytedeco/javacv/pull/810))\r\n * Fix image loading issue with the `CaffeGooglenet.java` sample ([pull #805](https://github.com/bytedeco/javacv/pull/805))\r\n * Prevent `FFmpegFrameGrabber.setTimestamp()` from going into an infinite loop ([issue #731](https://github.com/bytedeco/javacv/issues/731))\r\n * Fix `FFmpegFrameRecorder.record()` when called with `AV_PIX_FMT_NV21` ([pull #787](https://github.com/bytedeco/javacv/pull/787))\r\n * Add `FFmpegLockCallback` to use more efficient thread-safe mechanisms ([pull #770](https://github.com/bytedeco/javacv/pull/770))\r\n * Make `FFmpegFrameGrabber` support streams with changing resolution ([pull #769](https://github.com/bytedeco/javacv/pull/769))\r\n * Add new `DeinterlacedVideoPlayer` sample ([pull #757](https://github.com/bytedeco/javacv/pull/757))\r\n\r\n### July 25, 2017 version 1.3.3\r\n * Fix `Java2DFrameConverter.cloneBufferedImage()` not copying the data ([pull #739](https://github.com/bytedeco/javacv/pull/739))\r\n * Make sure `OpenCVFrameConverter` always resets `Frame.opaque` even when `Pointer` is equal ([issue deeplearning4j/DataVec#316](https://github.com/deeplearning4j/DataVec/issues/316))\r\n * Fix `OutputStream` leak in `FFmpegFrameRecorder` ([pull #727](https://github.com/bytedeco/javacv/pull/727))\r\n * Synchronize on `FFmpegFrameRecorder.stop()` to avoid potential race conditions ([issue #700](https://github.com/bytedeco/javacv/issues/700))\r\n * Add `src/main/java/cl/eye/CLCamera.java` to remove build dependency on external module\r\n * Fix seeking issues with `FFmpegFrameGrabber(InputStream)` ([pull #703](https://github.com/bytedeco/javacv/pull/703))\r\n * Upgrade dependencies for FFmpeg 3.3.2, FlyCapture 2.11.3.121 ([pull bytedeco/javacpp-presets#424](https://github.com/bytedeco/javacpp-presets/pull/424))\r\n * Initialize the `avdevice` module for `FFmpegFrameRecorder` in the same way as with `FFmpegFrameGrabber`\r\n * Add `FrameGrabber.sampleMode` property and have `FFmpegFrameGrabber` convert audio samples to user-specified format ([issue #18](https://github.com/bytedeco/javacv/issues/18))\r\n * Add new `ImageSegmentation` ([pull #460](https://github.com/bytedeco/javacv/pull/460)) and `FFmpegStreamingTimeout` ([pull #712](https://github.com/bytedeco/javacv/pull/712)) samples\r\n * Fix up and add missing functionality to `FlyCapture2FrameGrabber` ([pull #655](https://github.com/bytedeco/javacv/pull/655))\r\n * Take `OpenCVFrameGrabber.setFormat()` value to set FOURCC of `VideoCapture` ([pull #651](https://github.com/bytedeco/javacv/pull/651))\r\n * Fix call to `FaceRecognizer.predict()` in samples ([issue #642](https://github.com/bytedeco/javacv/issues/642))\r\n\r\n### March 13, 2017 version 1.3.2\r\n * Add `Java2DFrameUtils` to facilitate conversion between `Frame`, `BufferedImage`, `IplImage`, and `Mat`\r\n * Add new `JavaFxPlayVideoAndAudio` sample ([pull #618](https://github.com/bytedeco/javacv/pull/618))\r\n * Get rid of deprecated calls in `FFmpegFrameFilter`, `FFmpegFrameGrabber` and `FFmpegFrameRecorder` ([issue #607](https://github.com/bytedeco/javacv/issues/607))\r\n * Fix crash in `FFmpegFrameGrabber.restart()` ([issue #605](https://github.com/bytedeco/javacv/issues/605))\r\n * Upgrade dependencies for OpenCV 3.2.0, FFmpeg 3.2.1, libdc1394 2.2.5\r\n\r\n### January 14, 2017 version 1.3.1\r\n * Let `FFmpegFrameRecorder` pass options to the protocol as well ([issue #598](https://github.com/bytedeco/javacv/issues/598))\r\n * Add `RealSenseFrameGrabber` and `OpenKinect2FrameGrabber` to `FrameGrabber.list` to have them loaded by default\r\n * Remove confusing and no longer useful profiles from the `pom.xml` file\r\n * Provide new `FFmpegFrameGrabber(InputStream)` and `FFmpegFrameRecorder(OutputStream)` constructors ([issue #95](https://github.com/bytedeco/javacv/issues/95))\r\n * Make `FrameFilter`, `FrameGrabber`, and `FrameRecorder` implement `Closeable` to let us try-with-resources\r\n * Fix potential crash when recording audio with `FFmpegFrameRecorder`\r\n * Add `OpenKinect2FrameGrabber` to capture images with libfreenect2 ([pull #584](https://github.com/bytedeco/javacv/pull/584))\r\n * Add `OpenKinectFrameGrabber.grabIR()` and stabilize `RealSenseFrameGrabber` ([pull #585](https://github.com/bytedeco/javacv/pull/585))\r\n\r\n### December 7, 2016 version 1.3\r\n * Fix unnecessary memory allocation in `OpenCVFrameGrabber` ([pull #575](https://github.com/bytedeco/javacv/pull/575))\r\n * Add `FFmpegFrameFilter` to `RecordActivity` sample for Android ([pull #550](https://github.com/bytedeco/javacv/pull/550))\r\n * Introduce platform artifact for easier cross-platform builds and to avoid issues with some build systems ([issue #395](https://github.com/bytedeco/javacv/issues/395))\r\n * Add `RealSenseFrameGrabber` to capture images with librealsense ([pull #486](https://github.com/bytedeco/javacv/pull/486))\r\n * Add `BioInspiredRetina.java` sample for the `opencv_bioinspired` module ([pull #505](https://github.com/bytedeco/javacv/pull/505))\r\n * Update the `JavaCV` class with appropriate documentation comments ([issue #444](https://github.com/bytedeco/javacv/issues/444))\r\n * Fix Javadoc links for externally referenced classes\r\n * Fix seeking when calling `FFmpegFrameGrabber.setTimestamp()` on audio-only files\r\n * Add more appropriate default pixel formats for JPEG formats in `FFmpegFrameRecorder` ([issue #410](https://github.com/bytedeco/javacv/issues/410))\r\n\r\n### May 15, 2016 version 1.2\r\n * Optimize `AndroidFrameConverter` a bit and add a test ([pull #379](https://github.com/bytedeco/javacv/pull/379))\r\n * Fix `DC1394FrameGrabber` on the Windows platform ([issue bytedeco/procamcalib#4](https://github.com/bytedeco/procamcalib/issues/4))\r\n * Support `AVPacket` in `FFmpegFrameGrabber` and `FFmpegFrameRecorder` to copy without re-encoding ([issue #93](https://github.com/bytedeco/javacv/issues/93))\r\n * Lower Maven prerequisite in the `pom.xml` file to 3.0 ([issue bytedeco/javacpp#93](https://github.com/bytedeco/javacpp/issues/93))\r\n * Add new `PrincipalComponentAnalysis` sample ([pull #373](https://github.com/bytedeco/javacv/pull/373))\r\n * Upgrade `OpenCVFrameRecorder` to use the new C++ `VideoWriter` API ([pull #370](https://github.com/bytedeco/javacv/pull/370))\r\n * Upgrade `OpenCVFrameGrabber` to use the new C++ `VideoCapture` API ([pull #361](https://github.com/bytedeco/javacv/pull/361))\r\n * Add `CaffeGooglenet.java` sample for the `opencv_dnn` module ([pull #341](https://github.com/bytedeco/javacv/pull/341))\r\n * Clean up `IPCameraFrameGrabber` and fix incorrectly reading some headers ([pull #323](https://github.com/bytedeco/javacv/pull/323), [pull #345](https://github.com/bytedeco/javacv/pull/345))\r\n * Fix swallowed `InterruptedException` and throw appropriate exception in `FrameGrabber.start()` ([issue #315](https://github.com/bytedeco/javacv/issues/315))\r\n * Fix `IPCameraFrameGrabber.stop()` not checking for null ([pull #300](https://github.com/bytedeco/javacv/pull/300))\r\n * Upgrade dependencies for OpenCV 3.1.0, FFmpeg 3.0.2, FlyCapture 2.9.3.43, libdc1394 2.2.4\r\n * Let users call `FFmpegFrameFilter.push(null)` to indicate EOF, as required by some filters like \"palettegen\" ([issue #287](https://github.com/bytedeco/javacv/issues/287))\r\n * Call `cvHaarDetectObjects()` with `CV_HAAR_FIND_BIGGEST_OBJECT | CV_HAAR_DO_ROUGH_SEARCH` instead of `CV_HAAR_DO_CANNY_PRUNING` in the face detection samples to get acceptable performance with OpenCV 3.0 ([issue #272](https://github.com/bytedeco/javacv/issues/272))\r\n * Change `WakeLock` for `keepScreenOn` in `AndroidManifest.xml` file and add `setPreviewDisplay()` call on `surfaceChanged()` event for the `RecordActivity` sample ([pull #269](https://github.com/bytedeco/javacv/pull/269), [pull #271](https://github.com/bytedeco/javacv/pull/271))\r\n\r\n### October 25, 2015 version 1.1\r\n * Make `FrameConverter` for images return `null` when `Frame.image == null` ([issue #249](https://github.com/bytedeco/javacv/issues/249))\r\n * Add `FFmpegLogCallback` to redirect easily to Java log messages from FFmpeg\r\n * Upgrade all Maven dependencies and plugins to latest versions, thus bumping minimum requirements to Java SE 7, Android 4.0, and Maven 3.0\r\n * Fix broken `FFmpegFrameGrabber.grabImage()` after `setTimestamp()` ([issue #236](https://github.com/bytedeco/javacv/issues/236))\r\n * Add `FFmpegFrameGrabber.grabSamples()` to grab only audio samples, and ignore video frames ([issue #235](https://github.com/bytedeco/javacv/issues/235))\r\n * Fix broken `setVideoCodecName()` and `setAudioCodecName()` for `FFmpegFrameRecorder` ([issue #229](https://github.com/bytedeco/javacv/issues/229))\r\n * Remove `FaceRecognition.java` sample, which requires the deprecated `opencv_legacy` module ([issue #200](https://github.com/bytedeco/javacv/issues/200))\r\n * Fix potential crash in `ObjectFinder` with FLANN ([issue #210](https://github.com/bytedeco/javacv/issues/210))\r\n * Add `FFmpegFrameFilter` to let users process `Frame` images with `libavfilter` easily ([issue #164](https://github.com/bytedeco/javacv/issues/164))\r\n * Add `FaceRecognizerInVideo.java` sample that does a combo of face detection and recognition ([issue #203](https://github.com/bytedeco/javacv/issues/203))\r\n * Return `AVStream.r_frame_rate` when `AVStream.avg_frame_rate` is invalid in `FFmpegFrameGrabber.getFrameRate()` ([issue #292](https://code.google.com/p/javacv/issues/detail?id=292))\r\n * Update some samples to make them work with OpenCV 3.0\r\n * Add new convenience `FFmpegFrameRecorder.record(Frame frame, int pixelFormat)` method ([issue #181](https://github.com/bytedeco/javacv/issues/181))\r\n * Let `Java2DFrameConverter.copy()` from `ByteBuffer` with 4 channels to `BufferedImage.TYPE_INT_RGB`, among others, also taking into account the `flipChannels` argument ([issue #181](https://github.com/bytedeco/javacv/issues/181))\r\n\r\n### July 11, 2015 version 1.0\r\n * Offer the Apache License, Version 2.0, as a new choice of license, in addition to the GPLv2 with Classpath exception\r\n * Upgrade support to OpenCV 3.0.0\r\n * Upgrade supported FFmpeg API to the 2.7 release branch\r\n * Switch descriptor used by `ObjectFinder` from SURF to AKAZE\r\n * Let users get resized images from `FFmpegFrameGrabber` by calling `setImageWidth()` and `setImageHeight()` before `start()`\r\n * Add check for supported display size in the `RecordActivity` sample ([pull #153](https://github.com/bytedeco/javacv/pull/153))\r\n * Clarify the semantics of `FrameConverter` ([issue #150](https://github.com/bytedeco/javacv/issues/150))\r\n * Fix `FFmpegFrameRecorder` not saving the last few frames, especially when encoding with x264 ([issue #50](https://github.com/bytedeco/javacv/issues/50))\r\n * Add `FrameConverterTest` and fix a couple of bugs uncovered by it\r\n * Make `Frame implements Indexable` for easy and efficient access to image pixels\r\n * Fix `AbstractMethodError` thrown from `OpenCVFrameConverter` on some versions of the JDK ([issue #143](https://github.com/bytedeco/javacv/issues/143))\r\n * Add `FFmpegFrameGrabber.grabImage()` method to restore the functionality previously provided by `IplImage grab()` ([issue #116](https://github.com/bytedeco/javacv/issues/116))\r\n * Give users of `FFmpegFrameGrabber` and `FFmpegFrameRecorder` access to more options and metadata ([issue #132](https://github.com/bytedeco/javacv/issues/132))\r\n * Add the ability to specify from which video and audio streams `FFmpegFrameGrabber` should grab from ([issue #135](https://github.com/bytedeco/javacv/issues/135))\r\n * Fix `Java2DFrameConverter` when used with `BufferedImage.TYPE_INT_RGB` or other types based on `int` ([issue #140](https://github.com/bytedeco/javacv/issues/140))\r\n * Add new `WebcamAndMicrophoneCapture` sample ([pull #131](https://github.com/bytedeco/javacv/pull/131))\r\n * Add `aspectRatio` property to `FrameGrabber` and `FrameRecorder`, to be able to use pixel aspect ratios other than 1.0 ([issue #90](https://github.com/bytedeco/javacv/issues/90))\r\n\r\n### April 4, 2015 version 0.11\r\n * Upgrade support to OpenCV 2.4.11\r\n * Upgrade supported FFmpeg API to the 2.6 release branch\r\n * Add new `Square` sample, thanks to Geir Ruud\r\n * Add `AndroidFrameConverter`, `Java2DFrameConverter` and `OpenCVFrameConverter`, and use them to refactor `Frame`, `CanvasFrame`, `FrameGrabber`, and `FrameRecorder` in a way to help users avoid coupling with Android, Java 2D, or OpenCV ([issue #84](https://github.com/bytedeco/javacv/issues/84))\r\n * Fix `Demo` class in the `README.md` file ([issue #102](https://github.com/bytedeco/javacv/issues/102))\r\n * Add new `ColoredObjectTrack` sample ([pull #99](https://github.com/bytedeco/javacv/pull/99))\r\n * Add `option` property to `FFmpegFrameGrabber` to let users set such things as \"analyzeduration\", \"probesize\", or \"list_devices\"\r\n * Fix \"AVFrame.format is not set\" and \"AVFrame.width or height is not set\" warning messages ([issue #76](https://github.com/bytedeco/javacv/issues/76))\r\n\r\n### December 23, 2014 version 0.10\r\n * Upgrade support to OpenCV 2.4.10\r\n * Upgrade supported FFmpeg API to the 2.5 release branch\r\n * Fix `time_base` warnings displayed by `FFmpegFrameRecorder` ([issue #75](https://github.com/bytedeco/javacv/issues/75))\r\n * Add new `TemplateMatching` sample, thanks to Waldemar Neto\r\n * Update instructions in the `README.md` file for manual installation in Android Studio\r\n * Replace deprecated `CvMat` and `IplImage` functionality used in the `Demo` class with new `Indexer` API, and provide sample `pom.xml` file for Maven\r\n * Make `FFmpegFrameGrabber.getFrameRate()` return `AVStream.avg_frame_rate` instead of `r_frame_rate` ([issue #63](https://github.com/bytedeco/javacv/issues/63))\r\n * Disable DocLint, which prevents the build from succeeding on Java 8 ([issue bytedeco/javacpp#5](https://github.com/bytedeco/javacpp/issues/5))\r\n * Add `FlyCapture2FrameGrabber` with cross-platform support of FlyCapture2 ([pull #45](https://github.com/bytedeco/javacv/pull/45))\r\n * Fix issue that would prevent `CanvasFrame` from working on Mac OS X with recent versions of the JDK ([issue #39](https://github.com/bytedeco/javacv/issues/39) and [issue #314](http://code.google.com/p/javacv/issues/detail?id=314))\r\n * Upgrade `RecordActivity` sample with a continuous record loop, thanks to Federico Sendra and Juan Manuel Sobral\r\n * Make `FrameGrabber.createDefault()` throw an exception on unsupported input, instead of returning a cryptic `null` ([issue #30](https://github.com/bytedeco/javacv/issues/30))\r\n * Add `videoCodec`, `videoBitrate`, `audioCodec`, and `audioBitrate` properties to `FrameGrabber`\r\n * Work around `avcodec` and `avdevice` not loading properly for `FFmpegFrameGrabber` and `FFmpegFrameRecorder` ([issue #24](https://github.com/bytedeco/javacv/issues/24))\r\n * Do key frame detection in `FFmpegFrameRecorder` based on `AVPacket`, not `AVPicture` ([pull #20](https://github.com/bytedeco/javacv/pull/20))\r\n\r\n### July 27, 2014 version 0.9\r\n * Remove `platform` property from `pom.xml`, replaced with the `platform.dependency` one in JavaCPP Presets ([issue #10](https://github.com/bytedeco/javacv/issues/10))\r\n * Add new `RLSA` sample, thanks to Nicholas Woodward ([issue #469](http://code.google.com/p/javacv/issues/detail?id=469))\r\n * Fix a timestamp rounding issue in `FFmpegFrameGrabber` that causes `setFrameNumber()` to sometimes pick the wrong frame if FPS is not a proper divisor of 1000000 ([issue #5](https://github.com/bytedeco/javacv/issues/5))\r\n * Increase the flexibility of the `pom.xml` file by making it possible to specify a custom version of JavaCPP\r\n * Add missing dependencies for JogAmp in the `pom.xml` file ([issue #2](https://github.com/bytedeco/javacv/issues/2))\r\n * Add new `OpenCVFaceRecognizer` sample, thanks to Petter Christian Bjelland\r\n * Add new `OpticalFlowDense` sample, thanks to Dawit Gebreyohannes ([issue #468](http://code.google.com/p/javacv/issues/detail?id=468))\r\n * Make it easier to try out the `FaceRecognition.java` sample ([issue #1](https://github.com/bytedeco/javacv/issues/1))\r\n\r\n### April 28, 2014 version 0.8\r\n * Move from Google Code to GitHub as main source code repository\r\n * Upgrade support to OpenCV 2.4.9\r\n * Upgrade supported FFmpeg API to the 2.2 release branch\r\n * Fix `FFmpegFrameRecorder` not refreshing the resampler when the format of samples changes (issue #465)\r\n * Rename the `com.googlecode.javacv.cpp` package to `org.bytedeco.javacpp`, and `com.googlecode.javacv` to `org.bytedeco.javacv`\r\n * Removed old NetBeans project files that cause a conflict when trying to open as a Maven project (issue #210)\r\n * Adjusted the samples a bit because of small changes in the API with the move to the JavaCPP Presets\r\n * Fixed `ObjectFinder` not working with recent versions of OpenCV, especially on Android (issue #214)\r\n * Added new `FrameRecorder.gopSize` property to let users set a desired GOP size instead of the default one of 12\r\n * `FFmpegFrameGrabber` now takes into account calls to `setPixelFormat()` (issue #429), but does not enforce it\r\n * Added a `Frame.audioChannels` field for resampling purposes in `FFmpegFrameRecorder` (issue #388)\r\n * In `FFmpegFrameRecorder`, fixed audio encoding with the Vorbis codec (issue #428) and the WebM container (issue #435), and other audio related things\r\n * Added missing `allocateArray()` constructors to `CameraParams` and `MatchesInfo` (issue #421)\r\n * Fixed errors such as \"jniopencv_nonfree.dll: Can't find dependent libraries\" by adding the `opencv_ocl` module as dependency\r\n * Added support to seek in audio-only streams with `FFmpegFrameGrabber.setTimestamp()` (issue #417)\r\n * Fixed potential thread concurrency issues and crash in the `stopRecording()` and `onDestroy()` methods of the `RecordActivity` sample, thanks to Jacob Duron\r\n * To capture the last frame of a video file, reverted `FFmpegFrameGrabber.setTimestamp()` to its previous behavior (issue #413)\r\n * Updated `samples/FaceApplet.jnlp` to make it work with JDK/JRE 7u45\r\n\r\n### January 6, 2014 version 0.7\r\n * Upgraded support to OpenCV 2.4.8\r\n * Upgraded supported FFmpeg API to the 2.1 release branch\r\n * Updated `freenect` to reflect the latest changes of OpenKinect's master branch\r\n * Updated `videoInput` to reflect the latest changes in the \"update2013\" branch\r\n * Added `Frame.opaque` field to give access to the raw `AVFrame` in the case of `FFmpegFrameGrabber` (issue #399)\r\n * Added new `FFmpegFrameGrabber.grabKeyFrame()` method to grab key frames (I-frames) directly (issue #312)\r\n * `VideoInputFrameGrabber` now uses 640x480 as default image size to prevent \"videoInput.getPixels() Error: Could not get pixels.\"\r\n * Fixed `FFmpegFrameGrabber.setTimestamp()` not working for streams with audio (issue #398)\r\n * Fixed wrong `haarcascade_frontalface_alt.xml` file getting downloaded by the `Demo` class (issue #402)\r\n * Added a `Frame.sampleRate` field to allow audio samples to be resampled by `FFmpegFrameRecorder` (issue #388)\r\n * Incorporated `IPCameraFrameGrabber` from Greg Perry (issue #384)\r\n * Fixed thread safety issues with FFmpeg in `FFmpegFrameGrabber` and `FFmpegFrameRecorder` (issue #377)\r\n * Fixed memory leak in the `MotionDetector.java` sample file (issue #372)\r\n * New `videoCodecName` and `audioCodecName` properties to allow users of `FFmpegFrameRecorder` to use codecs such as \"libx264rgb\" (issue #369)\r\n\r\n### September 15, 2013 version 0.6\r\n * Upgraded supported FFmpeg API to the 2.0 release branch (with Java interface files now based on code automatically produced by [JavaCPP Presets](https://github.com/bytedeco/javacpp-presets))\r\n * Fixed `FFmpegFrameGrabber.getFrameNumber()`\r\n * Upgraded support to OpenCV 2.4.6\r\n * Fixed callbacks when used with custom class loaders such as with Web containers\r\n * Upgraded to ARToolKitPlus 2.3.0 (issue #234)\r\n * Fixed drawing issues with `MarkerDetector.draw()`\r\n * Fixed `FFmpegFrameGrabber.getTimestamp()` not returning values for audio frames (issue #328)\r\n * Added new `Frame.keyFrame` field returned by `FFmpegFrameGrabber.grabFrame()` to know when a grabbed frame is a key frame or not (issue #312)\r\n * Worked around problem in `samples/RecordActivity.java` that would happen when trying to record a frame with an invalid timestamp (issue #313)\r\n * Fixed potential resource leak that could occur after `FFmpegFrameRecorder` throwing an `Exception`\r\n * Fixed `FFmpegFrameGrabber` not returning the last few frames of video streams (issue #315)\r\n * Fixed wrong dependencies of OpenCV preventing correct loading (issue #304)\r\n * Renamed `FrameRecorder.record(Buffer[] samples)` to a cleaner `record(Buffer ... samples)` (issue #303)\r\n * Fixed `FFmpegFrameRecorder` not flushing buffers on `stop()` (issue #302)\r\n\r\n### April 7, 2013 version 0.5\r\n * Upgraded support to OpenCV 2.4.5\r\n * Upgraded supported FFmpeg API to the 1.2 release branch\r\n * New methods `FFmpegFrameRecorder.setVideoOption()` and `setAudioOption()` generalize the way to set arbitrary codec options, such as \"profile\", \"preset\", \"tune\", etc. used by the x264 codec\r\n * Included better format guessing inside `FFmpegFrameRecorder` for protocols like RTP\r\n * Added support for planar audio formats to `FFmpegFrameGrabber` and `FFmpegFrameRecorder`, as required by newer versions of FFmpeg for at least MP3 and AAC\r\n * Enhanced `FFmpegFrameRecorder` by making it use the closest supported frame rate for the given codec instead of failing\r\n * To support variable bitrate (VBR) encoding, appended new `videoQuality` and `audioQuality` properties to `FFmpegFrameRecorder`, which usually have an effective range of [0, 51] and overrides the `videoBitrate` and `audioBitrate` properties\r\n\r\n### March 3, 2013 version 0.4\r\n * Upgraded support to OpenCV 2.4.4\r\n * `CanvasFrame.waitKey(-1)` does not wait anymore and returns the last `KeyEvent` dispatched since the last call to it\r\n * Upgraded supported FFmpeg API to the 1.1 release branch\r\n * Fixed bug in `FaceRecognition.java` sample (issue #276)\r\n * Included `Sobel()`, `Scharr()`, `Laplacian()`, and `Canny()` from `opencv_imgproc` whose equivalent functions in the C API have missing parameters\r\n * Extended `OpenKinectFrameGrabber` with `setDepthFormat()` and `setVideoFormat()` methods to be able to set both formats independently (issue #273)\r\n * Fixed `Blender.blend()` having its `@OutputMat` incorrectly annotated as `@InputMat` (issue #272)\r\n * Added new `RecordActivity.java` Android sample from Shawn Van Every and Qianliang Zhang\r\n * Added missing `allocate()` methods for `FunctionPointer` in `AVIOContext` and others, which prevented these FFmpeg callbacks from functioning\r\n * Fixed infinite loop in `FrameGrabber.Array.grab()` (as used by ProCamCalib in the case of stereo cameras, issue #262) when `FrameGrabber.getTimestamp()` returns an invalid negative value (as with `opencv_highgui`) or when using different types of (unsynchronized) `FrameGrabber` together\r\n * Fixed `cvQueryHistValue_1D()` and other functions that use a raw `CvArr` object\r\n * Fixed problem when subclassing `CanvasFrame`\r\n\r\n### November 4, 2012 version 0.3\r\n * Upgraded support to OpenCV 2.4.3 (issue #233)\r\n * Fixed functions like `Algorithm.getMat()` and `HOGDescriptor.getDefaultPeopleDetector()` returning `null` instead of the expected data\r\n * Implemented better, more transparent, handling of `cv::Ptr`\r\n * When allocating an empty `IplImage`, `CvMat`, `CvBGCodeBookModel`, etc. its memory content now gets zeroed out, giving OpenCV a better chance of displaying an error message instead of crashing\r\n * Upgraded supported FFmpeg API to the 1.0 release branch\r\n * Appended to `StringVector` and `MatVector` new convenient bulk constructors and `put()` methods taking arrays of `String`, `IplImage`, `CvMat`, etc.\r\n * Included new `Blobs` module from David Grossman and the corresponding `BlobDemo` sample\r\n * Added missing `opencv_core.partition()` function (issue #144)\r\n * Fixed up the samples a bit (issue #229 and issue #230)\r\n * Switched the majority of `@Adapter` annotations to more concise ones like `@StdVector` as allowed by new capabilities of JavaCPP\r\n * Fixed `FFmpegFrameGrabber.getLengthInFrames()` and `OpenCVFrameGrabber.getLengthInTime()` (issue #231 and issue #236)\r\n * Enhanced `FFmpegFrameRecorder` to support conversion between audio sample formats (for the experimental AAC encoder among other things) and to let two different threads call `record(samples)` and `record(image)` simultaneously, plus a couple of other features like `setFrameNumber()`, which lets users skip image frames (achieving variable frame rate)\r\n * Added a `javacpp.skip` property to `pom.xml`, such that a command like `mvn package -Pall -Djavacpp.skip=true` only recompiles the Java source files, but also added `platform.root` and `compiler.path` properties, which map directly to JavaCPP's for convenience\r\n\r\n### July 21, 2012 version 0.2\r\n * Provided new `javacv-linux-arm.jar` build thanks to Jeremy Nicola (issue #184)\r\n * Additional default properties inside `pom.xml` make it easier to build JavaCV from source (issue #202), calling `mvn package` now succeeds with only OpenCV and a C++ compiler for JavaCPP\r\n * Made a few minor updates for OpenCV 2.4.2\r\n * New `Pointer.limit` property of JavaCPP can now be used to get the `size` of an output parameter, and to specify the maximum `size` on input as well\r\n * Upgraded supported FFmpeg API to the 0.11 release branch\r\n * Added audio support to `FFmpegFrameGrabber` (call `grabFrame()` instead of `grab()`) and `FFmpegFrameRecorder` (call `setAudioChannels()` before `start()`, and `record(Frame)` instead of `record(IplImage)`) (issue #160)\r\n * Gave better default `FFmpegFrameRecorder` settings to H.263, MPEG-4, etc. codecs and fixed H.264 encoding with libx264 (issue #160)\r\n * Refined the `FaceApplet` sample\r\n * Fixed `FlannBasedMatcher` constructor, `FaceRecognizer.train()`, and `Stitcher.stitch()/composePanorama()` (issue #211)\r\n * Fixed `CanvasFrame` sometimes blanking out under Windows and maybe Linux (issue #212)\r\n\r\n### May 27, 2012 version 0.1\r\n * Started using version numbers, friendly to tools like Maven, and placing packages in a sort of [Maven repository](http://maven2.javacv.googlecode.com/git/)\r\n * JavaCV can now extract and load native dependent libraries such as `libopencv_core.so.2.4`, `libopencv_core.2.4.dylib`, `opencv_core240.dll`, etc. from Java resources placed inside the `com.googlecode.javacv.cpp.<platform.name>` package (i.e.: under the `/com/googlecode/javacv/cpp/<platform.name>/` directory of a JAR file in the classpath) (issue #146)\r\n * Included new `FaceApplet` sample to demonstrate [How to use JavaCV in an applet](http://code.google.com/p/javacv/wiki/HowToMakeAnApplet)\r\n * Added handy `IplImage.asCvMat()` and `CvMat.asIplImage()` conversion methods\r\n * Fixed a few small things with `OpenCVFrameGrabber`, `opencv_contrib`, `opencv_legacy`, and `opencv_stitching`\r\n\r\n### May 12, 2012\r\n * Upgraded support to OpenCV 2.4.0 (issue #187)\r\n * Moved the source code repository to Git\r\n * Added `pom.xml` file for Maven support and changed the directory structure of the source code to match Maven's standard directory layout\r\n * Made it easier to create one massive statically linked native library by passing something like \"-Xcompiler -Wl,-static -o javacv\" as command line options to JavaCPP, usually from inside `build.xml` or `pom.xml` (issue #146)\r\n * Fixed missing parameter from `CvANN_MLP.create()`\r\n * Added methods `cvCalcCovarMatrixEx()`, `cvEigenDecomposite()`, and `cvEigenProjection()` taking an `IplImage[]` as argument for convenience\r\n * `VideoInputFrameGrabber.start()` now accepts a `connection` argument such as `VI_COMPOSITE` to support analog cameras and what not\r\n * Fixed `FaceRecognition` sample (issue #188)\r\n * Added a few convenience methods to avoid the need to create empty `CvAttrList`\r\n\r\n### March 29, 2012\r\n * Added missing array allocators and `position()` methods to `KDTree.Node`, `DefaultRngAuto`, `CvAffinePose`, `KeyPoint`, `BaseKeypoint`, `ReferenceTrees`, `DMatch`, `*.Params`, `CvFuzzy*`, `Octree.Node`, `CvDefParam`, `Cv*Blob*`, `Cv*Track*`, `CvDrawShape`, `CvVectors`, `CvParamGrid`, `Cv*Params`, `CvSVM*`, `CvPair16u32s`, `CvDTree*`  `CvTrainTestSplit`, `CvMLData`, `FeatureEvaluator`, and `*DataMatrixCode`\r\n * Increased versatility of `IplImage.createFrom()`, `copyFrom()`, `copyTo()`, `getBufferedImage()` by providing a `flipChannels` parameter, whose effect was previously mistakenly forced onto four-channel images of byte values only (issue #163)\r\n * Fixed a couple of things with `CvMat.get()/put()` (issue #167)\r\n * In addition to an `IplImage`, we may now specify the pixel format of the data when calling `FFmpegFrameRecorder.record()`, but otherwise when `IplImage.nChannels == 2`, it assumes `PIX_FMT_NV21`, allowing for easy and efficient encoding of data captured from the camera on Android (issue #160), image objects we can also convert to RGB using `cvCvtColor()` with `CV_YUV420sp2BGR`\r\n * Fixed seeking capabilities of `FFmpegFrameGrabber` (issue #162) and added `getLengthInFrames()` and `getLengthInTime()` methods to query the duration of streams, when known\r\n * Enhanced `IplImage.clone()` and `create*Compatible()` with cloning of their `BufferedImage` to make it easier to keep color components in the right order (issue #163)\r\n * Refactored `FrameGrabber` and `FrameRecorder` a bit to accommodate new `createDefault(...)` and `create(String className, ...)` factory methods, offering to users an easier selection method to work around limitations of some APIs (issue #70)\r\n * Adjusted `GNImageAligner`, `ProCamTransformer`, etc. to support alignment of only the projector display on textureless surface planes\r\n * Renamed a few more `Settings` properties to reflect better their meanings\r\n\r\n### February 18, 2012\r\n * Added `GLCanvasFrame` to show OpenGL renderbuffers on screen, plus a new factory method `JavaCVCL.createCLGLImageFrom()` to create compatible ones from `IplImage` objects, as well as more user-friendly `getGLContext()`, `getGL()` and `getGL2()` methods\r\n * Fixed various things of the original `CanvasFrame`, and `JavaCV.createCLImage()` and `createIplImage()`, also appending `From` to their names \r\n * New `createPinnedBuffer()` and `createPinnedIplImage()` factory methods in `JavaCVCL` to allocate page-locked memory for faster CPU<->GPU transfers, but it does not seem to work for OpenCL image objects, only linear buffer objects :(\r\n * Fixed and enhanced `GNImageAlignerCL` and `ProjectorDevice` (its `useOpenGL` property) to support fully OpenCL and OpenGL acceleration\r\n * Refactored `Parallel` a bit so that we may set the number of threads it uses via its static `numThreads` property or the \"com.googlecode.javacv.numthreads\" system property, which defaults to `Parallel.getNumCores() = Runtime.getRuntime().availableProcessors()`\r\n * Cleaned up and renamed some methods in `JavaCV`, while adding `boundingRect()`, functionally similar to `cvBoundingRect`, but better adapted to compute a properly aligned and padded ROI\r\n * Inserted a couple of missing `allocate()` inside `opencv_flann`\r\n * Updated `ObjectFinder` with a `Settings.useFLANN` property to let it use FLANN via OpenCV\r\n * Cleaned up and optimized `HandMouse`\r\n * `CanvasFrame`, `FrameGrabber`, `FrameRecorder`, and `ProjectiveDevice` objects now throw `Exception` objects of a nested class instead of the too generic `java.lang.Exception` one\r\n * Moved parallel execution of `cvkernels.multiWarpColorTransform()`, modifying `ImageTransformer` classes, from `GNImageAligner` into `cvkernels`, which now also supports other image types than `float`\r\n * Renamed some `Settings` properties here and there to correct typos and reflect better their meanings\r\n * Updated `freenect` to reflect the latest changes of OpenKinect's master branch\r\n * FFmpeg and other libraries did not work under Android when compiled with the latest NDK, r7 (issue #147): Fixed in JavaCPP\r\n * Moved `IplImage.timestamp` to `FrameGrabber`, also adding a `frameNumber` property, both allowing to seek within streams too\r\n * Removed `triggerFlushSize` property from `CameraDevice` and `FrameGrabber`, instead relying on the `numBuffers` property to decide the required size of a buffer flush\r\n * Corrected the logic behind `FFmpegFrameGrabber.getFrameRate()` and `getTimestamp()` (issue #151)\r\n * Created a `BufferRing` class for convenient circular rings of large buffers that require manual release of resources, such as OpenCL memory\r\n * Added a few more useful methods to `FrameGrabber`, including `restart()`, `flush()`, and `delayedGrab()` (to be used in conjunction with `getDelayedTime()` and `getDelayedImage()`)\r\n * Inserted `cvLoadImageBGRA()` and `cvLoadImageRGBA()` methods into `opencv_highgui` to load color images compatible with OpenCL more easily\r\n * `JavaCvErrorCallback` now outputs messages to `Logger` instead of `System.err`\r\n * Defined `VI_COM_MULTI_THREADED` for `videoInput`, allowing it to run on multiple threads if needed\r\n\r\n### January 8, 2012\r\n * JavaCV should now have an easier time automatically finding libraries inside standard directories such as `/usr/local/lib/`, `/opt/local/lib/`, and `C:\\opencv\\`, even when they are not part of the system configuration or PATH (issue #127)\r\n * Renamed `set()` and `fill()` methods to `put()` inside `CvPoint*` classes, for better naming consistency\r\n * Renamed `FrameGrabber.ColorMode` to `ImageMode` and its `BGR` value to `COLOR` to reflect the fact that a `FrameGrabber` instance can return color images in some arbitrary format, but added a new `pixelFormat` property to let users know or specify the exact pixel format desired, such as `PIX_FMT_BGR24`, etc. in the case of `FFmpegFrameGrabber`\r\n * After `FFmpegFrameGrabber.start()`, the `format`, `imageWidth`, `imageHeight`, and `frameRate` properties switch to their effective values\r\n * Added new `FrameGrabber.sensorPattern` property to obtain the Bayer filter layout of raw data from `DC1394FrameGrabber` and `FlyCaptureFrameGrabber`\r\n * Readded to `KDTree`, `Index`, and `HOGDescriptor` some functions with `FloatPointer` and `IntPointer` arguments that were mistakenly removed when OpenCV switched to using `cv::InputArray` and `cv::OutputArray` parameter types (issue #134)\r\n * Renamed `ProjectiveGainBiasTransformer` to `ProjectiveColorTransformer`\r\n * Added a few classes to do some processing using OpenCL and OpenGL: `JavaCVCL`, `GNImageAlignerCL`, `ProjectiveTransformerCL`, `ProjectiveColorTransformerCL`, and `ProCamTransformerCL` with some other related files\r\n * Renamed `Parallel.numCores` to the more conventional `Parallel.NUM_CORES`\r\n * Added new `FaceRecognition.java` sample from Stephen L. Reed\r\n * Inserted a couple of missing calls to `Loader.load()` (issue #142)\r\n * Improved hacks for `Loader.load()` in JavaCPP make JavaCV work on Android 4.0\r\n * New `PS3EyeFrameGrabber` from Jiri Masa can now grab images using the SDK from Code Laboratories\r\n\r\n### October 1, 2011\r\n * Fixed `DC1394FrameGrabber` and `FlyCaptureFrameGrabber` to behave as expected with all Bayer/Raw/Mono/RGB/YUV cameras modes (within the limits of libdc1394 and PGR FlyCapture) (issue #91)\r\n * Fixed regression of `IplImage.copyFrom()` and `createFrom()` with `BufferedImage` objects of `SinglePixelPackedSampleModel` (issue #102)\r\n * C++ functions using `std::vector` objects as output parameters now work on Windows Vista and Windows 7 as well\r\n\r\n### August 20, 2011\r\n * Upgraded support to OpenCV 2.3.1\r\n * An output argument of type `cv::Mat` or `cv::OutputArray` returned with a size 0 now correctly sets `CvArr.address = 0`\r\n * Fixed `IplImage.createFrom()` and `copyFrom()` when called on objects returned by `BufferedImage.getSubimage()`\r\n * Added missing allocator to `CvRNG`\r\n * `OpenCVFrameGrabber` now detects when CV_CAP_PROP_POS_MSEC is broken and gives up calling `cvGetCaptureProperty()`\r\n * New `OpenKinectFrameGrabber.grabDepth()` and `grabVideo()` methods to capture \"depth\" and \"video\" simultaneously, regardless of the mode\r\n\r\n### July 5, 2011\r\n * Upgraded support to OpenCV 2.3.0\r\n * Fixed `OpenKinectFrameGrabber`, which can now also capture depth images when `setFormat(\"depth\")` is called before `start()`\r\n * Fixed `CvMatArray` and `IplImageArray` as well as histogram related functions\r\n * Fixed `FFmpegFrameGrabber`, and `FFmpegFrameRecorder` now works on Android also\r\n * Fixed calls, such as `opencv_flann.Index.knnSearch()`, that require a `MatAdapter` or an `ArrayAdapter` for output\r\n\r\n### June 10, 2011\r\n * New `freenect` wrapper and corresponding `OpenKinectFrameGrabber` to capture from Microsoft's Kinect stereo camera using OpenKinect\r\n * JavaCV now exposes all C++ functions and classes of OpenCV not covered by the C API\r\n * Fixed various erroneous declarations and calls, including those due to changes in JavaCPP\r\n\r\n### May 11, 2011\r\n * Removed `CvMat` object pooling in favor of more efficient `ThreadLocal` objects created by `CvMat.createThreadLocal()`\r\n * Changed `Marker.getCenter()` back to the centroid, because it has better noise averaging properties and gives in practice more accurate results than the actual center\r\n * Added hack to `OpenCVFrameGrabber.start()` to wait for `cvRetrieveFrame()` to return something else than `null` under Mac OS X\r\n * FFmpeg now works properly on Windows and Android (issue #63) with newer binaries\r\n * New `videoInputLib` wrapper and corresponding `VideoInputFrameGrabber` to capture using DirectShow, useful under Windows 7 where OpenCV and FFmpeg can fail to capture using Video for Windows (issue #58)\r\n * `GeometricCalibrator` now reports the maximum errors in addition to the average (RMS) errors\r\n\r\n### April 7, 2011\r\n * Added a `format` property to `CameraDevice`, `FrameGrabber`, and `FrameRecorder`, mostly useful for `FFmpegFrameGrabber`, where interesting values include \"dv1394\", \"mjpeg\", \"video4linux2\", \"vfwcap\", and \"x11grab\"\r\n * `OpenCVFrameRecorder` now uses `CV_FOURCC_PROMPT` under Windows as default since `CV_FOURCC_DEFAULT` crashes (issue #49)\r\n * Added hack to make sure the temporarily extracted library files get properly deleted under Windows\r\n * JavaCPP now loads classes more lazily\r\n * Fixed most occurences of `UnsatisfiedLinkError` (issue #54), but some corner cases may require a call to `Loader.load()` on the class one wishes to use\r\n * Added (rudimentary) outlier detection and modified zero threshold handling in the image alignment framework\r\n * New `JavaCV.hysteresisThreshold()` feature\r\n * New `HandMouse` functionality, which depends on the image alignment framework\r\n * Fixed `ProjectiveDevice.distort()`, which mistakenly undistorted images instead\r\n * New `HoughLines` sample thanks to Jeremy Nicola\r\n\r\n### February 19, 2011\r\n * Switched from JNA to JavaCPP, which has a lower overhead and supports C++, bringing hope that future versions of JavaCV will support features of OpenCV available only through the C++ API\r\n * Consequently, the syntax of various operations have changed a bit, but the transition should not be too painful\r\n * As a happier consequence, this also fixes the problem with SSE instructions on 32-bit x86 (issue #36)\r\n * Also, JavaCPP does not have any limitations or performance issues with large data structures (issue #10 and issue #14)\r\n * Added support for OpenCV 2.2 (issue #42), but dropped support for all previous versions\r\n * Added samples provided by users (issue #1, issue #45, and issue #46)\r\n * Added deinterlace setting to `FFmpegFrameGrabber` having it call `avpicture_deinterlace()` (issue #38)\r\n * Enhanced a few things of the image alignment algorithm\r\n * Tried to fix image format conversion inside `FlyCaptureFrameGrabber`, but this is going to require more careful debugging\r\n * Fixed and added various other things I forget\r\n\r\n### December 2, 2010\r\n * Now works on Android with the Dalvik VM (for more details, please refer to the FacePreview sample available on the download page)\r\n * Added more hacks to `CanvasFrame` in the hope to make it behave better outside the EDT\r\n * Made clearer the error messages thrown from `FrameGrabber` objects, when `start()` may not have been called\r\n * Fixed version specific declarations of `CvStereoBMState` and related functions\r\n * Fixed conditions that could crash `cvkernels`\r\n\r\n### November 4, 2010\r\n * Renamed the package namespace to `com.googlecode.javacv`, which makes more sense now that JavaCV has been well anchored at Google Code for more than a year, piggybacking on the unique and easy-to-remember domain name\r\n * Included new FFmpeg wrapper classes `avutil`, `avcodec`, `avformat`, `avdevice`, `avfilter`, `postprocess`, and `swscale`, eliminating the need of the separate FFmpeg-Java package\r\n * `CanvasFrame` now redraws its `Canvas` after the user resizes the `Frame`\r\n * Fixed the `Error` thrown when calling `CanvasFrame.showImage()` from the EDT\r\n * Added check to `DC1394FrameGrabber` so that a \"Failed to initialize libdc1394\" does not crash the JVM\r\n * `FFmpegFrameGrabber` does not crash anymore when forgetting to call `start()` before a `grab()` or `trigger()`\r\n * `FrameGrabber` now selects the default grabber a bit better\r\n * Made sweeping changes (for the better, but still not finalized) to `GNImageAligner`, `ProjectiveTransformer`, `ProjectiveGainBiasTransformer`, and `ProCamTransformer`...\r\n * Added to `JavaCV` more methods related to transformation of planes: `perspectiveTransform()`, `getPlaneParameters()`, `getPerspectiveTransform()`, and `HtoRt()`, as well as `ProjectiveDevice.getFrontoParallelH()`\r\n * Added a static `autoSynch` flag to all `Structure` classes of `cxcore`, `cv`, and `cvaux`, which you may set to `false` prior to the return of things like big and heavy `CvSeq` to make them load faster and to avoid stack overflows, but accessing fields will then require manual calls to `readField()` and `writeField()` (issue #10 and #14)\r\n * Added missing `ByValue` subclasses to `CvSeq`, `CvSet`, `CvContourTree`, and `CvChain`... Any others missing?\r\n * Fixed `Exception` thrown from `cvCreateHist()` under JNA 3.2.7 (issue #26)\r\n * Enhanced `CvMat.put()`, which now supports setting submatrices\r\n * Improved inside `IplImage` the support of `BufferedImage`, especially those using a `DirectColorModel` (issue #23)\r\n * Fixed crash in `cvkernels` when color transformation `X` is `null`\r\n\r\n### July 30, 2010\r\n * Fixed crash that would occur in `CanvasFrame` for some video drivers\r\n * `FFmpegFrameGrabber` now supports other input formats (devices), such as `x11grab` that can be used for screencasting\r\n * Added `JavaCV.median()` function, and `JavaCV.fractalTriangleWave()` now respects image ROI\r\n * Fixed background subtraction in `cvaux`\r\n * Fixed crash inside the code for direct alignment caused by the ROI getting set outside the image plane\r\n * Added `deltaScale` and `tryToFixPlane` to `GNImageAligner.Settings` (the first used in `ImageTransformer.Parameters` as increment, randomly selected forward or backward, for finite difference), which sometimes help to jump over local minima\r\n\r\n### May 30, 2010\r\n * Removed redundant `CvMemStorage.clearMem()` method, use `cvClearMemStorage()`\r\n * Fixed the sample `Test2` class that did not work under Windows\r\n * Fixed corruption by the `cvkernels` `transformer` at the borders\r\n * Modified `CanvasFrame` constructors and added a `gamma` argument used by `showImage(IplImage)`\r\n * `CanvasFrame` now lets users resize the frame, while displayed images are stretched to fit the new size\r\n * Renamed `CanvasFrame.acquireGraphics()` to `createGraphics()` for consistency\r\n * When `FlyCaptureFrameGrabber` cannot set fastest speed, it now safely fails by setting any supported speed\r\n * Added a new `Parallel.loop()` method that can use more threads than the number of CPU cores detected\r\n * Added new `numThreads` property to `GNImageAligner` and fixed a few minor inconsistencies as well\r\n * Fixed incorrect `Java.HnToRt()`, and added a few `norm()` and `randn()` methods\r\n * For functions with `float[]` and `double[]` arguments in `cvaux` and `cv`, added complementary `FloatBuffer` and `DoubleBuffer` declarations\r\n * Fixed loading problems with `cvaux`\r\n * Fixed and enhanced histogram, back projection, and other CAMSHIFT related functionality\r\n * Added code for `CvRNG`\r\n * Added \"/opt/local/lib/\" and \"/opt/local/lib64/\" (standard on Mac OS X) to the default list of search paths for OpenCV\r\n * Added `CvScalar.getVal()` and `CvIntScalar.getVal()`, which simply return the `val` field, convenient for Scala where `val` is a reserved word\r\n * Fixed the construction of `IplImage` from a `Pointer`\r\n * Removed incorrect cases when an `IplImage` gets converted to a `BufferedImage.TYPE_CUSTOM`\r\n * Made `CvArr.PointerByReference` a bit more consistent and general\r\n\r\n### April 16, 2010\r\n * Modified `IplImage`, `FrameGrabber`, and `CanvasFrame` to get better default behavior of gamma correction\r\n * Fixed `cv.CvHistogram` and related histogram functions\r\n * `CameraDevice.Settings.triggerFlushSize` now defaults to 5 (only affects `OpenCVFrameGrabber` and `FFmpegFrameGrabber`)\r\n * Replaced `LMImageAligner` by `GNImageAligner`, a more appropriate name for Gauss-Newton with `lineSearch`\r\n * Fixed a few things related with `ProjectiveDevice.Settings`\r\n\r\n### April 8, 2010\r\n * Added support for OpenCV 2.1\r\n\r\n### April 5, 2010\r\n * Fixed up `clone()` methods to avoid the need to cast\r\n * Removed the `fullScreen` argument from `CanvasFrame` constructors, which will now switch to full-screen mode only when a `screenNumber` is explicitly passed\r\n * Renamed `FrameGrabber.ColorMode.GRAYSCALE` to `GRAY`\r\n * Replaced deprecated functions from `FFmpegFrameGrabber` and `FFmpegFrameRecorder`\r\n * `FFmpegFrameGrabber` can now resize images\r\n\r\n### March 21, 2010\r\n * Added new classes and methods used by ProCamTracker: `cvkernels`, `JavaCV.fractalTriangleWave()`, `ImageAligner`, `LMImageAligner`, `ImageTransformer`, `ProjectiveTransformer`, `ProjectiveGainBiasTransformer`, `ProCamTransformer`, and `ReflectanceInitializer`\r\n * `CameraDevice.Settings` has a new `deviceFile` property (used by a `FrameGrabber`), which brings up a file dialog for some `PropertyEditor`s\r\n * Moved in `CameraSettings`, `ProjectorSettings`, and `FrameGrabber.PropertyEditor` from the `procamcalib` package\r\n * Added to `CameraDevice.Settings` and `FrameGrabber` a `triggerFlushSize` property to indicate the number of buffers to flush on `trigger()` to compensate for cheap cameras that keep old images in memory indefinitely\r\n * Changed the type of `CameraDevice.Settings.deviceNumber` to `Integer` so we may set it to `null`\r\n * Fixed and enhanced `CanvasFrame.showImage()` methods a bit\r\n * In `triggerMode` `DC1394FrameGrabber` now tries to use a real software trigger and only falls back to one-shot mode on error\r\n * Fixed array constructors of `IplImage.PointerByReference()` and `CvImgObsInfo.PointerByReference()`\r\n * Added `CvPoint.fillArray()` methods to reuse preallocated arrays and changed `createArray()` a bit as well\r\n * Fixed and enhanced all `IplImage.copy*()` methods, including new support for ROIs and subimages, which affects `create*()` and `getBufferedImage()` methods as well\r\n * Updated `Marker` to support different size and spacing in X and Y\r\n * Added `Settings` to `ObjectFinder`\r\n * Fixed distortion problem in `ProjectiveDevice` and `ProCamColorCalibrator` with OpenCV 1.1pre1\r\n * Split `ProjectiveDevice.Settings` into `ProjectiveDevice.CalibrationSettings` (for applications like ProCamCalib) and `ProjectiveDevice.CalibratedSettings` (for applications like ProCamTracker)\r\n * Renamed `gamma` to `responseGamma` in `ProjectiveDevice`, and moved previous `nominalDistance` parameter to `Settings`\r\n * Added `ProjectiveDevice.rescale()` to rescale calibration parameters when switching a device to a new image size\r\n * `ProjectiveDevice.undistort()` and `distort()` can now `useFixedPointMaps` of OpenCV\r\n * `ProjectiveDevice` and its subclasses now `throw new Exception()` if the `parameterFile` cannot be read\r\n\r\n### February 13, 2010\r\n * Relicensed JavaCV under the GPLv2 with Classpath exception (see LICENSE.txt). Please note that if your application links with code that needs ARToolKitPlus, for example, it will become subject to the full GPL, without Classpath exception\r\n * Added `devicePath` setting to `CameraDevice` that works with `FFmpegFrameGrabber`, `OpenCVFrameGrabber`, and other `FrameGrabber` with a String constructor\r\n * Added \"C:/OpenCV2.0/bin/release/\" to the directory list to search for OpenCV DLLs\r\n * Moved `cvFindHomography()`, `cvFindExtrinsicCameraParams2()`, `cvReprojectImageTo3D()`, `cvSaveImage()`, and `cvRetrieveFrame()` to version specific classes since their number of arguments differ with the version of OpenCV\r\n * Enhanced `CvMat.put(CvMat mat)` to work better even when the matrices are not actually compatible\r\n * Added new `IplImage` factory methods `createCompatible(IplImage image)`, `createIfNotCompatible(IplImage image, IplImage template)`, and `createFrom(BufferedImage image)`\r\n * Fixed `distortionCoeffs` corruption that might occur in `ProjectiveDevice`\r\n\r\n### January 3, 2010\r\n * Added wrapper for the `cvaux` module of OpenCV\r\n * Added abstract `FrameRecorder` class and a `OpenCVFrameRecorder` class\r\n * Fixed read() problem that might occur within Pointer constructors\r\n * Running `java -jar javacv.jar` now displays version information\r\n\r\n### December 22, 2009\r\n * Fixed `CanvasFrame` from getting stuck in a maximized window\r\n * Removed all `setAutoWrite(false)` from `cxcore` now that the bug appears fixed in JNA\r\n * Added `FFmpegFrameGrabber` and `FFmpegFrameRecorder` to easily record live footage and grab back offline into JavaCV\r\n\r\n### November 24, 2009\r\n * Added more convenient constructors and factory methods for `CvPoint*`, `CvSize*`, `CvRect`, `CvTermCriteria`, `CvSlice`, and `CvAttrList`\r\n * Added _R2_ correlation coefficient field to `ProjectiveDevice`\r\n * Enhanced and fixed color conversion spaghetti code in `FlyCaptureFrameGrabber`\r\n * Fixed the `CvHaarFeature` Structure \r\n * Renamed `CvIntScalar` factory methods to match with `CvScalar`\r\n * Enhanced and fixed some problems with gamma correction in `IplImage`\r\n * Added a `highgui.CV_FOURCC()` method that takes chars as parameter\r\n * Moved `MarkedPlane.drawMarkers()` to `Marker.draw()` for better code reuse\r\n * Added `MarkedPlane.getTotalWarp()` with a \"useCenters\" parameter\r\n * Changed default values of `MarkerDetector.binarizationKWhiteMarkers` to 1.0 and `ProjectorDevice.brightnessBackground` to 0.0\r\n * Fixed issue with image width and memory alignment in `MarkerDetector`\r\n * `Marker.getCenter()` now computes the actual physical center instead of the centroid\r\n * `OpenCVFrameGrabber.getDeviceDescriptions()` now throws `UnsupportedOperationException`\r\n * Added support in `OpenCVFrameGrabber` to grab frames from video files\r\n * Added `ProjectiveDevice.getRectifyingHomography()` method\r\n * Added `JavaCvErrorCallback` to easily catch errors of OpenCV in Java\r\n\r\n### October 19, 2009\r\n * Moved the functionality of `CvMatPool` to the `CvMat.take()` and `.pool()` methods\r\n * Added color calibration for projector-camera systems (`ProCamColorCalibrator`)\r\n * Updated `DC1394FrameGrabber` to handle more conversion use cases automatically\r\n * Fixed `CvIntScalar` to mirror `CvScalar`\r\n\r\n### October 14, 2009\r\n * Change of plan: JavaCV now works with any of OpenCV 1.0, 1.1pre1, or 2.0! Version specific functionality is enclosed in subclasses, e.g., the class `cv.v20` can access everything from the `cv` module of OpenCV 2.0\r\n * Added a few missing functions and adjusted some mappings to make them closer to the C API\r\n * Added a few more helper methods to `CvPoint*`\r\n * Added temporary storage to `ObjectFinder` to plug the memory leak\r\n\r\n### October 2, 2009\r\n * Fixed problem when loading distortion coefficients with `ProjectiveDevice`\r\n * Added automatic read and write for functions with arrays of `Structure` or `PointerByReference`\r\n * Added to `cv.java` a few missing functions related to calibration\r\n * Fixed up a bit helper methods for `CvPoint*`, `CvScalar`, `CvRect`, `CvBox2D`, `CvMat`, `IplImage`, `CvMemStorage`, `CvSeq`, and `CvSeqBlock`\r\n * Added `CvMatPool` to `MarkedPlane` and `Marker`\r\n * Added a few new `distort()` methods to `ProjectiveDevice`\r\n * Last version to support OpenCV 1.1pre1: Future version will require OpenCV 2.0\r\n\r\n### August 27, 2009\r\n * `IplImage` now flips the buffer on copy if necessary\r\n * Added needed Pointer constructor for `CvSURFPoint` and `CvConvexityDefect`\r\n * Cleaned up a bit the messy Buffers in `CvMat`\r\n\r\n### August 26, 2009\r\n * Added `get*Buffer()` functions to `IplImage`\r\n * Added more options for gamma correction in `IplImage` and `ProjectiveDevice`\r\n * Further cleaned up the namespace and constructors of `ProjectiveDevices`\r\n * `CanvasFrame.waitKey()` now only checks `KeyEvent.KEY_PRESSED`\r\n * Added `CvMatPool` to avoid recreating matrices\r\n * Moved `CvScalar` functions to `cxcore`\r\n\r\n### August 19, 2009\r\n * Switched to using `import static` for relief from namespace hell\r\n * Fixed color channel reversal of Bayer images in `DC1394FrameGrabber`\r\n\r\n### August 11, 2009\r\nInitial release\r\n\r\n\r\nAcknowledgments\r\n---------------\r\nThis project was conceived at the [Okutomi & Tanaka Laboratory](http://www.ok.ctrl.titech.ac.jp/), Tokyo Institute of Technology, where I was supported for my doctoral research program by a generous scholarship from the Ministry of Education, Culture, Sports, Science and Technology (MEXT) of the Japanese Government. I extend my gratitude further to all who have reported bugs, donated code, or made suggestions for improvements (details above)!\r\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "You may use this work under the terms of either the Apache License,\r\nVersion 2.0, or the GNU General Public License (GPL), either version 2,\r\nor any later version, with \"Classpath\" exception (details below).\r\n\r\nYou don't have to do anything special to choose one license or the other\r\nand you don't have to notify anyone which license you are using. You are\r\nfree to use this work in any project (even commercial projects) as long\r\nas the copyright header is left intact.\r\n\r\n===============================================================================\r\n\r\n                                 Apache License\r\n                           Version 2.0, January 2004\r\n                        http://www.apache.org/licenses/\r\n\r\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r\n\r\n   1. Definitions.\r\n\r\n      \"License\" shall mean the terms and conditions for use, reproduction,\r\n      and distribution as defined by Sections 1 through 9 of this document.\r\n\r\n      \"Licensor\" shall mean the copyright owner or entity authorized by\r\n      the copyright owner that is granting the License.\r\n\r\n      \"Legal Entity\" shall mean the union of the acting entity and all\r\n      other entities that control, are controlled by, or are under common\r\n      control with that entity. For the purposes of this definition,\r\n      \"control\" means (i) the power, direct or indirect, to cause the\r\n      direction or management of such entity, whether by contract or\r\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\r\n      outstanding shares, or (iii) beneficial ownership of such entity.\r\n\r\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\r\n      exercising permissions granted by this License.\r\n\r\n      \"Source\" form shall mean the preferred form for making modifications,\r\n      including but not limited to software source code, documentation\r\n      source, and configuration files.\r\n\r\n      \"Object\" form shall mean any form resulting from mechanical\r\n      transformation or translation of a Source form, including but\r\n      not limited to compiled object code, generated documentation,\r\n      and conversions to other media types.\r\n\r\n      \"Work\" shall mean the work of authorship, whether in Source or\r\n      Object form, made available under the License, as indicated by a\r\n      copyright notice that is included in or attached to the work\r\n      (an example is provided in the Appendix below).\r\n\r\n      \"Derivative Works\" shall mean any work, whether in Source or Object\r\n      form, that is based on (or derived from) the Work and for which the\r\n      editorial revisions, annotations, elaborations, or other modifications\r\n      represent, as a whole, an original work of authorship. For the purposes\r\n      of this License, Derivative Works shall not include works that remain\r\n      separable from, or merely link (or bind by name) to the interfaces of,\r\n      the Work and Derivative Works thereof.\r\n\r\n      \"Contribution\" shall mean any work of authorship, including\r\n      the original version of the Work and any modifications or additions\r\n      to that Work or Derivative Works thereof, that is intentionally\r\n      submitted to Licensor for inclusion in the Work by the copyright owner\r\n      or by an individual or Legal Entity authorized to submit on behalf of\r\n      the copyright owner. For the purposes of this definition, \"submitted\"\r\n      means any form of electronic, verbal, or written communication sent\r\n      to the Licensor or its representatives, including but not limited to\r\n      communication on electronic mailing lists, source code control systems,\r\n      and issue tracking systems that are managed by, or on behalf of, the\r\n      Licensor for the purpose of discussing and improving the Work, but\r\n      excluding communication that is conspicuously marked or otherwise\r\n      designated in writing by the copyright owner as \"Not a Contribution.\"\r\n\r\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\r\n      on behalf of whom a Contribution has been received by Licensor and\r\n      subsequently incorporated within the Work.\r\n\r\n   2. Grant of Copyright License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      copyright license to reproduce, prepare Derivative Works of,\r\n      publicly display, publicly perform, sublicense, and distribute the\r\n      Work and such Derivative Works in Source or Object form.\r\n\r\n   3. Grant of Patent License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      (except as stated in this section) patent license to make, have made,\r\n      use, offer to sell, sell, import, and otherwise transfer the Work,\r\n      where such license applies only to those patent claims licensable\r\n      by such Contributor that are necessarily infringed by their\r\n      Contribution(s) alone or by combination of their Contribution(s)\r\n      with the Work to which such Contribution(s) was submitted. If You\r\n      institute patent litigation against any entity (including a\r\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\r\n      or a Contribution incorporated within the Work constitutes direct\r\n      or contributory patent infringement, then any patent licenses\r\n      granted to You under this License for that Work shall terminate\r\n      as of the date such litigation is filed.\r\n\r\n   4. Redistribution. You may reproduce and distribute copies of the\r\n      Work or Derivative Works thereof in any medium, with or without\r\n      modifications, and in Source or Object form, provided that You\r\n      meet the following conditions:\r\n\r\n      (a) You must give any other recipients of the Work or\r\n          Derivative Works a copy of this License; and\r\n\r\n      (b) You must cause any modified files to carry prominent notices\r\n          stating that You changed the files; and\r\n\r\n      (c) You must retain, in the Source form of any Derivative Works\r\n          that You distribute, all copyright, patent, trademark, and\r\n          attribution notices from the Source form of the Work,\r\n          excluding those notices that do not pertain to any part of\r\n          the Derivative Works; and\r\n\r\n      (d) If the Work includes a \"NOTICE\" text file as part of its\r\n          distribution, then any Derivative Works that You distribute must\r\n          include a readable copy of the attribution notices contained\r\n          within such NOTICE file, excluding those notices that do not\r\n          pertain to any part of the Derivative Works, in at least one\r\n          of the following places: within a NOTICE text file distributed\r\n          as part of the Derivative Works; within the Source form or\r\n          documentation, if provided along with the Derivative Works; or,\r\n          within a display generated by the Derivative Works, if and\r\n          wherever such third-party notices normally appear. The contents\r\n          of the NOTICE file are for informational purposes only and\r\n          do not modify the License. You may add Your own attribution\r\n          notices within Derivative Works that You distribute, alongside\r\n          or as an addendum to the NOTICE text from the Work, provided\r\n          that such additional attribution notices cannot be construed\r\n          as modifying the License.\r\n\r\n      You may add Your own copyright statement to Your modifications and\r\n      may provide additional or different license terms and conditions\r\n      for use, reproduction, or distribution of Your modifications, or\r\n      for any such Derivative Works as a whole, provided Your use,\r\n      reproduction, and distribution of the Work otherwise complies with\r\n      the conditions stated in this License.\r\n\r\n   5. Submission of Contributions. Unless You explicitly state otherwise,\r\n      any Contribution intentionally submitted for inclusion in the Work\r\n      by You to the Licensor shall be under the terms and conditions of\r\n      this License, without any additional terms or conditions.\r\n      Notwithstanding the above, nothing herein shall supersede or modify\r\n      the terms of any separate license agreement you may have executed\r\n      with Licensor regarding such Contributions.\r\n\r\n   6. Trademarks. This License does not grant permission to use the trade\r\n      names, trademarks, service marks, or product names of the Licensor,\r\n      except as required for reasonable and customary use in describing the\r\n      origin of the Work and reproducing the content of the NOTICE file.\r\n\r\n   7. Disclaimer of Warranty. Unless required by applicable law or\r\n      agreed to in writing, Licensor provides the Work (and each\r\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\r\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r\n      implied, including, without limitation, any warranties or conditions\r\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r\n      PARTICULAR PURPOSE. You are solely responsible for determining the\r\n      appropriateness of using or redistributing the Work and assume any\r\n      risks associated with Your exercise of permissions under this License.\r\n\r\n   8. Limitation of Liability. In no event and under no legal theory,\r\n      whether in tort (including negligence), contract, or otherwise,\r\n      unless required by applicable law (such as deliberate and grossly\r\n      negligent acts) or agreed to in writing, shall any Contributor be\r\n      liable to You for damages, including any direct, indirect, special,\r\n      incidental, or consequential damages of any character arising as a\r\n      result of this License or out of the use or inability to use the\r\n      Work (including but not limited to damages for loss of goodwill,\r\n      work stoppage, computer failure or malfunction, or any and all\r\n      other commercial damages or losses), even if such Contributor\r\n      has been advised of the possibility of such damages.\r\n\r\n   9. Accepting Warranty or Additional Liability. While redistributing\r\n      the Work or Derivative Works thereof, You may choose to offer,\r\n      and charge a fee for, acceptance of support, warranty, indemnity,\r\n      or other liability obligations and/or rights consistent with this\r\n      License. However, in accepting such obligations, You may act only\r\n      on Your own behalf and on Your sole responsibility, not on behalf\r\n      of any other Contributor, and only if You agree to indemnify,\r\n      defend, and hold each Contributor harmless for any liability\r\n      incurred by, or claims asserted against, such Contributor by reason\r\n      of your accepting any such warranty or additional liability.\r\n\r\n   END OF TERMS AND CONDITIONS\r\n\r\n   APPENDIX: How to apply the Apache License to your work.\r\n\r\n      To apply the Apache License to your work, attach the following\r\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\r\n      replaced with your own identifying information. (Don't include\r\n      the brackets!)  The text should be enclosed in the appropriate\r\n      comment syntax for the file format. We also recommend that a\r\n      file or class name and description of purpose be included on the\r\n      same \"printed page\" as the copyright notice for easier\r\n      identification within third-party archives.\r\n\r\n   Copyright [yyyy] [name of copyright owner]\r\n\r\n   Licensed under the Apache License, Version 2.0 (the \"License\");\r\n   you may not use this file except in compliance with the License.\r\n   You may obtain a copy of the License at\r\n\r\n       http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n   Unless required by applicable law or agreed to in writing, software\r\n   distributed under the License is distributed on an \"AS IS\" BASIS,\r\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n   See the License for the specific language governing permissions and\r\n   limitations under the License.\r\n\r\n===============================================================================\r\n\r\n\t\t    GNU GENERAL PUBLIC LICENSE\r\n\t\t       Version 2, June 1991\r\n\r\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\r\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\r\n Everyone is permitted to copy and distribute verbatim copies\r\n of this license document, but changing it is not allowed.\r\n\r\n\t\t\t    Preamble\r\n\r\n  The licenses for most software are designed to take away your\r\nfreedom to share and change it.  By contrast, the GNU General Public\r\nLicense is intended to guarantee your freedom to share and change free\r\nsoftware--to make sure the software is free for all its users.  This\r\nGeneral Public License applies to most of the Free Software\r\nFoundation's software and to any other program whose authors commit to\r\nusing it.  (Some other Free Software Foundation software is covered by\r\nthe GNU Lesser General Public License instead.)  You can apply it to\r\nyour programs, too.\r\n\r\n  When we speak of free software, we are referring to freedom, not\r\nprice.  Our General Public Licenses are designed to make sure that you\r\nhave the freedom to distribute copies of free software (and charge for\r\nthis service if you wish), that you receive source code or can get it\r\nif you want it, that you can change the software or use pieces of it\r\nin new free programs; and that you know you can do these things.\r\n\r\n  To protect your rights, we need to make restrictions that forbid\r\nanyone to deny you these rights or to ask you to surrender the rights.\r\nThese restrictions translate to certain responsibilities for you if you\r\ndistribute copies of the software, or if you modify it.\r\n\r\n  For example, if you distribute copies of such a program, whether\r\ngratis or for a fee, you must give the recipients all the rights that\r\nyou have.  You must make sure that they, too, receive or can get the\r\nsource code.  And you must show them these terms so they know their\r\nrights.\r\n\r\n  We protect your rights with two steps: (1) copyright the software, and\r\n(2) offer you this license which gives you legal permission to copy,\r\ndistribute and/or modify the software.\r\n\r\n  Also, for each author's protection and ours, we want to make certain\r\nthat everyone understands that there is no warranty for this free\r\nsoftware.  If the software is modified by someone else and passed on, we\r\nwant its recipients to know that what they have is not the original, so\r\nthat any problems introduced by others will not reflect on the original\r\nauthors' reputations.\r\n\r\n  Finally, any free program is threatened constantly by software\r\npatents.  We wish to avoid the danger that redistributors of a free\r\nprogram will individually obtain patent licenses, in effect making the\r\nprogram proprietary.  To prevent this, we have made it clear that any\r\npatent must be licensed for everyone's free use or not licensed at all.\r\n\r\n  The precise terms and conditions for copying, distribution and\r\nmodification follow.\r\n\r\n\t\t    GNU GENERAL PUBLIC LICENSE\r\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r\n\r\n  0. This License applies to any program or other work which contains\r\na notice placed by the copyright holder saying it may be distributed\r\nunder the terms of this General Public License.  The \"Program\", below,\r\nrefers to any such program or work, and a \"work based on the Program\"\r\nmeans either the Program or any derivative work under copyright law:\r\nthat is to say, a work containing the Program or a portion of it,\r\neither verbatim or with modifications and/or translated into another\r\nlanguage.  (Hereinafter, translation is included without limitation in\r\nthe term \"modification\".)  Each licensee is addressed as \"you\".\r\n\r\nActivities other than copying, distribution and modification are not\r\ncovered by this License; they are outside its scope.  The act of\r\nrunning the Program is not restricted, and the output from the Program\r\nis covered only if its contents constitute a work based on the\r\nProgram (independent of having been made by running the Program).\r\nWhether that is true depends on what the Program does.\r\n\r\n  1. You may copy and distribute verbatim copies of the Program's\r\nsource code as you receive it, in any medium, provided that you\r\nconspicuously and appropriately publish on each copy an appropriate\r\ncopyright notice and disclaimer of warranty; keep intact all the\r\nnotices that refer to this License and to the absence of any warranty;\r\nand give any other recipients of the Program a copy of this License\r\nalong with the Program.\r\n\r\nYou may charge a fee for the physical act of transferring a copy, and\r\nyou may at your option offer warranty protection in exchange for a fee.\r\n\r\n  2. You may modify your copy or copies of the Program or any portion\r\nof it, thus forming a work based on the Program, and copy and\r\ndistribute such modifications or work under the terms of Section 1\r\nabove, provided that you also meet all of these conditions:\r\n\r\n    a) You must cause the modified files to carry prominent notices\r\n    stating that you changed the files and the date of any change.\r\n\r\n    b) You must cause any work that you distribute or publish, that in\r\n    whole or in part contains or is derived from the Program or any\r\n    part thereof, to be licensed as a whole at no charge to all third\r\n    parties under the terms of this License.\r\n\r\n    c) If the modified program normally reads commands interactively\r\n    when run, you must cause it, when started running for such\r\n    interactive use in the most ordinary way, to print or display an\r\n    announcement including an appropriate copyright notice and a\r\n    notice that there is no warranty (or else, saying that you provide\r\n    a warranty) and that users may redistribute the program under\r\n    these conditions, and telling the user how to view a copy of this\r\n    License.  (Exception: if the Program itself is interactive but\r\n    does not normally print such an announcement, your work based on\r\n    the Program is not required to print an announcement.)\r\n\r\nThese requirements apply to the modified work as a whole.  If\r\nidentifiable sections of that work are not derived from the Program,\r\nand can be reasonably considered independent and separate works in\r\nthemselves, then this License, and its terms, do not apply to those\r\nsections when you distribute them as separate works.  But when you\r\ndistribute the same sections as part of a whole which is a work based\r\non the Program, the distribution of the whole must be on the terms of\r\nthis License, whose permissions for other licensees extend to the\r\nentire whole, and thus to each and every part regardless of who wrote it.\r\n\r\nThus, it is not the intent of this section to claim rights or contest\r\nyour rights to work written entirely by you; rather, the intent is to\r\nexercise the right to control the distribution of derivative or\r\ncollective works based on the Program.\r\n\r\nIn addition, mere aggregation of another work not based on the Program\r\nwith the Program (or with a work based on the Program) on a volume of\r\na storage or distribution medium does not bring the other work under\r\nthe scope of this License.\r\n\r\n  3. You may copy and distribute the Program (or a work based on it,\r\nunder Section 2) in object code or executable form under the terms of\r\nSections 1 and 2 above provided that you also do one of the following:\r\n\r\n    a) Accompany it with the complete corresponding machine-readable\r\n    source code, which must be distributed under the terms of Sections\r\n    1 and 2 above on a medium customarily used for software interchange; or,\r\n\r\n    b) Accompany it with a written offer, valid for at least three\r\n    years, to give any third party, for a charge no more than your\r\n    cost of physically performing source distribution, a complete\r\n    machine-readable copy of the corresponding source code, to be\r\n    distributed under the terms of Sections 1 and 2 above on a medium\r\n    customarily used for software interchange; or,\r\n\r\n    c) Accompany it with the information you received as to the offer\r\n    to distribute corresponding source code.  (This alternative is\r\n    allowed only for noncommercial distribution and only if you\r\n    received the program in object code or executable form with such\r\n    an offer, in accord with Subsection b above.)\r\n\r\nThe source code for a work means the preferred form of the work for\r\nmaking modifications to it.  For an executable work, complete source\r\ncode means all the source code for all modules it contains, plus any\r\nassociated interface definition files, plus the scripts used to\r\ncontrol compilation and installation of the executable.  However, as a\r\nspecial exception, the source code distributed need not include\r\nanything that is normally distributed (in either source or binary\r\nform) with the major components (compiler, kernel, and so on) of the\r\noperating system on which the executable runs, unless that component\r\nitself accompanies the executable.\r\n\r\nIf distribution of executable or object code is made by offering\r\naccess to copy from a designated place, then offering equivalent\r\naccess to copy the source code from the same place counts as\r\ndistribution of the source code, even though third parties are not\r\ncompelled to copy the source along with the object code.\r\n\r\n  4. You may not copy, modify, sublicense, or distribute the Program\r\nexcept as expressly provided under this License.  Any attempt\r\notherwise to copy, modify, sublicense or distribute the Program is\r\nvoid, and will automatically terminate your rights under this License.\r\nHowever, parties who have received copies, or rights, from you under\r\nthis License will not have their licenses terminated so long as such\r\nparties remain in full compliance.\r\n\r\n  5. You are not required to accept this License, since you have not\r\nsigned it.  However, nothing else grants you permission to modify or\r\ndistribute the Program or its derivative works.  These actions are\r\nprohibited by law if you do not accept this License.  Therefore, by\r\nmodifying or distributing the Program (or any work based on the\r\nProgram), you indicate your acceptance of this License to do so, and\r\nall its terms and conditions for copying, distributing or modifying\r\nthe Program or works based on it.\r\n\r\n  6. Each time you redistribute the Program (or any work based on the\r\nProgram), the recipient automatically receives a license from the\r\noriginal licensor to copy, distribute or modify the Program subject to\r\nthese terms and conditions.  You may not impose any further\r\nrestrictions on the recipients' exercise of the rights granted herein.\r\nYou are not responsible for enforcing compliance by third parties to\r\nthis License.\r\n\r\n  7. If, as a consequence of a court judgment or allegation of patent\r\ninfringement or for any other reason (not limited to patent issues),\r\nconditions are imposed on you (whether by court order, agreement or\r\notherwise) that contradict the conditions of this License, they do not\r\nexcuse you from the conditions of this License.  If you cannot\r\ndistribute so as to satisfy simultaneously your obligations under this\r\nLicense and any other pertinent obligations, then as a consequence you\r\nmay not distribute the Program at all.  For example, if a patent\r\nlicense would not permit royalty-free redistribution of the Program by\r\nall those who receive copies directly or indirectly through you, then\r\nthe only way you could satisfy both it and this License would be to\r\nrefrain entirely from distribution of the Program.\r\n\r\nIf any portion of this section is held invalid or unenforceable under\r\nany particular circumstance, the balance of the section is intended to\r\napply and the section as a whole is intended to apply in other\r\ncircumstances.\r\n\r\nIt is not the purpose of this section to induce you to infringe any\r\npatents or other property right claims or to contest validity of any\r\nsuch claims; this section has the sole purpose of protecting the\r\nintegrity of the free software distribution system, which is\r\nimplemented by public license practices.  Many people have made\r\ngenerous contributions to the wide range of software distributed\r\nthrough that system in reliance on consistent application of that\r\nsystem; it is up to the author/donor to decide if he or she is willing\r\nto distribute software through any other system and a licensee cannot\r\nimpose that choice.\r\n\r\nThis section is intended to make thoroughly clear what is believed to\r\nbe a consequence of the rest of this License.\r\n\r\n  8. If the distribution and/or use of the Program is restricted in\r\ncertain countries either by patents or by copyrighted interfaces, the\r\noriginal copyright holder who places the Program under this License\r\nmay add an explicit geographical distribution limitation excluding\r\nthose countries, so that distribution is permitted only in or among\r\ncountries not thus excluded.  In such case, this License incorporates\r\nthe limitation as if written in the body of this License.\r\n\r\n  9. The Free Software Foundation may publish revised and/or new versions\r\nof the General Public License from time to time.  Such new versions will\r\nbe similar in spirit to the present version, but may differ in detail to\r\naddress new problems or concerns.\r\n\r\nEach version is given a distinguishing version number.  If the Program\r\nspecifies a version number of this License which applies to it and \"any\r\nlater version\", you have the option of following the terms and conditions\r\neither of that version or of any later version published by the Free\r\nSoftware Foundation.  If the Program does not specify a version number of\r\nthis License, you may choose any version ever published by the Free Software\r\nFoundation.\r\n\r\n  10. If you wish to incorporate parts of the Program into other free\r\nprograms whose distribution conditions are different, write to the author\r\nto ask for permission.  For software which is copyrighted by the Free\r\nSoftware Foundation, write to the Free Software Foundation; we sometimes\r\nmake exceptions for this.  Our decision will be guided by the two goals\r\nof preserving the free status of all derivatives of our free software and\r\nof promoting the sharing and reuse of software generally.\r\n\r\n\t\t\t    NO WARRANTY\r\n\r\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\r\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\r\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\r\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\r\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\r\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\r\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\r\nREPAIR OR CORRECTION.\r\n\r\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\r\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\r\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\r\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\r\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\r\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\r\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\r\nPOSSIBILITY OF SUCH DAMAGES.\r\n\r\n\t\t     END OF TERMS AND CONDITIONS\r\n\r\n\t    How to Apply These Terms to Your New Programs\r\n\r\n  If you develop a new program, and you want it to be of the greatest\r\npossible use to the public, the best way to achieve this is to make it\r\nfree software which everyone can redistribute and change under these terms.\r\n\r\n  To do so, attach the following notices to the program.  It is safest\r\nto attach them to the start of each source file to most effectively\r\nconvey the exclusion of warranty; and each file should have at least\r\nthe \"copyright\" line and a pointer to where the full notice is found.\r\n\r\n    <one line to give the program's name and a brief idea of what it does.>\r\n    Copyright (C) <year>  <name of author>\r\n\r\n    This program is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n\r\n    This program is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n\r\n    You should have received a copy of the GNU General Public License along\r\n    with this program; if not, write to the Free Software Foundation, Inc.,\r\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r\n\r\nAlso add information on how to contact you by electronic and paper mail.\r\n\r\nIf the program is interactive, make it output a short notice like this\r\nwhen it starts in an interactive mode:\r\n\r\n    Gnomovision version 69, Copyright (C) year name of author\r\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r\n    This is free software, and you are welcome to redistribute it\r\n    under certain conditions; type `show c' for details.\r\n\r\nThe hypothetical commands `show w' and `show c' should show the appropriate\r\nparts of the General Public License.  Of course, the commands you use may\r\nbe called something other than `show w' and `show c'; they could even be\r\nmouse-clicks or menu items--whatever suits your program.\r\n\r\nYou should also get your employer (if you work as a programmer) or your\r\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\r\nnecessary.  Here is a sample; alter the names:\r\n\r\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\r\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\r\n\r\n  <signature of Ty Coon>, 1 April 1989\r\n  Ty Coon, President of Vice\r\n\r\nThis General Public License does not permit incorporating your program into\r\nproprietary programs.  If your program is a subroutine library, you may\r\nconsider it more useful to permit linking proprietary applications with the\r\nlibrary.  If this is what you want to do, use the GNU Lesser General\r\nPublic License instead of this License.\r\n\r\n\r\n\"CLASSPATH\" EXCEPTION TO THE GPL\r\n\r\n    Linking this library statically or dynamically with other modules is making\r\n    a combined work based on this library.  Thus, the terms and conditions of\r\n    the GNU General Public License cover the whole combination.\r\n\r\n    As a special exception, the copyright holders of this library give you\r\n    permission to link this library with independent modules to produce an\r\n    executable, regardless of the license terms of these independent modules,\r\n    and to copy and distribute the resulting executable under terms of your\r\n    choice, provided that you also meet, for each linked independent module,\r\n    the terms and conditions of the license of that module.  An independent\r\n    module is a module which is not derived from or based on this library.  If\r\n    you modify this library, you may extend this exception to your version of\r\n    the library, but you are not obligated to do so.  If you do not wish to do\r\n    so, delete this exception statement from your version.\r\n"
  },
  {
    "path": "README.md",
    "content": "JavaCV\r\n======\r\n\r\n[![Gitter](https://badges.gitter.im/bytedeco/javacv.svg)](https://gitter.im/bytedeco/javacv) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.bytedeco/javacv-platform/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.bytedeco/javacv-platform) [![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/https/oss.sonatype.org/org.bytedeco/javacv.svg)](http://bytedeco.org/builds/) [![Build Status](https://travis-ci.org/bytedeco/javacv.svg?branch=master)](https://travis-ci.org/bytedeco/javacv) <sup>Commercial support:</sup> [![xscode](https://img.shields.io/badge/Available%20on-xs%3Acode-blue?style=?style=plastic&logo=appveyor&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////////VXz1bAAAAAJ0Uk5T/wDltzBKAAAAlUlEQVR42uzXSwqAMAwE0Mn9L+3Ggtgkk35QwcnSJo9S+yGwM9DCooCbgn4YrJ4CIPUcQF7/XSBbx2TEz4sAZ2q1RAECBAiYBlCtvwN+KiYAlG7UDGj59MViT9hOwEqAhYCtAsUZvL6I6W8c2wcbd+LIWSCHSTeSAAECngN4xxIDSK9f4B9t377Wd7H5Nt7/Xz8eAgwAvesLRjYYPuUAAAAASUVORK5CYII=)](https://xscode.com/bytedeco/javacv)\r\n\r\n\r\nIntroduction\r\n------------\r\nJavaCV uses wrappers from the [JavaCPP Presets](https://github.com/bytedeco/javacpp-presets) of commonly used libraries by researchers in the field of computer vision ([OpenCV](http://opencv.org/), [FFmpeg](http://ffmpeg.org/), [libdc1394](http://damien.douxchamps.net/ieee1394/libdc1394/), [FlyCapture](https://www.flir.com/products/flycapture-sdk/), [Spinnaker](https://www.flir.com/products/spinnaker-sdk/), [OpenKinect](http://openkinect.org/), [librealsense](https://github.com/IntelRealSense/librealsense), [CL PS3 Eye Driver](https://codelaboratories.com/downloads/), [videoInput](http://muonics.net/school/spring05/videoInput/), [ARToolKitPlus](https://launchpad.net/artoolkitplus), [flandmark](https://github.com/uricamic/flandmark), [Leptonica](http://www.leptonica.org/), and [Tesseract](https://github.com/tesseract-ocr/tesseract)) and provides utility classes to make their functionality easier to use on the Java platform, including Android.\r\n\r\nJavaCV also comes with hardware accelerated full-screen image display (`CanvasFrame` and `GLCanvasFrame`), easy-to-use methods to execute code in parallel on multiple cores (`Parallel`), user-friendly geometric and color calibration of cameras and projectors (`GeometricCalibrator`, `ProCamGeometricCalibrator`, `ProCamColorCalibrator`), detection and matching of feature points (`ObjectFinder`), a set of classes that implement direct image alignment of projector-camera systems (mainly `GNImageAligner`, `ProjectiveTransformer`, `ProjectiveColorTransformer`, `ProCamTransformer`, and `ReflectanceInitializer`), a blob analysis package (`Blobs`), as well as miscellaneous functionality in the `JavaCV` class. Some of these classes also have an OpenCL and OpenGL counterpart, their names ending with `CL` or starting with `GL`, i.e.: `JavaCVCL`, `GLCanvasFrame`, etc.\r\n\r\nTo learn how to use the API, since documentation currently lacks, please refer to the [Sample Usage](#sample-usage) section below as well as the [sample programs](https://github.com/bytedeco/javacv/tree/master/samples/), including two for Android (`FacePreview.java` and `RecordActivity.java`), also found in the `samples` directory. You may also find it useful to refer to the source code of [ProCamCalib](https://github.com/bytedeco/procamcalib) and [ProCamTracker](https://github.com/bytedeco/procamtracker) as well as [examples ported from OpenCV2 Cookbook](https://github.com/bytedeco/javacv-examples/) and the associated [wiki pages](https://github.com/bytedeco/javacv-examples/tree/master/OpenCV_Cookbook).\r\n\r\nPlease keep me informed of any updates or fixes you make to the code so that I may integrate them into the next release. Thank you! And feel free to ask questions on [the mailing list](http://groups.google.com/group/javacv) or [the discussion forum](https://github.com/bytedeco/javacv/discussions) if you encounter any problems with the software! I am sure it is far from perfect...\r\n\r\n\r\nDownloads\r\n---------\r\nArchives containing JAR files are available as [releases](https://github.com/bytedeco/javacv/releases). The binary archive contains builds for Android, iOS, Linux, Mac OS X, and Windows. The JAR files for specific child modules or platforms can also be obtained individually from the [Maven Central Repository](http://search.maven.org/#search|ga|1|bytedeco).\r\n\r\nTo install manually the JAR files, follow the instructions in the [Manual Installation](#manual-installation) section below.\r\n\r\nWe can also have everything downloaded and installed automatically with:\r\n\r\n * Maven (inside the `pom.xml` file)\r\n```xml\r\n  <dependency>\r\n    <groupId>org.bytedeco</groupId>\r\n    <artifactId>javacv-platform</artifactId>\r\n    <version>1.5.13</version>\r\n  </dependency>\r\n```\r\n\r\n * Gradle (inside the `build.gradle.kts` or `build.gradle` file)\r\n```groovy\r\n  dependencies {\r\n    implementation(\"org.bytedeco:javacv-platform:1.5.13\")\r\n  }\r\n```\r\n\r\n * Leiningen (inside the `project.clj` file)\r\n```clojure\r\n  :dependencies [\r\n    [org.bytedeco/javacv-platform \"1.5.13\"]\r\n  ]\r\n```\r\n\r\n * sbt (inside the `build.sbt` file)\r\n```scala\r\n  libraryDependencies += \"org.bytedeco\" % \"javacv-platform\" % \"1.5.13\"\r\n```\r\n\r\nThis downloads binaries for all platforms, but to get binaries for only one platform we can set the `javacpp.platform` system property (via the `-D` command line option) to something like `android-arm`, `linux-x86_64`, `macosx-x86_64`, `windows-x86_64`, etc. Please refer to the [README.md file of the JavaCPP Presets](https://github.com/bytedeco/javacpp-presets#downloads) for details. Another option available to Gradle users is [Gradle JavaCPP](https://github.com/bytedeco/gradle-javacpp), and similarly for Scala users there is [SBT-JavaCV](https://github.com/bytedeco/sbt-javacv).\r\n\r\n\r\nRequired Software\r\n-----------------\r\nTo use JavaCV, you will first need to download and install the following software:\r\n\r\n * An implementation of Java SE 8 or newer:\r\n   * OpenJDK  http://openjdk.java.net/install/  or\r\n   * Oracle JDK  http://www.oracle.com/technetwork/java/javase/downloads/  or\r\n   * IBM JDK  http://www.ibm.com/developerworks/java/jdk/  or\r\n   * Microsoft JDK  https://www.microsoft.com/openjdk  etc\r\n\r\nFurther, although not always required, some functionality of JavaCV also relies on:\r\n\r\n * CL Eye Platform SDK (Windows only)  http://codelaboratories.com/downloads/\r\n * Android SDK API 24 or newer  http://developer.android.com/sdk/\r\n * JOCL and JOGL from JogAmp  http://jogamp.org/\r\n\r\nFinally, please make sure everything has the same bitness: **32-bit and 64-bit modules do not mix under any circumstances**.\r\n\r\n\r\nManual Installation\r\n-------------------\r\nSimply put all the desired JAR files (`opencv*.jar`, `ffmpeg*.jar`, etc.), in addition to `javacpp.jar` and `javacv.jar`, somewhere in your class path. Here are some more specific instructions for common cases:\r\n\r\nNetBeans (Java SE 8 or newer):\r\n\r\n 1. In the Projects window, right-click the Libraries node of your project, and select \"Add JAR/Folder...\".\r\n 2. Locate the JAR files, select them, and click OK.\r\n\r\nEclipse (Java SE 8 or newer):\r\n\r\n 1. Navigate to Project > Properties > Java Build Path > Libraries and click \"Add External JARs...\".\r\n 2. Locate the JAR files, select them, and click OK.\r\n \r\nVisual Studio Code (Java SE 8 or newer):\r\n\r\n 1. Navigate to Java Projects > Referenced Libraries, and click `+`.\r\n 2. Locate the JAR files, select them, and click OK.\r\n\r\nIntelliJ IDEA (Android 7.0 or newer):\r\n\r\n 1. Follow the instructions on this page: http://developer.android.com/training/basics/firstapp/\r\n 2. Copy all the JAR files into the `app/libs` subdirectory.\r\n 3. Navigate to File > Project Structure > app > Dependencies, click `+`, and select \"2 File dependency\".\r\n 4. Select all the JAR files from the `libs` subdirectory.\r\n 5. In the AndroidManifest.xml add `android:extractNativeLibs=\"true\"`\r\n\r\nAfter that, the wrapper classes for OpenCV and FFmpeg, for example, can automatically access all of their C/C++ APIs:\r\n\r\n * [OpenCV documentation](http://docs.opencv.org/master/)\r\n * [FFmpeg documentation](http://ffmpeg.org/doxygen/trunk/)\r\n\r\n\r\nSample Usage\r\n------------\r\nThe class definitions are basically ports to Java of the original header files in C/C++, and I deliberately decided to keep as much of the original syntax as possible. For example, here is a method that tries to load an image file, smooth it, and save it back to disk:\r\n\r\n```java\r\nimport org.bytedeco.opencv.opencv_core.*;\r\nimport org.bytedeco.opencv.opencv_imgproc.*;\r\nimport static org.bytedeco.opencv.global.opencv_core.*;\r\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\r\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\r\n\r\npublic class Smoother {\r\n    public static void smooth(String filename) {\r\n        Mat image = imread(filename);\r\n        if (image != null) {\r\n            GaussianBlur(image, image, new Size(3, 3), 0);\r\n            imwrite(filename, image);\r\n        }\r\n    }\r\n}\r\n```\r\n\r\nJavaCV also comes with helper classes and methods on top of OpenCV and FFmpeg to facilitate their integration to the Java platform. Here is a small demo program demonstrating the most frequently useful parts:\r\n\r\n```java\r\nimport java.io.File;\r\nimport java.net.URL;\r\nimport org.bytedeco.javacv.*;\r\nimport org.bytedeco.javacpp.*;\r\nimport org.bytedeco.javacpp.indexer.*;\r\nimport org.bytedeco.opencv.opencv_core.*;\r\nimport org.bytedeco.opencv.opencv_imgproc.*;\r\nimport org.bytedeco.opencv.opencv_calib3d.*;\r\nimport org.bytedeco.opencv.opencv_objdetect.*;\r\nimport static org.bytedeco.opencv.global.opencv_core.*;\r\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\r\nimport static org.bytedeco.opencv.global.opencv_calib3d.*;\r\nimport static org.bytedeco.opencv.global.opencv_objdetect.*;\r\n\r\npublic class Demo {\r\n    public static void main(String[] args) throws Exception {\r\n        String classifierName = null;\r\n        if (args.length > 0) {\r\n            classifierName = args[0];\r\n        } else {\r\n            URL url = new URL(\"https://raw.github.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_alt.xml\");\r\n            File file = Loader.cacheResource(url);\r\n            classifierName = file.getAbsolutePath();\r\n        }\r\n\r\n        // We can \"cast\" Pointer objects by instantiating a new object of the desired class.\r\n        CascadeClassifier classifier = new CascadeClassifier(classifierName);\r\n        if (classifier == null) {\r\n            System.err.println(\"Error loading classifier file \\\"\" + classifierName + \"\\\".\");\r\n            System.exit(1);\r\n        }\r\n\r\n        // The available FrameGrabber classes include OpenCVFrameGrabber (opencv_videoio),\r\n        // DC1394FrameGrabber, FlyCapture2FrameGrabber, OpenKinectFrameGrabber, OpenKinect2FrameGrabber,\r\n        // RealSenseFrameGrabber, RealSense2FrameGrabber, PS3EyeFrameGrabber, VideoInputFrameGrabber, and FFmpegFrameGrabber.\r\n        FrameGrabber grabber = FrameGrabber.createDefault(0);\r\n        grabber.start();\r\n\r\n        // CanvasFrame, FrameGrabber, and FrameRecorder use Frame objects to communicate image data.\r\n        // We need a FrameConverter to interface with other APIs (Android, Java 2D, JavaFX, Tesseract, OpenCV, etc).\r\n        OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();\r\n\r\n        // FAQ about IplImage and Mat objects from OpenCV:\r\n        // - For custom raw processing of data, createBuffer() returns an NIO direct\r\n        //   buffer wrapped around the memory pointed by imageData, and under Android we can\r\n        //   also use that Buffer with Bitmap.copyPixelsFromBuffer() and copyPixelsToBuffer().\r\n        // - To get a BufferedImage from an IplImage, or vice versa, we can chain calls to\r\n        //   Java2DFrameConverter and OpenCVFrameConverter, one after the other.\r\n        // - Java2DFrameConverter also has static copy() methods that we can use to transfer\r\n        //   data more directly between BufferedImage and IplImage or Mat via Frame objects.\r\n        Mat grabbedImage = converter.convert(grabber.grab());\r\n        int height = grabbedImage.rows();\r\n        int width = grabbedImage.cols();\r\n\r\n        // Objects allocated with `new`, clone(), or a create*() factory method are automatically released\r\n        // by the garbage collector, but may still be explicitly released by calling deallocate().\r\n        // You shall NOT call cvReleaseImage(), cvReleaseMemStorage(), etc. on objects allocated this way.\r\n        Mat grayImage = new Mat(height, width, CV_8UC1);\r\n        Mat rotatedImage = grabbedImage.clone();\r\n\r\n        // The OpenCVFrameRecorder class simply uses the VideoWriter of opencv_videoio,\r\n        // but FFmpegFrameRecorder also exists as a more versatile alternative.\r\n        FrameRecorder recorder = FrameRecorder.createDefault(\"output.avi\", width, height);\r\n        recorder.start();\r\n\r\n        // CanvasFrame is a JFrame containing a Canvas component, which is hardware accelerated.\r\n        // It can also switch into full-screen mode when called with a screenNumber.\r\n        // We should also specify the relative monitor/camera response for proper gamma correction.\r\n        CanvasFrame frame = new CanvasFrame(\"Some Title\", CanvasFrame.getDefaultGamma()/grabber.getGamma());\r\n\r\n        // Let's create some random 3D rotation...\r\n        Mat randomR    = new Mat(3, 3, CV_64FC1),\r\n            randomAxis = new Mat(3, 1, CV_64FC1);\r\n        // We can easily and efficiently access the elements of matrices and images\r\n        // through an Indexer object with the set of get() and put() methods.\r\n        DoubleIndexer Ridx = randomR.createIndexer(),\r\n                   axisIdx = randomAxis.createIndexer();\r\n        axisIdx.put(0, (Math.random() - 0.5) / 4,\r\n                       (Math.random() - 0.5) / 4,\r\n                       (Math.random() - 0.5) / 4);\r\n        Rodrigues(randomAxis, randomR);\r\n        double f = (width + height) / 2.0;  Ridx.put(0, 2, Ridx.get(0, 2) * f);\r\n                                            Ridx.put(1, 2, Ridx.get(1, 2) * f);\r\n        Ridx.put(2, 0, Ridx.get(2, 0) / f); Ridx.put(2, 1, Ridx.get(2, 1) / f);\r\n        System.out.println(Ridx);\r\n\r\n        // We can allocate native arrays using constructors taking an integer as argument.\r\n        Point hatPoints = new Point(3);\r\n\r\n        while (frame.isVisible() && (grabbedImage = converter.convert(grabber.grab())) != null) {\r\n            // Let's try to detect some faces! but we need a grayscale image...\r\n            cvtColor(grabbedImage, grayImage, CV_BGR2GRAY);\r\n            RectVector faces = new RectVector();\r\n            classifier.detectMultiScale(grayImage, faces);\r\n            long total = faces.size();\r\n            for (long i = 0; i < total; i++) {\r\n                Rect r = faces.get(i);\r\n                int x = r.x(), y = r.y(), w = r.width(), h = r.height();\r\n                rectangle(grabbedImage, new Point(x, y), new Point(x + w, y + h), Scalar.RED, 1, CV_AA, 0);\r\n\r\n                // To access or pass as argument the elements of a native array, call position() before.\r\n                hatPoints.position(0).x(x - w / 10     ).y(y - h / 10);\r\n                hatPoints.position(1).x(x + w * 11 / 10).y(y - h / 10);\r\n                hatPoints.position(2).x(x + w / 2      ).y(y - h / 2 );\r\n                fillConvexPoly(grabbedImage, hatPoints.position(0), 3, Scalar.GREEN, CV_AA, 0);\r\n            }\r\n\r\n            // Let's find some contours! but first some thresholding...\r\n            threshold(grayImage, grayImage, 64, 255, CV_THRESH_BINARY);\r\n\r\n            // To check if an output argument is null we may call either isNull() or equals(null).\r\n            MatVector contours = new MatVector();\r\n            findContours(grayImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);\r\n            long n = contours.size();\r\n            for (long i = 0; i < n; i++) {\r\n                Mat contour = contours.get(i);\r\n                Mat points = new Mat();\r\n                approxPolyDP(contour, points, arcLength(contour, true) * 0.02, true);\r\n                drawContours(grabbedImage, new MatVector(points), -1, Scalar.BLUE);\r\n            }\r\n\r\n            warpPerspective(grabbedImage, rotatedImage, randomR, rotatedImage.size());\r\n\r\n            Frame rotatedFrame = converter.convert(rotatedImage);\r\n            frame.showImage(rotatedFrame);\r\n            recorder.record(rotatedFrame);\r\n        }\r\n        frame.dispose();\r\n        recorder.stop();\r\n        grabber.stop();\r\n    }\r\n}\r\n```\r\n\r\nFurthermore, after creating a `pom.xml` file with the following content:\r\n```xml\r\n<project>\r\n    <modelVersion>4.0.0</modelVersion>\r\n    <groupId>org.bytedeco.javacv</groupId>\r\n    <artifactId>demo</artifactId>\r\n    <version>1.5.13</version>\r\n    <properties>\r\n        <maven.compiler.source>1.8</maven.compiler.source>\r\n        <maven.compiler.target>1.8</maven.compiler.target>\r\n    </properties>\r\n    <dependencies>\r\n        <dependency>\r\n            <groupId>org.bytedeco</groupId>\r\n            <artifactId>javacv-platform</artifactId>\r\n            <version>1.5.13</version>\r\n        </dependency>\r\n\r\n        <!-- Additional dependencies required to use CUDA and cuDNN -->\r\n        <dependency>\r\n            <groupId>org.bytedeco</groupId>\r\n            <artifactId>opencv-platform-gpu</artifactId>\r\n            <version>4.13.0-1.5.13</version>\r\n        </dependency>\r\n\r\n        <!-- Optional GPL builds with (almost) everything enabled -->\r\n        <dependency>\r\n            <groupId>org.bytedeco</groupId>\r\n            <artifactId>ffmpeg-platform-gpl</artifactId>\r\n            <version>8.0.1-1.5.13</version>\r\n        </dependency>\r\n    </dependencies>\r\n    <build>\r\n        <sourceDirectory>.</sourceDirectory>\r\n    </build>\r\n</project>\r\n```\r\n\r\nAnd by placing the source code above in `Demo.java`, or similarly for other classes found in the [`samples`](samples), we can use the following command to have everything first installed automatically and then executed by Maven:\r\n```bash\r\n $ mvn compile exec:java -Dexec.mainClass=Demo\r\n```\r\n\r\n**Note**: In case of errors, please make sure that the `artifactId` in the `pom.xml` file reads `javacv-platform`, not `javacv` only, for example. The artifact `javacv-platform` adds all the necessary binary dependencies.\r\n\r\n\r\nBuild Instructions\r\n------------------\r\nIf the binary files available above are not enough for your needs, you might need to rebuild them from the source code. To this end, the project files were created for:\r\n\r\n * Maven 3.x  http://maven.apache.org/download.html\r\n * JavaCPP 1.5.13  https://github.com/bytedeco/javacpp\r\n * JavaCPP Presets 1.5.13  https://github.com/bytedeco/javacpp-presets\r\n\r\nOnce installed, simply call the usual `mvn install` command for JavaCPP, its Presets, and JavaCV. By default, no other dependencies than a C++ compiler for JavaCPP are required. Please refer to the comments inside the `pom.xml` files for further details.\r\n\r\nInstead of building the native libraries manually, we can run `mvn install` for JavaCV only and rely on the snapshot artifacts from the CI builds:\r\n\r\n * http://bytedeco.org/builds/\r\n\r\n\r\n----\r\nProject lead: Samuel Audet [samuel.audet `at` gmail.com](mailto:samuel.audet&nbsp;at&nbsp;gmail.com)  \r\nDeveloper site: https://github.com/bytedeco/javacv  \r\nDiscussion group: http://groups.google.com/group/javacv\r\n"
  },
  {
    "path": "platform/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>org.bytedeco</groupId>\n    <artifactId>javacpp-presets</artifactId>\n    <version>1.5.14-SNAPSHOT</version>\n    <relativePath></relativePath>\n  </parent>\n\n  <groupId>org.bytedeco</groupId>\n  <artifactId>javacv-platform</artifactId>\n  <name>JavaCV Platform</name>\n\n  <properties>\n    <javacpp.version>${project.version}</javacpp.version>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>javacv</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>javacpp-platform</artifactId>\n      <version>${javacpp.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>openblas-platform</artifactId>\n      <version>0.3.31-${javacpp.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>opencv-platform</artifactId>\n      <version>4.13.0-${javacpp.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>ffmpeg-platform</artifactId>\n      <version>8.0.1-${javacpp.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>flycapture-platform</artifactId>\n      <version>2.13.3.31-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>libdc1394-platform</artifactId>\n      <version>2.2.6-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>libfreenect-platform</artifactId>\n      <version>0.5.7-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>libfreenect2-platform</artifactId>\n      <version>0.2.0-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>librealsense-platform</artifactId>\n      <version>1.12.4-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>librealsense2-platform</artifactId>\n      <version>2.53.1-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>videoinput-platform</artifactId>\n      <version>0.200-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>artoolkitplus-platform</artifactId>\n      <version>2.3.1-1.5.9</version>\n    </dependency>\n<!--    <dependency>-->\n<!--      <groupId>org.bytedeco</groupId>-->\n<!--      <artifactId>flandmark-platform</artifactId>-->\n<!--      <version>1.07-1.5.8</version>-->\n<!--    </dependency>-->\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>leptonica-platform</artifactId>\n      <version>1.87.0-${javacpp.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>tesseract-platform</artifactId>\n      <version>5.5.2-${javacpp.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>4.13.2</version>\n      <scope>test</scope>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>opencv-platform-gpu</artifactId>\n      <version>4.13.0-${javacpp.version}</version>\n      <scope>test</scope>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>ffmpeg-platform-gpl</artifactId>\n      <version>8.0.1-${javacpp.version}</version>\n      <scope>test</scope>\n    </dependency>\n\n  </dependencies>\n\n  <repositories>\n    <repository>\n      <id>central-portal-snapshots</id>\n      <name>Central Portal Snapshots</name>\n      <url>https://central.sonatype.com/repository/maven-snapshots/</url>\n      <releases>\n        <enabled>false</enabled>\n      </releases>\n      <snapshots>\n        <enabled>true</enabled>\n      </snapshots>\n    </repository>\n  </repositories>\n\n  <build>\n    <plugins>\n      <plugin>\n        <artifactId>maven-jar-plugin</artifactId>\n        <version>3.5.0</version>\n        <executions>\n          <execution>\n            <id>default-jar</id>\n            <configuration>\n              <archive>\n                <manifestEntries>\n                  <Class-Path>javacv.jar javacpp-platform.jar openblas-platform.jar opencv-platform.jar ffmpeg-platform.jar flycapture-platform.jar libdc1394-platform.jar libfreenect-platform.jar libfreenect2-platform.jar librealsense-platform.jar librealsense2-platform.jar videoinput-platform.jar artoolkitplus-platform.jar flandmark-platform.jar leptonica-platform.jar tesseract-platform.jar</Class-Path>\n                  <Name>org/bytedeco/javacv/</Name>\n                </manifestEntries>\n              </archive>\n            </configuration>\n          </execution>\n          <execution>\n            <id>empty-javadoc-jar</id>\n            <goals>\n              <goal>jar</goal>\n            </goals>\n            <configuration>\n              <classifier>javadoc</classifier>\n            </configuration>\n          </execution>\n          <execution>\n            <id>empty-sources-jar</id>\n            <goals>\n              <goal>jar</goal>\n            </goals>\n            <configuration>\n              <classifier>sources</classifier>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <groupId>org.moditect</groupId>\n        <artifactId>moditect-maven-plugin</artifactId>\n        <version>1.3.0</version>\n        <configuration>\n          <jvmVersion>9</jvmVersion>\n          <overwriteExistingFiles>true</overwriteExistingFiles>\n          <outputDirectory>${project.build.directory}</outputDirectory>\n        </configuration>\n        <executions>\n          <execution>\n            <id>add-module-infos</id>\n            <phase>package</phase>\n            <goals>\n              <goal>add-module-info</goal>\n            </goals>\n            <configuration>\n              <modules>\n                <module>\n                  <file>${project.build.directory}/${project.artifactId}.jar</file>\n                  <moduleInfoFile>${project.basedir}/src/main/java9/module-info.java</moduleInfoFile>\n                </module>\n              </modules>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <artifactId>maven-dependency-plugin</artifactId>\n        <version>3.10.0</version>\n        <executions>\n          <execution>\n            <id>properties</id>\n            <goals>\n              <goal>properties</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>copy-dependencies</id>\n            <goals>\n              <goal>copy-dependencies</goal>\n            </goals>\n            <configuration>\n              <outputDirectory>${project.build.directory}</outputDirectory>\n              <stripVersion>true</stripVersion>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <artifactId>maven-surefire-plugin</artifactId>\n        <version>3.5.5</version>\n        <configuration>\n            <argLine>-Xmx2g</argLine>\n        </configuration>\n      </plugin>\n      <plugin>\n        <artifactId>maven-assembly-plugin</artifactId>\n        <version>3.8.0</version>\n        <configuration>\n          <attach>false</attach>\n          <descriptors>\n            <descriptor>src/main/assembly/bin.xml</descriptor>\n            <descriptor>src/main/assembly/src.xml</descriptor>\n          </descriptors>\n        </configuration>\n        <executions>\n          <execution>\n            <phase>package</phase>\n            <goals>\n              <goal>single</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n  <profiles>\n    <profile>\n      <id>sign-artifacts</id>\n      <activation>\n        <property>\n          <name>performRelease</name>\n          <value>true</value>\n        </property>\n      </activation>\n      <repositories>\n        <repository>\n          <id>central-portal-staging</id>\n          <name>Central Portal Staging</name>\n          <url>https://central.sonatype.com/api/v1/publisher/deployments/download/</url>\n          <releases>\n            <enabled>true</enabled>\n          </releases>\n          <snapshots>\n            <enabled>false</enabled>\n          </snapshots>\n        </repository>\n      </repositories>\n      <build>\n        <plugins>\n          <plugin>\n            <artifactId>maven-gpg-plugin</artifactId>\n            <version>3.2.8</version>\n            <executions>\n              <execution>\n                <id>sign-artifacts</id>\n                <phase>verify</phase>\n                <goals>\n                  <goal>sign</goal>\n                </goals>\n              </execution>\n            </executions>\n            <configuration>\n              <passphrase>${env.GPG_PASSPHRASE}</passphrase>\n              <useAgent>false</useAgent>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n  </profiles>\n\n</project>\n"
  },
  {
    "path": "platform/src/main/assembly/bin.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd\">\n  <id>${project.version}-bin</id>\n  <formats>\n    <format>zip</format>\n  </formats>\n  <baseDirectory>${project.artifactId}-${project.version}-bin</baseDirectory>\n  <fileSets>\n    <fileSet>\n      <directory>${project.basedir}/..</directory>\n      <outputDirectory>/</outputDirectory>\n      <includes>\n        <include>samples/*</include>\n        <include>CHANGELOG*</include>\n        <include>README*</include>\n        <include>LICENSE*</include>\n        <include>NOTICE*</include>\n      </includes>\n    </fileSet>\n    <fileSet>\n      <directory>${project.build.directory}</directory>\n      <outputDirectory>/</outputDirectory>\n      <includes>\n        <include>*.jar</include>\n      </includes>\n      <excludes>\n        <exclude>android*.jar</exclude>\n        <exclude>gluegen*.jar</exclude>\n        <exclude>hamcrest*.jar</exclude>\n        <exclude>junit*.jar</exclude>\n        <exclude>jogl*.jar</exclude>\n        <exclude>jocl*.jar</exclude>\n        <exclude>*-javadoc.jar</exclude>\n        <exclude>*-sources.jar</exclude>\n      </excludes>\n      <fileMode>0644</fileMode>\n    </fileSet>\n    <fileSet>\n      <directory>${project.build.directory}/site</directory>\n      <outputDirectory>docs</outputDirectory>\n    </fileSet>\n  </fileSets>\n</assembly>\n"
  },
  {
    "path": "platform/src/main/assembly/src.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd\">\n  <id>${project.version}-src</id>\n  <formats>\n    <format>zip</format>\n  </formats>\n  <baseDirectory>${project.artifactId}-${project.version}</baseDirectory>\n  <fileSets>\n    <fileSet>\n      <directory>${project.basedir}/..</directory>\n      <outputDirectory>/</outputDirectory>\n      <useDefaultExcludes>true</useDefaultExcludes>\n      <excludes>\n        <!-- TODO: use expresssions instead: ${project.build.sourceDirectory}, etc -->\n        <exclude>**/target/**</exclude>\n        <exclude>**/cppbuild/**</exclude>\n      </excludes>\n    </fileSet>\n  </fileSets>\n</assembly>\n\n"
  },
  {
    "path": "platform/src/main/java9/module-info.java",
    "content": "module org.bytedeco.javacv.platform {\n    requires transitive org.bytedeco.javacv;\n    requires org.bytedeco.opencv.platform;\n    requires org.bytedeco.ffmpeg.platform;\n    requires org.bytedeco.flycapture.platform;\n    requires org.bytedeco.libdc1394.platform;\n    requires org.bytedeco.libfreenect.platform;\n    requires org.bytedeco.libfreenect2.platform;\n    requires org.bytedeco.librealsense.platform;\n    requires org.bytedeco.librealsense2.platform;\n    requires org.bytedeco.videoinput.platform;\n    requires org.bytedeco.artoolkitplus.platform;\n    requires org.bytedeco.flandmark.platform;\n    requires org.bytedeco.leptonica.platform;\n    requires org.bytedeco.tesseract.platform;\n}\n"
  },
  {
    "path": "platform/src/test/java/org/bytedeco/javacv/FrameConverterTest.java",
    "content": "/*\n * Copyright (C) 2015-2016 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.awt.image.BufferedImage;\nimport java.awt.image.DataBufferByte;\nimport java.awt.image.DataBufferInt;\nimport java.awt.image.WritableRaster;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.IntBuffer;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.indexer.Indexer;\nimport org.bytedeco.javacpp.indexer.UByteIndexer;\nimport org.junit.Test;\n\nimport org.bytedeco.leptonica.PIX;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.junit.Assert.*;\n\n/**\n * Test cases for FrameConverter classes. Also uses other classes from JavaCV.\n *\n * @author Samuel Audet\n */\npublic class FrameConverterTest {\n\n    @Test public void testAndroidFrameConverter() {\n        System.out.println(\"AndroidFrameConverter\");\n\n        AndroidFrameConverter converter = new AndroidFrameConverter();\n\n        int width = 512;\n        int height = 1024;\n        byte[] yuvData = new byte[3 * width * height / 2];\n        for (int i = 0; i < yuvData.length; i++) {\n            yuvData[i] = (byte)i;\n        }\n        Mat yuvImage = new Mat(3 * height / 2, width, CV_8UC1, new BytePointer(yuvData));\n        Mat bgrImage = new Mat(height, width, CV_8UC3);\n        cvtColor(yuvImage, bgrImage, CV_YUV2BGR_NV21);\n        Frame bgrFrame = converter.convert(yuvData, width, height);\n\n        UByteIndexer bgrImageIdx = bgrImage.createIndexer();\n        UByteIndexer bgrFrameIdx = bgrFrame.createIndexer();\n        assertEquals(bgrImageIdx.rows(), bgrFrameIdx.rows());\n        assertEquals(bgrImageIdx.cols(), bgrFrameIdx.cols());\n        assertEquals(bgrImageIdx.channels(), bgrFrameIdx.channels());\n        for (int i = 0; i < bgrImageIdx.rows(); i++) {\n            for (int j = 0; j < bgrImageIdx.cols(); j++) {\n                for (int k = 0; k < bgrImageIdx.channels(); k++) {\n                    assertEquals((float)bgrImageIdx.get(i, j, k), (float)bgrFrameIdx.get(i, j, k), 1.0f);\n                }\n            }\n        }\n        bgrImageIdx.release();\n        bgrFrameIdx.release();\n\n        Frame grayFrame = new Frame(1024 + 1, 768, Frame.DEPTH_UBYTE, 1);\n        Frame colorFrame = new Frame(640 + 1, 480, Frame.DEPTH_UBYTE, 3);\n\n        UByteIndexer grayFrameIdx = grayFrame.createIndexer();\n        for (int i = 0; i < grayFrameIdx.rows(); i++) {\n            for (int j = 0; j < grayFrameIdx.cols(); j++) {\n                grayFrameIdx.put(i, j, i + j);\n            }\n        }\n\n        UByteIndexer colorFrameIdx = colorFrame.createIndexer();\n        for (int i = 0; i < colorFrameIdx.rows(); i++) {\n            for (int j = 0; j < colorFrameIdx.cols(); j++) {\n                for (int k = 0; k < colorFrameIdx.channels(); k++) {\n                    colorFrameIdx.put(i, j, k, i + j + k);\n                }\n            }\n        }\n\n        width = grayFrame.imageWidth;\n        height = grayFrame.imageHeight;\n        int stride = grayFrame.imageStride;\n        int rowBytes = width * 4;\n        ByteBuffer in = (ByteBuffer)grayFrame.image[0];\n        ByteBuffer buffer = converter.gray2rgba(in, width, height, stride, rowBytes);\n        for (int y = 0; y < height; y++) {\n            for (int x = 0; x < width; x++) {\n                // GRAY -> RGBA\n                byte B = in.get(y * stride + x);\n                assertEquals(buffer.get(y * rowBytes + 4 * x    ), B);\n                assertEquals(buffer.get(y * rowBytes + 4 * x + 1), B);\n                assertEquals(buffer.get(y * rowBytes + 4 * x + 2), B);\n                assertEquals(buffer.get(y * rowBytes + 4 * x + 3), (byte)0xFF);\n            }\n        }\n\n        width = colorFrame.imageWidth;\n        height = colorFrame.imageHeight;\n        stride = colorFrame.imageStride;\n        rowBytes = width * 4;\n        in = (ByteBuffer)colorFrame.image[0];\n        buffer = converter.bgr2rgba(in, width, height, stride, rowBytes);\n        for (int y = 0; y < height; y++) {\n            for (int x = 0; x < width; x++) {\n                // BGR -> RGBA\n                byte B = in.get(y * stride + 3 * x    );\n                byte G = in.get(y * stride + 3 * x + 1);\n                byte R = in.get(y * stride + 3 * x + 2);\n                assertEquals(buffer.get(y * rowBytes + 4 * x    ), R);\n                assertEquals(buffer.get(y * rowBytes + 4 * x + 1), G);\n                assertEquals(buffer.get(y * rowBytes + 4 * x + 2), B);\n                assertEquals(buffer.get(y * rowBytes + 4 * x + 3), (byte)0xFF);\n            }\n        }\n\n        colorFrameIdx.release();\n        grayFrameIdx.release();\n        converter.close();\n        colorFrame.close();\n        grayFrame.close();\n    }\n\n    @Test public void testJava2DFrameConverter() {\n        System.out.println(\"Java2DFrameConverter\");\n\n        int[] depths = {Frame.DEPTH_UBYTE, Frame.DEPTH_SHORT, Frame.DEPTH_FLOAT};\n        int[] channels = {1, 3, 4};\n        for (int i = 0; i < depths.length; i++) {\n            for (int j = 0; j < channels.length; j++) {\n                Frame frame = new Frame(640 + 1, 480, depths[i], channels[j]);\n                Java2DFrameConverter converter = new Java2DFrameConverter();\n\n                Indexer frameIdx = frame.createIndexer();\n                for (int y = 0; y < frameIdx.rows(); y++) {\n                    for (int x = 0; x < frameIdx.cols(); x++) {\n                        for (int z = 0; z < frameIdx.channels(); z++) {\n                            frameIdx.putDouble(new long[] {y, x, z}, y + x + z);\n                        }\n                    }\n                }\n\n                BufferedImage image = converter.convert(frame);\n                converter.frame = null;\n                Frame frame2 = converter.convert(image);\n\n                Indexer frame2Idx = frame2.createIndexer();\n                for (int y = 0; y < frameIdx.rows(); y++) {\n                    for (int x = 0; x < frameIdx.cols(); x++) {\n                        for (int z = 0; z < frameIdx.channels(); z++) {\n                            double value = frameIdx.getDouble(y, x, z);\n                            assertEquals(value, frame2Idx.getDouble(y, x, z), 0);\n                        }\n                    }\n                }\n\n                try {\n                    frame2Idx.getDouble(frameIdx.rows() + 1, frameIdx.cols() + 1);\n                    fail(\"IndexOutOfBoundsException should have been thrown.\");\n                } catch (IndexOutOfBoundsException e) { }\n\n                frameIdx.release();\n                frame2Idx.release();\n                converter.close();\n                frame.close();\n            }\n        }\n\n        int[] types = {BufferedImage.TYPE_INT_RGB, BufferedImage.TYPE_INT_ARGB,\n                       BufferedImage.TYPE_INT_ARGB_PRE, BufferedImage.TYPE_INT_BGR};\n        for (int i = 0; i < types.length; i++) {\n            BufferedImage image = new BufferedImage(640 + 1, 480, types[i]);\n            Java2DFrameConverter converter = new Java2DFrameConverter();\n\n            WritableRaster raster = image.getRaster();\n            int[] array = ((DataBufferInt)raster.getDataBuffer()).getData();\n            for (int j = 0; j < array.length; j++) {\n                array[j] = j;\n            }\n\n            Frame frame = converter.convert(image);\n            converter.bufferedImage = null;\n            BufferedImage image2 = converter.convert(frame);\n\n            WritableRaster raster2 = image2.getRaster();\n            byte[] array2 = ((DataBufferByte)raster2.getDataBuffer()).getData();\n            for (int j = 0; j < array.length; j++) {\n                int n = ((array2[4 * j    ] & 0xFF) << 24) | ((array2[4 * j + 1] & 0xFF) << 16)\n                      | ((array2[4 * j + 2] & 0xFF) << 8)  |  (array2[4 * j + 3] & 0xFF);\n                assertEquals(array[j], n);\n            }\n            converter.close();\n        }\n    }\n\n    @Test public void testOpenCVFrameConverter() {\n        System.out.println(\"OpenCVFrameConverter\");\n        Loader.load(org.bytedeco.opencv.opencv_java.class);\n\n        for (int depth = 8; depth <= 64; depth *= 2) {\n            assertEquals(depth, OpenCVFrameConverter.getFrameDepth(OpenCVFrameConverter.getIplImageDepth(depth)));\n            assertEquals(depth, OpenCVFrameConverter.getFrameDepth(OpenCVFrameConverter.getMatDepth(depth)));\n            if (depth < 64) {\n                assertEquals(-depth, OpenCVFrameConverter.getFrameDepth(OpenCVFrameConverter.getIplImageDepth(-depth)));\n                assertEquals(-depth, OpenCVFrameConverter.getFrameDepth(OpenCVFrameConverter.getMatDepth(-depth)));\n            }\n        }\n\n        Frame frame = new Frame(640 + 1, 480, Frame.DEPTH_UBYTE, 3);\n        OpenCVFrameConverter.ToIplImage converter1 = new OpenCVFrameConverter.ToIplImage();\n        OpenCVFrameConverter.ToMat converter2 = new OpenCVFrameConverter.ToMat();\n        OpenCVFrameConverter.ToOrgOpenCvCoreMat converter3 = new OpenCVFrameConverter.ToOrgOpenCvCoreMat();\n\n        UByteIndexer frameIdx = frame.createIndexer();\n        for (int i = 0; i < frameIdx.rows(); i++) {\n            for (int j = 0; j < frameIdx.cols(); j++) {\n                for (int k = 0; k < frameIdx.channels(); k++) {\n                    frameIdx.put(i, j, k, i + j + k);\n                }\n            }\n        }\n\n        IplImage image = converter1.convert(frame);\n        Mat mat = converter2.convert(frame);\n        final org.opencv.core.Mat cvmat = converter3.convert(frame);\n\n        converter1.frame = null;\n        converter2.frame = null;\n        converter3.frame = null;\n        Frame frame1 = converter1.convert(image);\n        Frame frame2 = converter2.convert(mat);\n        Frame frame3 = converter3.convert(cvmat);\n        assertEquals(frame2.opaque, mat);\n        assertEquals(frame3.opaque, cvmat);\n\n        Mat mat2 = new Mat(mat.rows(), mat.cols(), mat.type(), mat.data(), mat.step());\n        org.opencv.core.Mat cvmat2 = new org.opencv.core.Mat(cvmat.rows(), cvmat.cols(), cvmat.type(),\n                new BytePointer() { { address = cvmat.dataAddr(); } }.capacity(cvmat.rows() * cvmat.cols() * cvmat.elemSize()).asByteBuffer(),\n                cvmat.step1() * cvmat.elemSize1());\n        assertNotEquals(mat, mat2);\n        assertNotEquals(cvmat, cvmat2);\n\n        frame2 = converter2.convert(mat2);\n        frame3 = converter3.convert(cvmat2);\n        assertEquals(frame2.opaque, mat2);\n        assertEquals(frame3.opaque, cvmat2);\n        assertEquals(frame3.imageStride, cvmat2.step1() * cvmat2.elemSize1());\n\n        UByteIndexer frame1Idx = frame1.createIndexer();\n        UByteIndexer frame2Idx = frame2.createIndexer();\n        UByteIndexer frame3Idx = frame3.createIndexer();\n        for (int i = 0; i < frameIdx.rows(); i++) {\n            for (int j = 0; j < frameIdx.cols(); j++) {\n                for (int k = 0; k < frameIdx.channels(); k++) {\n                    int b = frameIdx.get(i, j, k);\n                    assertEquals(b, frame1Idx.get(i, j, k));\n                    assertEquals(b, frame2Idx.get(i, j, k));\n                    assertEquals(b, frame3Idx.get(i, j, k));\n                }\n            }\n        }\n\n        try {\n            frame1Idx.get(frameIdx.rows() + 1, frameIdx.cols() + 1);\n            fail(\"IndexOutOfBoundsException should have been thrown.\");\n        } catch (IndexOutOfBoundsException e) { }\n\n        try {\n            frame2Idx.get(frameIdx.rows() + 1, frameIdx.cols() + 1);\n            fail(\"IndexOutOfBoundsException should have been thrown.\");\n        } catch (IndexOutOfBoundsException e) { }\n\n        try {\n            frame3Idx.get(frameIdx.rows() + 1, frameIdx.cols() + 1);\n            fail(\"IndexOutOfBoundsException should have been thrown.\");\n        } catch (IndexOutOfBoundsException e) { }\n\n        frameIdx.release();\n        frame1Idx.release();\n        frame2Idx.release();\n        frame3Idx.release();\n        converter1.close();\n        converter2.close();\n        converter3.close();\n        frame.close();\n    }\n\n    @Test public void testLeptonicaFrameConverter() {\n        System.out.println(\"LeptonicaFrameConverter\");\n\n        Frame frame = new Frame(640 + 1, 480, Frame.DEPTH_UBYTE, 3);\n        LeptonicaFrameConverter converter = new LeptonicaFrameConverter();\n\n        UByteIndexer frameIdx = frame.createIndexer();\n        for (int i = 0; i < frameIdx.rows(); i++) {\n            for (int j = 0; j < frameIdx.cols(); j++) {\n                for (int k = 0; k < frameIdx.channels(); k++) {\n                    frameIdx.put(i, j, k, i + j + k);\n                }\n            }\n        }\n\n        PIX pix = converter.convert(frame);\n\n        converter.frame = null;\n        Frame frame1 = converter.convert(pix);\n//        assertEquals(frame1.opaque, pix);\n\n        PIX pix2 = PIX.createHeader(pix.w(), pix.h(), pix.d()).data(pix.data()).wpl(pix.wpl());\n        assertNotEquals(pix, pix2);\n\n        Frame frame2 = converter.convert(pix2);\n//        assertEquals(frame2.opaque, pix2);\n\n        IntBuffer frameBuf = ((ByteBuffer)frame.image[0].position(0)).asIntBuffer();\n        IntBuffer frame1Buf = ((ByteBuffer)frame1.image[0].position(0)).asIntBuffer();\n        IntBuffer frame2Buf = ((ByteBuffer)frame2.image[0].position(0)).asIntBuffer();\n        IntBuffer pixBuf = pix.createBuffer().order(ByteOrder.BIG_ENDIAN).asIntBuffer();\n        IntBuffer pix2Buf = pix2.createBuffer().order(ByteOrder.BIG_ENDIAN).asIntBuffer();\n        for (int i = 0; i < frameBuf.capacity(); i++) {\n            int j = frameBuf.get(i);\n            assertEquals(j, frame1Buf.get(i));\n            assertEquals(j, frame2Buf.get(i));\n            assertEquals(j, pixBuf.get(i));\n            assertEquals(j, pix2Buf.get(i));\n        }\n\n        try {\n            frame1Buf.get(frameBuf.capacity() + 1);\n            fail(\"IndexOutOfBoundsException should have been thrown.\");\n        } catch (IndexOutOfBoundsException e) { }\n\n        try {\n            frame2Buf.get(frameBuf.capacity() + 1);\n            fail(\"IndexOutOfBoundsException should have been thrown.\");\n        } catch (IndexOutOfBoundsException e) { }\n\n        try {\n            pixBuf.get(frameBuf.capacity() + 1);\n            fail(\"IndexOutOfBoundsException should have been thrown.\");\n        } catch (IndexOutOfBoundsException e) { }\n\n        try {\n            pix2Buf.get(frameBuf.capacity() + 1);\n            fail(\"IndexOutOfBoundsException should have been thrown.\");\n        } catch (IndexOutOfBoundsException e) { }\n\n        pix2.deallocate();\n        pix.deallocate();\n        converter.close();\n        frame.close();\n    }\n}\n"
  },
  {
    "path": "platform/src/test/java/org/bytedeco/javacv/FrameFilterTest.java",
    "content": "/*\n * Copyright (C) 2018 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.nio.ByteBuffer;\nimport java.nio.ShortBuffer;\nimport org.bytedeco.javacpp.Loader;\nimport org.junit.Test;\n\nimport static org.bytedeco.ffmpeg.global.avcodec.*;\nimport static org.bytedeco.ffmpeg.global.avutil.*;\nimport static org.junit.Assert.*;\n\n/**\n * Test cases for FrameFilter classes. Also uses other classes from JavaCV.\n *\n * @author Samuel Audet\n */\npublic class FrameFilterTest {\n\n    @Test\n    public void testFFmpegFrameFilter() {\n        System.out.println(\"FFmpegFrameFilter\");\n\n        File tempFile = new File(Loader.getTempDir(), \"test.mov\");\n        try {\n            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(tempFile, 800, 600, 2);\n            recorder.setFormat(\"mov\");\n            recorder.setPixelFormat(AV_PIX_FMT_YUV420P);\n            recorder.setFrameRate(30);\n            recorder.setVideoCodec(AV_CODEC_ID_H264);\n            recorder.setVideoQuality(10);\n            recorder.setSampleFormat(AV_SAMPLE_FMT_FLTP);\n            recorder.setSampleRate(48000);\n            recorder.setAudioCodec(AV_CODEC_ID_AAC);\n            recorder.setAudioQuality(10);\n            recorder.start();\n\n            int n = 1000;\n            Frame frame = new Frame(800, 600, Frame.DEPTH_UBYTE, 3);\n            for (int i = 0; i < n; i++) {\n                recorder.record(frame);\n            }\n            Frame audioFrame = new Frame();\n            ShortBuffer audioBuffer = ShortBuffer.allocate(48000 * 2 * n / 30);\n            audioFrame.sampleRate = 48000;\n            audioFrame.audioChannels = 2;\n            audioFrame.samples = new ShortBuffer[] {audioBuffer};\n            recorder.record(audioFrame);\n            recorder.stop();\n            recorder.release();\n\n            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(tempFile);\n            grabber.setSampleMode(FrameGrabber.SampleMode.FLOAT);\n            grabber.start();\n\n            FFmpegFrameFilter filter = new FFmpegFrameFilter(\n                    \"scale=400x300,transpose=cclock_flip,format=gray\",\n                    \"volume=0.5,aformat=sample_fmts=u8:channel_layouts=mono\",\n                    grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());\n            filter.setPixelFormat(grabber.getPixelFormat());\n            filter.setSampleFormat(grabber.getSampleFormat());\n            filter.setFrameRate(grabber.getFrameRate());\n            filter.setSampleRate(grabber.getSampleRate());\n            filter.start();\n\n            FFmpegFrameFilter nullFilter = new FFmpegFrameFilter(null, null, 0, 0, 0);\n            nullFilter.start();\n\n            int a = 0, b = 0, c = 0, d = 0;\n            Frame frame2;\n            while ((frame2 = grabber.grab()) != null) {\n                if (frame2.image != null) {\n                    a++;\n                }\n                if (frame2.samples != null) {\n                    b++;\n                }\n                filter.push(frame2);\n                Frame frame3;\n                while ((frame3 = filter.pull()) != null) {\n                    if (frame3.image != null) {\n                        c++;\n                        assertEquals(300, frame3.imageWidth);\n                        assertEquals(400, frame3.imageHeight);\n                        assertEquals(1, frame3.imageChannels);\n                    }\n                    if (frame3.samples != null) {\n                        d++;\n                        assertEquals(1, frame3.audioChannels);\n                        assertEquals(1, frame3.samples.length);\n                        assertTrue(frame3.samples[0] instanceof ByteBuffer);\n                        assertEquals(frame2.samples.length, frame3.samples.length);\n                        assertEquals(frame2.samples[0].limit() / 2, frame3.samples[0].limit());\n                    }\n                    assertEquals(frame2.timestamp, frame3.timestamp);\n                }\n                nullFilter.push(frame2);\n                assertEquals(frame2, nullFilter.pull());\n            }\n            filter.push(null);\n            assertEquals(null, filter.pull());\n            assertEquals(a, c);\n            assertEquals(b, d);\n            assertEquals(null, grabber.grab());\n            filter.stop();\n            filter.release();\n            grabber.restart();\n            grabber.stop();\n            grabber.release();\n            frame.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n            fail(\"Exception should not have been thrown: \" + e);\n        } finally {\n            tempFile.delete();\n        }\n    }\n\n    @Test\n    public void testFFmpegFrameFilterMultipleInputs() {\n        System.out.println(\"FFmpegFrameFilterMultipleInputs\");\n\n        File tempFile = new File(Loader.getTempDir(), \"test.avi\");\n        try {\n            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(tempFile, 320, 200, 2);\n            recorder.setVideoCodec(AV_CODEC_ID_VP8);\n            recorder.setAudioCodec(AV_CODEC_ID_VORBIS);\n            recorder.start();\n\n            int n = 1000;\n            Frame frame = new Frame(320, 200, Frame.DEPTH_UBYTE, 3);\n            for (int i = 0; i < n; i++) {\n                recorder.record(frame);\n            }\n            Frame audioFrame = new Frame();\n            ShortBuffer audioBuffer = ShortBuffer.allocate(8000 * 2 * n / 30);\n            audioFrame.sampleRate = 8000;\n            audioFrame.audioChannels = 2;\n            audioFrame.samples = new ShortBuffer[] {audioBuffer};\n            recorder.record(audioFrame);\n            recorder.stop();\n            recorder.release();\n\n            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(tempFile);\n            grabber.start();\n\n            FFmpegFrameFilter filter = new FFmpegFrameFilter(\n                    \"[0:v][1:v]hstack=inputs=2[v]\",\n                    \"[0:a][1:a]amerge[a]\",\n                    grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());\n            filter.setPixelFormat(grabber.getPixelFormat());\n            filter.setSampleFormat(grabber.getSampleFormat());\n            filter.setVideoInputs(2);\n            filter.setAudioInputs(2);\n            filter.start();\n\n            int a = 0, b = 0, c = 0, d = 0;\n            Frame frame2;\n            while ((frame2 = grabber.grab()) != null) {\n                if (frame2.image != null) {\n                    a++;\n                }\n                if (frame2.samples != null) {\n                    b++;\n                }\n                filter.push(0, frame2);\n                filter.push(1, frame2);\n                Frame frame3;\n                while ((frame3 = filter.pull()) != null) {\n                    if (frame3.image != null) {\n                        c++;\n                        assertEquals(640, frame3.imageWidth);\n                        assertEquals(200, frame3.imageHeight);\n                        assertEquals(3, frame3.imageChannels);\n                    }\n                    if (frame3.samples != null) {\n                        d++;\n                        assertEquals(4, frame3.audioChannels);\n                        assertEquals(1, frame3.samples.length);\n                        assertTrue(frame3.samples[0] instanceof ShortBuffer);\n                        assertEquals(frame2.samples.length, frame3.samples.length);\n                        assertEquals(2 * frame2.samples[0].limit(), frame3.samples[0].limit());\n                    }\n                }\n            }\n            filter.push(0, null);\n            filter.push(1, null);\n            assertEquals(null, filter.pull());\n            assertEquals(a, c);\n            assertEquals(b, d);\n            assertEquals(null, grabber.grab());\n            filter.stop();\n            filter.release();\n            grabber.restart();\n            grabber.stop();\n            grabber.release();\n            frame.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n            fail(\"Exception should not have been thrown: \" + e);\n        } finally {\n            tempFile.delete();\n        }\n    }\n\n}\n"
  },
  {
    "path": "platform/src/test/java/org/bytedeco/javacv/FrameGrabberChangingResolutionTest.java",
    "content": "/*\n * Copyright (C) 2016-2017 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.FloatBuffer;\nimport java.nio.ShortBuffer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.indexer.UByteIndexer;\nimport org.junit.Test;\n\nimport static org.bytedeco.ffmpeg.global.avcodec.*;\nimport static org.bytedeco.ffmpeg.global.avutil.*;\nimport static org.junit.Assert.*;\n\n/**\n * Complex Test case for FrameGrabber classes - change the resolution during runtime.\n * Also uses other classes from JavaCV.\n *\n * @author Samuel Audet, Michael Fritscher\n */\npublic class FrameGrabberChangingResolutionTest {\n    private File tempFile = new File(Loader.getTempDir(), \"test.mkv\");\n    private File tempTargetFile = new File(Loader.getTempDir(), \"target.mkv\");\n    private boolean endRequested;\n\n    private void makeTestfile() throws Exception {\n        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(new FileOutputStream(tempFile), 640, 480, 2);\n        recorder.setFormat(\"matroska\"); // mp4 doesn't support streaming\n        recorder.setPixelFormat(AV_PIX_FMT_YUV420P);\n        recorder.setVideoCodec(AV_CODEC_ID_H264);\n        recorder.setVideoQuality(0); // lossless\n        recorder.setFrameRate(30);\n        recorder.startUnsafe();\n\n        Frame[] frames = new Frame[60];\n        for (int n = 0; n < frames.length; n++) {\n            Frame frame = new Frame(640, 480, Frame.DEPTH_UBYTE, 3);\n            UByteIndexer frameIdx = frame.createIndexer();\n            for (int i = 0; i < frameIdx.rows(); i++) {\n                for (int j = 0; j < frameIdx.cols(); j++) {\n                    for (int k = 0; k < frameIdx.channels(); k++) {\n                        frameIdx.put(i, j, k, n + i + j + k);\n                    }\n                }\n            }\n            recorder.record(frame);\n            frames[n] = frame;\n        }\n        recorder.stop();\n        recorder.release();\n        for (int n = 0; n < frames.length; n++) {\n            frames[n].close();\n        }\n    }\n\n    final public void setupUDPSender(final int x, final int y, final int bandwidth, final int count) throws IOException {\n        final FFmpegFrameGrabber fg = new FFmpegFrameGrabber(tempFile);\n        fg.setFrameRate(30);\n\n        final FFmpegFrameRecorder fr = new FFmpegFrameRecorder(\"udp://127.0.0.1:2345\", 0);\n        fr.setVideoCodecName(\"mpeg2video\");\n        fr.setFormat(\"mpegts\");\n\n        fr.setImageWidth(x);\n        fr.setImageHeight(y);\n        fr.setVideoBitrate(bandwidth);\n\n        fr.setFrameRate(30);\n\n        fg.startUnsafe();\n        fr.startUnsafe();\n\n        final boolean[] b = new boolean[1];\n        Thread t = new Thread() {\n            public void run() {\n                try {\n                    for (int i = 0; i < count; i++) {\n                        /*- System.out.println(\"S: \" + fg.getFrameNumber() + \" \" + fg.getTimestamp() + \" \"\n                                + fg.getFrameRate() + \" \" + fg.getImageWidth() + \"x\" + fg.getImageHeight() + \" \"\n                                + fg.getVideoCodec() + \" \" + fg.getVideoBitrate() + \" \" + i); */\n                        Frame source = fg.grabFrame();\n                        fr.record(source);\n                    }\n                    fg.close();\n                    fr.close();\n                    b[0] = true;\n                } catch (Exception e) {\n                    e.printStackTrace();\n                    fail(\"Exception should not have been thrown: \" + e);\n                    try {\n                        fg.close();\n                        fr.close();\n                    } catch (Exception e1) {\n                        e1.printStackTrace();\n                    }\n                    b[0] = true;\n                }\n            }\n        };\n        t.setName(\"Sender\");\n        t.start();\n\n        while (!b[0]) {\n            try {\n                Thread.sleep(100);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    final public void setupUDPReceiver() throws IOException {\n        Thread t = new Thread() {\n\n            public void run() {\n                FFmpegFrameGrabber fg = new FFmpegFrameGrabber(\"udp://127.0.0.1:2345\");\n                fg.setFrameRate(30);\n\n                FFmpegFrameRecorder fr = new FFmpegFrameRecorder(tempTargetFile, 0);\n                fr.setVideoCodecName(\"mpeg2video\");\n                fr.setFormat(\"mpegts\");\n\n                fr.setImageWidth(640);\n                fr.setImageHeight(480);\n                fr.setVideoBitrate(8000000);\n\n                fr.setFrameRate(30);\n\n                try {\n                    fg.startUnsafe();\n                    fr.startUnsafe();\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n\n                // Tests whether the width of the picture changes trough all\n                // qualities and every step has a few pictures.\n                try {\n                    int n = 0;\n                    int m = 0; // Pictures in this quality\n                    int q = 0; // which quality state?\n                    int[] qualities = { 160, 320, 640, 160, 320, 640, 320, 160 };\n                    while (!endRequested) {\n                        /*- System.out.println(\"R: \" + fg.getFrameNumber() + \" \" + fg.getTimestamp() + \" \"\n                                + fg.getFrameRate() + \" \" + fg.getImageWidth() + \"x\" + fg.getImageHeight() + \" \"\n                                + fg.getVideoCodec() + \" \" + fg.getVideoBitrate()); */\n                        Frame source = fg.grabFrame();\n                        n++;\n                        m++;\n                        // System.out.println(\"WRITTEN: \" + n + \" \" + m + \" \" +\n                        // q + \" \" + source.imageWidth);\n                        if (source.imageWidth != qualities[q]) {\n                            q++;\n                            assertEquals(source.imageWidth, qualities[q]);\n                            assertTrue(m > 5);\n                            assertTrue(m <= 60);\n                            m = 0;\n                        }\n                        fr.record(source);\n                    }\n                    assertEquals(q, qualities.length - 1);\n                    assertTrue(n > 300);\n                    assertTrue(n <= 480);\n                    fr.close();\n                } catch (Exception e) {\n                    e.printStackTrace();\n                    fail(\"Exception should not have been thrown: \" + e);\n                    try {\n                        fg.close();\n                        fr.close();\n                    } catch (Exception e1) {\n                        e1.printStackTrace();\n                    }\n                }\n            }\n        };\n        t.setName(\"Receiver\");\n        t.start();\n    }\n\n    @Test\n    public void testFFmpegFrameGrabber() {\n        System.out.println(\"FFmpegFrameGrabber\");\n\n        try {\n            makeTestfile();\n\n            setupUDPReceiver();\n\n            System.out.println(\"Changing to 160x120\");\n            setupUDPSender(160, 120, 50000, 60);\n\n            System.out.println(\"Changing to 320x240\");\n            setupUDPSender(320, 240, 100000, 60);\n\n            System.out.println(\"Changing to 640x480\");\n            setupUDPSender(640, 480, 200000, 60);\n\n            System.out.println(\"Changing to 160x120\");\n            setupUDPSender(160, 120, 50000, 60);\n\n            System.out.println(\"Changing to 320x240\");\n            setupUDPSender(320, 240, 100000, 60);\n\n            System.out.println(\"Changing to 640x480\");\n            setupUDPSender(640, 480, 200000, 60);\n\n            System.out.println(\"Changing to 320x240\");\n            setupUDPSender(320, 240, 100000, 60);\n\n            System.out.println(\"Changing to 160x120\");\n            setupUDPSender(160, 120, 50000, 60);\n\n            Thread.sleep(3000);\n            endRequested = true;\n        } catch (Exception e) {\n            tempFile.delete();\n            tempTargetFile.delete();\n            e.printStackTrace();\n            fail(\"Exception should not have been thrown: \" + e);\n        }\n\n        try {\n            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new FileInputStream(tempTargetFile));\n            grabber.setSampleMode(FrameGrabber.SampleMode.FLOAT);\n            grabber.startUnsafe();\n\n            int n = 0;\n            Frame frame2;\n            while ((frame2 = grabber.grab()) != null) {\n                if (frame2.image != null) {\n                    n++;\n                    assertEquals(640, frame2.imageWidth);\n                }\n            }\n\n            // It seems that ffmpeg lose some frames while switching (ideal\n            // value would be 240)\n            // System.out.println(\"END NUMBER: \" + n);\n            assertTrue(n > 300);\n            assertTrue(n <= 480);\n            assertEquals(null, grabber.grab());\n            grabber.stop();\n            grabber.release();\n        } catch (Exception e) {\n            e.printStackTrace();\n            fail(\"Exception should not have been thrown: \" + e);\n        } finally {\n            tempFile.delete();\n            tempTargetFile.delete();\n        }\n    }\n}\n"
  },
  {
    "path": "platform/src/test/java/org/bytedeco/javacv/FrameGrabberTest.java",
    "content": "/*\n * Copyright (C) 2016-2023 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.FloatBuffer;\nimport java.nio.ShortBuffer;\nimport java.util.Random;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.PointerScope;\nimport org.bytedeco.javacpp.indexer.UByteIndexer;\nimport org.junit.Test;\n\nimport static org.bytedeco.ffmpeg.global.avcodec.*;\nimport static org.bytedeco.ffmpeg.global.avutil.*;\nimport static org.junit.Assert.*;\n\n/**\n * Test cases for FrameGrabber classes. Also uses other classes from JavaCV.\n *\n * @author Samuel Audet\n */\npublic class FrameGrabberTest {\n\n    @Test\n    public void testFFmpegFrameGrabber() {\n        System.out.println(\"FFmpegFrameGrabber\");\n\n        File tempFile = new File(Loader.getTempDir(), \"test.mkv\");\n        try {\n            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(new FileOutputStream(tempFile), 640, 480, 2);\n            recorder.setFormat(\"matroska\"); // mp4 doesn't support streaming\n            recorder.setPixelFormat(AV_PIX_FMT_BGR24);\n            recorder.setVideoCodecName(\"jpegls\");\n            recorder.setVideoQuality(0); // lossless\n            recorder.setSampleFormat(AV_SAMPLE_FMT_S16);\n            recorder.setSampleRate(44100);\n            recorder.setAudioCodecName(\"pcm_s16le\");\n            recorder.start();\n\n            Frame[] frames = new Frame[1000];\n            for (int n = 0; n < frames.length; n++) {\n                Frame frame = new Frame(640, 480, Frame.DEPTH_UBYTE, 3);\n                UByteIndexer frameIdx = frame.createIndexer();\n                for (int i = 0; i < frameIdx.rows(); i++) {\n                    for (int j = 0; j < frameIdx.cols(); j++) {\n                        for (int k = 0; k < frameIdx.channels(); k++) {\n                            frameIdx.put(i, j, k, n + i + j + k);\n                        }\n                    }\n                }\n                recorder.record(frame);\n                frames[n] = frame;\n            }\n            Frame audioFrame = new Frame();\n            ShortBuffer audioBuffer = ShortBuffer.allocate(64 * 1024);\n            audioFrame.sampleRate = 44100;\n            audioFrame.audioChannels = 2;\n            audioFrame.samples = new ShortBuffer[] {audioBuffer};\n            for (int i = 0; i < audioBuffer.capacity(); i++) {\n                audioBuffer.put(i, (short)i);\n            }\n            recorder.record(audioFrame);\n            recorder.stop();\n            recorder.release();\n\n            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new FileInputStream(tempFile));\n            grabber.setSampleMode(FrameGrabber.SampleMode.FLOAT);\n            grabber.start();\n\n            int n = 0, m = 0;\n            Frame frame2;\n            long startTime = System.nanoTime();\n            while ((frame2 = grabber.grabAtFrameRate()) != null) {\n                long delay = frame2.timestamp * 1000 - (System.nanoTime() - startTime);\n                if (delay < -1_000_000_000 / grabber.getFrameRate()) {\n                    // skip to catch up with frame rate\n                    if (frame2.image != null) {\n                        n++;\n                    } else {\n                        m++;\n                    }\n                    continue;\n                }\n                Frame clone2 = frame2.clone();\n                if (frame2.image != null) {\n                    Frame frame = frames[n++];\n                    assertEquals(frame.imageWidth, frame2.imageWidth);\n                    assertEquals(frame.imageHeight, frame2.imageHeight);\n                    assertEquals(frame.imageChannels, frame2.imageChannels);\n                    assertEquals(frame.imageWidth, clone2.imageWidth);\n                    assertEquals(frame.imageHeight, clone2.imageHeight);\n                    assertEquals(frame.imageChannels, clone2.imageChannels);\n\n                    UByteIndexer frameIdx = frame.createIndexer();\n                    UByteIndexer frame2Idx = frame2.createIndexer();\n                    UByteIndexer clone2Idx = clone2.createIndexer();\n                    for (int i = 0; i < frameIdx.rows(); i++) {\n                        for (int j = 0; j < frameIdx.cols(); j++) {\n                            for (int k = 0; k < frameIdx.channels(); k++) {\n                                int b = frameIdx.get(i, j, k);\n                                assertEquals(b, frame2Idx.get(i, j, k));\n                                assertEquals(b, clone2Idx.get(i, j, k));\n                            }\n                        }\n                    }\n                } else {\n                    FloatBuffer audioBuffer2 = (FloatBuffer)frame2.samples[0];\n                    FloatBuffer cloneBuffer2 = (FloatBuffer)clone2.samples[0];\n                    while (audioBuffer2.hasRemaining()) {\n                        assertEquals((float)audioBuffer.get(m) / (Short.MAX_VALUE + 1), audioBuffer2.get(), 0);\n                        assertEquals((float)audioBuffer.get(m) / (Short.MAX_VALUE + 1), cloneBuffer2.get(), 0);\n                        m++;\n                    }\n                }\n                clone2.close();\n            }\n            long stopTime = System.nanoTime();\n            assertEquals(n, (stopTime - startTime) * grabber.getFrameRate() / 1_000_000_000, 3.0);\n            assertEquals(frames.length, n);\n            assertEquals(null, grabber.grab());\n            grabber.restart();\n            grabber.stop();\n            grabber.release();\n            for (n = 0; n < frames.length; n++) {\n                frames[n].close();\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n            fail(\"Exception should not have been thrown: \" + e);\n        } finally {\n            tempFile.delete();\n        }\n    }\n\n    @Test\n    public void testFFmpegFrameGrabberLockingTest() {\n        final boolean[] failed = {false};\n        final int numberOfInstances = 20;\n        System.out.println(\"FFmpegFrameGrabberLocking\");\n\n        Runnable[] runables = new Runnable[numberOfInstances];\n        Thread[] threads = new Thread[numberOfInstances];\n        final boolean[] finish = new boolean[numberOfInstances];\n        for (int instance = 0; instance < numberOfInstances; instance++) {\n            final int instance_final = instance;\n            Runnable r = new Runnable() {\n                public void run() {\n\n                    File tempFile = new File(Loader.getTempDir(), \"test\" + instance_final + \".mkv\");\n                    try (PointerScope scope = new PointerScope()) {\n                        FFmpegLogCallback.set();\n                        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(new FileOutputStream(tempFile), 640, 480, 2);\n                        recorder.setFormat(\"matroska\"); // mp4 doesn't support streaming\n                        recorder.setPixelFormat(AV_PIX_FMT_BGR24);\n                        recorder.setVideoCodecName(\"jpegls\");\n                        recorder.setVideoQuality(0); // lossless\n                        recorder.setSampleFormat(AV_SAMPLE_FMT_S16);\n                        recorder.setSampleRate(44100);\n                        recorder.setAudioCodecName(\"pcm_s16le\");\n                        recorder.startUnsafe();\n\n                        Frame[] frames = new Frame[10];\n                        for (int n = 0; n < frames.length; n++) {\n                            Frame frame = new Frame(640, 480, Frame.DEPTH_UBYTE, 3);\n                            UByteIndexer frameIdx = frame.createIndexer();\n                            for (int i = 0; i < frameIdx.rows(); i++) {\n                                for (int j = 0; j < frameIdx.cols(); j++) {\n                                    for (int k = 0; k < frameIdx.channels(); k++) {\n                                        frameIdx.put(i, j, k, n + i + j + k);\n                                    }\n                                }\n                            }\n                            recorder.record(frame);\n                            frames[n] = frame;\n                        }\n                        Frame audioFrame = new Frame();\n                        ShortBuffer audioBuffer = ShortBuffer.allocate(64 * 1024);\n                        audioFrame.sampleRate = 44100;\n                        audioFrame.audioChannels = 2;\n                        audioFrame.samples = new ShortBuffer[] { audioBuffer };\n                        for (int i = 0; i < audioBuffer.capacity(); i++) {\n                            audioBuffer.put(i, (short) i);\n                        }\n                        recorder.record(audioFrame);\n                        recorder.stop();\n                        recorder.release();\n\n                        Thread.sleep(1000);\n\n                        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new FileInputStream(tempFile));\n                        grabber.setSampleMode(FrameGrabber.SampleMode.FLOAT);\n                        grabber.startUnsafe();\n\n                        int n = 0, m = 0;\n                        Frame frame2;\n                        while ((frame2 = grabber.grab()) != null) {\n                            if (frame2.image != null) {\n                                Frame frame = frames[n++];\n                                assertEquals(frame.imageWidth, frame2.imageWidth);\n                                assertEquals(frame.imageHeight, frame2.imageHeight);\n                                assertEquals(frame.imageChannels, frame2.imageChannels);\n\n                                UByteIndexer frameIdx = frame.createIndexer();\n                                UByteIndexer frame2Idx = frame2.createIndexer();\n                                for (int i = 0; i < frameIdx.rows(); i++) {\n                                    for (int j = 0; j < frameIdx.cols(); j++) {\n                                        for (int k = 0; k < frameIdx.channels(); k++) {\n                                            int b = frameIdx.get(i, j, k);\n                                            assertEquals(b, frame2Idx.get(i, j, k));\n                                        }\n                                    }\n                                }\n                            } else {\n                                FloatBuffer audioBuffer2 = (FloatBuffer) frame2.samples[0];\n                                while (audioBuffer2.hasRemaining()) {\n                                    assertEquals((float) audioBuffer.get(m++) / (Short.MAX_VALUE + 1),\n                                            audioBuffer2.get(), 0);\n                                }\n                            }\n                        }\n                        assertEquals(frames.length, n);\n                        assertEquals(null, grabber.grab());\n                        grabber.restart();\n                        grabber.stop();\n                        grabber.release();\n                        for (n = 0; n < frames.length; n++) {\n                            frames[n].close();\n                        }\n                    } catch (Error | Exception e) {\n                        failed[0] = true;\n                        e.printStackTrace();\n                        fail(\"Exception should not have been thrown: \" + e);\n                    } finally {\n                        tempFile.delete();\n                        finish[instance_final] = true;\n                    }\n                }\n            };\n\n            runables[instance_final] = r;\n        }\n\n        for (int instance = 0; instance < numberOfInstances; instance++) {\n            threads[instance] = new Thread(runables[instance]);\n            threads[instance].setName(\"Testthread-\" + instance);\n        }\n\n        for (int instance = 0; instance < numberOfInstances; instance++) {\n            threads[instance].start();\n        }\n\n        while (true) {\n            boolean finished = true;\n            for (int instance = 0; instance < numberOfInstances; instance++) {\n                if (!finish[instance]) {\n                    finished = false;\n                    break;\n                }\n            }\n\n            if (!finished) {\n                System.out.println(\"Still waiting...\");\n                try {\n                    Thread.sleep(500);\n                } catch (InterruptedException e) {\n                    // TODO Auto-generated catch block\n                    e.printStackTrace();\n                }\n            } else {\n                break;\n            }\n        }\n        assertFalse(failed[0]);\n    }\n\n    @Test\n    public void testFFmpegFrameGrabberSeeking() throws IOException {\n        System.out.println(\"FFmpegFrameGrabberSeeking\");\n\n        for(int seektestnum = 0; seektestnum < 3; seektestnum++) try (PointerScope scope = new PointerScope()) {\n            FFmpegLogCallback.set();\n            String fileName = seektestnum==0?\"testAV.mp4\":seektestnum==1?\"testV.mp4\":\"testA.mp4\";\n            File tempFile = new File(Loader.getTempDir(), fileName);\n            tempFile.deleteOnExit();\n            FFmpegFrameRecorder recorder = seektestnum == 0? new FFmpegFrameRecorder(tempFile, 640, 480, 2)\n                                         : seektestnum == 1? new FFmpegFrameRecorder(tempFile, 640, 480, 0)\n                                         : new FFmpegFrameRecorder(tempFile, 0, 0, 2);\n            recorder.setFormat(\"mp4\");\n            recorder.setFrameRate(30);\n            recorder.setPixelFormat(AV_PIX_FMT_YUV420P);\n            recorder.setVideoCodec(AV_CODEC_ID_MPEG4);\n            recorder.setVideoQuality(10);\n            recorder.setSampleRate(48000);\n            recorder.setSampleFormat(AV_SAMPLE_FMT_FLTP);\n            recorder.setAudioCodec(AV_CODEC_ID_AAC);\n            recorder.setAudioQuality(0);\n            recorder.setDisplayRotation((seektestnum - 2) * 90.0);\n            recorder.start();\n            if (seektestnum!=2) {\n                Frame frame = new Frame(640, 480, Frame.DEPTH_UBYTE, 3);\n                UByteIndexer frameIdx = frame.createIndexer();\n                for (int n = 0; n < 10000; n++) {\n                    for (int i = 0; i < frameIdx.rows(); i++) {\n                        for (int j = 0; j < frameIdx.cols(); j++) {\n                            for (int k = 0; k < frameIdx.channels(); k++) {\n                                frameIdx.put(i, j, k, n + i + j + k);\n                            }\n                        }\n                    }\n                    recorder.record(frame);\n                    if (n == 5000 && seektestnum!=1){\n                        Frame audioFrame = new Frame();\n                        ShortBuffer audioBuffer = ShortBuffer.allocate(48000 * 2 * 10000 / 30);\n                        audioFrame.sampleRate = 48000;\n                        audioFrame.audioChannels = 2;\n                        audioFrame.samples = new ShortBuffer[] {audioBuffer};\n                        for (int i = 0; i < audioBuffer.capacity(); i++) {\n                            audioBuffer.put(i, (short)i);\n                        }\n                        recorder.record(audioFrame);\n                    }\n                }\n                frame.close();\n            } else {\n                Frame audioFrame = new Frame();\n                ShortBuffer audioBuffer = ShortBuffer.allocate(48000 * 2 * 10000 / 30);\n                audioFrame.sampleRate = 48000;\n                audioFrame.audioChannels = 2;\n                audioFrame.samples = new ShortBuffer[] {audioBuffer};\n                for (int i = 0; i < audioBuffer.capacity(); i++) {\n                    audioBuffer.put(i, (short)i);\n                }\n                recorder.record(audioFrame);\n            }\n            recorder.stop();\n            recorder.release();\n\n            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(tempFile);\n            grabber.setVideoOption(\"threads\", \"1\"); // more precise without threads\n            grabber.start();\n            assertEquals((seektestnum - 2) * 90.0, grabber.getDisplayRotation(), 0);\n            int length = (int) ( grabber.getLengthInTime() - 1000000L);\n\n\n            System.out.println();\n            System.out.println(\"Seek in file containing \"+(seektestnum==0?\"video and audio\":seektestnum==1?\"video only\":\"audio only\"));\n            System.out.println(\"============================================\");\n            System.out.println(\"Testing file \"+tempFile.getName());\n            System.out.println(\"Length = \"+grabber.getLengthInTime());\n            System.out.println(\"Framerate = \"+grabber.getFrameRate());\n            System.out.println();\n            System.out.println(\"has video stream = \"+(grabber.hasVideo()?\"YES\":\"NO\")+\", has audio stream = \"+(grabber.hasAudio()?\"YES\":\"NO\"));\n            long tolerance = 1000000L + (grabber.getFrameRate() > 0.0? (long) (5000000/grabber.getFrameRate()):500000L);\n            Random random = new Random(29);\n\n            for (int frametypenum = 0; frametypenum < 4; frametypenum++) {\n                long mindelta = Long.MAX_VALUE;\n                long maxdelta = Long.MIN_VALUE;\n                System.out.println();\n                System.out.println(\"Seek by \" \n                                    + (frametypenum == 0 ? \"any\" : frametypenum == 1 ? \"video\" : frametypenum == 2  ? \"audio\" : \"old method\")\n                                    + (frametypenum == 0 ? \" frames\" : \"\"));\n\n                System.out.println(\"--------------------\");\n                for (int i = 0; i < 200; i++) {\n                    long timestamp = random.nextInt(length);\n                    switch (frametypenum) {\n                        case 0:\n                            grabber.setTimestamp(timestamp, true);\n                            break;\n                        case 1:\n                            grabber.setVideoTimestamp(timestamp);\n                            break;\n                        case 2:\n                            grabber.setAudioTimestamp(timestamp);\n                            break;\n                        case 3:\n                            grabber.setTimestamp(timestamp);\n                            break;\n                    }\n\n                    Frame frame = grabber.grab();\n                    long timestamp2 = grabber.getTimestamp();\n                    long delta = timestamp2 - timestamp;\n                    if (delta > maxdelta) maxdelta = delta;\n                    if (delta < mindelta) mindelta = delta;\n                    assertTrue(frame.image != null ^ frame.samples != null);\n                    System.out.println(timestamp2 + \" - \" + timestamp + \" = \" + delta + \" type: \" + frame.getTypes());\n                    assertTrue(Math.abs(delta) < tolerance);\n                    /*\n                    if (seektestnum==0) {\n                        boolean wasVideo = frame.image != null;\n                        boolean wasAudio = frame.samples != null;\n                        Frame frame2 = grabber.grab();\n                        while ((wasVideo && frame2.image != null)\n                                || (wasAudio && frame2.samples != null)) {\n                            frame2 = grabber.grab();\n                        }\n                        assertTrue(wasVideo ^ frame2.image != null);\n                        assertTrue(wasAudio ^ frame2.samples != null);\n                        long timestamp3 = grabber.getTimestamp();\n                        System.out.println(timestamp3 + \" - \" + timestamp + \" = \" + (timestamp3 - timestamp));\n                        assertTrue(timestamp3 >= timestamp - tolerance && timestamp3 < timestamp + tolerance);\n                    }\n                    */\n                }\n                System.out.println();\n                System.out.println(\"------------------------------------\");\n                System.out.println(\"delta from \" + mindelta + \" to \" + maxdelta);\n                System.out.println();\n            }\n            if (seektestnum==0) {\n                System.out.println();\n                System.out.println(\"======== Check sequential setVideoFrameNumber (issue #1697) ========\");\n                for (int i = 0; i < 10; i++) {\n                    grabber.setVideoFrameNumber(i);\n                    long timestamp = grabber.grabImage().timestamp;\n                    System.out.println(\"frame number:\" + i + \" timestamp:\" + timestamp);\n                    assertTrue(i == Math.round(timestamp * grabber.getFrameRate() / 1000000L));\n                }\n            }\n            if (seektestnum==2) {\n\n                long count1 = 0;\n\n                long duration = grabber.getLengthInTime();\n\n                System.out.println();\n                System.out.println(\"======== Check seeking in audio ========\");\n                System.out.println(\"FrameRate = \"+grabber.getFrameRate()+\" AudioFrameRate = \"+grabber.getAudioFrameRate()+\", duration = \"+duration+\" audio frames = \"+grabber.getLengthInAudioFrames());\n\n\n\n                double deltaTimeStamp=0.0;\n                if (grabber.hasAudio() && grabber.getAudioFrameRate() > 0) {\n                    deltaTimeStamp = 1000000.0/grabber.getAudioFrameRate();\n\n                }\n                System.out.println(\"AudioFrameDuration = \"+deltaTimeStamp);\n                System.out.println();\n                System.out.println(\"======== Check setAudioFrameNumber ========\");\n                count1=0;\n\n                while (count1++<1000) {\n                    int audioFrameToSeek = random.nextInt(grabber.getLengthInAudioFrames()-100);\n                    grabber.setAudioFrameNumber(audioFrameToSeek);\n                    Frame setFrame = grabber.grabSamples();\n                    if (setFrame == null) {\n                        System.out.println(\"null frame after seek to audio frame\");\n                    } else {\n                        long audioTs = grabber.getTimestamp();\n                        System.out.println(\"audioFrame # \"+audioFrameToSeek+\", timeStamp = \"+audioTs+\", difference = \"+Math.round(audioTs*grabber.getAudioFrameRate()/1000000 - audioFrameToSeek));\n                        assertTrue(Math.abs(audioTs*grabber.getAudioFrameRate()/1000000 - audioFrameToSeek)<10);\n                    }\n                }\n            }\n            grabber.stop();\n            System.out.println();\n            System.out.println(\"======= seek in \" +fileName+\" is finished ===========\" );\n        }\n\n    }\n}\n"
  },
  {
    "path": "platform/src/test/java/org/bytedeco/javacv/SeekableByteArrayOutputStreamTest.java",
    "content": "/*\n * Copyright (C) 2019 Sven Vorlauf\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport static org.junit.Assert.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Arrays;\nimport java.util.Random;\n\nimport org.bytedeco.ffmpeg.global.avcodec;\nimport org.bytedeco.ffmpeg.global.avutil;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.indexer.UByteIndexer;\nimport org.hamcrest.core.IsEqual;\nimport org.hamcrest.core.IsNot;\nimport org.junit.Test;\n\npublic class SeekableByteArrayOutputStreamTest {\n\n    private static final int WIDTH = 640;\n    private static final int HEIGHT = 360;\n    private static final int FRAME_COUNT = 100;\n\n    private int writeByte(byte[] originalBytes, int offset, SeekableByteArrayOutputStream byteArrayOutputStream) {\n        byteArrayOutputStream.write(originalBytes[offset]);\n        return 1;\n    }\n\n    private int writePartialBytes(byte[] originalBytes, int offset, Random random,\n            SeekableByteArrayOutputStream byteArrayOutputStream) throws IOException {\n        int chunkSize = Math.min(random.nextInt(50), originalBytes.length - offset);\n        byteArrayOutputStream.write(originalBytes, offset, chunkSize);\n        return chunkSize;\n    }\n\n    private int writeBytes(byte[] originalBytes, int offset, Random random,\n            SeekableByteArrayOutputStream byteArrayOutputStream) throws IOException {\n        int chunkSize = Math.min(random.nextInt(50), originalBytes.length - offset);\n        byteArrayOutputStream.write(Arrays.copyOfRange(originalBytes, offset, offset + chunkSize));\n        return chunkSize;\n    }\n\n    private void createVideo(FFmpegFrameRecorder recorder) throws Exception {\n        recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4);\n        recorder.setFormat(\"mp4\");\n        recorder.setFrameRate(30);\n        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);\n        recorder.start();\n        for (int n = 0; n < FRAME_COUNT; n++) {\n            Frame frame = new Frame(WIDTH, HEIGHT, Frame.DEPTH_UBYTE, 3);\n            UByteIndexer frameIdx = frame.createIndexer();\n            for (int i = 0; i < frameIdx.rows(); i++) {\n                for (int j = 0; j < frameIdx.cols(); j++) {\n                    for (int k = 0; k < frameIdx.channels(); k++) {\n                        frameIdx.put(i, j, k, n + i + j + k);\n                    }\n                }\n            }\n            recorder.record(frame);\n            frame.close();\n        }\n        recorder.close();\n    }\n\n    @Test\n    public void serialWriteByteTest() {\n        System.out.println(\"SeekableByteArrayOutputStreamSerialWriteByte\");\n        Random random = new Random(-1);\n        byte[] originalBytes = new byte[1000];\n        random.nextBytes(originalBytes);\n\n        try (SeekableByteArrayOutputStream byteArrayOutputStream = new SeekableByteArrayOutputStream()) {\n            int offset = 0;\n            while (offset < originalBytes.length) {\n                offset += writeByte(originalBytes, offset, byteArrayOutputStream);\n            }\n            assertArrayEquals(originalBytes, byteArrayOutputStream.toByteArray());\n        } catch (Exception e) {\n            fail(\"Exception should not have been thrown: \" + e);\n        }\n    }\n\n    @Test\n    public void serialWriteBytesTest() {\n        System.out.println(\"SeekableByteArrayOutputStreamSerialWriteBytes\");\n        Random random = new Random(-1);\n        byte[] originalBytes = new byte[1000];\n        random.nextBytes(originalBytes);\n\n        try (SeekableByteArrayOutputStream byteArrayOutputStream = new SeekableByteArrayOutputStream()) {\n            int offset = 0;\n            while (offset < originalBytes.length) {\n                offset += writeBytes(originalBytes, offset, random, byteArrayOutputStream);\n            }\n            assertArrayEquals(originalBytes, byteArrayOutputStream.toByteArray());\n        } catch (Exception e) {\n            fail(\"Exception should not have been thrown: \" + e);\n        }\n    }\n\n    @Test\n    public void serialWritePartialBytesTest() {\n        System.out.println(\"SeekableByteArrayOutputStreamSerialWritePartialBytes\");\n        Random random = new Random(-1);\n        byte[] originalBytes = new byte[1000];\n        random.nextBytes(originalBytes);\n\n        try (SeekableByteArrayOutputStream byteArrayOutputStream = new SeekableByteArrayOutputStream()) {\n            int offset = 0;\n            while (offset < originalBytes.length) {\n                offset += writePartialBytes(originalBytes, offset, random, byteArrayOutputStream);\n            }\n            assertArrayEquals(originalBytes, byteArrayOutputStream.toByteArray());\n        } catch (Exception e) {\n            fail(\"Exception should not have been thrown: \" + e);\n        }\n    }\n\n    @Test\n    public void serialWriteTest() {\n        System.out.println(\"SeekableByteArrayOutputStreamSerialWrite\");\n        Random random = new Random(-1);\n        byte[] originalBytes = new byte[1000];\n        random.nextBytes(originalBytes);\n\n        try (SeekableByteArrayOutputStream byteArrayOutputStream = new SeekableByteArrayOutputStream()) {\n            int offset = 0;\n            while (offset < originalBytes.length) {\n                switch (random.nextInt(3)) {\n                case 0:\n                    offset += writeByte(originalBytes, offset, byteArrayOutputStream);\n                    break;\n                case 1:\n                    offset += writeBytes(originalBytes, offset, random, byteArrayOutputStream);\n                    break;\n                case 2:\n                    offset += writePartialBytes(originalBytes, offset, random, byteArrayOutputStream);\n                    break;\n                }\n            }\n            assertArrayEquals(originalBytes, byteArrayOutputStream.toByteArray());\n        } catch (Exception e) {\n            fail(\"Exception should not have been thrown: \" + e);\n        }\n    }\n\n    public void seekWriteTest() {\n        System.out.println(\"SeekableByteArrayOutputStreamSeekWrite\");\n        Random random = new Random(-1);\n        byte[] originalBytes = new byte[1000];\n        random.nextBytes(originalBytes);\n        try (SeekableByteArrayOutputStream byteArrayOutputStream = new SeekableByteArrayOutputStream()) {\n            int offset = 0;\n            for (int i = 0; i < 10; i++) {\n                // write 100 bytes\n                byteArrayOutputStream.write(originalBytes, offset, 100);\n\n                int position = random.nextInt(offset + 20);\n                int newBytesPosition = position + 500 % 1000;\n                int length = 10 + random.nextInt(20);\n                // get current bytes\n                byte[] writtenOriginalBytes = Arrays.copyOfRange(byteArrayOutputStream.toByteArray(), position,\n                        position + length);\n\n                // bytes to write at the new position\n                byte[] newBytes = Arrays.copyOfRange(originalBytes, newBytesPosition, newBytesPosition + length);\n\n                // just assert that the new bytes are different to the written ones\n                assertThat(writtenOriginalBytes, IsNot.not(IsEqual.equalTo(newBytes)));\n\n                // replace bytes\n                byteArrayOutputStream.seek(position, 0);\n                byteArrayOutputStream.write(newBytes);\n                byte[] writtenNewBytes = Arrays.copyOfRange(byteArrayOutputStream.toByteArray(), position,\n                        position + length);\n                assertThat(newBytes, IsEqual.equalTo(writtenNewBytes));\n\n                // write back original bytes\n                byteArrayOutputStream.seek(position, 0);\n                byteArrayOutputStream.write(originalBytes, position, length);\n\n                // get back to the end of the stream\n                byteArrayOutputStream.seek(offset, 0);\n                offset += 100;\n            }\n            while (offset < originalBytes.length) {\n                switch (random.nextInt(3)) {\n                case 0:\n                    offset += writeByte(originalBytes, offset, byteArrayOutputStream);\n                    break;\n                case 1:\n                    offset += writeBytes(originalBytes, offset, random, byteArrayOutputStream);\n                    break;\n                case 2:\n                    offset += writePartialBytes(originalBytes, offset, random, byteArrayOutputStream);\n                    break;\n                }\n            }\n            assertArrayEquals(originalBytes, byteArrayOutputStream.toByteArray());\n        } catch (Exception e) {\n            fail(\"Exception should not have been thrown: \" + e);\n        }\n    }\n\n    @Test\n    public void testVideoBytesEqual() {\n        // if this test fails it might be due to indeterministic multithreaded encoding\n        System.out.println(\"SeekableByteArrayOutputStreamVideo\");\n        File tempFile = new File(Loader.getTempDir(), \"test.mp4\");\n        try {\n            createVideo(new FFmpegFrameRecorder(tempFile, WIDTH, HEIGHT, 0));\n            byte[] fileBytes = Files.readAllBytes(tempFile.toPath());\n\n            SeekableByteArrayOutputStream byteArrayOutputStream = new SeekableByteArrayOutputStream();\n            createVideo(new FFmpegFrameRecorder(byteArrayOutputStream, WIDTH, HEIGHT, 0));\n            assertArrayEquals(fileBytes, byteArrayOutputStream.toByteArray());\n        } catch (Exception e) {\n            fail(\"Exception should not have been thrown: \" + e);\n        } finally {\n            tempFile.delete();\n        }\n    }\n}\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <groupId>org.bytedeco</groupId>\n  <artifactId>javacv</artifactId>\n  <version>1.5.14-SNAPSHOT</version>\n\n  <name>JavaCV</name>\n  <description>Java interface to OpenCV, FFmpeg, and more</description>\n  <url>http://bytedeco.org/javacv/</url>\n\n  <licenses>\n    <license>\n      <name>Apache License, Version 2.0</name>\n      <url>http://www.apache.org/licenses/LICENSE-2.0</url>\n      <distribution>repo</distribution>\n    </license>\n    <license>\n      <name>GNU General Public License (GPL) version 2, or any later version</name>\n      <url>http://www.gnu.org/licenses/</url>\n      <distribution>repo</distribution>\n    </license>\n    <license>\n      <name>GPLv2 with Classpath exception</name>\n      <url>http://www.gnu.org/software/classpath/license.html</url>\n      <distribution>repo</distribution>\n    </license>\n  </licenses>\n\n  <developers>\n    <developer>\n      <name>Samuel Audet</name>\n      <email>samuel.audet@gmail.com</email>\n    </developer>\n  </developers>\n\n  <scm>\n    <url>https://github.com/bytedeco/javacv</url>\n    <connection>scm:git:git://github.com/bytedeco/javacv.git</connection>\n    <developerConnection>scm:git:ssh://git@github.com/bytedeco/javacv.git</developerConnection>\n  </scm>\n\n  <properties>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <maven.build.timestamp.format>yyyyMMddhhmm</maven.build.timestamp.format>\n    <javacpp.version>${project.version}</javacpp.version>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>javacpp</artifactId>\n      <version>${javacpp.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>openblas</artifactId>\n      <version>0.3.31-${javacpp.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>opencv</artifactId>\n      <version>4.13.0-${javacpp.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>ffmpeg</artifactId>\n      <version>8.0.1-${javacpp.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>flycapture</artifactId>\n      <version>2.13.3.31-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>libdc1394</artifactId>\n      <version>2.2.6-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>libfreenect</artifactId>\n      <version>0.5.7-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>libfreenect2</artifactId>\n      <version>0.2.0-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>librealsense</artifactId>\n      <version>1.12.4-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>librealsense2</artifactId>\n      <version>2.53.1-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>videoinput</artifactId>\n      <version>0.200-1.5.9</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>artoolkitplus</artifactId>\n      <version>2.3.1-1.5.9</version>\n    </dependency>\n<!--    <dependency>-->\n<!--      <groupId>org.bytedeco</groupId>-->\n<!--      <artifactId>flandmark</artifactId>-->\n<!--      <version>1.07-1.5.8</version>-->\n<!--    </dependency>-->\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>leptonica</artifactId>\n      <version>1.87.0-${javacpp.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.bytedeco</groupId>\n      <artifactId>tesseract</artifactId>\n      <version>5.5.2-${javacpp.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>com.google.android</groupId>\n      <artifactId>android</artifactId>\n      <version>4.1.1.4</version>\n      <exclusions>\n        <exclusion>\n            <groupId>*</groupId>\n            <artifactId>*</artifactId>\n        </exclusion>\n      </exclusions>\n      <optional>true</optional>\n    </dependency>\n\n    <dependency>\n      <groupId>org.jogamp.gluegen</groupId>\n      <artifactId>gluegen-rt-main</artifactId>\n      <version>2.6.0</version>\n      <optional>true</optional>\n    </dependency>\n    <dependency>\n      <groupId>org.jogamp.jogl</groupId>\n      <artifactId>jogl-all-main</artifactId>\n      <version>2.6.0</version>\n      <optional>true</optional>\n    </dependency>\n    <dependency>\n      <groupId>org.jogamp.jocl</groupId>\n      <artifactId>jocl-main</artifactId>\n      <version>2.6.0</version>\n      <optional>true</optional>\n    </dependency>\n\n    <dependency>\n      <groupId>com.badlogicgames.gdx</groupId>\n      <artifactId>gdx</artifactId>\n      <version>1.14.0</version>\n      <optional>true</optional>\n    </dependency>\n  </dependencies>\n\n  <repositories>\n    <repository>\n      <id>central-portal-snapshots</id>\n      <name>Central Portal Snapshots</name>\n      <url>https://central.sonatype.com/repository/maven-snapshots/</url>\n      <releases>\n        <enabled>false</enabled>\n      </releases>\n      <snapshots>\n        <enabled>true</enabled>\n      </snapshots>\n    </repository>\n  </repositories>\n\n  <build>\n    <finalName>${project.artifactId}</finalName>\n    <plugins>\n      <plugin>\n        <artifactId>maven-compiler-plugin</artifactId>\n        <version>3.15.0</version>\n        <configuration>\n          <source>1.8</source>\n          <target>1.8</target>\n          <parameters>true</parameters>\n          <excludes>\n            <exclude>org/bytedeco/javacv/FFmpegLockCallback.java</exclude>\n            <exclude>org/bytedeco/javacv/FlyCaptureFrameGrabber.java</exclude>\n          </excludes>\n        </configuration>\n      </plugin>\n      <plugin>\n        <artifactId>maven-jar-plugin</artifactId>\n        <version>3.5.0</version>\n        <configuration>\n          <archive>\n            <manifest>\n              <mainClass>org.bytedeco.javacv.JavaCV</mainClass>\n              <addClasspath>true</addClasspath>\n            </manifest>\n            <manifestEntries>\n              <!-- <addClasspath/> is broken: http://jira.codehaus.org/browse/MJAR-61 -->\n              <Class-Path>javacpp.jar openblas.jar opencv.jar ffmpeg.jar flycapture.jar libdc1394.jar libfreenect.jar libfreenect2.jar librealsense.jar librealsense2.jar videoinput.jar artoolkitplus.jar flandmark.jar leptonica.jar tesseract.jar</Class-Path>\n              <Implementation-Title>${project.name}</Implementation-Title>\n              <Implementation-Vendor>Bytedeco</Implementation-Vendor>\n              <Implementation-Version>${project.version}</Implementation-Version>\n              <Specification-Title>${project.name}</Specification-Title>\n              <Specification-Vendor>Bytedeco</Specification-Vendor>\n              <Specification-Version>${project.version}</Specification-Version>\n              <Multi-Release>true</Multi-Release>\n            </manifestEntries>\n          </archive>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>org.moditect</groupId>\n        <artifactId>moditect-maven-plugin</artifactId>\n        <version>1.3.0</version>\n        <configuration>\n          <jvmVersion>9</jvmVersion>\n          <overwriteExistingFiles>true</overwriteExistingFiles>\n          <outputDirectory>${project.build.directory}</outputDirectory>\n        </configuration>\n        <executions>\n          <execution>\n            <id>add-module-infos</id>\n            <phase>package</phase>\n            <goals>\n              <goal>add-module-info</goal>\n            </goals>\n            <configuration>\n              <modules>\n                <module>\n                  <file>${project.build.directory}/${project.artifactId}.jar</file>\n                  <moduleInfoFile>${project.basedir}/src/main/java9/module-info.java</moduleInfoFile>\n                </module>\n              </modules>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <artifactId>maven-install-plugin</artifactId>\n        <version>3.1.4</version>\n        <configuration>\n          <createChecksum>true</createChecksum>\n        </configuration>\n      </plugin>\n      <plugin>\n        <artifactId>maven-source-plugin</artifactId>\n        <version>3.4.0</version>\n        <executions>\n          <execution>\n            <id>attach-sources</id>\n            <phase>leave-disabled-to-not-generate-sources-twice-on-release</phase>\n          </execution>\n          <execution>\n            <id>attach-source</id>\n            <goals>\n              <goal>jar-no-fork</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <artifactId>maven-javadoc-plugin</artifactId>\n        <version>3.12.0</version>\n        <executions>\n          <execution>\n            <id>attach-javadocs</id>\n            <goals>\n              <goal>jar</goal>\n            </goals>\n            <configuration>\n              <links>\n                <link>http://bytedeco.org/javacpp/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/openblas/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/opencv/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/ffmpeg/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/flycapture/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/libdc1394/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/libfreenect/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/libfreenect2/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/librealsense/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/librealsense2/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/videoinput/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/artoolkitplus/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/flandmark/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/leptonica/apidocs</link>\n                <link>http://bytedeco.org/javacpp-presets/tesseract/apidocs</link>\n                <link>https://developer.android.com/reference</link>\n                <link>https://jogamp.org/deployment/v2.3.2/javadoc/gluegen/javadoc</link>\n                <link>https://jogamp.org/deployment/v2.3.2/javadoc/jocl/javadoc</link>\n                <link>https://jogamp.org/deployment/v2.3.2/javadoc/jogl/javadoc</link>\n                <link>http://junit.org/junit4/javadoc/4.13.2</link>\n              </links>\n              <sourceFileExcludes>\n                <exclude>org/bytedeco/javacv/FlyCaptureFrameGrabber.java</exclude>\n                <exclude>org/bytedeco/javacv/FFmpegLockCallback.java</exclude>\n              </sourceFileExcludes>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n  <profiles>\n    <profile>\n      <id>OpenJFX-8</id>\n      <activation>\n        <jdk>(,11)</jdk>\n      </activation>\n      <dependencies>\n        <dependency>\n          <groupId>net.java.openjfx.backport</groupId>\n          <artifactId>openjfx-78-backport</artifactId>\n          <version>1.8.0-ea-b96.1</version>\n          <scope>provided</scope>\n        </dependency>\n      </dependencies>\n    </profile>\n    <profile>\n      <id>OpenJFX-11</id>\n      <activation>\n        <jdk>11</jdk>\n      </activation>\n      <dependencies>\n        <dependency>\n          <groupId>org.openjfx</groupId>\n          <artifactId>javafx-graphics</artifactId>\n          <version>11</version>\n        </dependency>\n      </dependencies>\n    </profile>\n    <profile>\n      <id>OpenJFX-17</id>\n      <activation>\n        <jdk>17</jdk>\n      </activation>\n      <dependencies>\n        <dependency>\n          <groupId>org.openjfx</groupId>\n          <artifactId>javafx-graphics</artifactId>\n          <version>17</version>\n        </dependency>\n      </dependencies>\n    </profile>\n    <profile>\n      <id>OpenJFX-21</id>\n      <activation>\n        <jdk>21</jdk>\n      </activation>\n      <dependencies>\n        <dependency>\n          <groupId>org.openjfx</groupId>\n          <artifactId>javafx-graphics</artifactId>\n          <version>21</version>\n        </dependency>\n      </dependencies>\n    </profile>\n    <profile>\n      <id>OpenJFX-25</id>\n      <activation>\n        <jdk>25</jdk>\n      </activation>\n      <dependencies>\n        <dependency>\n          <groupId>org.openjfx</groupId>\n          <artifactId>javafx-graphics</artifactId>\n          <version>25</version>\n        </dependency>\n      </dependencies>\n    </profile>\n\n    <profile>\n      <id>doclint-java8-disable</id>\n      <activation>\n        <jdk>[1.8,)</jdk>\n      </activation>\n      <build>\n        <plugins>\n          <plugin>\n            <artifactId>maven-javadoc-plugin</artifactId>\n            <configuration>\n              <doclint>none</doclint>\n              <failOnError>false</failOnError>\n              <source>8</source>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n\n    <profile>\n      <id>sign-artifacts</id>\n      <activation>\n        <property>\n          <name>performRelease</name>\n          <value>true</value>\n        </property>\n      </activation>\n      <repositories>\n        <repository>\n          <id>central-portal-staging</id>\n          <name>Central Portal Staging</name>\n          <url>https://central.sonatype.com/api/v1/publisher/deployments/download/</url>\n          <releases>\n            <enabled>true</enabled>\n          </releases>\n          <snapshots>\n            <enabled>false</enabled>\n          </snapshots>\n        </repository>\n      </repositories>\n      <build>\n        <plugins>\n          <plugin>\n            <artifactId>maven-gpg-plugin</artifactId>\n            <version>3.2.8</version>\n            <executions>\n              <execution>\n                <id>sign-artifacts</id>\n                <phase>verify</phase>\n                <goals>\n                  <goal>sign</goal>\n                </goals>\n              </execution>\n            </executions>\n            <configuration>\n              <passphrase>${env.GPG_PASSPHRASE}</passphrase>\n              <useAgent>false</useAgent>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n\n    <profile>\n      <id>central-publishing</id>\n      <activation>\n        <property>\n          <name>!altDeploymentRepository</name>\n        </property>\n      </activation>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>org.sonatype.central</groupId>\n            <artifactId>central-publishing-maven-plugin</artifactId>\n            <version>0.8.0</version>\n            <extensions>true</extensions>\n            <configuration>\n              <publishingServerId>central</publishingServerId>\n              <autoPublish>false</autoPublish>\n              <checksums>required</checksums>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n  </profiles>\n\n</project>\n"
  },
  {
    "path": "samples/AudioSplitMergeHelper.java",
    "content": "import org.bytedeco.javacv.*;\n\nimport java.nio.Buffer;\nimport java.nio.ShortBuffer;\n\n/**\n * This code is a sample which split a 2-channel stereo audio into 2 single-channel mono audios\n * or merge 2 single-channel mono audios into a 2-channel stereo.\n * <p>\n * The code has been tested on s16le audio.\n * <p>\n * s16le means short 16bit little end. For other format, you may need change the ShortBuffer to other Buffer subclass\n * <p>\n * For s16lep, s32lep,xxxxxp format, the sample point arrangement format is no longer in ‘LRLRLR'.\n * Instead, it is arragement in format 'LLLLLL','RRRRRR'. So you have to change the short copy code.\n * <p>\n * <p>\n * ///////////////////////////////////////////////////////////////////////////\n * JavaCV is an excellent open-source streaming processing framework in the Java field\n * <p>\n * But I see many people, especially in China, making profits for themselves by introducing its usage,\n * which is not in line with the concept of open source projects.\n * I hope that If this code helped you, you can share your experience and knowledge with others in the world, rather\n * than for personal gain. Spread the spirit of open source.\n * ///////////////////////////////////////////////////////////////////////////\n * <p>\n * Acknowledge: Thanks for my hot girlfriend.\n *\n * @author steeveen\n * @date 2023/7/1 14:32\n */\npublic class AudioSplitMergeHelper {\n\n\n    /**\n     * split a 2-channel stereo audio into 2 single-channel mono audios\n     * <p>\n     * If you want to split this 2-channel stereo to 2 single-channel stereo, you should create 2 2-channel stereos\n     * and fill one channel with 0 data. It is similar in principle, so the code won't go into too much here.\n     *\n     * @param input       the file path which is to be splited\n     * @param outputLeft  the file path which store the left channel audio file\n     * @param outputRight the file path which store the right channel audio file\n     * @throws FrameGrabber.Exception\n     * @throws FrameRecorder.Exception\n     */\n    public static void split(String input, String outputLeft, String outputRight) throws FrameGrabber.Exception, FrameRecorder.Exception {\n        //grabber from input\n        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(input);\n        grabber.start();\n        //two recorders for two channels\n        FFmpegFrameRecorder leftRecorder = new FFmpegFrameRecorder(outputLeft, 1);\n        leftRecorder.setSampleRate(grabber.getSampleRate());\n        leftRecorder.start();\n        FFmpegFrameRecorder rightRecorder = new FFmpegFrameRecorder(outputRight, 1);\n        rightRecorder.setSampleRate(grabber.getSampleRate());\n        rightRecorder.start();\n\n        Frame frame = null;\n        while ((frame = grabber.grabSamples()) != null) {\n            // use s16le for example. so select ShortBuffer to receive the sample\n            ShortBuffer sb = (ShortBuffer) frame.samples[0];\n            short[] shorts = new short[sb.limit()];\n            sb.get(shorts);\n            //Split the LRLRLR to LLL in left channel and RRR int right channel\n            Frame leftFrame = frame.clone();\n            ShortBuffer leftSb = ShortBuffer.allocate(sb.capacity() / 2);\n            leftFrame.samples = new Buffer[]{leftSb};\n            leftFrame.audioChannels = 1;\n\n            Frame rightFrame = frame.clone();\n            ShortBuffer rightSb = ShortBuffer.allocate(sb.capacity() / 2);\n            rightFrame.samples = new Buffer[]{rightSb};\n            rightFrame.audioChannels = 1;\n\n            for (int i = 0; i < shorts.length; i++) {\n                if (i % 2 == 0) {\n                    leftSb.put(shorts[i]);\n                } else {\n                    rightSb.put(shorts[i]);\n                }\n            }\n            // reset the buffer to read mode\n            leftSb.rewind();\n            rightSb.rewind();\n            leftRecorder.record(leftFrame);\n            rightRecorder.record(rightFrame);\n        }\n        //release source\n        grabber.close();\n        leftRecorder.close();\n        rightRecorder.close();\n    }\n\n    /**\n     * Merge 2 single-channel mono audios into a 2-channel stereo.\n     * As usual the two input audios should have the same parameter and length;\n     *\n     * @param inputLeft  the left channel to be merged in\n     * @param inputRight the right channel to be merged in\n     * @param output     the merged stereo audio\n     * @throws FFmpegFrameGrabber.Exception\n     * @throws FFmpegFrameRecorder.Exception\n     */\n    public static void merge(String inputLeft, String inputRight, String output) throws FrameGrabber.Exception, FrameRecorder.Exception {\n        FFmpegFrameGrabber leftGrabber = new FFmpegFrameGrabber(inputLeft);\n        leftGrabber.start();\n        FFmpegFrameGrabber rightGrabber = new FFmpegFrameGrabber(inputRight);\n        rightGrabber.start();\n        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(output, 2);\n        //you'd better confirm the two input have the same samplerate. otherwise, you should control it manually by yourself\n        recorder.setSampleRate(leftGrabber.getSampleRate());\n        recorder.start();\n\n        Frame leftFrame = null;\n        Frame rightFrame = null;\n        int index = 0;\n        int maxLength = leftGrabber.getLengthInAudioFrames();\n        while (index < maxLength) {\n            // carry the bit data from two input into result frame by frame\n            leftFrame = leftGrabber.grabSamples();\n            rightFrame = rightGrabber.grabSamples();\n            ShortBuffer leftSb = (ShortBuffer) leftFrame.samples[0];\n            ShortBuffer rightSb = (ShortBuffer) rightFrame.samples[0];\n            short[] leftShorts = new short[leftSb.limit()];\n            short[] rightShorts = new short[rightSb.limit()];\n            leftSb.get(leftShorts);\n            rightSb.get(rightShorts);\n            ShortBuffer mergeSb = ShortBuffer.allocate(leftSb.capacity() + rightSb.capacity());\n\n            // create a template from the existing frame\n            Frame mergeFrame = leftFrame.clone();\n            // replace the frame tempalte by our merged buffer\n            mergeFrame.samples = new Buffer[]{mergeSb};\n            mergeFrame.audioChannels = 2;\n\n            for (int i = 0; i < leftShorts.length; i++) {\n                mergeSb.put(leftShorts[i]);\n                mergeSb.put(rightShorts[i]);\n            }\n\n            //reset buffer to read mode\n            mergeSb.flip();\n            recorder.record(mergeFrame);\n            index++;\n        }\n        //release source\n        leftGrabber.close();\n        rightGrabber.close();\n        recorder.close();\n    }\n}\n"
  },
  {
    "path": "samples/BioInspiredRetina.java",
    "content": "import java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.File;\nimport javax.imageio.ImageIO;\n\nimport org.bytedeco.javacpp.tools.Slf4jLogger;\nimport org.bytedeco.javacv.CanvasFrame;\nimport org.bytedeco.javacv.Java2DFrameConverter;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_bioinspired.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_bioinspired.*;\n\n\n/**\n * Bioinspired Retina demonstration\n * This retina model allows spatio-temporal image processing\n * As a summary, these are the retina model properties:\n * It applies a spectral whithening (mid-frequency details enhancement)\n * high frequency spatio-temporal noise reduction\n * low frequency luminance to be reduced (luminance range compression)\n * local logarithmic luminance compression allows details to be enhanced in low light conditions\n *\n * Created by mbetzel on 04.09.2016.\n */\npublic class BioInspiredRetina {\n\n    static {\n        System.setProperty(\"org.bytedeco.javacpp.logger\", \"slf4jlogger\");\n        System.setProperty(\"org.slf4j.simpleLogger.defaultLogLevel\", \"debug\");\n    }\n\n    private static final Slf4jLogger logger = (Slf4jLogger) org.bytedeco.javacpp.tools.Logger.create(BioInspiredRetina.class);\n\n    public static void main(String[] args) {\n        try {\n            logger.info(String.valueOf(logger.isDebugEnabled()));\n            logger.info(\"Start\");\n            new BioInspiredRetina().execute(args);\n            logger.info(\"Stop\");\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private void execute(String[] args) throws Exception {\n        BufferedImage bufferedImage = args.length >= 1 ? ImageIO.read(new File(args[0])) : ImageIO.read(this.getClass().getResourceAsStream(\"BlackBalls.jpg\"));\n        System.out.println(\"Image type: \" + bufferedImage.getType());\n        Mat matrix = new OpenCVFrameConverter.ToMat().convert(new Java2DFrameConverter().convert(bufferedImage));\n        normalize(matrix, matrix, 0, 255, NORM_MINMAX, -1, noArray());\n        showImage(matrix);\n        matrix.convertTo(matrix, CV_32F);\n        Mat gammaTransformedImage = new Mat(matrix.size(), CV_32F);\n        pow(matrix, 1. / 5, gammaTransformedImage);\n        Retina retina = Retina.create(gammaTransformedImage.size());\n        Mat retinaOutput_parvo = new Mat();\n        Mat retinaOutput_magno = new Mat();\n        retina.clearBuffers();\n        retina.run(gammaTransformedImage);\n        retina.getParvo(retinaOutput_parvo);\n        retina.getMagno(retinaOutput_magno);\n        showImage(retinaOutput_parvo);\n        showImage(retinaOutput_magno);\n    }\n\n\n    private void showImage(Mat matrix) {\n        CanvasFrame canvasFrame = new CanvasFrame(\"Retina demonstration\", 1);\n        canvasFrame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n        canvasFrame.setCanvasSize(640, 480);\n        Canvas canvas = canvasFrame.getCanvas();\n        canvasFrame.getContentPane().removeAll();\n        ScrollPane scrollPane = new ScrollPane();\n        scrollPane.add(canvas);\n        canvasFrame.add(scrollPane);\n        canvasFrame.showImage(new OpenCVFrameConverter.ToMat().convert(matrix));\n    }\n\n}\n"
  },
  {
    "path": "samples/BlobDemo.java",
    "content": "import org.bytedeco.javacv.Blobs;\nimport org.bytedeco.javacv.CanvasFrame;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n///////////////////////////////////////////////////////////////////\n//*                                                             *//\n//* As the author of this code, I place all of this code into   *//\n//* the public domain. Users can use it for any legal purpose.  *//\n//*                                                             *//\n//*             - Dave Grossman                                 *//\n//*                                                             *//\n///////////////////////////////////////////////////////////////////\npublic class BlobDemo\n{\n    public static void main(String[] args)\n    {\n        System.out.println(\"STARTING...\\n\");\n        demo();\n        System.out.println(\"ALL DONE\");\n    }\n\n    public static void demo()\n    {\n        int MinArea = 6;\n        int ErodeCount =0;\n        int DilateCount = 0;\n        \n        IplImage RawImage = null;\n\n        // Read an image.\n        for(int k = 0; k < 7; k++)\n        {\n            if(k == 0) { RawImage = cvLoadImage(\"BlackBalls.jpg\"); MinArea = 250; ErodeCount = 0; DilateCount = 1; }\n            else if(k == 1) { RawImage = cvLoadImage(\"Shapes1.jpg\"); MinArea = 6; ErodeCount = 0; DilateCount = 1; }\n            else if(k == 2) { RawImage = cvLoadImage(\"Shapes2.jpg\"); MinArea = 250; ErodeCount = 0; DilateCount = 1; }\n            else if(k == 3) { RawImage = cvLoadImage(\"Blob1.jpg\"); MinArea = 2800; ErodeCount = 1; DilateCount = 1; }\n            else if(k == 4) { RawImage = cvLoadImage(\"Blob2.jpg\"); MinArea = 2800; ErodeCount = 1; DilateCount = 1; }\n            else if(k == 5) { RawImage = cvLoadImage(\"Blob3.jpg\"); MinArea = 2800; ErodeCount = 1; DilateCount = 1; }\n            else if(k == 6) { RawImage = cvLoadImage(\"Rice.jpg\"); MinArea = 30; ErodeCount = 2; DilateCount = 1; }\n            //ShowImage(RawImage, \"RawImage\", 512);\n        \n            IplImage GrayImage = cvCreateImage(cvGetSize(RawImage), IPL_DEPTH_8U, 1);     \n            cvCvtColor(RawImage, GrayImage, CV_BGR2GRAY);\n            //ShowImage(GrayImage, \"GrayImage\", 512);\n\n            IplImage BWImage = cvCreateImage(cvGetSize(GrayImage), IPL_DEPTH_8U, 1); \n            cvThreshold(GrayImage, BWImage, 127, 255, CV_THRESH_BINARY);\n            //ShowImage(BWImage, \"BWImage\");\n            \n            IplImage WorkingImage = cvCreateImage(cvGetSize(BWImage), IPL_DEPTH_8U, 1);     \n            cvErode(BWImage, WorkingImage, null, ErodeCount);    \n            cvDilate(WorkingImage, WorkingImage, null, DilateCount);\n            //ShowImage(WorkingImage, \"WorkingImage\", 512);\n        \n            //cvSaveImage(\"Working.jpg\", WorkingImage);\n            //PrintGrayImage(WorkingImage, \"WorkingImage\");\n            //BinaryHistogram(WorkingImage);\n        \n            Blobs Regions = new Blobs();\n            Regions.BlobAnalysis(\n                    WorkingImage,               // image\n                    -1, -1,                     // ROI start col, row\n                    -1, -1,                     // ROI cols, rows\n                    1,                          // border (0 = black; 1 = white)\n                    MinArea);                   // minarea\n            Regions.PrintRegionData();\n\n            for(int i = 1; i <= Blobs.MaxLabel; i++)\n            {\n                double [] Region = Blobs.RegionData[i];\n                int Parent = (int) Region[Blobs.BLOBPARENT];\n                int Color = (int) Region[Blobs.BLOBCOLOR];\n                int MinX = (int) Region[Blobs.BLOBMINX];\n                int MaxX = (int) Region[Blobs.BLOBMAXX];\n                int MinY = (int) Region[Blobs.BLOBMINY];\n                int MaxY = (int) Region[Blobs.BLOBMAXY];\n                Highlight(RawImage,  MinX, MinY, MaxX, MaxY, 1);\n            }\n            \n            ShowImage(RawImage, \"RawImage\", 512);\n\n            cvReleaseImage(GrayImage); GrayImage = null;\n            cvReleaseImage(BWImage); BWImage = null;\n            cvReleaseImage(WorkingImage); WorkingImage = null;\n        }\n        cvReleaseImage(RawImage); RawImage = null;\n    }\n\n    // Versions with 2, 3, and 4 parms respectively\n    public static void ShowImage(IplImage image, String caption)\n    {\n        CvMat mat = image.asCvMat();\n        int width = mat.cols(); if(width < 1) width = 1;\n        int height = mat.rows(); if(height < 1) height = 1;\n        double aspect = 1.0 * width / height;\n        if(height < 128) { height = 128; width = (int) ( height * aspect ); }\n        if(width < 128) width = 128;\n        height = (int) ( width / aspect );\n        ShowImage(image, caption, width, height);\n    }\n    public static void ShowImage(IplImage image, String caption, int size)\n    {\n        if(size < 128) size = 128;\n        CvMat mat = image.asCvMat();\n        int width = mat.cols(); if(width < 1) width = 1;\n        int height = mat.rows(); if(height < 1) height = 1;\n        double aspect = 1.0 * width / height;\n        if(height != size) { height = size; width = (int) ( height * aspect ); }\n        if(width != size) width = size;\n        height = (int) ( width / aspect );\n        ShowImage(image, caption, width, height);\n    }\n    public static void ShowImage(IplImage image, String caption, int width, int height)\n    {\n        CanvasFrame canvas = new CanvasFrame(caption, 1);   // gamma=1\n        canvas.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n        canvas.setCanvasSize(width, height);\n        OpenCVFrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n        canvas.showImage(converter.convert(image));\n    }\n    \n    public static void Highlight(IplImage image, int [] inVec)\n    {\n        Highlight(image, inVec[0], inVec[1], inVec[2], inVec[3], 1);\n    }\n    public static void Highlight(IplImage image, int [] inVec, int Thick)\n    {\n        Highlight(image, inVec[0], inVec[1], inVec[2], inVec[3], Thick);\n    }\n    public static void Highlight(IplImage image, int xMin, int yMin, int xMax, int yMax)\n    {\n        Highlight(image, xMin, yMin, xMax, yMax, 1);\n    }\n    public static void Highlight(IplImage image, int xMin, int yMin, int xMax, int yMax, int Thick)\n    {\n        CvPoint pt1 = cvPoint(xMin,yMin);\n        CvPoint pt2 = cvPoint(xMax,yMax);\n        CvScalar color = cvScalar(255,0,0,0);       // blue [green] [red]\n        cvRectangle(image, pt1, pt2, color, Thick, 4, 0);\n    }\n    \n    public static void PrintGrayImage(IplImage image, String caption)\n    {\n        int size = 512; // impractical to print anything larger\n        CvMat mat = image.asCvMat();\n        int cols = mat.cols(); if(cols < 1) cols = 1;\n        int rows = mat.rows(); if(rows < 1) rows = 1;\n        double aspect = 1.0 * cols / rows;\n        if(rows > size) { rows = size; cols = (int) ( rows * aspect ); }\n        if(cols > size) cols = size;\n        rows = (int) ( cols / aspect );\n        PrintGrayImage(image, caption, 0, cols, 0, rows);\n    }\n    public static void PrintGrayImage(IplImage image, String caption, int MinX, int MaxX, int MinY, int MaxY)\n    {\n        int size = 512; // impractical to print anything larger\n        CvMat mat = image.asCvMat();\n        int cols = mat.cols(); if(cols < 1) cols = 1;\n        int rows = mat.rows(); if(rows < 1) rows = 1;\n        \n        if(MinX < 0) MinX = 0; if(MinX > cols) MinX = cols; \n        if(MaxX < 0) MaxX = 0; if(MaxX > cols) MaxX = cols; \n        if(MinY < 0) MinY = 0; if(MinY > rows) MinY = rows; \n        if(MaxY < 0) MaxY = 0; if(MaxY > rows) MaxY = rows; \n        \n        System.out.println(\"\\n\" + caption);\n        System.out.print(\"   +\");\n        for(int icol = MinX; icol < MaxX; icol++) System.out.print(\"-\");\n        System.out.println(\"+\");\n        \n        for(int irow = MinY; irow < MaxY; irow++)\n        {\n            if(irow<10) System.out.print(\" \");\n            if(irow<100) System.out.print(\" \");\n            System.out.print(irow);\n            System.out.print(\"|\");\n            for(int icol = MinX; icol < MaxX; icol++)\n            {\n                int val = (int) mat.get(irow,icol);\n                String C = \" \";\n                if(val == 0) C = \"*\";\n                System.out.print(C);\n            }\n            System.out.println(\"|\");\n        }\n        System.out.print(\"   +\");\n        for(int icol = MinX; icol < MaxX; icol++) System.out.print(\"-\");\n        System.out.println(\"+\");\n    }\n\n    public static void PrintImageProperties(IplImage image)\n    {\n        CvMat mat = image.asCvMat();\n        int cols = mat.cols();\n        int rows = mat.rows();\n        int depth = mat.depth();\n        System.out.println(\"ImageProperties for \" + image + \" : cols=\" + cols + \" rows=\" + rows + \" depth=\" + depth);\n    }\n    \n    public static float BinaryHistogram(IplImage image)\n    {\n        CvScalar Sum = cvSum(image);\n        float WhitePixels = (float) ( Sum.getVal(0) / 255 );\n        CvMat mat = image.asCvMat();\n        float TotalPixels = mat.cols() * mat.rows();\n        //float BlackPixels = TotalPixels - WhitePixels;\n        return WhitePixels / TotalPixels;\n    }\n  \n    // Counterclockwise small angle rotation by skewing - Does not stretch border pixels\n    public static IplImage SkewGrayImage(IplImage Src, double angle)    // angle is in radians\n    {\n        //double radians = - Math.PI * angle / 360.0;   // Half because skew is horizontal and vertical\n        double sin = - Math.sin(angle);\n        double AbsSin = Math.abs(sin);\n        \n        int nChannels = Src.nChannels();\n        if(nChannels != 1) \n        {\n            System.out.println(\"ERROR: SkewGrayImage: Require 1 channel: nChannels=\" + nChannels);\n            System.exit(1);\n        }\n        \n        CvMat SrcMat = Src.asCvMat();\n        int SrcCols = SrcMat.cols();\n        int SrcRows = SrcMat.rows();\n\n        double WidthSkew = AbsSin * SrcRows; \n        double HeightSkew = AbsSin * SrcCols;\n        \n        int DstCols = (int) ( SrcCols + WidthSkew ); \n        int DstRows = (int) ( SrcRows + HeightSkew );\n    \n        CvMat DstMat = cvCreateMat(DstRows, DstCols, CV_8UC1);  // Type matches IPL_DEPTH_8U\n        cvSetZero(DstMat);\n        cvNot(DstMat, DstMat);\n        \n        for(int irow = 0; irow < DstRows; irow++)\n        {\n            int dcol = (int) ( WidthSkew * irow / SrcRows );\n            for(int icol = 0; icol < DstCols; icol++)\n            {\n                int drow = (int) ( HeightSkew - HeightSkew * icol / SrcCols );\n                int jrow = irow - drow;\n                int jcol = icol - dcol;\n                if(jrow < 0 || jcol < 0 || jrow >= SrcRows || jcol >= SrcCols) DstMat.put(irow, icol, 255);\n                else DstMat.put(irow, icol, (int) SrcMat.get(jrow,jcol));\n            }\n        }\n        \n        IplImage Dst = cvCreateImage(cvSize(DstCols, DstRows), IPL_DEPTH_8U, 1);\n        Dst = DstMat.asIplImage();\n        return Dst;\n    }\n    \n    public static IplImage TransposeImage(IplImage SrcImage)\n    {\n        CvMat mat = SrcImage.asCvMat();\n        int cols = mat.cols();\n        int rows = mat.rows();\n        IplImage DstImage = cvCreateImage(cvSize(rows, cols), IPL_DEPTH_8U, 1);\n        cvTranspose(SrcImage, DstImage);\n        cvFlip(DstImage,DstImage,1);\n        return DstImage;\n    }\n}\n\n"
  },
  {
    "path": "samples/CaffeGooglenet.java",
    "content": "/*\n * JavaCV version of OpenCV caffe_googlenet.cpp\n * https://github.com/ludv1x/opencv_contrib/blob/master/modules/dnn/samples/caffe_googlenet.cpp\n *\n * Paolo Bolettieri <paolo.bolettieri@gmail.com>\n */\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_dnn.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_dnn.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\npublic class CaffeGooglenet {\n\n    /* Find best class for the blob (i. e. class with maximal probability) */\n    public static void getMaxClass(Mat probBlob, Point classId, double[] classProb) {\n        Mat probMat = probBlob.reshape(1, 1); //reshape the blob to 1x1000 matrix\n        minMaxLoc(probMat, null, classProb, null, classId, null);\n    }\n\n    public static List<String> readClassNames() {\n        String filename = \"synset_words.txt\";\n        List<String> classNames = null;\n\n        try (BufferedReader br = new BufferedReader(new FileReader(new File(filename)))) {\n            classNames = new ArrayList<String>();\n            String name = null;\n            while ((name = br.readLine()) != null) {\n                classNames.add(name.substring(name.indexOf(' ')+1));\n            }\n        } catch (IOException ex) {\n            System.err.println(\"File with classes labels not found \" + filename);\n            System.exit(-1);\n        }\n        return classNames;\n    }\n\n    public static void main(String[] args) throws Exception {\n        String modelTxt = \"bvlc_googlenet.prototxt\";\n        String modelBin = \"bvlc_googlenet.caffemodel\";\n        String imageFile = (args.length > 0) ? args[0] : \"space_shuttle.jpg\";\n\n        //! [Initialize network]\n        Net net = null;\n        try {                                 //Try to import Caffe GoogleNet model\n            net = readNetFromCaffe(modelTxt, modelBin);\n        } catch (Exception e) {               //Importer can throw errors, we will catch them\n            e.printStackTrace();\n        }\n\n        if (net == null || net.empty()) {\n            System.err.println(\"Can't load network by using the following files: \");\n            System.err.println(\"prototxt:   \" + modelTxt);\n            System.err.println(\"caffemodel: \" + modelBin);\n            System.err.println(\"bvlc_googlenet.caffemodel can be downloaded here:\");\n            System.err.println(\"http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel\");\n            System.exit(-1);\n        }\n        //! [Initialize network]\n\n        //! [Prepare blob]\n        Mat img = imread(imageFile);\n\n        if (img.empty()) {\n            System.err.println(\"Can't read image from the file: \" + imageFile);\n            System.exit(-1);\n        }\n\n        resize(img, img, new Size(224, 224)); //GoogLeNet accepts only 224x224 RGB-images\n        Mat inputBlob = blobFromImage(img);   //Convert Mat to 4-dimensional dnn::Blob from image\n        //! [Prepare blob]\n\n        //! [Set input blob]\n        net.setInput(inputBlob, \"data\", 1.0, null);      //set the network input\n        //! [Set input blob]\n\n        //! [Make forward pass]\n        Mat prob = net.forward(\"prob\");       //compute output\n        //! [Make forward pass]\n\n        //! [Gather output]\n        Point classId = new Point();\n        double[] classProb = new double[1];\n        getMaxClass(prob, classId, classProb);//find the best class\n        //! [Gather output]\n\n        //! [Print results]\n        List<String> classNames = readClassNames();\n\n        System.out.println(\"Best class: #\" + classId.x() + \" '\" + classNames.get(classId.x()) + \"'\");\n        System.out.println(\"Best class: #\" + classId.x());\n        System.out.println(\"Probability: \" + classProb[0] * 100 + \"%\");\n        //! [Print results]\n    } //main\n}\n"
  },
  {
    "path": "samples/ColoredObjectTrack.java",
    "content": "/*\n * Just an example using the opencv to make a colored object tracking,\n * i adpted this code to bytedeco/javacv, i think this will help some people.\n *\n * Waldemar <waldemarnt@outlook.com>\n */\n\nimport java.awt.Color;\nimport java.awt.Graphics;\nimport java.awt.image.BufferedImage;\nimport javax.swing.JPanel;\nimport org.bytedeco.javacv.CanvasFrame;\nimport org.bytedeco.javacv.FrameGrabber;\nimport org.bytedeco.javacv.Java2DFrameConverter;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\npublic class ColoredObjectTrack implements Runnable {\n \n    public static void main(String[] args) {\n        ColoredObjectTrack cot = new ColoredObjectTrack();\n        Thread th = new Thread(cot);\n        th.start();\n    }\n\n    final int INTERVAL = 10;// 1sec\n    final int CAMERA_NUM = 0; // Default camera for this time\n\n    /**\n     * Correct the color range- it depends upon the object, camera quality,\n     * environment.\n     */\n    static CvScalar rgba_min = cvScalar(0, 0, 130, 0);// RED wide dabur birko\n    static CvScalar rgba_max = cvScalar(80, 80, 255, 0);\n\n    IplImage image;\n    CanvasFrame canvas = new CanvasFrame(\"Web Cam Live\");\n    CanvasFrame path = new CanvasFrame(\"Detection\");\n    int ii = 0;\n    JPanel jp = new JPanel();\n\n    public ColoredObjectTrack() {\n        canvas.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n        path.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n        path.setContentPane(jp);\n    }\n\n    @Override\n    public void run() {\n        try {\n            FrameGrabber grabber = FrameGrabber.createDefault(CAMERA_NUM);\n            OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();\n            grabber.start();\n            IplImage img;\n            int posX = 0;\n            int posY = 0;\n            while (true) {\n                img = converter.convert(grabber.grab());\n                if (img != null) {\n                    // show image on window\n                    cvFlip(img, img, 1);// l-r = 90_degrees_steps_anti_clockwise\n                    canvas.showImage(converter.convert(img));\n                    IplImage detectThrs = getThresholdImage(img);\n \n                    CvMoments moments = new CvMoments();\n                    cvMoments(detectThrs, moments, 1);\n                    double mom10 = cvGetSpatialMoment(moments, 1, 0);\n                    double mom01 = cvGetSpatialMoment(moments, 0, 1);\n                    double area = cvGetCentralMoment(moments, 0, 0);\n                    posX = (int) (mom10 / area);\n                    posY = (int) (mom01 / area);\n                    // only if its a valid position\n                    if (posX > 0 && posY > 0) {\n                        paint(img, posX, posY);\n                    }\n                }\n                // Thread.sleep(INTERVAL);\n            }\n        } catch (Exception e) {\n        }\n    }\n\n    private void paint(IplImage img, int posX, int posY) {\n        Graphics g = jp.getGraphics();\n        path.setSize(img.width(), img.height());\n        // g.clearRect(0, 0, img.width(), img.height());\n        g.setColor(Color.RED);\n        // g.fillOval(posX, posY, 20, 20);\n        g.drawOval(posX, posY, 20, 20);\n        System.out.println(posX + \" , \" + posY);\n \n    }\n\n    private IplImage getThresholdImage(IplImage orgImg) {\n        IplImage imgThreshold = cvCreateImage(cvGetSize(orgImg), 8, 1);\n        //\n        cvInRangeS(orgImg, rgba_min, rgba_max, imgThreshold);// red\n \n        cvSmooth(imgThreshold, imgThreshold, CV_MEDIAN, 15,0,0,0);\n        cvSaveImage(++ii + \"dsmthreshold.jpg\", imgThreshold);\n        return imgThreshold;\n    }\n\n\n    public IplImage Equalize(BufferedImage bufferedimg) {\n        Java2DFrameConverter converter1 = new Java2DFrameConverter();\n        OpenCVFrameConverter.ToIplImage converter2 = new OpenCVFrameConverter.ToIplImage();\n        IplImage iploriginal = converter2.convert(converter1.convert(bufferedimg));\n        IplImage srcimg = IplImage.create(iploriginal.width(), iploriginal.height(), IPL_DEPTH_8U, 1);\n        IplImage destimg = IplImage.create(iploriginal.width(), iploriginal.height(), IPL_DEPTH_8U, 1);\n        cvCvtColor(iploriginal, srcimg, CV_BGR2GRAY);\n        cvEqualizeHist(srcimg, destimg);\n        return destimg;\n    }\n}\n"
  },
  {
    "path": "samples/DeepLearningFaceDetection.java",
    "content": "import org.bytedeco.javacpp.indexer.FloatIndexer;\nimport org.bytedeco.javacv.CanvasFrame;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_dnn.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_videoio.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_dnn.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_videoio.*;\n\n/**\n * Created on Jul 28, 2018 \n *\n * @author Taha Emara \n * Email : taha@emaraic.com\n *\n * This example does face detection using deep learning model which provides a\n * great accuracy compared to OpenCV face detection using Haar cascades.\n *\n * This example is based on this code\n * https://github.com/opencv/opencv/blob/master/modules/dnn/misc/face_detector_accuracy.py\n *\n * To run this example you need two files: deploy.prototxt can be downloaded\n * from\n * https://github.com/opencv/opencv/blob/master/samples/dnn/face_detector/deploy.prototxt\n *\n * and res10_300x300_ssd_iter_140000.caffemodel\n * https://github.com/opencv/opencv_3rdparty/blob/dnn_samples_face_detector_20170830/res10_300x300_ssd_iter_140000.caffemodel\n *\n */\npublic class DeepLearningFaceDetection {\n\n    private static final String PROTO_FILE = \"deploy.prototxt\";\n    private static final String CAFFE_MODEL_FILE = \"res10_300x300_ssd_iter_140000.caffemodel\";\n    private static final OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();\n    private static Net net = null;\n\n    static {\n        net = readNetFromCaffe(PROTO_FILE, CAFFE_MODEL_FILE);\n    }\n\n    private static void detectAndDraw(Mat image) {//detect faces and draw a blue rectangle arroung each face\n\n        resize(image, image, new Size(300, 300));//resize the image to match the input size of the model\n\n        //create a 4-dimensional blob from image with NCHW (Number of images in the batch -for training only-, Channel, Height, Width) dimensions order,\n        //for more detailes read the official docs at https://docs.opencv.org/trunk/d6/d0f/group__dnn.html#gabd0e76da3c6ad15c08b01ef21ad55dd8\n        Mat blob = blobFromImage(image, 1.0, new Size(300, 300), new Scalar(104.0, 177.0, 123.0, 0), false, false, CV_32F);\n\n        net.setInput(blob);//set the input to network model\n        Mat output = net.forward();//feed forward the input to the netwrok to get the output matrix\n\n        Mat ne = new Mat(new Size(output.size(3), output.size(2)), CV_32F, output.ptr(0, 0));//extract a 2d matrix for 4d output matrix with form of (number of detections x 7)\n\n        FloatIndexer srcIndexer = ne.createIndexer(); // create indexer to access elements of the matric\n\n        for (int i = 0; i < output.size(3); i++) {//iterate to extract elements\n            float confidence = srcIndexer.get(i, 2);\n            float f1 = srcIndexer.get(i, 3);\n            float f2 = srcIndexer.get(i, 4);\n            float f3 = srcIndexer.get(i, 5);\n            float f4 = srcIndexer.get(i, 6);\n            if (confidence > .6) {\n                float tx = f1 * 300;//top left point's x\n                float ty = f2 * 300;//top left point's y\n                float bx = f3 * 300;//bottom right point's x\n                float by = f4 * 300;//bottom right point's y\n                rectangle(image, new Rect(new Point((int) tx, (int) ty), new Point((int) bx, (int) by)), new Scalar(255, 0, 0, 0));//print blue rectangle \n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        VideoCapture capture = new VideoCapture();\n        capture.set(CAP_PROP_FRAME_WIDTH, 1280);\n        capture.set(CAP_PROP_FRAME_HEIGHT, 720);\n\n        if (!capture.open(0)) {\n            System.out.println(\"Can not open the cam !!!\");\n        }\n\n        Mat colorimg = new Mat();\n\n        CanvasFrame mainframe = new CanvasFrame(\"Face Detection\", CanvasFrame.getDefaultGamma() / 2.2);\n        mainframe.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n        mainframe.setCanvasSize(600, 600);\n        mainframe.setLocationRelativeTo(null);\n        mainframe.setVisible(true);\n\n        while (true) {\n            while (capture.read(colorimg) && mainframe.isVisible()) {\n                detectAndDraw(colorimg);\n                mainframe.showImage(converter.convert(colorimg));\n                try {\n                    Thread.sleep(50);\n                } catch (InterruptedException ex) {\n                    System.out.println(ex.getMessage());\n                }\n\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "samples/DeinterlacedVideoPlayer.java",
    "content": "import org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacv.FFmpegFrameFilter;\nimport org.bytedeco.javacv.Frame;\nimport org.bytedeco.javacv.FrameFilter;\nimport org.bytedeco.javacv.FrameGrabber;\nimport org.bytedeco.javacv.FrameGrabber.Exception;\nimport org.bytedeco.javacv.OpenCVFrameGrabber;\n\nimport org.bytedeco.ffmpeg.global.avutil;\n\npublic class DeinterlacedVideoPlayer {\n\t\n\tprivate static final int DEVICE_ID = 0;\n\tprivate static final int WIDTH = 640;\n\tprivate static final int HEIGHT = 480;\n\tprivate static final int FRAMERATE = 25;\n\tprivate static final int PIXEL_FORMAT = avutil.AV_PIX_FMT_BGR24;;\n\t\n\tprivate String ffmpegString = \"yadif=mode=0:parity=-1:deint=0,format=bgr24\";\n\tprivate FrameGrabber grabber;\n\t\n\tpublic DeinterlacedVideoPlayer() {}\n\t\n\tpublic void start() {\n\t\tFrameFilter filter = null;\n\t\ttry {\n\t\t\tstartFrameGrabber();\n\t\t\t\n\t\t\tFrame frame = null;\n\t\t\twhile ((frame = grabber.grab()) != null) {\n\t\t\t\tif (filter == null) {\n\t\t\t\t\tfilter = new FFmpegFrameFilter(ffmpegString, frame.imageWidth, frame.imageHeight);\n\t\t\t\t\tfilter.setPixelFormat(PIXEL_FORMAT);\n\t\t\t\t\tfilter.start();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tfilter.push(frame);\n\t\t\t\tframe = filter.pull();\n\t\t\t\t\n\t\t\t\t// do something with the filtered frame\n\t\t\t\t\n\t\t\t}\n\t\t} catch (Exception | org.bytedeco.javacv.FrameFilter.Exception e) {\n\t\t\tthrow new RuntimeException(e.getMessage(), e);\n\t\t} finally {\n\t\t\treleaseGrabberAndFilter(this.grabber, filter);\n\t\t}\n\t}\n\n\tprivate void startFrameGrabber() throws Exception {\n\t\tgrabber = new OpenCVFrameGrabber(DEVICE_ID);\n\t\tgrabber.setImageWidth(WIDTH);\n\t\tgrabber.setImageHeight(HEIGHT);\n\t\tgrabber.setFrameRate(FRAMERATE);\n\t\tgrabber.setPixelFormat(PIXEL_FORMAT);\n\t\tgrabber.start();\n\t}\n\t\n\tprivate void releaseGrabberAndFilter(FrameGrabber grabber, FrameFilter filter) {\n\t\ttry {\n\t\t\tif (grabber != null) {\n\t\t\t\tgrabber.release();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Cannot release frame grabber!\", e);\n\t\t} finally {\n\t\t\treleaseFilter(filter);\n\t\t}\n\t}\n\t\n\tprivate void releaseFilter(FrameFilter filter) {\n\t\tif (filter == null) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tfilter.close();\n\t\t} catch (org.bytedeco.javacv.FrameFilter.Exception e) {\n\t\t\tthrow new RuntimeException(\"Cannot close frame filter!\", e);\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "samples/Demo.java",
    "content": "/*\n * Copyright (C) 2009-2018 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport java.io.File;\nimport java.net.URL;\nimport org.bytedeco.javacv.*;\nimport org.bytedeco.javacpp.*;\nimport org.bytedeco.javacpp.indexer.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_calib3d.*;\nimport org.bytedeco.opencv.opencv_objdetect.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_calib3d.*;\nimport static org.bytedeco.opencv.global.opencv_objdetect.*;\n\npublic class Demo {\n    public static void main(String[] args) throws Exception {\n        String classifierName = null;\n        if (args.length > 0) {\n            classifierName = args[0];\n        } else {\n            URL url = new URL(\"https://raw.github.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_alt.xml\");\n            File file = Loader.cacheResource(url);\n            classifierName = file.getAbsolutePath();\n        }\n\n        // We can \"cast\" Pointer objects by instantiating a new object of the desired class.\n        CascadeClassifier classifier = new CascadeClassifier(classifierName);\n        if (classifier == null) {\n            System.err.println(\"Error loading classifier file \\\"\" + classifierName + \"\\\".\");\n            System.exit(1);\n        }\n\n        // The available FrameGrabber classes include OpenCVFrameGrabber (opencv_videoio),\n        // DC1394FrameGrabber, FlyCapture2FrameGrabber, OpenKinectFrameGrabber, OpenKinect2FrameGrabber,\n        // RealSenseFrameGrabber, RealSense2FrameGrabber, PS3EyeFrameGrabber, VideoInputFrameGrabber, and FFmpegFrameGrabber.\n        FrameGrabber grabber = FrameGrabber.createDefault(0);\n        grabber.start();\n\n        // CanvasFrame, FrameGrabber, and FrameRecorder use Frame objects to communicate image data.\n        // We need a FrameConverter to interface with other APIs (Android, Java 2D, JavaFX, Tesseract, OpenCV, etc).\n        OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();\n\n        // FAQ about IplImage and Mat objects from OpenCV:\n        // - For custom raw processing of data, createBuffer() returns an NIO direct\n        //   buffer wrapped around the memory pointed by imageData, and under Android we can\n        //   also use that Buffer with Bitmap.copyPixelsFromBuffer() and copyPixelsToBuffer().\n        // - To get a BufferedImage from an IplImage, or vice versa, we can chain calls to\n        //   Java2DFrameConverter and OpenCVFrameConverter, one after the other.\n        // - Java2DFrameConverter also has static copy() methods that we can use to transfer\n        //   data more directly between BufferedImage and IplImage or Mat via Frame objects.\n        Mat grabbedImage = converter.convert(grabber.grab());\n        int height = grabbedImage.rows();\n        int width = grabbedImage.cols();\n\n        // Objects allocated with `new`, clone(), or a create*() factory method are automatically released\n        // by the garbage collector, but may still be explicitly released by calling deallocate().\n        // You shall NOT call cvReleaseImage(), cvReleaseMemStorage(), etc. on objects allocated this way.\n        Mat grayImage = new Mat(height, width, CV_8UC1);\n        Mat rotatedImage = grabbedImage.clone();\n\n        // The OpenCVFrameRecorder class simply uses the VideoWriter of opencv_videoio,\n        // but FFmpegFrameRecorder also exists as a more versatile alternative.\n        FrameRecorder recorder = FrameRecorder.createDefault(\"output.avi\", width, height);\n        recorder.start();\n\n        // CanvasFrame is a JFrame containing a Canvas component, which is hardware accelerated.\n        // It can also switch into full-screen mode when called with a screenNumber.\n        // We should also specify the relative monitor/camera response for proper gamma correction.\n        CanvasFrame frame = new CanvasFrame(\"Some Title\", CanvasFrame.getDefaultGamma()/grabber.getGamma());\n\n        // Let's create some random 3D rotation...\n        Mat randomR    = new Mat(3, 3, CV_64FC1),\n            randomAxis = new Mat(3, 1, CV_64FC1);\n        // We can easily and efficiently access the elements of matrices and images\n        // through an Indexer object with the set of get() and put() methods.\n        DoubleIndexer Ridx = randomR.createIndexer(),\n                   axisIdx = randomAxis.createIndexer();\n        axisIdx.put(0, (Math.random() - 0.5) / 4,\n                       (Math.random() - 0.5) / 4,\n                       (Math.random() - 0.5) / 4);\n        Rodrigues(randomAxis, randomR);\n        double f = (width + height) / 2.0;  Ridx.put(0, 2, Ridx.get(0, 2) * f);\n                                            Ridx.put(1, 2, Ridx.get(1, 2) * f);\n        Ridx.put(2, 0, Ridx.get(2, 0) / f); Ridx.put(2, 1, Ridx.get(2, 1) / f);\n        System.out.println(Ridx);\n\n        // We can allocate native arrays using constructors taking an integer as argument.\n        Point hatPoints = new Point(3);\n\n        while (frame.isVisible() && (grabbedImage = converter.convert(grabber.grab())) != null) {\n            // Let's try to detect some faces! but we need a grayscale image...\n            cvtColor(grabbedImage, grayImage, CV_BGR2GRAY);\n            RectVector faces = new RectVector();\n            classifier.detectMultiScale(grayImage, faces);\n            long total = faces.size();\n            for (long i = 0; i < total; i++) {\n                Rect r = faces.get(i);\n                int x = r.x(), y = r.y(), w = r.width(), h = r.height();\n                rectangle(grabbedImage, new Point(x, y), new Point(x + w, y + h), Scalar.RED, 1, CV_AA, 0);\n\n                // To access or pass as argument the elements of a native array, call position() before.\n                hatPoints.position(0).x(x - w / 10     ).y(y - h / 10);\n                hatPoints.position(1).x(x + w * 11 / 10).y(y - h / 10);\n                hatPoints.position(2).x(x + w / 2      ).y(y - h / 2 );\n                fillConvexPoly(grabbedImage, hatPoints.position(0), 3, Scalar.GREEN, CV_AA, 0);\n            }\n\n            // Let's find some contours! but first some thresholding...\n            threshold(grayImage, grayImage, 64, 255, CV_THRESH_BINARY);\n\n            // To check if an output argument is null we may call either isNull() or equals(null).\n            MatVector contours = new MatVector();\n            findContours(grayImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);\n            long n = contours.size();\n            for (long i = 0; i < n; i++) {\n                Mat contour = contours.get(i);\n                Mat points = new Mat();\n                approxPolyDP(contour, points, arcLength(contour, true) * 0.02, true);\n                drawContours(grabbedImage, new MatVector(points), -1, Scalar.BLUE);\n            }\n\n            warpPerspective(grabbedImage, rotatedImage, randomR, rotatedImage.size());\n\n            Frame rotatedFrame = converter.convert(rotatedImage);\n            frame.showImage(rotatedFrame);\n            recorder.record(rotatedFrame);\n        }\n        frame.dispose();\n        recorder.stop();\n        grabber.stop();\n    }\n}\n"
  },
  {
    "path": "samples/FFmpegStreamingTimeout.java",
    "content": "import java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport org.bytedeco.javacpp.Pointer;\nimport org.bytedeco.javacv.FFmpegFrameGrabber;\nimport org.bytedeco.javacv.Frame;\nimport org.bytedeco.javacv.FrameGrabber;\n\nimport org.bytedeco.ffmpeg.avformat.*;\nimport static org.bytedeco.ffmpeg.global.avformat.*;\n\n/**\n *\n * @author Dmitriy Gerashenko <d.a.gerashenko@gmail.com>\n */\npublic class FFmpegStreamingTimeout {\n\n    /**\n     * There is no universal option for streaming timeout. Each of protocols has\n     * its own list of options.\n     */\n    private static enum TimeoutOption {\n        /**\n         * Depends on protocol (FTP, HTTP, RTMP, RTSP, SMB, SSH, TCP, UDP, or UNIX).\n         * http://ffmpeg.org/ffmpeg-all.html\n         *\n         * Specific for RTSP:\n         * Set socket TCP I/O timeout in microseconds.\n         * http://ffmpeg.org/ffmpeg-all.html#rtsp\n         */\n        TIMEOUT,\n        /**\n         * Protocols\n         *\n         * Maximum time to wait for (network) read/write operations to complete,\n         * in microseconds.\n         *\n         * http://ffmpeg.org/ffmpeg-all.html#Protocols\n         */\n        RW_TIMEOUT;\n\n        public String getKey() {\n            return toString().toLowerCase();\n        }\n\n    }\n\n    private static final String SOURCE_RTSP = \"rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov\";\n    private static final int TIMEOUT = 10; // In seconds.\n\n    public static void main(String[] args) {\n        rtspStreamingTest();\n//        testWithCallback(); // This is not working properly. It's just for test.\n    }\n\n    private static void rtspStreamingTest() {\n        try {\n            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(SOURCE_RTSP);\n            /**\n             * \"rw_timeout\" - IS IGNORED when a network cable have been\n             * unplugged before a connection but the option takes effect after a\n             * connection was established.\n             *\n             * \"timeout\" - works fine.\n             */\n            grabber.setOption(\n                    TimeoutOption.TIMEOUT.getKey(),\n                    String.valueOf(TIMEOUT * 1000000)\n            ); // In microseconds.\n            grabber.start();\n\n            Frame frame = null;\n            /**\n             * When network is disabled (before grabber was started) grabber\n             * throws exception: \"org.bytedeco.javacv.FrameGrabber$Exception:\n             * avformat_open_input() error -138: Could not open input...\".\n             *\n             * When connections is lost (after a few grabbed frames)\n             * grabber.grab() returns null without exception.\n             */\n            while ((frame = grabber.grab()) != null) {\n                System.out.println(\"frame grabbed at \" + grabber.getTimestamp());\n            }\n            System.out.println(\"loop end with frame: \" + frame);\n        } catch (FrameGrabber.Exception ex) {\n            System.out.println(\"exception: \" + ex);\n        }\n        System.out.println(\"end\");\n    }\n\n    private static void testWithCallback() {\n        try {\n            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(SOURCE_RTSP);\n            /**\n             * grabber.getFormatContext() is null before grabber.start().\n             *\n             * But if network is disabled grabber.start() will never return.\n             *\n             * That's why interrupt_callback not suitable for \"network disabled\n             * case\".\n             */\n            grabber.start();\n\n            final AtomicBoolean interruptFlag = new AtomicBoolean(false);\n            AVIOInterruptCB.Callback_Pointer cp = new AVIOInterruptCB.Callback_Pointer() {\n                @Override\n                public int call(Pointer pointer) {\n                    // 0 - continue, 1 - exit\n                    int interruptFlagInt = interruptFlag.get() ? 1 : 0;\n                    System.out.println(\"callback, interrupt flag == \" + interruptFlagInt);\n                    return interruptFlagInt;\n                }\n\n            };\n            AVFormatContext oc = grabber.getFormatContext();\n            avformat_alloc_context();\n            AVIOInterruptCB cb = new AVIOInterruptCB();\n            cb.callback(cp);\n            oc.interrupt_callback(cb);\n            new Thread(new Runnable() { public void run() {\n                try {\n                    TimeUnit.SECONDS.sleep(TIMEOUT);\n                    interruptFlag.set(true);\n                    System.out.println(\"interrupt flag was changed\");\n                } catch (InterruptedException ex) {\n                    System.out.println(\"exception in interruption thread: \" + ex);\n                }\n            }}).start();\n\n            Frame frame = null;\n            /**\n             * On one of my RTSP cams grabber stops calling callback on\n             * connection lost. I think it's has something to do with message:\n             * \"[swscaler @ 0000000029af49e0] deprecated pixel format used, make\n             * sure you did set range correctly\".\n             *\n             * So there is at least one case when grabber stops calling\n             * callback.\n             */\n            while ((frame = grabber.grab()) != null) {\n                System.out.println(\"frame grabbed at \" + grabber.getTimestamp());\n            }\n            System.out.println(\"loop end with frame: \" + frame);\n        } catch (FrameGrabber.Exception ex) {\n            System.out.println(\"exception: \" + ex);\n        }\n        System.out.println(\"end\");\n    }\n}\n"
  },
  {
    "path": "samples/FaceApplet.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html lang=\"en-US\">\n  <head>\n    <title>FaceApplet</title>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n  </head>\n  <body>\n    <noscript>A browser with JavaScript enabled is required for this page to operate properly.</noscript>\n    <h1>FaceApplet</h1>\n    <script src=\"http://www.java.com/js/deployJava.js\"></script>\n    <script>\n        var attributes = { code:'FaceApplet.class', archive:'FaceApplet.jar', width:640, height:480 };\n        var parameters = { jnlp_href: 'FaceApplet.jnlp' };\n        deployJava.runApplet(attributes, parameters, '1.6');\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "samples/FaceApplet.java",
    "content": "import java.applet.Applet;\nimport java.awt.BasicStroke;\nimport java.awt.Color;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.image.BufferedImage;\nimport java.io.File;\nimport java.io.IOException;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacv.Frame;\nimport org.bytedeco.javacv.FrameGrabber;\nimport org.bytedeco.javacv.Java2DFrameConverter;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\nimport org.bytedeco.javacv.OpenCVFrameGrabber;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_objdetect.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_objdetect.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class FaceApplet extends Applet implements Runnable {\n\n    private CvHaarClassifierCascade classifier = null;\n    private CvMemStorage storage = null;\n    private FrameGrabber grabber = null;\n    private IplImage grabbedImage = null, grayImage = null, smallImage = null;\n    private CvSeq faces = null;\n    private boolean stop = false;\n    private Exception exception = null;\n    OpenCVFrameConverter.ToIplImage grabberConverter = new OpenCVFrameConverter.ToIplImage();\n    Java2DFrameConverter paintConverter = new Java2DFrameConverter();\n\n    @Override public void init() {\n        try {\n            // Load the classifier file from Java resources.\n            String classiferName = \"haarcascade_frontalface_alt.xml\";\n            File classifierFile = Loader.extractResource(classiferName, null, \"classifier\", \".xml\");\n            if (classifierFile == null || classifierFile.length() <= 0) {\n                throw new IOException(\"Could not extract \\\"\" + classiferName + \"\\\" from Java resources.\");\n            }\n\n            // Preload the opencv_objdetect module to work around a known bug.\n            Loader.load(opencv_objdetect.class);\n            classifier = new CvHaarClassifierCascade(cvLoad(classifierFile.getAbsolutePath()));\n            classifierFile.delete();\n            if (classifier.isNull()) {\n                throw new IOException(\"Could not load the classifier file.\");\n            }\n\n            storage = CvMemStorage.create();\n        } catch (Exception e) {\n            if (exception == null) {\n                exception = e;\n                repaint();\n            }\n        }\n    }\n\n    @Override public void start() {\n        try {\n            new Thread(this).start();\n        } catch (Exception e) {\n            if (exception == null) {\n                exception = e;\n                repaint();\n            }\n        }\n    }\n\n    public void run() {\n        try {\n            try {\n                grabber = FrameGrabber.createDefault(0);\n                grabber.setImageWidth(getWidth());\n                grabber.setImageHeight(getHeight());\n                grabber.start();\n                grabbedImage = grabberConverter.convert(grabber.grab());\n            } catch (Exception e) {\n                if (grabber != null) grabber.release();\n                grabber = new OpenCVFrameGrabber(0);\n                grabber.setImageWidth(getWidth());\n                grabber.setImageHeight(getHeight());\n                grabber.start();\n                grabbedImage = grabberConverter.convert(grabber.grab());\n            }\n            grayImage  = IplImage.create(grabbedImage.width(),   grabbedImage.height(),   IPL_DEPTH_8U, 1);\n            smallImage = IplImage.create(grabbedImage.width()/4, grabbedImage.height()/4, IPL_DEPTH_8U, 1);\n            stop = false;\n            while (!stop && (grabbedImage = grabberConverter.convert(grabber.grab())) != null) {\n                if (faces == null) {\n                    cvClearMemStorage(storage);\n                    cvCvtColor(grabbedImage, grayImage, CV_BGR2GRAY);\n                    cvResize(grayImage, smallImage, CV_INTER_AREA);\n                    faces = cvHaarDetectObjects(smallImage, classifier, storage, 1.1, 3,\n                            CV_HAAR_FIND_BIGGEST_OBJECT | CV_HAAR_DO_ROUGH_SEARCH);\n                    repaint();\n                }\n            }\n            grabbedImage = grayImage = smallImage = null;\n            grabber.stop();\n            grabber.release();\n            grabber = null;\n        } catch (Exception e) {\n            if (exception == null) {\n                exception = e;\n                repaint();\n            }\n        }\n    }\n\n    @Override public void update(Graphics g) {\n        paint(g);\n    }\n\n    @Override public void paint(Graphics g) {\n        if (grabbedImage != null) {\n            Frame frame = grabberConverter.convert(grabbedImage);\n            BufferedImage image = paintConverter.getBufferedImage(frame, 2.2/grabber.getGamma());\n            Graphics2D g2 = image.createGraphics();\n            if (faces != null) {\n                g2.setColor(Color.RED);\n                g2.setStroke(new BasicStroke(2));\n                int total = faces.total();\n                for (int i = 0; i < total; i++) {\n                    CvRect r = new CvRect(cvGetSeqElem(faces, i));\n                    g2.drawRect(r.x()*4, r.y()*4, r.width()*4, r.height()*4);\n                }\n                faces = null;\n            }\n            g.drawImage(image, 0, 0, null);\n        }\n        if (exception != null) {\n            int y = 0, h = g.getFontMetrics().getHeight();\n            g.drawString(exception.toString(), 5, y += h);\n            for (StackTraceElement e : exception.getStackTrace()) {\n                g.drawString(\"        at \" + e.toString(), 5, y += h);\n            }\n        }\n    }\n\n    @Override public void stop() {\n        stop = true;\n    }\n\n    @Override public void destroy() { }\n}\n"
  },
  {
    "path": "samples/FaceApplet.jnlp",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jnlp spec=\"1.0+\" href=\"FaceApplet.jnlp\">\n    <information>\n        <title>FaceApplet</title>\n        <vendor>Samuel Audet</vendor>\n    </information>\n    <resources>\n        <j2se version=\"1.6+\" href=\"http://java.sun.com/products/autodl/j2se\" />\n        <jar href=\"FaceApplet.jar\" main=\"true\" />\n    </resources>\n    <security>\n       <all-permissions/>\n    </security>\n    <applet-desc name=\"FaceApplet\" main-class=\"FaceApplet\" width=\"640\" height=\"480\" />\n    <update check=\"background\"/>\n</jnlp>\n"
  },
  {
    "path": "samples/FacePreview.java",
    "content": "/*\n * Copyright (C) 2010-2019 Samuel Audet\n *\n * FacePreview - A fusion of OpenCV's facedetect and Android's CameraPreview samples,\n *               with JavaCV + JavaCPP as the glue in between.\n *\n * This file was based on CameraPreview.java that came with the Samples for\n * Android SDK API 8, revision 1 and contained the following copyright notice:\n *\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n *\n * IMPORTANT - Make sure the AndroidManifest.xml file looks like this:\n *\n * <?xml version=\"1.0\" encoding=\"utf-8\"?>\n * <manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n *     package=\"org.bytedeco.javacv.facepreview\"\n *     android:versionCode=\"1\"\n *     android:versionName=\"1.0\" >\n *     <uses-sdk android:minSdkVersion=\"4\" />\n *     <uses-permission android:name=\"android.permission.CAMERA\" />\n *     <uses-feature android:name=\"android.hardware.camera\" />\n *     <application android:label=\"@string/app_name\">\n *         <activity\n *             android:name=\"FacePreview\"\n *             android:label=\"@string/app_name\"\n *             android:screenOrientation=\"landscape\">\n *             <intent-filter>\n *                 <action android:name=\"android.intent.action.MAIN\" />\n *                 <category android:name=\"android.intent.category.LAUNCHER\" />\n *             </intent-filter>\n *         </activity>\n *     </application>\n * </manifest>\n */\n\npackage org.bytedeco.javacv.facepreview;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.ImageFormat;\nimport android.graphics.Paint;\nimport android.hardware.Camera;\nimport android.hardware.Camera.Size;\nimport android.os.Bundle;\nimport android.view.SurfaceHolder;\nimport android.view.SurfaceView;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.widget.FrameLayout;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.nio.ByteBuffer;\nimport java.util.List;\nimport org.bytedeco.javacpp.Loader;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_objdetect.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_objdetect.*;\n\n// ----------------------------------------------------------------------\n\npublic class FacePreview extends Activity {\n    private FrameLayout layout;\n    private FaceView faceView;\n    private Preview mPreview;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        // Hide the window title.\n        requestWindowFeature(Window.FEATURE_NO_TITLE);\n\n        super.onCreate(savedInstanceState);\n\n        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n\n        // Create our Preview view and set it as the content of our activity.\n        try {\n            layout = new FrameLayout(this);\n            faceView = new FaceView(this);\n            mPreview = new Preview(this, faceView);\n            layout.addView(mPreview);\n            layout.addView(faceView);\n            setContentView(layout);\n        } catch (IOException e) {\n            e.printStackTrace();\n            new AlertDialog.Builder(this).setMessage(e.getMessage()).create().show();\n        }\n    }\n}\n\n// ----------------------------------------------------------------------\n\nclass FaceView extends View implements Camera.PreviewCallback {\n    public static final int SUBSAMPLING_FACTOR = 4;\n\n    private Mat grayImage;\n    private CascadeClassifier classifier;\n    private RectVector faces;\n\n    public FaceView(FacePreview context) throws IOException {\n        super(context);\n\n        // Load the classifier file from Java resources.\n        File classifierFile = Loader.extractResource(getClass(),\n                \"/org/bytedeco/javacv/facepreview/haarcascade_frontalface_alt.xml\",\n                context.getCacheDir(), \"classifier\", \".xml\");\n        if (classifierFile == null || classifierFile.length() <= 0) {\n            throw new IOException(\"Could not extract the classifier file from Java resource.\");\n        }\n\n        classifier = new CascadeClassifier(classifierFile.getAbsolutePath());\n        classifierFile.delete();\n        if (classifier.isNull()) {\n            throw new IOException(\"Could not load the classifier file.\");\n        }\n    }\n\n    public void onPreviewFrame(final byte[] data, final Camera camera) {\n        try {\n            Camera.Size size = camera.getParameters().getPreviewSize();\n            processImage(data, size.width, size.height);\n            camera.addCallbackBuffer(data);\n        } catch (RuntimeException e) {\n            // The camera has probably just been released, ignore.\n        }\n    }\n\n    protected void processImage(byte[] data, int width, int height) {\n        // First, downsample our image and convert it into a grayscale Mat\n        int f = SUBSAMPLING_FACTOR;\n        if (grayImage == null || grayImage.cols() != width/f || grayImage.rows() != height/f) {\n            grayImage = new Mat(height/f, width/f, CV_8UC1);\n        }\n        int imageWidth  = grayImage.cols();\n        int imageHeight = grayImage.rows();\n        int dataStride = f*width;\n        int imageStride = (int)grayImage.step(0);\n        ByteBuffer imageBuffer = grayImage.createBuffer();\n        for (int y = 0; y < imageHeight; y++) {\n            int dataLine = y*dataStride;\n            int imageLine = y*imageStride;\n            for (int x = 0; x < imageWidth; x++) {\n                imageBuffer.put(imageLine + x, data[dataLine + f*x]);\n            }\n        }\n\n        faces = new RectVector();\n        classifier.detectMultiScale(grayImage, faces);\n        postInvalidate();\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        Paint paint = new Paint();\n        paint.setColor(Color.RED);\n        paint.setTextSize(20);\n\n        String s = \"FacePreview - This side up.\";\n        float textWidth = paint.measureText(s);\n        canvas.drawText(s, (getWidth()-textWidth)/2, 20, paint);\n\n        if (faces != null) {\n            paint.setStrokeWidth(2);\n            paint.setStyle(Paint.Style.STROKE);\n            float scaleX = (float)getWidth()/grayImage.cols();\n            float scaleY = (float)getHeight()/grayImage.rows();\n            long total = faces.size();\n            for (long i = 0; i < total; i++) {\n                Rect r = faces.get(i);\n                int x = r.x(), y = r.y(), w = r.width(), h = r.height();\n                canvas.drawRect(x*scaleX, y*scaleY, (x+w)*scaleX, (y+h)*scaleY, paint);\n            }\n        }\n    }\n}\n\n// ----------------------------------------------------------------------\n\nclass Preview extends SurfaceView implements SurfaceHolder.Callback {\n    SurfaceHolder mHolder;\n    Camera mCamera;\n    Camera.PreviewCallback previewCallback;\n\n    Preview(Context context, Camera.PreviewCallback previewCallback) {\n        super(context);\n        this.previewCallback = previewCallback;\n\n        // Install a SurfaceHolder.Callback so we get notified when the\n        // underlying surface is created and destroyed.\n        mHolder = getHolder();\n        mHolder.addCallback(this);\n        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);\n    }\n\n    public void surfaceCreated(SurfaceHolder holder) {\n        // The Surface has been created, acquire the camera and tell it where\n        // to draw.\n        mCamera = Camera.open();\n        try {\n            mCamera.setPreviewDisplay(holder);\n        } catch (IOException exception) {\n            mCamera.release();\n            mCamera = null;\n            // TODO: add more exception handling logic here\n        }\n    }\n\n    public void surfaceDestroyed(SurfaceHolder holder) {\n        // Surface will be destroyed when we return, so stop the preview.\n        // Because the CameraDevice object is not a shared resource, it's very\n        // important to release it when the activity is paused.\n        mCamera.stopPreview();\n        mCamera.release();\n        mCamera = null;\n    }\n\n\n    private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {\n        final double ASPECT_TOLERANCE = 0.05;\n        double targetRatio = (double) w / h;\n        if (sizes == null) return null;\n\n        Size optimalSize = null;\n        double minDiff = Double.MAX_VALUE;\n\n        int targetHeight = h;\n\n        // Try to find an size match aspect ratio and size\n        for (Size size : sizes) {\n            double ratio = (double) size.width / size.height;\n            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;\n            if (Math.abs(size.height - targetHeight) < minDiff) {\n                optimalSize = size;\n                minDiff = Math.abs(size.height - targetHeight);\n            }\n        }\n\n        // Cannot find the one match the aspect ratio, ignore the requirement\n        if (optimalSize == null) {\n            minDiff = Double.MAX_VALUE;\n            for (Size size : sizes) {\n                if (Math.abs(size.height - targetHeight) < minDiff) {\n                    optimalSize = size;\n                    minDiff = Math.abs(size.height - targetHeight);\n                }\n            }\n        }\n        return optimalSize;\n    }\n\n    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {\n        // Now that the size is known, set up the camera parameters and begin\n        // the preview.\n        Camera.Parameters parameters = mCamera.getParameters();\n\n        List<Size> sizes = parameters.getSupportedPreviewSizes();\n        Size optimalSize = getOptimalPreviewSize(sizes, w, h);\n        parameters.setPreviewSize(optimalSize.width, optimalSize.height);\n\n        mCamera.setParameters(parameters);\n        if (previewCallback != null) {\n            mCamera.setPreviewCallbackWithBuffer(previewCallback);\n            Camera.Size size = parameters.getPreviewSize();\n            byte[] data = new byte[size.width*size.height*\n                    ImageFormat.getBitsPerPixel(parameters.getPreviewFormat())/8];\n            mCamera.addCallbackBuffer(data);\n        }\n        mCamera.startPreview();\n    }\n\n}\n"
  },
  {
    "path": "samples/FaceRecognizerInVideo.java",
    "content": "import java.io.File;\n\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.DoublePointer;\n\nimport org.bytedeco.javacv.Frame;\nimport org.bytedeco.javacv.FrameGrabber.Exception;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\nimport org.bytedeco.javacv.OpenCVFrameGrabber;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_face.*;\nimport org.bytedeco.opencv.opencv_highgui.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_objdetect.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_face.*;\nimport static org.bytedeco.opencv.global.opencv_highgui.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_objdetect.*;\n\n/**\n * This is an example how to detect face in a video file with javacv\n * @author Vincent He (chinadragon0515@gmail.com)\n *\n */\npublic class FaceRecognizerInVideo {\n\n    public static void main(String[] args) throws Exception {\n\n        OpenCVFrameConverter.ToMat converterToMat = new OpenCVFrameConverter.ToMat();\n\n        if (args.length < 2) {\n            System.out.println(\"Two parameters are required to run this program, first parameter is the analized video and second parameter is the trained result for fisher faces.\");\n        }\n\n        String videoFileName = args[0];\n        String trainedResult = args[1];\n\n        CascadeClassifier face_cascade = new CascadeClassifier(\n                \"data\\\\haarcascade_frontalface_default.xml\");\n        FaceRecognizer lbphFaceRecognizer = LBPHFaceRecognizer.create();\n        lbphFaceRecognizer.read(trainedResult);\n\n        File f = new File(videoFileName);\n\n        OpenCVFrameGrabber grabber = null;\n        try {\n            grabber = OpenCVFrameGrabber.createDefault(f);\n            grabber.start();\n        } catch (Exception e) {\n            System.err.println(\"Failed start the grabber.\");\n        }\n\n        Frame videoFrame = null;\n        Mat videoMat = new Mat();\n        while (true) {\n            videoFrame = grabber.grab();\n            videoMat = converterToMat.convert(videoFrame);\n            Mat videoMatGray = new Mat();\n            // Convert the current frame to grayscale:\n            cvtColor(videoMat, videoMatGray, COLOR_BGRA2GRAY);\n            equalizeHist(videoMatGray, videoMatGray);\n\n            Point p = new Point();\n            RectVector faces = new RectVector();\n            // Find the faces in the frame:\n            face_cascade.detectMultiScale(videoMatGray, faces);\n\n            // At this point you have the position of the faces in\n            // faces. Now we'll get the faces, make a prediction and\n            // annotate it in the video. Cool or what?\n            for (int i = 0; i < faces.size(); i++) {\n                Rect face_i = faces.get(i);\n\n                Mat face = new Mat(videoMatGray, face_i);\n                // If fisher face recognizer is used, the face need to be\n                // resized.\n                // resize(face, face_resized, new Size(im_width, im_height),\n                // 1.0, 1.0, INTER_CUBIC);\n\n                // Now perform the prediction, see how easy that is:\n                IntPointer label = new IntPointer(1);\n                DoublePointer confidence = new DoublePointer(1);\n                lbphFaceRecognizer.predict(face, label, confidence);\n                int prediction = label.get(0);\n\n                // And finally write all we've found out to the original image!\n                // First of all draw a green rectangle around the detected face:\n                rectangle(videoMat, face_i, new Scalar(0, 255, 0, 1));\n\n                // Create the text we will annotate the box with:\n                String box_text = \"Prediction = \" + prediction;\n                // Calculate the position for annotated text (make sure we don't\n                // put illegal values in there):\n                int pos_x = Math.max(face_i.tl().x() - 10, 0);\n                int pos_y = Math.max(face_i.tl().y() - 10, 0);\n                // And now put it into the image:\n                putText(videoMat, box_text, new Point(pos_x, pos_y),\n                        FONT_HERSHEY_PLAIN, 1.0, new Scalar(0, 255, 0, 2.0));\n            }\n            // Show the result:\n            imshow(\"face_recognizer\", videoMat);\n\n            char key = (char) waitKey(20);\n            // Exit this loop on escape:\n            if (key == 27) {\n                destroyAllWindows();\n                break;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "samples/HoughLines.java",
    "content": "import javax.swing.JFrame;\nimport org.bytedeco.javacpp.*;\nimport org.bytedeco.javacv.*;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\n\n/**\n * C to Java translation of the houghlines.c sample provided in the c sample directory of OpenCV 2.1,\n * using the JavaCV Java wrapper of OpenCV 2.2 developped by Samuel Audet.\n *\n * @author Jeremy Nicola\n * jeremy.nicola@gmail.com\n */\npublic class HoughLines {\n\n    /**\n     * usage: java HoughLines imageDir\\imageName TransformType\n     */\n    public static void main(String[] args) {\n\n        String fileName = args.length >= 1 ? args[0] : \"pic1.png\"; // if no params provided, compute the defaut image\n        IplImage src = cvLoadImage(fileName, 0);\n        IplImage dst;\n        IplImage colorDst;\n        CvMemStorage storage = cvCreateMemStorage(0);\n        CvSeq lines = new CvSeq();\n\n        CanvasFrame source = new CanvasFrame(\"Source\");\n        CanvasFrame hough = new CanvasFrame(\"Hough\");\n        OpenCVFrameConverter.ToIplImage sourceConverter = new OpenCVFrameConverter.ToIplImage();\n        OpenCVFrameConverter.ToIplImage houghConverter = new OpenCVFrameConverter.ToIplImage();\n        if (src == null) {\n            System.out.println(\"Couldn't load source image.\");\n            return;\n        }\n\n        dst = cvCreateImage(cvGetSize(src), src.depth(), 1);\n        colorDst = cvCreateImage(cvGetSize(src), src.depth(), 3);\n\n        cvCanny(src, dst, 50, 200, 3);\n        cvCvtColor(dst, colorDst, CV_GRAY2BGR);\n\n        /*\n         * apply the probabilistic hough transform\n         * which returns for each line deteced two points ((x1, y1); (x2,y2))\n         * defining the detected segment\n         */\n        if (args.length == 2 && args[1].contentEquals(\"probabilistic\")) { \n            System.out.println(\"Using the Probabilistic Hough Transform\");\n            lines = cvHoughLines2(dst, storage, CV_HOUGH_PROBABILISTIC, 1, Math.PI / 180, 40, 50, 10, 0, CV_PI);\n            for (int i = 0; i < lines.total(); i++) {\n                // Based on JavaCPP, the equivalent of the C code:\n                // CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);\n                // CvPoint first=line[0], second=line[1]\n                // is:\n                Pointer line = cvGetSeqElem(lines, i);\n                CvPoint pt1  = new CvPoint(line).position(0);\n                CvPoint pt2  = new CvPoint(line).position(1);\n\n                System.out.println(\"Line spotted: \");\n                System.out.println(\"\\t pt1: \" + pt1);\n                System.out.println(\"\\t pt2: \" + pt2);\n                cvLine(colorDst, pt1, pt2, CV_RGB(255, 0, 0), 3, CV_AA, 0); // draw the segment on the image\n            }\n        }\n        /*\n         * Apply the multiscale hough transform which returns for each line two float parameters (rho, theta)\n         * rho: distance from the origin of the image to the line\n         * theta: angle between the x-axis and the normal line of the detected line\n         */\n        else if(args.length==2 && args[1].contentEquals(\"multiscale\")){\n                        System.out.println(\"Using the multiscale Hough Transform\"); //\n            lines = cvHoughLines2(dst, storage, CV_HOUGH_MULTI_SCALE, 1, Math.PI / 180, 40, 1, 1, 0, CV_PI);\n            for (int i = 0; i < lines.total(); i++) {\n                CvPoint2D32f point = new CvPoint2D32f(cvGetSeqElem(lines, i));\n\n                float rho=point.x();\n                float theta=point.y();\n\n                double a = Math.cos((double) theta), b = Math.sin((double) theta);\n                double x0 = a * rho, y0 = b * rho;\n                CvPoint pt1 = cvPoint((int) Math.round(x0 + 1000 * (-b)), (int) Math.round(y0 + 1000 * (a))), pt2 = cvPoint((int) Math.round(x0 - 1000 * (-b)), (int) Math.round(y0 - 1000 * (a)));\n                System.out.println(\"Line spoted: \");\n                System.out.println(\"\\t rho= \" + rho);\n                System.out.println(\"\\t theta= \" + theta);\n                cvLine(colorDst, pt1, pt2, CV_RGB(255, 0, 0), 3, CV_AA, 0);\n            }\n        }\n        /*\n         * Default: apply the standard hough transform. Outputs: same as the multiscale output.\n         */\n        else {\n            System.out.println(\"Using the Standard Hough Transform\");\n            lines = cvHoughLines2(dst, storage, CV_HOUGH_STANDARD, 1, Math.PI / 180, 90, 0, 0, 0, CV_PI);\n            for (int i = 0; i < lines.total(); i++) {\n                CvPoint2D32f point = new CvPoint2D32f(cvGetSeqElem(lines, i));\n\n                float rho=point.x();\n                float theta=point.y();\n\n                double a = Math.cos((double) theta), b = Math.sin((double) theta);\n                double x0 = a * rho, y0 = b * rho;\n                CvPoint pt1 = cvPoint((int) Math.round(x0 + 1000 * (-b)), (int) Math.round(y0 + 1000 * (a))), pt2 = cvPoint((int) Math.round(x0 - 1000 * (-b)), (int) Math.round(y0 - 1000 * (a)));\n                System.out.println(\"Line spotted: \");\n                System.out.println(\"\\t rho= \" + rho);\n                System.out.println(\"\\t theta= \" + theta);\n                cvLine(colorDst, pt1, pt2, CV_RGB(255, 0, 0), 3, CV_AA, 0);\n            }\n        }\n        source.showImage(sourceConverter.convert(src));\n        hough.showImage(houghConverter.convert(colorDst));\n\n        source.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n        hough.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n    }\n}\n"
  },
  {
    "path": "samples/ImageSegmentation.java",
    "content": "/*\n * JavaCV version of OpenCV imageSegmentation.cpp\n * https://github.com/opencv/opencv/blob/master/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp\n *\n * The OpenCV example image is available at the following address\n * https://github.com/opencv/opencv/blob/master/samples/data/cards.png\n *\n * Paolo Bolettieri <paolo.bolettieri@gmail.com>\n */\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bytedeco.javacpp.indexer.FloatIndexer;\nimport org.bytedeco.javacpp.indexer.IntIndexer;\nimport org.bytedeco.javacpp.indexer.UByteIndexer;\nimport org.bytedeco.javacv.CanvasFrame;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\npublic class ImageSegmentation {\n    private static final int[] WHITE = {255, 255, 255};\n    private static final int[] BLACK = {0, 0, 0};\n\n    public static void main(String[] args) {\n        // Load the image\n        Mat src = imread(args[0]);\n        // Check if everything was fine\n        if (src.data().isNull())\n            return;\n        // Show source image\n        imshow(\"Source Image\", src);\n\n        // Change the background from white to black, since that will help later to extract\n        // better results during the use of Distance Transform\n        UByteIndexer srcIndexer = src.createIndexer();\n        for (int x = 0; x < srcIndexer.rows(); x++) {\n            for (int y = 0; y < srcIndexer.cols(); y++) {\n                int[] values = new int[3];\n                srcIndexer.get(x, y, values);\n                if (Arrays.equals(values, WHITE)) {\n                    srcIndexer.put(x, y, BLACK);\n                }\n            }\n        }\n        // Show output image\n        imshow(\"Black Background Image\", src);\n\n        // Create a kernel that we will use for accuting/sharpening our image\n        Mat kernel = Mat.ones(3, 3, CV_32F).asMat();\n        FloatIndexer kernelIndexer = kernel.createIndexer();\n        kernelIndexer.put(1, 1, -8); // an approximation of second derivative, a quite strong kernel\n\n        // do the laplacian filtering as it is\n        // well, we need to convert everything in something more deeper then CV_8U\n        // because the kernel has some negative values,\n        // and we can expect in general to have a Laplacian image with negative values\n        // BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255\n        // so the possible negative number will be truncated\n        Mat imgLaplacian = new Mat();\n        Mat sharp = src; // copy source image to another temporary one\n        filter2D(sharp, imgLaplacian, CV_32F, kernel);\n        src.convertTo(sharp, CV_32F);\n        Mat imgResult = subtract(sharp, imgLaplacian).asMat();\n        // convert back to 8bits gray scale\n        imgResult.convertTo(imgResult, CV_8UC3);\n        imgLaplacian.convertTo(imgLaplacian, CV_8UC3);\n        // imshow( \"Laplace Filtered Image\", imgLaplacian );\n        imshow(\"New Sharped Image\", imgResult);\n\n        src = imgResult; // copy back\n        // Create binary image from source image\n        Mat bw = new Mat();\n        cvtColor(src, bw, CV_BGR2GRAY);\n        threshold(bw, bw, 40, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);\n        imshow(\"Binary Image\", bw);\n\n        // Perform the distance transform algorithm\n        Mat dist = new Mat();\n        distanceTransform(bw, dist, CV_DIST_L2, 3);\n        // Normalize the distance image for range = {0.0, 1.0}\n        // so we can visualize and threshold it\n        normalize(dist, dist, 0, 1., NORM_MINMAX, -1, null);\n        imshow(\"Distance Transform Image\", dist);\n\n        // Threshold to obtain the peaks\n        // This will be the markers for the foreground objects\n        threshold(dist, dist, .4, 1., CV_THRESH_BINARY);\n        // Dilate a bit the dist image\n        Mat kernel1 = Mat.ones(3, 3, CV_8UC1).asMat();\n        dilate(dist, dist, kernel1);\n        imshow(\"Peaks\", dist);\n        // Create the CV_8U version of the distance image\n        // It is needed for findContours()\n        Mat dist_8u = new Mat();\n        dist.convertTo(dist_8u, CV_8U);\n        // Find total markers\n        MatVector contours = new MatVector();\n        findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);\n        // Create the marker image for the watershed algorithm\n        Mat markers = Mat.zeros(dist.size(), CV_32SC1).asMat();\n        // Draw the foreground markers\n        for (int i = 0; i < contours.size(); i++)\n            drawContours(markers, contours, i, Scalar.all((i) + 1));\n        // Draw the background marker\n        circle(markers, new Point(5, 5), 3, RGB(255, 255, 255));\n        imshow(\"Markers\", multiply(markers, 10000).asMat());\n\n        // Perform the watershed algorithm\n        watershed(src, markers);\n        Mat mark = Mat.zeros(markers.size(), CV_8UC1).asMat();\n        markers.convertTo(mark, CV_8UC1);\n        bitwise_not(mark, mark);\n//            imshow(\"Markers_v2\", mark); // uncomment this if you want to see how the mark\n                                    // image looks like at that point\n        // Generate random colors\n        List<int[]> colors = new ArrayList<int[]>();\n        for (int i = 0; i < contours.size(); i++) {\n            int b = theRNG().uniform(0, 255);\n            int g = theRNG().uniform(0, 255);\n            int r = theRNG().uniform(0, 255);\n            int[] color = { b, g, r };\n            colors.add(color);\n        }\n        // Create the result image\n        Mat dst = Mat.zeros(markers.size(), CV_8UC3).asMat();\n        // Fill labeled objects with random colors\n        IntIndexer markersIndexer = markers.createIndexer();\n        UByteIndexer dstIndexer = dst.createIndexer();\n        for (int i = 0; i < markersIndexer.rows(); i++) {\n            for (int j = 0; j < markersIndexer.cols(); j++) {\n                int index = markersIndexer.get(i, j);\n                if (index > 0 && index <= contours.size())\n                    dstIndexer.put(i, j, colors.get(index - 1));\n                else\n                    dstIndexer.put(i, j, BLACK);\n            }\n        }\n        // Visualize the final image\n        imshow(\"Final Result\", dst);\n    }\n\n    //I wrote a custom imshow method for problems using the OpenCV original one\n    private static void imshow(String txt, Mat img) {\n        CanvasFrame canvasFrame = new CanvasFrame(txt);\n        canvasFrame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n        canvasFrame.setCanvasSize(img.cols(), img.rows());\n        canvasFrame.showImage(new OpenCVFrameConverter.ToMat().convert(img));\n    }\n\n}\n\n"
  },
  {
    "path": "samples/JavaFxPlayVideoAndAudio.java",
    "content": "import java.nio.ByteBuffer;\nimport java.nio.ShortBuffer;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport javafx.application.Application;\nimport javafx.application.Platform;\nimport javafx.scene.Scene;\nimport javafx.scene.image.Image;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\nimport javax.sound.sampled.AudioFormat;\nimport javax.sound.sampled.AudioSystem;\nimport javax.sound.sampled.DataLine;\nimport javax.sound.sampled.SourceDataLine;\nimport org.bytedeco.javacv.FFmpegFrameGrabber;\nimport org.bytedeco.javacv.Frame;\nimport org.bytedeco.javacv.JavaFXFrameConverter;\n\n/**\n * @author Dmitriy Gerashenko <d.a.gerashenko@gmail.com>\n * @author Jarek Sacha\n */\npublic class JavaFxPlayVideoAndAudio extends Application {\n\n    private static class PlaybackTimer {\n        private Long startTime = -1L;\n        private final DataLine soundLine;\n\n        public PlaybackTimer(DataLine soundLine) {\n            this.soundLine = soundLine;\n        }\n\n        public PlaybackTimer() {\n            this.soundLine = null;\n        }\n\n        public void start() {\n            if (soundLine == null) {\n                startTime = System.nanoTime();\n            }\n        }\n\n        public long elapsedMicros() {\n            if (soundLine == null) {\n                if (startTime < 0) {\n                    throw new IllegalStateException(\"PlaybackTimer not initialized.\");\n                }\n                return (System.nanoTime() - startTime) / 1000;\n            } else {\n                return soundLine.getMicrosecondPosition();\n            }\n        }\n    }\n\n    private static final Logger LOG = Logger.getLogger(JavaFxPlayVideoAndAudio.class.getName());\n\n    private static volatile Thread playThread;\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n    @Override\n    public void start(final Stage primaryStage) throws Exception {\n        final StackPane root = new StackPane();\n        final ImageView imageView = new ImageView();\n\n        root.getChildren().add(imageView);\n        imageView.fitWidthProperty().bind(primaryStage.widthProperty());\n        imageView.fitHeightProperty().bind(primaryStage.heightProperty());\n\n        final Scene scene = new Scene(root, 640, 480);\n\n        primaryStage.setTitle(\"Video + audio\");\n        primaryStage.setScene(scene);\n        primaryStage.show();\n\n        playThread = new Thread(new Runnable() { public void run() {\n            try {\n                final String videoFilename = getParameters().getRaw().get(0);\n                final FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoFilename);\n                grabber.start();\n                primaryStage.setWidth(grabber.getImageWidth());\n                primaryStage.setHeight(grabber.getImageHeight());\n                final PlaybackTimer playbackTimer;\n                final SourceDataLine soundLine;\n                if (grabber.getAudioChannels() > 0) {\n                    final AudioFormat audioFormat = new AudioFormat(grabber.getSampleRate(), 16, grabber.getAudioChannels(), true, true);\n\n                    final DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);\n                    soundLine = (SourceDataLine) AudioSystem.getLine(info);\n                    soundLine.open(audioFormat);\n                    soundLine.start();\n                    playbackTimer = new PlaybackTimer(soundLine);\n                } else {\n                    soundLine = null;\n                    playbackTimer = new PlaybackTimer();\n                }\n\n                final JavaFXFrameConverter converter = new JavaFXFrameConverter();\n\n                final ExecutorService audioExecutor = Executors.newSingleThreadExecutor();\n                final ExecutorService imageExecutor = Executors.newSingleThreadExecutor();\n\n                final long maxReadAheadBufferMicros = 1000 * 1000L;\n\n                long lastTimeStamp = -1L;\n                while (!Thread.interrupted()) {\n                    final Frame frame = grabber.grab();\n                    if (frame == null) {\n                        break;\n                    }\n                    if (lastTimeStamp < 0) {\n                        playbackTimer.start();\n                    }\n                    lastTimeStamp = frame.timestamp;\n                    if (frame.image != null) {\n                        final Frame imageFrame = frame.clone();\n\n                        imageExecutor.submit(new Runnable() {\n                            public void run() {\n                                final Image image = converter.convert(imageFrame);\n                                final long timeStampDeltaMicros = imageFrame.timestamp - playbackTimer.elapsedMicros();\n                                imageFrame.close();\n                                if (timeStampDeltaMicros > 0) {\n                                    final long delayMillis = timeStampDeltaMicros / 1000L;\n                                    try {\n                                        Thread.sleep(delayMillis);\n                                    } catch (InterruptedException e) {\n                                        Thread.currentThread().interrupt();\n                                    }\n                                }\n                                Platform.runLater(new Runnable() {\n                                    public void run() {\n                                        imageView.setImage(image);\n                                    }\n                                });\n                            }\n                        });\n                    } else if (frame.samples != null) {\n                        if (soundLine == null) {\n                            throw new IllegalStateException(\"Internal error: sound playback not initialized\");\n                        }\n                        final ShortBuffer channelSamplesShortBuffer = (ShortBuffer) frame.samples[0];\n                        channelSamplesShortBuffer.rewind();\n\n                        final ByteBuffer outBuffer = ByteBuffer.allocate(channelSamplesShortBuffer.capacity() * 2);\n\n                        for (int i = 0; i < channelSamplesShortBuffer.capacity(); i++) {\n                            short val = channelSamplesShortBuffer.get(i);\n                            outBuffer.putShort(val);\n                        }\n\n                        audioExecutor.submit(new Runnable() {\n                            public void run() {\n                                soundLine.write(outBuffer.array(), 0, outBuffer.capacity());\n                                outBuffer.clear();\n                            }\n                        });\n                    }\n                    final long timeStampDeltaMicros = frame.timestamp - playbackTimer.elapsedMicros();\n                    if (timeStampDeltaMicros > maxReadAheadBufferMicros) {\n                        Thread.sleep((timeStampDeltaMicros - maxReadAheadBufferMicros) / 1000);\n                    }\n                }\n\n                if (!Thread.interrupted()) {\n                    long delay = (lastTimeStamp - playbackTimer.elapsedMicros()) / 1000 +\n                            Math.round(1 / grabber.getFrameRate() * 1000);\n                    Thread.sleep(Math.max(0, delay));\n                }\n                grabber.stop();\n                grabber.release();\n                if (soundLine != null) {\n                    soundLine.stop();\n                }\n                audioExecutor.shutdownNow();\n                audioExecutor.awaitTermination(10, TimeUnit.SECONDS);\n                imageExecutor.shutdownNow();\n                imageExecutor.awaitTermination(10, TimeUnit.SECONDS);\n\n                Platform.exit();\n            } catch (Exception exception) {\n                LOG.log(Level.SEVERE, null, exception);\n                System.exit(1);\n            }\n        }});\n        playThread.start();\n    }\n\n    @Override\n    public void stop() {\n        playThread.interrupt();\n    }\n\n}\n"
  },
  {
    "path": "samples/KazemiFacemarkExample.java",
    "content": "/**\n * Kazemi Facemark example for JavaCV\n * \n * @author Théophile Gonos\n *\n * Link to Kazemi model : \n * https://raw.githubusercontent.com/opencv/opencv_3rdparty/contrib_face_alignment_20170818/face_landmark_model.dat\n */\n\nimport java.io.IOException;\nimport java.net.URISyntaxException;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_face.*;\nimport org.bytedeco.opencv.opencv_highgui.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_objdetect.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_face.*;\nimport static org.bytedeco.opencv.global.opencv_highgui.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_objdetect.*;\n\n\npublic class KazemiFacemarkExample {\n    public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {\n        // Load Face Detector\n        CascadeClassifier faceDetector = new CascadeClassifier (\"haarcascade_frontalface_alt2.xml\");\n \n        // Create an instance of Facemark\n        FacemarkKazemi facemark = FacemarkKazemi.create();\n \n        // Load landmark detector \n        facemark.loadModel(\"face_landmark_model.dat\");\n \n        // Load image\n        Mat img = imread(\"face.jpg\");\n        \n        // convert to grayscale and equalize histograe for better detection\n        Mat gray = new Mat ();\n        cvtColor(img, gray, COLOR_BGR2GRAY);\n        equalizeHist( gray, gray );\n       \n        // Find faces on the image\n        RectVector faces = new RectVector ();\n        faceDetector.detectMultiScale(gray, faces);\n        \n        System.out.println (\"Faces detected: \"+faces.size());\n        // Variable for landmarks. \n        // Landmarks for one face is a vector of points\n        // There can be more than one face in the image.\n        Point2fVectorVector landmarks = new Point2fVectorVector();\n\n        // Run landmark detector\n        boolean success = facemark.fit(img, faces, landmarks);\n        \n        if(success) {\n            // If successful, render the landmarks on each face\n            for (long i = 0; i < landmarks.size(); i++) {\n                Point2fVector v = landmarks.get(i);\n                drawFacemarks(img, v, Scalar.YELLOW);\n            }\n        }\n\n        // Display results \n        imshow(\"Kazemi Facial Landmark\", img);\n        cvWaitKey(0);\n        // Save results\n        imwrite (\"kazemi_landmarks.jpg\", img);\n    }\n}\n"
  },
  {
    "path": "samples/LBFFacemarkExampleWithVideo.java",
    "content": "/**\n * LBF Facemark example for JavaCV with Video camera and Transparent API\n * \n * @author Théophile Gonos\n *\n * you can find the lbfmodel here:\n * https://raw.githubusercontent.com/kurnianggoro/GSOC2017/master/data/lbfmodel.yaml\n */\n\nimport java.io.IOException;\nimport java.net.URISyntaxException;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_face.*;\nimport org.bytedeco.opencv.opencv_highgui.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_objdetect.*;\nimport org.bytedeco.opencv.opencv_videoio.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_face.*;\nimport static org.bytedeco.opencv.global.opencv_highgui.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_objdetect.*;\nimport static org.bytedeco.opencv.global.opencv_videoio.*;\n\npublic class LBFFacemarkExampleWithVideo {\n    \n    /**\n     * @param args the command line arguments\n     * @throws java.io.IOException\n     * @throws java.net.URISyntaxException\n     * @throws java.lang.InterruptedException\n     */\n    public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {\n        // Load Face Detector\n        CascadeClassifier faceDetector = new CascadeClassifier (\"haarcascade_frontalface_alt2.xml\");\n \n        // Create an instance of Facemark\n        Facemark facemark = FacemarkLBF.create();\n \n        // Load landmark detector \n        facemark.loadModel(\"lbfmodel.yaml\");\n \n        // Set up webcam for video capture\n        VideoCapture cam = new VideoCapture (0);\n        // Variable to store a video frame and its grayscale \n        Mat frame = new Mat ();\n        \n        // Read a frame\n        while(cam.read(frame)) {\n            // convert to grayscale and equalize histograe for better detection\n            // + use of transparent API\n            UMat gray = new UMat ();\n            frame.copyTo(gray);\n            cvtColor(gray, gray, COLOR_BGR2GRAY);\n            equalizeHist( gray, gray );\n       \n            // Find faces on the image\n            RectVector faces = new RectVector ();\n            faceDetector.detectMultiScale(gray, faces);\n            \n            System.out.println (\"Faces detected: \"+faces.size());\n            // Verify is at least one face is detected\n            // With some Facemark algorithms it crashes if there is no faces\n            if (!faces.empty()) {\n        \n                // Variable for landmarks. \n                // Landmarks for one face is a vector of points\n                // There can be more than one face in the image.\n                Point2fVectorVector landmarks = new Point2fVectorVector();\n\n                // Run landmark detector\n                boolean success = facemark.fit(frame, faces, landmarks);\n        \n                if(success) {\n                    // If successful, render the landmarks on the face\n                    for (long i = 0; i < landmarks.size(); i++) {\n                        Point2fVector v = landmarks.get(i);\n                        drawFacemarks(frame, v, Scalar.YELLOW);\n                    }\n                }\n            }\n            // Display results \n            imshow(\"LBF Facial Landmark\", frame);\n            // Exit loop if ESC is pressed\n            if (waitKey(1) == 27) break;\n        }\n    }\n}\n"
  },
  {
    "path": "samples/MotionDetector.java",
    "content": "/* \n * I developed some code for recognize motion detections with JavaCV.\n * Actually, it works with an array of Rect, performing, every cicle, an\n * intersection test with area of difference with the rect of interests\n * (this array is callad \"sa\", stands for SizedArea). I hope could this\n * helps someone.\n * \n * Feel free to ask about any question regarding the code above, cheers!\n *\n * Angelo Marchesin <marchesin.angelo@gmail.com>\n */\n\nimport org.bytedeco.javacpp.*;\nimport org.bytedeco.javacv.*;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\npublic class MotionDetector {\n    public static void main(String[] args) throws Exception {\n        OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);\n        OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();\n        grabber.start();\n\n        IplImage frame = converter.convert(grabber.grab());\n        IplImage image = null;\n        IplImage prevImage = null;\n        IplImage diff = null;\n\n        CanvasFrame canvasFrame = new CanvasFrame(\"Some Title\");\n        canvasFrame.setCanvasSize(frame.width(), frame.height());\n\n        CvMemStorage storage = CvMemStorage.create();\n\n        while (canvasFrame.isVisible() && (frame = converter.convert(grabber.grab())) != null) {\n            cvClearMemStorage(storage);\n\n            cvSmooth(frame, frame, CV_GAUSSIAN, 9, 9, 2, 2);\n            if (image == null) {\n                image = IplImage.create(frame.width(), frame.height(), IPL_DEPTH_8U, 1);\n                cvCvtColor(frame, image, CV_RGB2GRAY);\n            } else {\n                prevImage = IplImage.create(frame.width(), frame.height(), IPL_DEPTH_8U, 1);\n                prevImage = image;\n                image = IplImage.create(frame.width(), frame.height(), IPL_DEPTH_8U, 1);\n                cvCvtColor(frame, image, CV_RGB2GRAY);\n            }\n\n            if (diff == null) {\n                diff = IplImage.create(frame.width(), frame.height(), IPL_DEPTH_8U, 1);\n            }\n\n            if (prevImage != null) {\n                // perform ABS difference\n                cvAbsDiff(image, prevImage, diff);\n                // do some threshold for wipe away useless details\n                cvThreshold(diff, diff, 64, 255, CV_THRESH_BINARY);\n\n                canvasFrame.showImage(converter.convert(diff));\n\n                // recognize contours\n                CvSeq contour = new CvSeq(null);\n                cvFindContours(diff, storage, contour, Loader.sizeof(CvContour.class), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);\n\n                while (contour != null && !contour.isNull()) {\n                    if (contour.elem_size() > 0) {\n                        CvBox2D box = cvMinAreaRect2(contour, storage);\n                        // test intersection\n                        if (box != null) {\n                            CvPoint2D32f center = box.center();\n                            CvSize2D32f size = box.size();\n/*                            for (int i = 0; i < sa.length; i++) {\n                                if ((Math.abs(center.x - (sa[i].offsetX + sa[i].width / 2))) < ((size.width / 2) + (sa[i].width / 2)) &&\n                                    (Math.abs(center.y - (sa[i].offsetY + sa[i].height / 2))) < ((size.height / 2) + (sa[i].height / 2))) {\n\n                                    if (!alarmedZones.containsKey(i)) {\n                                        alarmedZones.put(i, true);\n                                        activeAlarms.put(i, 1);\n                                    } else {\n                                        activeAlarms.remove(i);\n                                        activeAlarms.put(i, 1);\n                                    }\n                                    System.out.println(\"Motion Detected in the area no: \" + i +\n                                            \" Located at points: (\" + sa[i].x + \", \" + sa[i].y+ \") -\"\n                                            + \" (\" + (sa[i].x +sa[i].width) + \", \"\n                                            + (sa[i].y+sa[i].height) + \")\");\n                                }\n                            }\n*/\n                        }\n                    }\n                    contour = contour.h_next();\n                }\n            }\n        }\n        grabber.stop();\n        canvasFrame.dispose();\n    }\n}\n"
  },
  {
    "path": "samples/OpenCVFaceRecognizer.java",
    "content": "import java.io.File;\nimport java.io.FilenameFilter;\nimport java.nio.IntBuffer;\n\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.DoublePointer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_face.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_face.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\n\n/**\n * I couldn't find any tutorial on how to perform face recognition using OpenCV and Java,\n * so I decided to share a viable solution here. The solution is very inefficient in its\n * current form as the training model is built at each run, however it shows what's needed\n * to make it work.\n *\n * The class below takes two arguments: The path to the directory containing the training\n * faces and the path to the image you want to classify. Not that all images has to be of\n * the same size and that the faces already has to be cropped out of their original images\n * (Take a look here http://fivedots.coe.psu.ac.th/~ad/jg/nui07/index.html if you haven't\n * done the face detection yet).\n *\n * For the simplicity of this post, the class also requires that the training images have\n * filename format: <label>-rest_of_filename.png. For example:\n *\n * 1-jon_doe_1.png\n * 1-jon_doe_2.png\n * 2-jane_doe_1.png\n * 2-jane_doe_2.png\n * ...and so on.\n *\n * Source: http://pcbje.com/2012/12/doing-face-recognition-with-javacv/\n *\n * @author Petter Christian Bjelland\n */\npublic class OpenCVFaceRecognizer {\n    public static void main(String[] args) {\n        String trainingDir = args[0];\n        Mat testImage = imread(args[1], IMREAD_GRAYSCALE);\n\n        File root = new File(trainingDir);\n\n        FilenameFilter imgFilter = new FilenameFilter() {\n            public boolean accept(File dir, String name) {\n                name = name.toLowerCase();\n                return name.endsWith(\".jpg\") || name.endsWith(\".pgm\") || name.endsWith(\".png\");\n            }\n        };\n\n        File[] imageFiles = root.listFiles(imgFilter);\n\n        MatVector images = new MatVector(imageFiles.length);\n\n        Mat labels = new Mat(imageFiles.length, 1, CV_32SC1);\n        IntBuffer labelsBuf = labels.createBuffer();\n\n        int counter = 0;\n\n        for (File image : imageFiles) {\n            Mat img = imread(image.getAbsolutePath(), IMREAD_GRAYSCALE);\n\n            int label = Integer.parseInt(image.getName().split(\"\\\\-\")[0]);\n\n            images.put(counter, img);\n\n            labelsBuf.put(counter, label);\n\n            counter++;\n        }\n\n        FaceRecognizer faceRecognizer = FisherFaceRecognizer.create();\n        // FaceRecognizer faceRecognizer = EigenFaceRecognizer.create();\n        // FaceRecognizer faceRecognizer = LBPHFaceRecognizer.create();\n\n        faceRecognizer.train(images, labels);\n\n        IntPointer label = new IntPointer(1);\n        DoublePointer confidence = new DoublePointer(1);\n        faceRecognizer.predict(testImage, label, confidence);\n        int predictedLabel = label.get(0);\n\n        System.out.println(\"Predicted label: \" + predictedLabel);\n    }\n}\n"
  },
  {
    "path": "samples/OpenCVFeatures2dSerialization.java",
    "content": "import java.awt.image.BufferedImage;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.charset.StandardCharsets;\nimport javax.imageio.ImageIO;\n\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacv.Java2DFrameConverter;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_features2d.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_features2d.*;\n\n/**\n * (De)serialize OpenCV structures using XML to files and memory\n * <p>\n * Created by Maurice Betzel on 24.11.2017.\n */\n\n\npublic class OpenCVFeatures2dSerialization {\n\n    public static void main(String[] args) throws IOException {\n        String imageFile = (args.length > 0) ? args[0] : \"Blob3.jpg\";\n        BufferedImage bufferedImage = ImageIO.read(new File(imageFile));\n        try (Mat matrix = new OpenCVFrameConverter.ToMat().convert(new Java2DFrameConverter().convert(bufferedImage))\n        ) {\n            String fileName = \"serialized.xml\";\n            serializeFile(matrix, fileName);\n            deserializeFile(fileName);\n\n            String serialized = serializeMemory(matrix);\n            System.out.println(serialized);\n            deserializeMemory(serialized);\n        }\n    }\n\n    private static void serializeFile(Mat matrix, String fileName) throws UnsupportedEncodingException {\n        try (KeyPointVector keyPointVectorSerialize = new KeyPointVector(); Mat objectDescriptorsSerialize = new Mat(); AKAZE akaze = AKAZE.create();\n             FileStorage fileStorage = new FileStorage(fileName, FileStorage.WRITE, StandardCharsets.UTF_8.name())\n        ) {\n            akaze.detectAndCompute(matrix, new Mat(), keyPointVectorSerialize, objectDescriptorsSerialize, false);\n            System.out.println(\"Vector size: \" + keyPointVectorSerialize.size());\n            System.out.println(\"Descriptor size: \" + objectDescriptorsSerialize.cols());\n            write(fileStorage, \"keyPoints\", keyPointVectorSerialize);\n            write(fileStorage, \"descriptors\", objectDescriptorsSerialize);\n            fileStorage.release();\n        }\n    }\n\n    private static void deserializeFile(String file) {\n        try (KeyPointVector keyPointVectorDeserialize = new KeyPointVector(); Mat objectDescriptorsDeserialize = new Mat();\n             FileStorage fileStorage = new FileStorage(file, FileStorage.READ, StandardCharsets.UTF_8.name());\n             FileNode keyPointsFileNode = fileStorage.get(\"keyPoints\"); FileNode descriptorsFileNode = fileStorage.get(\"descriptors\")\n        ) {\n            read(keyPointsFileNode, keyPointVectorDeserialize);\n            read(descriptorsFileNode, objectDescriptorsDeserialize);\n            System.out.println(\"Vector size: \" + keyPointVectorDeserialize.size());\n            System.out.println(\"Descriptor size: \" + objectDescriptorsDeserialize.cols());\n            fileStorage.release();\n        }\n    }\n\n    private static String serializeMemory(Mat matrix) throws UnsupportedEncodingException {\n        try (KeyPointVector keyPointVectorSerialize = new KeyPointVector(); Mat objectDescriptorsSerialize = new Mat(); AKAZE akaze = AKAZE.create();\n             FileStorage fileStorage = new FileStorage(\".xml\", FileStorage.WRITE | FileStorage.MEMORY, StandardCharsets.UTF_8.name())\n        ) {\n            akaze.detectAndCompute(matrix, new Mat(), keyPointVectorSerialize, objectDescriptorsSerialize, false);\n            System.out.println(\"Vector size: \" + keyPointVectorSerialize.size());\n            System.out.println(\"Descriptor size: \" + objectDescriptorsSerialize.cols());\n            write(fileStorage, \"keyPoints\", keyPointVectorSerialize);\n            write(fileStorage, \"descriptors\", objectDescriptorsSerialize);\n            BytePointer bytePointer = fileStorage.releaseAndGetString();\n            return bytePointer.getString(StandardCharsets.UTF_8.name());\n        }\n    }\n\n    private static void deserializeMemory(String serialized) {\n        try (KeyPointVector keyPointVectorDeserialize = new KeyPointVector(); Mat objectDescriptorsDeserialize = new Mat();\n             FileStorage fileStorage = new FileStorage(serialized, FileStorage.READ | FileStorage.MEMORY, StandardCharsets.UTF_8.name());\n             FileNode keyPointsFileNode = fileStorage.get(\"keyPoints\"); FileNode descriptorsFileNode = fileStorage.get(\"descriptors\")\n        ) {\n            read(keyPointsFileNode, keyPointVectorDeserialize);\n            read(descriptorsFileNode, objectDescriptorsDeserialize);\n            System.out.println(\"Vector size: \" + keyPointVectorDeserialize.size());\n            System.out.println(\"Descriptor size: \" + objectDescriptorsDeserialize.cols());\n            fileStorage.release();\n        }\n    }\n\n}\n"
  },
  {
    "path": "samples/OpticalFlowDense.java",
    "content": "import java.nio.FloatBuffer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_optflow.*;\nimport org.bytedeco.opencv.opencv_video.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_optflow.*;\nimport static org.bytedeco.opencv.global.opencv_video.*;\n\n/**\n * This code will calculate the optical flow for every pixel using DenseOpticalFlow between two images (Frame-1 &\n * Frame-2) and put the velocity of every pixel to another image (OF) in their coordinate.\n *\n * @author Dawit Gebreyohannes\n */\npublic class OpticalFlowDense {\n\n    public static void main(final String[] args) {\n        final Mat pFrame = imread(\"samples/image0.png\", IMREAD_GRAYSCALE),\n                cFrame = imread(\"samples/image1.png\", IMREAD_GRAYSCALE),\n                pGray = new Mat(), cGray = new Mat(), Optical_Flow = new Mat();\n\n        pFrame.convertTo(pGray, CV_32FC1);\n        cFrame.convertTo(cGray, CV_32FC1);\n\n        final DenseOpticalFlow tvl1 = DualTVL1OpticalFlow.create();\n        tvl1.calc(pGray, cGray, Optical_Flow);\n\n        final Mat OF = new Mat(pGray.rows(), pGray.cols(), CV_32FC1);\n        final FloatBuffer in = Optical_Flow.createBuffer(),\n                out = OF.createBuffer();\n\n        final int height = pGray.rows(), width = pGray.cols();\n\n        for (int y = 0; y < height; y++) {\n            for (int x = 0; x < width; x++) {\n                final float xVelocity = in.get();\n                final float yVelocity = in.get();\n                final float pixelVelocity = (float) Math\n                        .sqrt(xVelocity * xVelocity + yVelocity * yVelocity);\n                out.put(pixelVelocity);\n            }\n        }\n        imwrite(\"OF.png\", OF);\n    }\n}\n"
  },
  {
    "path": "samples/OpticalFlowTracker.java",
    "content": "/*\n * Because I believe that examples are the easiest way how to use JavaCV, I am \n * sending a sample based on http://dasl.mem.drexel.edu/~noahKuntz/openCVTut9.html\n *\n * burgetrm@gmail.com\n */\n\nimport org.bytedeco.javacv.*;\nimport org.bytedeco.javacpp.*;\nimport org.bytedeco.javacpp.indexer.*;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_highgui.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_video.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_highgui.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_video.*;\n\npublic class OpticalFlowTracker {\n    private static final int MAX_CORNERS = 500;\n    private static final int win_size = 15;\n\n    public static void main(String[] args) {\n        // Load two images and allocate other structures\n        Mat imgA = imread(\n                \"image0.png\",\n                IMREAD_GRAYSCALE);\n        Mat imgB = imread(\n                \"image1.png\",\n                IMREAD_GRAYSCALE);\n\n        // Mat imgC = imread(\"OpticalFlow1.png\",\n        // IMREAD_UNCHANGED);\n        Mat imgC = imread(\n                \"image0.png\",\n                IMREAD_UNCHANGED);\n\n        // Get the features for tracking\n        Mat cornersA = new Mat();\n        goodFeaturesToTrack(imgA, cornersA, MAX_CORNERS,\n                0.05, 5.0, null, 3, false, 0.04);\n\n        cornerSubPix(imgA, cornersA,\n                new Size(win_size, win_size), new Size(-1, -1),\n                new TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03));\n\n        // Call Lucas Kanade algorithm\n        Mat features_found = new Mat();\n        Mat feature_errors = new Mat();\n\n        Mat cornersB = new Mat();\n        calcOpticalFlowPyrLK(imgA, imgB, cornersA, cornersB,\n                features_found, feature_errors, new Size(win_size, win_size), 5,\n                new TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.3), 0, 1e-4);\n\n        // Make an image of the results\n        FloatIndexer cornersAidx = cornersA.createIndexer();\n        FloatIndexer cornersBidx = cornersB.createIndexer();\n        UByteIndexer features_found_idx = features_found.createIndexer();\n        FloatIndexer feature_errors_idx = feature_errors.createIndexer();\n        for (int i = 0; i < cornersAidx.size(0); i++) {\n            if (features_found_idx.get(i) == 0 || feature_errors_idx.get(i) > 550) {\n                System.out.println(\"Error is \" + feature_errors_idx.get(i) + \"/n\");\n                continue;\n            }\n            System.out.println(\"Got it/n\");\n            Point p0 = new Point(Math.round(cornersAidx.get(i, 0)),\n                    Math.round(cornersAidx.get(i, 1)));\n            Point p1 = new Point(Math.round(cornersBidx.get(i, 0)),\n                    Math.round(cornersBidx.get(i, 1)));\n            line(imgC, p0, p1, RGB(255, 0, 0),\n                    2, 8, 0);\n        }\n\n        imwrite(\n                \"image0-1.png\",\n                imgC);\n        namedWindow(\"LKpyr_OpticalFlow\", 0);\n        imshow(\"LKpyr_OpticalFlow\", imgC);\n        waitKey(0);\n    }\n}\n"
  },
  {
    "path": "samples/PacketRecorderTest.java",
    "content": "import java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport org.bytedeco.javacv.*;\n\nimport org.bytedeco.ffmpeg.avcodec.AVPacket;\n\npublic class PacketRecorderTest {\n\n    private static final DateFormat DATE_FORMAT = new SimpleDateFormat(\"yyyyMMdd__hhmmSSS\");\n\n    private static final int RECORD_LENGTH = 5000;\n\n    private static final boolean AUDIO_ENABLED = false;\n\n    public static void main(String[] args) throws FrameRecorder.Exception, FrameGrabber.Exception {\n\n        String inputFile = \"/home/usr/videos/VIDEO_FILE_NAME.mp4\";\n\n        // Decodes-encodes\n        String outputFile = \"/tmp/\" + DATE_FORMAT.format(new Date()) + \"_frameRecord.mp4\";\n        PacketRecorderTest.frameRecord(inputFile, outputFile);\n\n        // copies codec (no need to re-encode)\n        outputFile = \"/tmp/\" + DATE_FORMAT.format(new Date()) + \"_packetRecord.mp4\";\n        PacketRecorderTest.packetRecord(inputFile, outputFile);\n\n    }\n\n    public static void frameRecord(String inputFile, String outputFile) throws FrameGrabber.Exception, FrameRecorder.Exception {\n\n        int audioChannel = AUDIO_ENABLED ? 1 : 0;\n\n        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);\n        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel);\n\n        grabber.start();\n        recorder.start();\n\n        Frame frame;\n        long t1 = System.currentTimeMillis();\n        while ((frame = grabber.grabFrame(AUDIO_ENABLED, true, true, false)) != null) {\n            recorder.record(frame);\n            if ((System.currentTimeMillis() - t1) > RECORD_LENGTH) {\n                break;\n            }\n        }\n        recorder.stop();\n        grabber.stop();\n    }\n\n    public static void packetRecord(String inputFile, String outputFile) throws FrameGrabber.Exception, FrameRecorder.Exception {\n\n        int audioChannel = AUDIO_ENABLED ? 1 : 0;\n\n        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);\n        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel);\n\n        grabber.start();\n        recorder.start(grabber.getFormatContext());\n\n        AVPacket packet;\n        long t1 = System.currentTimeMillis();\n        while ((packet = grabber.grabPacket()) != null) {\n            recorder.recordPacket(packet);\n            if ((System.currentTimeMillis() - t1) > RECORD_LENGTH) {\n                break;\n            }\n        }\n\n        recorder.stop();\n        grabber.stop();\n\n    }\n\n}\n"
  },
  {
    "path": "samples/PerspectiveWarpDemo.java",
    "content": "import org.bytedeco.javacpp.FloatPointer;\nimport org.bytedeco.javacv.CanvasFrame;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n * Created by Johan Swanberg on 2018-09-21\n * <p>\n * An example of how to use the perspective warp method in JavaCV.\n */\n\npublic class PerspectiveWarpDemo extends Thread {\n\n    static CanvasFrame frame = new CanvasFrame(\"Perspective Warp Demo - warped image\");\n    static CanvasFrame frameUnedited = new CanvasFrame(\"Perspective Warp Demo - Unedited image\");\n\n    public static void main(String[] args) {\n        Mat image = imread(\"shapes1.jpg\");\n        Mat perspectiveWarpedImg = performPerspectiveWarp(image, 30, 0, 200, 0, 400, 250, 40, 260);\n\n        OpenCVFrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n        frame.showImage(converter.convert(perspectiveWarpedImg));\n        frameUnedited.showImage(converter.convert(image));\n\n        image.release();\n        perspectiveWarpedImg.release();\n\n        frameUnedited.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n        frameUnedited.setLocationRelativeTo(null);\n        frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n        frame.setLocation(frameUnedited.getX()+frameUnedited.getWidth(), frameUnedited.getY());\n    }\n\n\n    /**\n     * Performs a perspective warp that takes four corners and stretches them to the corners of the image.\n     * x1,y1 represents the top left corner, 2 top right, going clockwise.\n     * This method does not release/deallocate the input image Mat, call inputMat.release() after this method\n     * if you don't plan on using the input more after this method.\n     *\n     * @param imageMat The image to perform the stretch on\n     * @return A stretched image mat.\n     */\n    private static Mat performPerspectiveWarp(Mat imageMat, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {\n\n        double originalImgWidth = imageMat.size().width();\n        double originalImgHeight = imageMat.size().height();\n\n        FloatPointer srcCorners = new FloatPointer(\n                x1, y1,\n                x2, y2,\n                x3, y3,\n                x4, y4);\n\n\n        FloatPointer dstCorners = new FloatPointer(\n                0, 0,\n                (int) originalImgWidth, 0,\n                (int) originalImgWidth, (int) originalImgHeight,\n                0, (int) originalImgHeight);\n\n        //create matrices with width 2 to hold the x,y values, and 4 rows, to hold the 4 different corners.\n        Mat src = new Mat(new Size(2, 4), CV_32F, srcCorners);\n        Mat dst = new Mat(new Size(2, 4), CV_32F, dstCorners);\n\n        Mat perspective = getPerspectiveTransform(src, dst);\n        Mat result = new Mat();\n        warpPerspective(imageMat, result, perspective, new Size((int) originalImgWidth, (int) originalImgHeight));\n\n        src.release();\n        dst.release();\n        srcCorners.deallocate();\n        dstCorners.deallocate();\n\n        return result;\n    }\n\n\n}\n\n"
  },
  {
    "path": "samples/PrincipalComponentAnalysis.java",
    "content": "import java.awt.image.BufferedImage;\nimport java.io.File;\nimport java.util.ArrayList;\nimport javax.imageio.ImageIO;\n\nimport org.bytedeco.javacpp.indexer.DoubleIndexer;\nimport org.bytedeco.javacpp.indexer.IntIndexer;\nimport org.bytedeco.javacpp.tools.Slf4jLogger;\nimport org.bytedeco.javacv.CanvasFrame;\nimport org.bytedeco.javacv.Java2DFrameConverter;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n * PrincipalComponentAnalysis with JavaCV\n * https://github.com/bytedeco/javacv\n * Based on \"Introduction to Principal Component Analysis (PrincipalComponentAnalysis) \":\n * http://docs.opencv.org/3.0.0/d1/dee/tutorial_introduction_to_pca.html\n *\n * @author Maurice Betzel\n */\n\npublic class PrincipalComponentAnalysis {\n\n    static {\n        System.setProperty(\"org.bytedeco.javacpp.logger\", \"slf4jlogger\");\n        System.setProperty(\"org.slf4j.simpleLogger.defaultLogLevel\", \"debug\");\n    }\n\n    private static final Slf4jLogger logger = (Slf4jLogger) org.bytedeco.javacpp.tools.Logger.create(PrincipalComponentAnalysis.class);\n\n    public static void main(String[] args) {\n        try {\n            logger.info(String.valueOf(logger.isDebugEnabled()));\n            logger.info(\"Start\");\n            new PrincipalComponentAnalysis().execute(args);\n            logger.info(\"Stop\");\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private void execute(String[] args) throws Exception {\n        // If no params provided, compute the default image\n        BufferedImage bufferedImage = args.length >= 1 ? ImageIO.read(new File(args[0])) : ImageIO.read(this.getClass().getResourceAsStream(\"shapes2.jpg\"));\n        System.out.println(\"Image type: \" + bufferedImage.getType());\n        // Convert BufferedImage to Mat and create AutoCloseable objects\n        try (Mat matrix = new OpenCVFrameConverter.ToMat().convert(new Java2DFrameConverter().convert(bufferedImage));\n             Mat mask = new Mat();\n             Mat gray = new Mat();\n             Mat denoised = new Mat();\n             Mat bin = new Mat();\n             Mat hierarchy = new Mat();\n             MatVector contours = new MatVector()) {\n\n            printMat(matrix);\n            cvtColor(matrix, gray, COLOR_BGR2GRAY);\n            //Normalize\n            GaussianBlur(gray, denoised, new Size(5, 5), 0);\n            threshold(denoised, mask, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);\n            normalize(gray, gray, 0, 255, NORM_MINMAX, -1, mask);\n            // Convert image to binary\n            threshold(gray, bin, 150, 255, THRESH_BINARY);\n            // Find contours\n            findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);\n            long contourCount = contours.size();\n            System.out.println(\"Countour count \" + contourCount);\n\n            for (int i = 0; i < contourCount; ++i) {\n                // Calculate the area of each contour\n                Mat contour = contours.get(i);\n                double area = contourArea(contour);\n                // Ignore contours that are too small or too large\n                if (area > 128 && area < 8192) {\n                    principalComponentAnalysis(contour, i, matrix);\n                }\n            }\n            CanvasFrame canvas = new CanvasFrame(\"PrincipalComponentAnalysis\", 1);\n            canvas.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n            canvas.setCanvasSize(320, 240);\n            OpenCVFrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n            canvas.showImage(converter.convert(matrix));\n        }\n    }\n\n    // contour is a one dimensional array\n    private void principalComponentAnalysis(Mat contour, int entry, Mat matrix) throws Exception {\n        PCA pca_analysis = null;\n        Mat mean = null;\n        Mat eigenVector = null;\n        Mat eigenValues = null;\n        //Construct a buffer used by the pca analysis\n        try (Mat data_pts = new Mat(contour.rows(), 2, CV_64FC1);\n             Mat placeholder = new Mat();\n             Point cntr = new Point()) {\n\n            IntIndexer contourIndexer = contour.createIndexer();\n            DoubleIndexer data_idx = data_pts.createIndexer();\n            for (int i = 0; i < contour.rows(); i++) {\n                data_idx.put(i, 0, contourIndexer.get(i, 0));\n                data_idx.put(i, 1, contourIndexer.get(i, 1));\n            }\n            contourIndexer.release();\n            data_idx.release();\n            //Perform PrincipalComponentAnalysis analysis\n            ArrayList<Point2d> eigen_vecs = new ArrayList(2);\n            ArrayList<Double> eigen_val = new ArrayList(2);\n            pca_analysis = new PCA(data_pts, placeholder, CV_PCA_DATA_AS_ROW);\n            mean = pca_analysis.mean();\n            eigenVector = pca_analysis.eigenvectors();\n            eigenValues = pca_analysis.eigenvalues();\n            DoubleIndexer mean_idx = mean.createIndexer();\n            DoubleIndexer eigenVectorIndexer = eigenVector.createIndexer();\n            DoubleIndexer eigenValuesIndexer = eigenValues.createIndexer();\n            for (int i = 0; i < 2; ++i) {\n                eigen_vecs.add(new Point2d(eigenVectorIndexer.get(i, 0), eigenVectorIndexer.get(i, 1)));\n                eigen_val.add(eigenValuesIndexer.get(0, i));\n            }\n            double cntrX = mean_idx.get(0, 0);\n            double cntrY = mean_idx.get(0, 1);\n            mean_idx.release();\n            eigenVectorIndexer.release();\n            eigenValuesIndexer.release();\n            double x1 = cntrX + 0.02 * (eigen_vecs.get(0).x() * eigen_val.get(0));\n            double y1 = cntrY + 0.02 * (eigen_vecs.get(0).y() * eigen_val.get(0));\n            double x2 = cntrX - 0.02 * (eigen_vecs.get(1).x() * eigen_val.get(1));\n            double y2 = cntrY - 0.02 * (eigen_vecs.get(1).y() * eigen_val.get(1));\n            // Draw the principal components, keep accuracy during calculations\n            cntr.x((int) Math.rint(cntrX));\n            cntr.y((int) Math.rint(cntrY));\n            circle(matrix, cntr, 5, new Scalar(255, 0, 255, 0));\n            double radian1 = Math.atan2(cntrY - y1, cntrX - x1);\n            double radian2 = Math.atan2(cntrY - y2, cntrX - x2);\n            double hypotenuse1 = Math.sqrt((cntrY - y1) * (cntrY - y1) + (cntrX - x1) * (cntrX - x1));\n            double hypotenuse2 = Math.sqrt((cntrY - y2) * (cntrY - y2) + (cntrX - x2) * (cntrX - x2));\n            //Enhance the vector signal by a factor of 2\n            double point1x = cntrX - 2 * hypotenuse1 * Math.cos(radian1);\n            double point1y = cntrY - 2 * hypotenuse1 * Math.sin(radian1);\n            double point2x = cntrX - 2 * hypotenuse2 * Math.cos(radian2);\n            double point2y = cntrY - 2 * hypotenuse2 * Math.sin(radian2);\n            drawAxis(matrix, radian1, cntr, point1x, point1y, Scalar.BLUE);\n            drawAxis(matrix, radian2, cntr, point2x, point2y, Scalar.CYAN);\n        } finally {\n            if(pca_analysis != null) {\n                pca_analysis.deallocate();\n            }\n            if(mean != null) {\n                mean.deallocate();\n            }\n            if(eigenVector != null) {\n                eigenVector.deallocate();\n            }\n            if(eigenValues != null) {\n                eigenValues.deallocate();\n            }\n        }\n    }\n\n    private void drawAxis(Mat matrix, double radian, Point cntr, double x, double y, Scalar colour) throws Exception {\n        try(Point q = new Point((int) x, (int) y);\n            Point arrowHook1 = new Point((int) (q.x() + 9 * Math.cos(radian + CV_PI / 4)), (int) (q.y() + 9 * Math.sin(radian + CV_PI / 4)));\n            Point arrowHook2 = new Point((int) (q.x() + 9 * Math.cos(radian - CV_PI / 4)), (int) (q.y() + 9 * Math.sin(radian - CV_PI / 4)))) {\n            // draw\n            line(matrix, cntr, q, colour);\n            line(matrix, arrowHook1, q, colour);\n            line(matrix, arrowHook2, q, colour);\n        }\n    }\n\n    public static void printMat(Mat mat) {\n        System.out.println(\"Channels: \" + mat.channels());\n        System.out.println(\"Rows: \" + mat.rows());\n        System.out.println(\"Cols: \" + mat.cols());\n        System.out.println(\"Type: \" + mat.type());\n        System.out.println(\"Dims: \" + mat.dims());\n        System.out.println(\"Depth: \" + mat.depth());\n    }\n\n}\n"
  },
  {
    "path": "samples/RLSA.java",
    "content": "import java.awt.image.BufferedImage;\nimport java.io.File;\nimport java.nio.ByteBuffer;\nimport javax.imageio.ImageIO;\nimport org.bytedeco.javacv.Java2DFrameConverter;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n * Based on \"Implementation Run Length Smoothing Algorithm in C++\":\n * http://stackoverflow.com/questions/21554431/implementation-run-length-smoothing-algorithm-in-c\n *\n * @author Nicholas Woodward\n */\npublic class RLSA {\n\n    public static void main(String[] args) {\n        String imagePath = args[0].trim();\n\n        IplImage image = null;\n        try {\n            Java2DFrameConverter converter1 = new Java2DFrameConverter();\n            OpenCVFrameConverter.ToIplImage converter2 = new OpenCVFrameConverter.ToIplImage();\n            BufferedImage img = ImageIO.read(new File(imagePath));\n            image = converter2.convert(converter1.convert(img));\n        } catch (Exception ex)  {\n            ex.printStackTrace();\n        }\n\n        if (image != null) {\n            IplImage rlsaImage = runLengthSmoothingAlgorithm(image);\n            // do something with the result image\n\n            image.release();\n            rlsaImage.release();\n        }\n    }\n\n    public static IplImage runLengthSmoothingAlgorithm(IplImage image) {\n        IplImage gry = image.clone();\n        cvThreshold(gry, gry, 128, 255, CV_THRESH_BINARY_INV);\n\n        CvMat tmpImg = gry.asCvMat();\n        ByteBuffer buffer = gry.getByteBuffer();\n\n        CvScalar temp = new CvScalar();\n        temp.val(255);\n\n        int hor_thres = 77; // adjust for your text size\n        int zero_count = 0;\n        int one_flag = 0;\n        for (int i = 0; i < tmpImg.rows(); i++) {\n            for (int j = 0; j < tmpImg.cols(); j++) {\n                int ind = i * gry.widthStep() + j * gry.nChannels() + 1;\n                double val = -1;\n                if (ind < buffer.capacity()) {\n                    val = (buffer.get(ind) & 0xFF);\n                }\n                if (val == 255) {\n                    if (one_flag == 255) {\n                        if (zero_count <= hor_thres) {\n                            tmpImg.put(i, j, 255);\n                            for (int n = (j-zero_count); n < j; n++) {\n                                tmpImg.put(i, n, 255);\n                            }\n                        } else {\n                            one_flag = 0;\n                        }\n                        zero_count = 0;\n                    }\n                    one_flag = 255;\n                } else if (one_flag == 255) {\n                    zero_count = zero_count + 1;\n                }\n            }\n        }\n\n        int ver_thres = 44; // adjustable\n        zero_count = 0;\n        one_flag = 0;\n        for (int i = 0; i < tmpImg.cols(); i++) {\n            for (int j = 0; j < tmpImg.rows(); j++) {\n                int ind = j * gry.widthStep() + i * gry.nChannels() + 1;\n                double val = -1;\n                if (ind < buffer.capacity()) {\n                    val = (buffer.get(ind) & 0xFF);\n                }\n                if (val == 255) {\n                    if (one_flag == 255) {\n                        if (zero_count <= ver_thres) {\n                            tmpImg.put(j, i, 255);\n                            for (int n = ((j-zero_count) >= 0) ? (j-zero_count) : 0; n < j; n++) {\n                                tmpImg.put(n, i, 255);\n                            }\n                        } else {\n                            one_flag = 0;\n                        }\n                        zero_count = 0;\n                    }\n                    one_flag = 255;\n                } else if (one_flag == 255) {\n                    zero_count = zero_count + 1;\n                }\n            }\n        }\n        return gry;\n    }\n}\n"
  },
  {
    "path": "samples/RealSense2DepthMeasuring.java",
    "content": "/*\n * Copyright (C) 2019 Florian Bruggisser\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport org.bytedeco.javacv.CanvasFrame;\nimport org.bytedeco.javacv.Frame;\nimport org.bytedeco.javacv.FrameGrabber;\nimport org.bytedeco.javacv.RealSense2FrameGrabber;\n\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\n\npublic class RealSense2DepthMeasuring {\n    public static void main(String[] args) throws FrameGrabber.Exception {\n        final RealSense2FrameGrabber rs2 = new RealSense2FrameGrabber();\n\n        // list all cameras\n        for (RealSense2FrameGrabber.RealSense2DeviceInfo info : rs2.getDeviceInfos()) {\n            System.out.printf(\"Device: %s %s %s Locked: %b\\n\",\n                    info.getName(),\n                    info.getFirmware(),\n                    info.getSerialNumber(),\n                    info.isLocked());\n        }\n\n        // enable the color & depth stream of the realsense camera\n        rs2.enableColorStream(640, 480, 30);\n        rs2.enableDepthStream(640, 480, 30);\n\n        // here are more examples of streams:\n        /*\n        rs2.enableColorStream(640, 480, 30); // color stream\n        rs2.enableIRStream(640, 480, 90); // ir stream\n        rs2.enableStream(new RealSense2FrameGrabber.RealSenseStream(\n                RS2_STREAM_INFRARED,\n                2,\n                new Size(640, 480),\n                30,\n                RS2_FORMAT_Y8\n        )); // second ir stream\n        */\n\n        // start realsense camera\n        rs2.start();\n\n        // start frame to view the stream\n        CanvasFrame canvasFrame = new CanvasFrame(\"RealSense\");\n        canvasFrame.setCanvasSize(rs2.getImageWidth(), rs2.getImageHeight());\n\n        // add mouse listener to see the depth at the clicked point\n        canvasFrame.getCanvas().addMouseListener(new MouseAdapter() {\n            @Override\n            public void mousePressed(MouseEvent e) {\n                try {\n                    System.out.println(\"Depth: \" + rs2.getDistance(e.getX(), e.getY()));\n                } catch (FrameGrabber.Exception ex) {\n                    ex.printStackTrace();\n                }\n            }\n        });\n\n        // run canvas\n        while (canvasFrame.isVisible()) {\n            // trigger camera to capture images\n            rs2.trigger();\n\n            // display images -> grab will return the first stream added\n            // use rs2.grabDepth(), rs2.grabColor() and rs2.grabIR() for the other streams\n            Frame frame = rs2.grab();\n\n            if (frame == null) {\n                System.err.println(\"Frame is null!\");\n                break;\n            }\n\n            // display frame\n            canvasFrame.showImage(frame);\n        }\n\n        // close realsense camera\n        rs2.stop();\n        rs2.release();\n        canvasFrame.dispose();\n\n    }\n}\n"
  },
  {
    "path": "samples/RecordActivity.java",
    "content": "/*\n * Copyright (C) 2012,2013 Qianliang Zhang, Shawn Van Every, Samuel Audet\n *\n * IMPORTANT - Make sure the AndroidManifest.xml file looks like this:\n *\n * <?xml version=\"1.0\" encoding=\"utf-8\"?>\n * <manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n *     package=\"org.bytedeco.javacv.recordactivity\"\n *     android:versionCode=\"1\"\n *     android:versionName=\"1.0\" >\n *     <uses-sdk android:minSdkVersion=\"4\" />\n *     <uses-permission android:name=\"android.permission.CAMERA\" />\n *     <uses-permission android:name=\"android.permission.INTERNET\"/>\n *     <uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>\n *     <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n *     <uses-feature android:name=\"android.hardware.camera\" />\n *     <application android:label=\"@string/app_name\">\n *         <activity\n *             android:name=\"RecordActivity\"\n *             android:label=\"@string/app_name\"\n *             android:screenOrientation=\"landscape\">\n *             <intent-filter>\n *                 <action android:name=\"android.intent.action.MAIN\" />\n *                 <category android:name=\"android.intent.category.LAUNCHER\" />\n *             </intent-filter>\n *         </activity>\n *     </application>\n * </manifest>\n *\n * And the res/layout/main.xml file like this:\n *\n * <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n *     xmlns:tools=\"http://schemas.android.com/tools\"\n *     android:id=\"@+id/record_layout\"\n *     android:layout_width=\"match_parent\"\n *     android:layout_height=\"match_parent\"\n *     android:keepScreenOn=\"true\">\n * \n *     <TextView\n *         android:id=\"@+id/textView1\"\n *         android:layout_width=\"wrap_content\"\n *         android:layout_height=\"wrap_content\"\n *         android:layout_centerHorizontal=\"true\"\n *         android:layout_centerVertical=\"true\"\n *         android:padding=\"8dp\"\n *         android:text=\"@string/app_name\"\n *         tools:context=\".RecordActivity\" />\n *\n *     <Button\n *         android:id=\"@+id/recorder_control\"\n *         android:layout_width=\"wrap_content\"\n *         android:layout_height=\"wrap_content\"\n *         android:layout_above=\"@+id/textView1\"\n *         android:layout_alignRight=\"@+id/textView1\"\n *         android:layout_marginRight=\"70dp\"\n *         android:text=\"Button\" />\n *\n * </LinearLayout>\n */\n\npackage org.bytedeco.javacv.recordactivity;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.pm.ActivityInfo;\nimport android.hardware.Camera;\nimport android.hardware.Camera.PreviewCallback;\nimport android.media.AudioFormat;\nimport android.media.AudioRecord;\nimport android.media.MediaRecorder;\nimport android.os.Bundle;\nimport android.os.PowerManager;\nimport android.util.Log;\nimport android.view.Display;\nimport android.view.KeyEvent;\nimport android.view.LayoutInflater;\nimport android.view.SurfaceHolder;\nimport android.view.SurfaceView;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.WindowManager;\nimport android.widget.Button;\nimport android.widget.LinearLayout;\nimport android.widget.RelativeLayout;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.ShortBuffer;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport org.bytedeco.ffmpeg.global.avutil;\nimport org.bytedeco.javacv.FFmpegFrameFilter;\nimport org.bytedeco.javacv.FFmpegFrameRecorder;\nimport org.bytedeco.javacv.Frame;\nimport org.bytedeco.javacv.FrameFilter;\n\npublic class RecordActivity extends Activity implements OnClickListener {\n\n    private final static String CLASS_LABEL = \"RecordActivity\";\n    private final static String LOG_TAG = CLASS_LABEL;\n\n    private String ffmpeg_link = \"/mnt/sdcard/stream.flv\";\n\n    long startTime = 0;\n    boolean recording = false;\n\n    private FFmpegFrameRecorder recorder;\n\n    private boolean isPreviewOn = false;\n\n    /*Filter information, change boolean to true if adding a fitler*/\n    private boolean addFilter = true;\n    private String filterString = \"\";\n    FFmpegFrameFilter filter;\n\n    private int sampleAudioRateInHz = 44100;\n    private int imageWidth = 320;\n    private int imageHeight = 240;\n    private int frameRate = 30;\n\n    /* audio data getting thread */\n    private AudioRecord audioRecord;\n    private AudioRecordRunnable audioRecordRunnable;\n    private Thread audioThread;\n    volatile boolean runAudioThread = true;\n\n    /* video data getting thread */\n    private Camera cameraDevice;\n    private CameraView cameraView;\n\n    private Frame yuvImage = null;\n\n    /* layout setting */\n    private final int bg_screen_bx = 232;\n    private final int bg_screen_by = 128;\n    private final int bg_screen_width = 700;\n    private final int bg_screen_height = 500;\n    private final int bg_width = 1123;\n    private final int bg_height = 715;\n    private final int live_width = 640;\n    private final int live_height = 480;\n    private int screenWidth, screenHeight;\n    private Button btnRecorderControl;\n\n    /* The number of seconds in the continuous record loop (or 0 to disable loop). */\n    final int RECORD_LENGTH = 0;\n    Frame[] images;\n    long[] timestamps;\n    ShortBuffer[] samples;\n    int imagesIndex, samplesIndex;\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n\n        setContentView(R.layout.main);\n\n        initLayout();\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n\n        recording = false;\n\n        if (cameraView != null) {\n            cameraView.stopPreview();\n        }\n\n        if(cameraDevice != null) {\n            cameraDevice.stopPreview();\n            cameraDevice.release();\n            cameraDevice = null;\n        }\n    }\n\n\n    private void initLayout() {\n\n        /* get size of screen */\n        Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();\n        screenWidth = display.getWidth();\n        screenHeight = display.getHeight();\n        RelativeLayout.LayoutParams layoutParam = null;\n        LayoutInflater myInflate = null;\n        myInflate = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n        RelativeLayout topLayout = new RelativeLayout(this);\n        setContentView(topLayout);\n        LinearLayout preViewLayout = (LinearLayout) myInflate.inflate(R.layout.main, null);\n        layoutParam = new RelativeLayout.LayoutParams(screenWidth, screenHeight);\n        topLayout.addView(preViewLayout, layoutParam);\n\n        /* add control button: start and stop */\n        btnRecorderControl = (Button) findViewById(R.id.recorder_control);\n        btnRecorderControl.setText(\"Start\");\n        btnRecorderControl.setOnClickListener(this);\n\n        /* add camera view */\n        int display_width_d = (int) (1.0 * bg_screen_width * screenWidth / bg_width);\n        int display_height_d = (int) (1.0 * bg_screen_height * screenHeight / bg_height);\n        int prev_rw, prev_rh;\n        if (1.0 * display_width_d / display_height_d > 1.0 * live_width / live_height) {\n            prev_rh = display_height_d;\n            prev_rw = (int) (1.0 * display_height_d * live_width / live_height);\n        } else {\n            prev_rw = display_width_d;\n            prev_rh = (int) (1.0 * display_width_d * live_height / live_width);\n        }\n        layoutParam = new RelativeLayout.LayoutParams(prev_rw, prev_rh);\n        layoutParam.topMargin = (int) (1.0 * bg_screen_by * screenHeight / bg_height);\n        layoutParam.leftMargin = (int) (1.0 * bg_screen_bx * screenWidth / bg_width);\n\n        cameraDevice = Camera.open();\n        Log.i(LOG_TAG, \"cameara open\");\n        cameraView = new CameraView(this, cameraDevice);\n        topLayout.addView(cameraView, layoutParam);\n        Log.i(LOG_TAG, \"cameara preview start: OK\");\n    }\n\n    //---------------------------------------\n    // initialize ffmpeg_recorder\n    //---------------------------------------\n    private void initRecorder() {\n\n        Log.w(LOG_TAG,\"init recorder\");\n\n        Log.i(LOG_TAG, \"ffmpeg_url: \" + ffmpeg_link);\n        recorder = new FFmpegFrameRecorder(ffmpeg_link, imageWidth, imageHeight, 1);\n        recorder.setFormat(\"flv\");\n        recorder.setSampleRate(sampleAudioRateInHz);\n        // Set in the surface changed method\n        recorder.setFrameRate(frameRate);\n\n        // The filterString  is any ffmpeg filter.\n        // Here is the link for a list: https://ffmpeg.org/ffmpeg-filters.html\n        filterString = \"transpose=2,crop=w=200:h=200:x=0:y=0\";\n        filter = new FFmpegFrameFilter(filterString, imageWidth, imageHeight);\n\n        //default format on android\n        filter.setPixelFormat(avutil.AV_PIX_FMT_NV21);\n\n        if (RECORD_LENGTH > 0) {\n            imagesIndex = 0;\n            images = new Frame[RECORD_LENGTH * frameRate];\n            timestamps = new long[images.length];\n            for (int i = 0; i < images.length; i++) {\n                images[i] = new Frame(imageWidth, imageHeight, Frame.DEPTH_UBYTE, 2);\n                timestamps[i] = -1;\n            }\n        } else if (yuvImage == null) {\n            yuvImage = new Frame(imageWidth, imageHeight, Frame.DEPTH_UBYTE, 2);\n            Log.i(LOG_TAG, \"create yuvImage\");\n        }\n\n        Log.i(LOG_TAG, \"recorder initialize success\");\n\n        audioRecordRunnable = new AudioRecordRunnable();\n        audioThread = new Thread(audioRecordRunnable);\n        runAudioThread = true;\n    }\n\n    public void startRecording() {\n\n        initRecorder();\n\n        try {\n            recorder.start();\n            startTime = System.currentTimeMillis();\n            recording = true;\n            audioThread.start();\n\n            if(addFilter) {\n                filter.start();\n            }\n\n        } catch (FFmpegFrameRecorder.Exception | FrameFilter.Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void stopRecording() {\n\n        runAudioThread = false;\n        try {\n            audioThread.join();\n        } catch (InterruptedException e) {\n            // reset interrupt to be nice\n            Thread.currentThread().interrupt();\n            return;\n        }\n        audioRecordRunnable = null;\n        audioThread = null;\n\n        if (recorder != null && recording) {\n            if (RECORD_LENGTH > 0) {\n                Log.v(LOG_TAG,\"Writing frames\");\n                try {\n                    int firstIndex = imagesIndex % samples.length;\n                    int lastIndex = (imagesIndex - 1) % images.length;\n                    if (imagesIndex <= images.length) {\n                        firstIndex = 0;\n                        lastIndex = imagesIndex - 1;\n                    }\n                    if ((startTime = timestamps[lastIndex] - RECORD_LENGTH * 1000000L) < 0) {\n                        startTime = 0;\n                    }\n                    if (lastIndex < firstIndex) {\n                        lastIndex += images.length;\n                    }\n                    for (int i = firstIndex; i <= lastIndex; i++) {\n                        long t = timestamps[i % timestamps.length] - startTime;\n                        if (t >= 0) {\n                            if (t > recorder.getTimestamp()) {\n                                recorder.setTimestamp(t);\n                            }\n                            recorder.record(images[i % images.length]);\n                        }\n                    }\n\n                    firstIndex = samplesIndex % samples.length;\n                    lastIndex = (samplesIndex - 1) % samples.length;\n                    if (samplesIndex <= samples.length) {\n                        firstIndex = 0;\n                        lastIndex = samplesIndex - 1;\n                    }\n                    if (lastIndex < firstIndex) {\n                        lastIndex += samples.length;\n                    }\n                    for (int i = firstIndex; i <= lastIndex; i++) {\n                        recorder.recordSamples(samples[i % samples.length]);\n                    }\n                } catch (FFmpegFrameRecorder.Exception e) {\n                    Log.v(LOG_TAG,e.getMessage());\n                    e.printStackTrace();\n                }\n            }\n\n            recording = false;\n            Log.v(LOG_TAG,\"Finishing recording, calling stop and release on recorder\");\n            try {\n                recorder.stop();\n                recorder.release();\n                filter.stop();\n                filter.release();\n            } catch (FFmpegFrameRecorder.Exception | FrameFilter.Exception e) {\n                e.printStackTrace();\n            }\n            recorder = null;\n\n        }\n    }\n\n    @Override\n    public boolean onKeyDown(int keyCode, KeyEvent event) {\n\n        if (keyCode == KeyEvent.KEYCODE_BACK) {\n            if (recording) {\n                stopRecording();\n            }\n\n            finish();\n\n            return true;\n        }\n\n        return super.onKeyDown(keyCode, event);\n    }\n\n\n    //---------------------------------------------\n    // audio thread, gets and encodes audio data\n    //---------------------------------------------\n    class AudioRecordRunnable implements Runnable {\n\n        @Override\n        public void run() {\n            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);\n\n            // Audio\n            int bufferSize;\n            ShortBuffer audioData;\n            int bufferReadResult;\n\n            bufferSize = AudioRecord.getMinBufferSize(sampleAudioRateInHz,\n                    AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);\n            audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleAudioRateInHz,\n                    AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);\n\n            if (RECORD_LENGTH > 0) {\n                samplesIndex = 0;\n                samples = new ShortBuffer[RECORD_LENGTH * sampleAudioRateInHz * 2 / bufferSize + 1];\n                for (int i = 0; i < samples.length; i++) {\n                    samples[i] = ShortBuffer.allocate(bufferSize);\n                }\n            } else {\n                audioData = ShortBuffer.allocate(bufferSize);\n            }\n\n            Log.d(LOG_TAG, \"audioRecord.startRecording()\");\n            audioRecord.startRecording();\n\n            /* ffmpeg_audio encoding loop */\n            while (runAudioThread) {\n                if (RECORD_LENGTH > 0) {\n                    audioData = samples[samplesIndex++ % samples.length];\n                    audioData.position(0).limit(0);\n                }\n                //Log.v(LOG_TAG,\"recording? \" + recording);\n                bufferReadResult = audioRecord.read(audioData.array(), 0, audioData.capacity());\n                audioData.limit(bufferReadResult);\n                if (bufferReadResult > 0) {\n                    Log.v(LOG_TAG,\"bufferReadResult: \" + bufferReadResult);\n                    // If \"recording\" isn't true when start this thread, it never get's set according to this if statement...!!!\n                    // Why?  Good question...\n                    if (recording) {\n                        if (RECORD_LENGTH <= 0) try {\n                            recorder.recordSamples(audioData);\n                            //Log.v(LOG_TAG,\"recording \" + 1024*i + \" to \" + 1024*i+1024);\n                        } catch (FFmpegFrameRecorder.Exception e) {\n                            Log.v(LOG_TAG,e.getMessage());\n                            e.printStackTrace();\n                        }\n                    }\n                }\n            }\n            Log.v(LOG_TAG,\"AudioThread Finished, release audioRecord\");\n\n            /* encoding finish, release recorder */\n            if (audioRecord != null) {\n                audioRecord.stop();\n                audioRecord.release();\n                audioRecord = null;\n                Log.v(LOG_TAG,\"audioRecord released\");\n            }\n        }\n    }\n\n    //---------------------------------------------\n    // camera thread, gets and encodes video data\n    //---------------------------------------------\n    class CameraView extends SurfaceView implements SurfaceHolder.Callback, PreviewCallback {\n\n        private SurfaceHolder mHolder;\n        private Camera mCamera;\n\n        public CameraView(Context context, Camera camera) {\n            super(context);\n            Log.w(\"camera\",\"camera view\");\n            mCamera = camera;\n            mHolder = getHolder();\n            mHolder.addCallback(CameraView.this);\n            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);\n            mCamera.setPreviewCallback(CameraView.this);\n        }\n\n        @Override\n        public void surfaceCreated(SurfaceHolder holder) {\n            try {\n                stopPreview();\n                mCamera.setPreviewDisplay(holder);\n            } catch (IOException exception) {\n                mCamera.release();\n                mCamera = null;\n            }\n        }\n\n        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {\n            stopPreview();\n\n            Camera.Parameters camParams = mCamera.getParameters();\n            List<Camera.Size> sizes = camParams.getSupportedPreviewSizes();\n            // Sort the list in ascending order\n            Collections.sort(sizes, new Comparator<Camera.Size>() {\n\n                public int compare(final Camera.Size a, final Camera.Size b) {\n                    return a.width * a.height - b.width * b.height;\n                }\n            });\n\n            // Pick the first preview size that is equal or bigger, or pick the last (biggest) option if we cannot\n            // reach the initial settings of imageWidth/imageHeight.\n            for (int i = 0; i < sizes.size(); i++) {\n                if ((sizes.get(i).width >= imageWidth && sizes.get(i).height >= imageHeight) || i == sizes.size() - 1) {\n                    imageWidth = sizes.get(i).width;\n                    imageHeight = sizes.get(i).height;\n                    Log.v(LOG_TAG, \"Changed to supported resolution: \" + imageWidth + \"x\" + imageHeight);\n                    break;\n                }\n            }\n            camParams.setPreviewSize(imageWidth, imageHeight);\n\n            Log.v(LOG_TAG,\"Setting imageWidth: \" + imageWidth + \" imageHeight: \" + imageHeight + \" frameRate: \" + frameRate);\n\n            camParams.setPreviewFrameRate(frameRate);\n            Log.v(LOG_TAG,\"Preview Framerate: \" + camParams.getPreviewFrameRate());\n\n            mCamera.setParameters(camParams);\n\n            // Set the holder (which might have changed) again\n            try {\n                mCamera.setPreviewDisplay(holder);\n                mCamera.setPreviewCallback(CameraView.this);\n                startPreview();\n            } catch (Exception e) {\n                Log.e(LOG_TAG, \"Could not set preview display in surfaceChanged\");\n            }\n        }\n\n        @Override\n        public void surfaceDestroyed(SurfaceHolder holder) {\n            try {\n                mHolder.addCallback(null);\n                mCamera.setPreviewCallback(null);\n            } catch (RuntimeException e) {\n                // The camera has probably just been released, ignore.\n            }\n        }\n\n        public void startPreview() {\n            if (!isPreviewOn && mCamera != null) {\n                isPreviewOn = true;\n                mCamera.startPreview();\n            }\n        }\n\n        public void stopPreview() {\n            if (isPreviewOn && mCamera != null) {\n                isPreviewOn = false;\n                mCamera.stopPreview();\n            }\n        }\n\n        @Override\n        public void onPreviewFrame(byte[] data, Camera camera) {\n            if (audioRecord == null || audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {\n                startTime = System.currentTimeMillis();\n                return;\n            }\n            if (RECORD_LENGTH > 0) {\n                int i = imagesIndex++ % images.length;\n                yuvImage = images[i];\n                timestamps[i] = 1000 * (System.currentTimeMillis() - startTime);\n            }\n\n\n            /* get video data */\n            if (yuvImage != null && recording) {\n                ((ByteBuffer)yuvImage.image[0].position(0)).put(data);\n\n                if (RECORD_LENGTH <= 0) try {\n                    Log.v(LOG_TAG,\"Writing Frame\");\n                    long t = 1000 * (System.currentTimeMillis() - startTime);\n                    if (t > recorder.getTimestamp()) {\n                        recorder.setTimestamp(t);\n                    }\n\n                    if(addFilter) {\n                        filter.push(yuvImage);\n                        Frame frame2;\n                        while ((frame2 = filter.pull()) != null) {\n                            recorder.record(frame2, filter.getPixelFormat());\n                        }\n                    } else {\n                        recorder.record(yuvImage);\n                    }\n                } catch (FFmpegFrameRecorder.Exception | FrameFilter.Exception e) {\n                    Log.v(LOG_TAG,e.getMessage());\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    @Override\n    public void onClick(View v) {\n        if (!recording) {\n            startRecording();\n            Log.w(LOG_TAG, \"Start Button Pushed\");\n            btnRecorderControl.setText(\"Stop\");\n        } else {\n            // This will trigger the audio recording loop to stop and then set isRecorderStart = false;\n            stopRecording();\n            Log.w(LOG_TAG, \"Stop Button Pushed\");\n            btnRecorderControl.setText(\"Start\");\n        }\n    }\n}\n"
  },
  {
    "path": "samples/Similarity.java",
    "content": "/**\n * Copyright 2021 JavaCV\n * \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport org.bytedeco.opencv.global.opencv_core;\nimport org.bytedeco.opencv.global.opencv_imgcodecs;\nimport org.bytedeco.opencv.global.opencv_imgproc;\nimport org.bytedeco.opencv.opencv_core.Mat;\nimport org.bytedeco.opencv.opencv_core.Scalar;\nimport org.bytedeco.opencv.opencv_core.Size;\n\n/**\n * OpenCV similarity measurement examples:\n * https://docs.opencv.org/master/d5/dc4/tutorial_video_input_psnr_ssim.html\n *\n * @author n-kai-cj\n */\npublic class Similarity {\n    private static double getPSNR(Mat I1, Mat I2) {\n        Mat s1 = new Mat();\n        opencv_core.absdiff(I1, I2, s1);             // |I1 - I2|\n        s1.convertTo(s1, opencv_core.CV_32F);        // cannot make a square on 8 bits\n        s1 = s1.mul(s1).asMat();                     // |I1 - I2|^2\n\n        Scalar s = opencv_core.sumElems(s1);         // sum elements per channel\n\n        double sse = s.get(0) + s.get(1) + s.get(2); // sum channels\n\n        if (sse <= 1e-10) {                          // for small values return zero\n            return 0;\n        } else {\n            double mse = sse / (double) (I1.channels() * I1.total());\n            double psnr = 10.0 * Math.log10((255 * 255) / mse);\n            return psnr;\n        }\n    }\n\n    private static Scalar getMSSIM(Mat i1, Mat i2) {\n        double C1 = 6.5025, C2 = 58.5225;\n        /***************************** INITS **********************************/\n        int d = opencv_core.CV_32F;\n        Mat I1 = new Mat();\n        Mat I2 = new Mat();\n        i1.convertTo(I1, d);                  // cannot calculate on one byte large values\n        i2.convertTo(I2, d);\n        Mat I2_2 = I2.mul(I2).asMat();        // I2^2\n        Mat I1_2 = I1.mul(I1).asMat();        // I1^2\n        Mat I1_I2 = I1.mul(I2).asMat();       // I1 * I2\n        /*************************** END INITS **********************************/\n        // PRELIMINARY COMPUTING\n        Mat mu1 = new Mat();\n        Mat mu2 = new Mat();\n        opencv_imgproc.GaussianBlur(I1, mu1, new Size(11, 11), 1.5);\n        opencv_imgproc.GaussianBlur(I2, mu2, new Size(11, 11), 1.5);\n        Mat mu1_2 = mu1.mul(mu1).asMat();\n        Mat mu2_2 = mu2.mul(mu2).asMat();\n        Mat mu1_mu2 = mu1.mul(mu2).asMat();\n        Mat sigma1_2 = new Mat();\n        Mat sigma2_2 = new Mat();\n        Mat sigma12 = new Mat();\n        opencv_imgproc.GaussianBlur(I1_2, sigma1_2, new Size(11, 11), 1.5);\n        sigma1_2 = opencv_core.subtract(sigma1_2, mu1_2).asMat();\n        opencv_imgproc.GaussianBlur(I2_2, sigma2_2, new Size(11, 11), 1.5);\n        sigma2_2 = opencv_core.subtract(sigma2_2, mu2_2).asMat();\n        opencv_imgproc.GaussianBlur(I1_I2, sigma12, new Size(11, 11), 1.5);\n        sigma12 = opencv_core.subtract(sigma12, mu1_mu2).asMat();\n        Mat t1, t2, t3;\n        t1 = opencv_core.add(opencv_core.multiply(2, mu1_mu2), Scalar.all(C1)).asMat();\n        t2 = opencv_core.add(opencv_core.multiply(2, sigma12), Scalar.all(C2)).asMat();\n        t3 = t1.mul(t2).asMat();                     // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))\n        t1 = opencv_core.add(opencv_core.add(mu1_2, mu2_2), Scalar.all(C1)).asMat();\n        t2 = opencv_core.add(opencv_core.add(sigma1_2, sigma2_2), Scalar.all(C2)).asMat();\n        t1 = t1.mul(t2).asMat();                     // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))\n        Mat ssim_map = new Mat();\n        opencv_core.divide(t3, t1, ssim_map);        // ssim_map =  t3./t1;\n        Scalar mssim = opencv_core.mean(ssim_map);   // mssim = average of ssim map\n        return mssim;\n    }\n\n    public static void main(String[] args) {\n        Mat img1 = opencv_imgcodecs.imread(\"face.jpg\");\n        Mat img2 = img1.clone();\n        opencv_imgproc.GaussianBlur(img2, img2, new Size(15, 15), 10);\n        double psnr = getPSNR(img1, img2);\n        Scalar mssim = getMSSIM(img1, img2);\n        System.out.println(\"PSNR: \" + psnr);\n        System.out.printf(\"SSIM: %f, %f, %f\\n\", mssim.get(0), mssim.get(1), mssim.get(2));\n    }\n}\n"
  },
  {
    "path": "samples/Smoother.java",
    "content": "/*\n * Copyright (C) 2009-2018 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\n\npublic class Smoother {\n    public static void smooth(String filename) {\n        Mat image = imread(filename);\n        if (image != null) {\n            GaussianBlur(image, image, new Size(3, 3), 0);\n            imwrite(filename, image);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/Square.java",
    "content": "import java.awt.event.KeyEvent;\n\nimport org.bytedeco.javacpp.*;\nimport org.bytedeco.javacv.*;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\n\n/**\n * I was unable to find the OpenCV squares.c translated into JavaCV, so this\n * is a line-by-line translation of the C source.\n * The squares.c source used, found here:\n *      https://code.ros.org/trac/opencv/browser/trunk/opencv/samples/c/squares.c?rev=1429\n *\n * This is a demo class for finding squares/rectangles in an image, using JavaCV.\n *\n * All individual imports are kept as is; if you are like me,\n * you probably dislike the catch all .* import when trying to understand stuff.\n *\n * The major headache of the C code was figuring out the\n * \"drawLines\" method, and what parameters \"cvPolyLine\" is supposed to use.\n *\n * @author geir.ruud@digitalinferno.com\n */\npublic class Square {\n\n    int thresh = 50;\n    IplImage img = null;\n    IplImage img0 = null;\n    CvMemStorage storage = null;\n    String wndname = \"Square Detection Demo\";\n\n    // Java spesific\n    CanvasFrame canvas = null;\n    OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();\n\n    // helper function:\n    // finds a cosine of angle between vectors\n    // from pt0->pt1 and from pt0->pt2\n    double angle(CvPoint pt1, CvPoint pt2, CvPoint pt0) {\n        double dx1 = pt1.x() - pt0.x();\n        double dy1 = pt1.y() - pt0.y();\n        double dx2 = pt2.x() - pt0.x();\n        double dy2 = pt2.y() - pt0.y();\n\n        return (dx1*dx2 + dy1*dy2) / Math.sqrt((dx1*dx1 + dy1*dy1) * (dx2*dx2 + dy2*dy2) + 1e-10);\n    }\n\n    // returns sequence of squares detected on the image.\n    // the sequence is stored in the specified memory storage\n    CvSeq findSquares4(IplImage img, CvMemStorage storage) {\n        // Java translation: moved into loop\n        // CvSeq contours = new CvSeq();\n        int i, c, l, N = 11;\n        CvSize sz = cvSize(img.width() & -2, img.height() & -2);\n        IplImage timg = cvCloneImage(img); // make a copy of input image\n        IplImage gray = cvCreateImage(sz, 8, 1);\n        IplImage pyr = cvCreateImage(cvSize(sz.width()/2, sz.height()/2), 8, 3);\n        IplImage tgray = null;\n        // Java translation: moved into loop\n        // CvSeq result = null;\n        // double s = 0.0, t = 0.0;\n\n        // create empty sequence that will contain points -\n        // 4 points per square (the square's vertices)\n        CvSeq squares = cvCreateSeq(0, Loader.sizeof(CvSeq.class), Loader.sizeof(CvPoint.class), storage);\n\n        // select the maximum ROI in the image\n        // with the width and height divisible by 2\n        cvSetImageROI(timg, cvRect(0, 0, sz.width(), sz.height()));\n\n        // down-scale and upscale the image to filter out the noise\n        cvPyrDown(timg, pyr, 7);\n        cvPyrUp(pyr, timg, 7);\n        tgray = cvCreateImage(sz, 8, 1);\n\n        // find squares in every color plane of the image\n        for (c = 0; c < 3; c++) {\n            // extract the c-th color plane\n            cvSetImageCOI(timg, c+1);\n            cvCopy(timg, tgray);\n\n            // try several threshold levels\n            for (l = 0; l < N; l++) {\n                // hack: use Canny instead of zero threshold level.\n                // Canny helps to catch squares with gradient shading\n                if (l == 0) {\n                    // apply Canny. Take the upper threshold from slider\n                    // and set the lower to 0 (which forces edges merging)\n                    cvCanny(tgray, gray, 0, thresh, 5);\n                    // dilate canny output to remove potential\n                    // holes between edge segments\n                    cvDilate(gray, gray, null, 1);\n                } else {\n                    // apply threshold if l!=0:\n                    //     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0\n                    cvThreshold(tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY);\n                }\n\n                // find contours and store them all as a list\n                // Java translation: moved into the loop\n                CvSeq contours = new CvSeq();\n                cvFindContours(gray, storage, contours, Loader.sizeof(CvContour.class), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));\n\n                // test each contour\n                while (contours != null && !contours.isNull()) {\n                    // approximate contour with accuracy proportional\n                    // to the contour perimeter\n                    // Java translation: moved into the loop\n                    CvSeq result = cvApproxPoly(contours, Loader.sizeof(CvContour.class), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);\n                    // square contours should have 4 vertices after approximation\n                    // relatively large area (to filter out noisy contours)\n                    // and be convex.\n                    // Note: absolute value of an area is used because\n                    // area may be positive or negative - in accordance with the\n                    // contour orientation\n                    if(result.total() == 4 && Math.abs(cvContourArea(result, CV_WHOLE_SEQ, 0)) > 1000 && cvCheckContourConvexity(result) != 0) {\n\n                        // Java translation: moved into loop\n                        double s = 0.0, t = 0.0;\n\n                        for( i = 0; i < 5; i++ ) {\n                            // find minimum angle between joint\n                            // edges (maximum of cosine)\n                            if( i >= 2 ) {\n                                //      Java translation:\n                                //          Comment from the HoughLines.java sample code:\n                                //          \"    Based on JavaCPP, the equivalent of the C code:\n                                //                  CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);\n                                //                  CvPoint first=line[0];\n                                //                  CvPoint second=line[1];\n                                //          is:\n                                //                  Pointer line = cvGetSeqElem(lines, i);\n                                //                  CvPoint first = new CvPoint(line).position(0);\n                                //                  CvPoint second = new CvPoint(line).position(1);\n                                //          \"\n                                //          ... so after some trial and error this seem to work\n//                                t = fabs(angle(\n//                                        (CvPoint*)cvGetSeqElem( result, i ),\n//                                        (CvPoint*)cvGetSeqElem( result, i-2 ),\n//                                        (CvPoint*)cvGetSeqElem( result, i-1 )));\n                                t = Math.abs(angle(new CvPoint(cvGetSeqElem(result, i)),\n                                        new CvPoint(cvGetSeqElem(result, i-2)),\n                                        new CvPoint(cvGetSeqElem(result, i-1))));\n                                s = s > t ? s : t;\n                            }\n                        }\n\n                        // if cosines of all angles are small\n                        // (all angles are ~90 degree) then write quandrange\n                        // vertices to resultant sequence\n                        if (s < 0.3)\n                            for( i = 0; i < 4; i++ ) {\n                                cvSeqPush(squares, cvGetSeqElem(result, i));\n                            }\n                    }\n\n                    // take the next contour\n                    contours = contours.h_next();\n                }\n            }\n        }\n\n        // release all the temporary images\n        cvReleaseImage(gray);\n        cvReleaseImage(pyr);\n        cvReleaseImage(tgray);\n        cvReleaseImage(timg);\n\n        return squares;\n    }\n\n    // the function draws all the squares in the image\n    void drawSquares(IplImage img, CvSeq squares) {\n\n        //      Java translation: Here the code is somewhat different from the C version.\n        //      I was unable to get straight forward CvPoint[] arrays\n        //      working with \"reader\" and the \"CV_READ_SEQ_ELEM\".\n\n//        CvSeqReader reader = new CvSeqReader();\n\n        IplImage cpy = cvCloneImage(img);\n        int i = 0;\n\n        // Used by attempt 3\n        // Create a \"super\"-slice, consisting of the entire sequence of squares\n        CvSlice slice = new CvSlice(squares);\n\n        // initialize reader of the sequence\n//        cvStartReadSeq(squares, reader, 0);\n\n         // read 4 sequence elements at a time (all vertices of a square)\n         for(i = 0; i < squares.total(); i += 4) {\n\n//              // Attempt 1:\n//              // This does not work, uses the \"reader\"\n//              CvPoint pt[] = new CvPoint[]{new CvPoint(1), new CvPoint(1), new CvPoint(1), new CvPoint(1)};\n//              PointerPointer rect = new PointerPointer(pt);\n//              int count[] = new int[]{4};\n//\n//              CV_READ_SEQ_ELEM(pt[0], reader);\n//              CV_READ_SEQ_ELEM(pt[1], reader);\n//              CV_READ_SEQ_ELEM(pt[2], reader);\n//              CV_READ_SEQ_ELEM(pt[3], reader);\n\n//              // Attempt 2:\n//              // This works, somewhat similar to the C code, somewhat messy, does not use the \"reader\"\n//              CvPoint pt[] = new CvPoint[]{\n//                      new CvPoint(cvGetSeqElem(squares, i)),\n//                      new CvPoint(cvGetSeqElem(squares, i + 1)),\n//                      new CvPoint(cvGetSeqElem(squares, i + 2)),\n//                      new CvPoint(cvGetSeqElem(squares, i + 3))};\n//              PointerPointer rect = new PointerPointer(pt);\n//              int count[] = new int[]{4};\n\n              // Attempt 3:\n              // This works, may be the \"cleanest\" solution, does not use the \"reader\"\n             CvPoint rect = new CvPoint(4);\n             IntPointer count = new IntPointer(1).put(4);\n             // get the 4 corner slice from the \"super\"-slice\n             cvCvtSeqToArray(squares, rect, slice.start_index(i).end_index(i + 4));\n\n//             // Attempt 4:\n//             // This works, and look the most like the original C code, uses the \"reader\"\n//             CvPoint rect = new CvPoint(4);\n//             int count[] = new int[]{4};\n//\n//             // read 4 vertices\n//             CV_READ_SEQ_ELEM(rect.position(0), reader);\n//             CV_READ_SEQ_ELEM(rect.position(1), reader);\n//             CV_READ_SEQ_ELEM(rect.position(2), reader);\n//             CV_READ_SEQ_ELEM(rect.position(3), reader);\n\n             // draw the square as a closed polyline\n             // Java translation: gotcha (re-)setting the opening \"position\" of the CvPoint sequence thing\n             cvPolyLine(cpy, rect.position(0), count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0);\n         }\n\n        // show the resultant image\n        // cvShowImage(wndname, cpy);\n        canvas.showImage(converter.convert(cpy));\n        cvReleaseImage(cpy);\n    }\n\n    String names[] = new String[]{ \"pic1.png\", \"pic2.png\", \"pic3.png\",\n                      \"pic4.png\", \"pic5.png\", \"pic6.png\" };\n\n    public static void main(String args[]) throws Exception {\n        new Square().main();\n    }\n\n    public void main() throws InterruptedException {\n        // Java translation: c not used\n        int i; // , c;\n        // create memory storage that will contain all the dynamic data\n        storage = cvCreateMemStorage(0);\n\n        for(i = 0; i < names.length; i++) {\n            // load i-th image\n\n            // Java translation\n            String filePathAndName = Square.class.getClassLoader().getResource(names[i]).getPath();\n            filePathAndName = filePathAndName == null || filePathAndName.isEmpty() ? names[i] : filePathAndName;\n            // img0 = cvLoadImage(names[i], 1);\n            img0 = cvLoadImage(filePathAndName, 1);\n            if (img0 == null) {\n                System.err.println(\"Couldn't load \" + names[i]);\n                continue;\n            }\n            img = cvCloneImage(img0);\n\n            // create window and a trackbar (slider) with parent \"image\" and set callback\n            // (the slider regulates upper threshold, passed to Canny edge detector)\n            // Java translation\n            canvas = new CanvasFrame(wndname, 1);\n            canvas.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);\n            // cvNamedWindow( wndname, 1 );\n\n            // find and draw the squares\n            drawSquares(img, findSquares4(img, storage));\n\n            // wait for key.\n            // Also the function cvWaitKey takes care of event processing\n            // Java translation\n            KeyEvent key = canvas.waitKey(0);\n            // c = cvWaitKey(0);\n\n            // release both images\n            cvReleaseImage(img);\n            cvReleaseImage(img0);\n            // clear memory storage - reset free space position\n            cvClearMemStorage(storage);\n\n            if (key.getKeyCode() == 27) {\n                break;\n            }\n        }\n        // cvDestroyWindow( wndname );\n        if (canvas != null) {\n            canvas.dispose();\n        }\n    }\n\n}\n"
  },
  {
    "path": "samples/TemplateMatching.java",
    "content": "import java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ThreadLocalRandom;\n\nimport org.bytedeco.javacv.*;\nimport org.bytedeco.javacpp.*;\nimport org.bytedeco.javacpp.indexer.FloatIndexer;\n\nimport org.bytedeco.opencv.opencv_calib3d.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_highgui.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_objdetect.*;\nimport static org.bytedeco.opencv.global.opencv_calib3d.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_highgui.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_objdetect.*;\n\n/**\n * Example of template javacv (opencv) template matching using the last java build\n *\n * We need 2 default parameters like this (source image, image to find )\n * \"C:\\Users\\Waldema\\Desktop\\bg.jpg\" \"C:\\Users\\Waldema\\Desktop\\imageToFind.jpg\" \n *\n * @author Waldemar Neto\n */\npublic class TemplateMatching {\n\n    public static void main(String[] args) throws Exception {\n        \n        newStyle(args);\n        //oldStyle(args);\n   \n    }\n    \n\n    \n    public static void newStyle(String[] args){\n        //read in image default colors\n        Mat sourceColor = imread(args[0]);\n        Mat sourceGrey = new Mat(sourceColor.size(), CV_8UC1);\n       cvtColor(sourceColor, sourceGrey, COLOR_BGR2GRAY);\n       //load in template in grey \n       Mat template = imread(args[1],IMREAD_GRAYSCALE);//int = 0\n       //Size for the result image\n       Size size = new Size(sourceGrey.cols()-template.cols()+1, sourceGrey.rows()-template.rows()+1);\n       Mat result = new Mat(size, CV_32FC1);\n       matchTemplate(sourceGrey, template, result, TM_CCORR_NORMED);\n       \n       DoublePointer minVal= new DoublePointer();\n       DoublePointer maxVal= new DoublePointer();\n       Point min = new Point();\n       Point max = new Point();\n       minMaxLoc(result, minVal, maxVal, min, max, null);\n       rectangle(sourceColor,new Rect(max.x(),max.y(),template.cols(),template.rows()), randColor(), 2, 0, 0);\n       \n       imshow(\"Original marked\", sourceColor);\n       imshow(\"Ttemplate\", template);\n       imshow(\"Results matrix\", result);\n       waitKey(0);\n       destroyAllWindows();\n        \n    }\n\n    // some usefull things.\n    public static Scalar randColor(){\n       int b,g,r;\n       b= ThreadLocalRandom.current().nextInt(0, 255 + 1);\n       g= ThreadLocalRandom.current().nextInt(0, 255 + 1);\n       r= ThreadLocalRandom.current().nextInt(0, 255 + 1);\n       return new Scalar (b,g,r,0);\n    }\n    \n    public static List<Point> getPointsFromMatAboveThreshold(Mat m, float t){\n       List<Point> matches = new ArrayList<Point>();\n       FloatIndexer indexer = m.createIndexer();\n       for (int y = 0; y < m.rows(); y++) {\n            for (int x = 0; x < m.cols(); x++) {\n               if (indexer.get(y,x)>t) {\n              System.out.println(\"(\" + x + \",\" + y +\") = \"+ indexer.get(y,x));\n              matches.add(new Point(x, y));\n          }\n         }\n       }\n       return matches;\n    }\n    \n    public static void oldStyle(String[] args){\n        //get color source image to draw red rect on later\n        IplImage srcColor = cvLoadImage(args[0]);\n        //create blank 1 channel image same size as the source \n        IplImage src = cvCreateImage(cvGetSize(srcColor), IPL_DEPTH_8U, 1);\n        //convert source to grey and copy to src\n        cvCvtColor(srcColor, src, CV_BGR2GRAY);\n        //get the image to match loaded in greyscale. \n        IplImage tmp = cvLoadImage(args[1], 0);\n        //this image will hold the strength of the match\n        //as the template is translated across the image \n        IplImage result = cvCreateImage(\n                        cvSize(src.width() - tmp.width() + 1,\n                        src.height() - tmp.height() + 1), IPL_DEPTH_32F, src.nChannels());\n\n        cvZero(result);\n\n        // Match Template Function from OpenCV\n        cvMatchTemplate(src, tmp, result, CV_TM_CCORR_NORMED);\n\n        // double[] min_val = new double[2];\n        // double[] max_val = new double[2];\n        DoublePointer min_val = new DoublePointer();\n        DoublePointer max_val = new DoublePointer();\n\n        CvPoint minLoc = new CvPoint();\n        CvPoint maxLoc = new CvPoint();\n\n        cvMinMaxLoc(result, min_val, max_val, minLoc, maxLoc, null);\n\n        // Get the Max or Min Correlation Value\n        // System.out.println(Arrays.toString(min_val));\n        // System.out.println(Arrays.toString(max_val));\n\n        CvPoint point = new CvPoint();\n        point.x(maxLoc.x() + tmp.width());\n        point.y(maxLoc.y() + tmp.height());\n        // cvMinMaxLoc(src, min_val, max_val,0,0,result);\n\n        cvRectangle(srcColor, maxLoc, point, CvScalar.RED, 2, 8, 0); // Draw a\n                                                                // Rectangle for\n                                                                // Matched\n                                                                // Region\n\n        cvShowImage(\"Lena Image\", srcColor);\n        cvWaitKey(0);\n        cvReleaseImage(srcColor);\n        cvReleaseImage(src);\n        cvReleaseImage(tmp);\n        cvReleaseImage(result);\n    }\n}\n"
  },
  {
    "path": "samples/WebcamAndMicrophoneCapture.java",
    "content": "/**\n * @author Ben Davenport\n * \n * This class is a simple example for broadcasting a video capture device (ie, webcam) and an audio capture device (ie, microphone)\n * using an FFmpegFrameRecorder. \n * \n * FFmpegFrameRecorder allows the output destination to be either a FILE or an RTMP endpoint (Wowza, FMS, et al)\n * \n * IMPORTANT: There are potential timing issues with audio/video synchronicity across threads, I am working on finding a solution, but\n * chime in if you can fig it out :o)\n */\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.ShortBuffer;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport javax.sound.sampled.AudioFormat;\nimport javax.sound.sampled.AudioSystem;\nimport javax.sound.sampled.DataLine;\nimport javax.sound.sampled.LineUnavailableException;\nimport javax.sound.sampled.Mixer;\nimport javax.sound.sampled.TargetDataLine;\n\nimport org.bytedeco.ffmpeg.global.avcodec;\nimport org.bytedeco.javacv.CanvasFrame;\nimport org.bytedeco.javacv.FFmpegFrameRecorder;\nimport org.bytedeco.javacv.Frame;\nimport org.bytedeco.javacv.FrameRecorder.Exception;\nimport org.bytedeco.javacv.OpenCVFrameGrabber;\n\npublic class WebcamAndMicrophoneCapture\n{\n    final private static int WEBCAM_DEVICE_INDEX = 1;\n    final private static int AUDIO_DEVICE_INDEX = 4;\n\n    final private static int FRAME_RATE = 30;\n    final private static int GOP_LENGTH_IN_FRAMES = 60;\n\n    private static long startTime = 0;\n    private static long videoTS = 0;\n\n    public static void main(String[] args) throws Exception, org.bytedeco.javacv.FrameGrabber.Exception\n    {\n        final int captureWidth = 1280;\n        final int captureHeight = 720;\n\n        // The available FrameGrabber classes include OpenCVFrameGrabber (opencv_videoio),\n        // DC1394FrameGrabber, FlyCapture2FrameGrabber, OpenKinectFrameGrabber,\n        // PS3EyeFrameGrabber, VideoInputFrameGrabber, and FFmpegFrameGrabber.\n        final OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(WEBCAM_DEVICE_INDEX);\n        grabber.setImageWidth(captureWidth);\n        grabber.setImageHeight(captureHeight);\n        grabber.start();\n\n        // org.bytedeco.javacv.FFmpegFrameRecorder.FFmpegFrameRecorder(String\n        // filename, int imageWidth, int imageHeight, int audioChannels)\n        // For each param, we're passing in...\n        // filename = either a path to a local file we wish to create, or an\n        // RTMP url to an FMS / Wowza server\n        // imageWidth = width we specified for the grabber\n        // imageHeight = height we specified for the grabber\n        // audioChannels = 2, because we like stereo\n        final FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(\n                \"rtmp://my-streaming-server/app_name_here/instance_name/stream_name\",\n                captureWidth, captureHeight, 2);\n        recorder.setInterleaved(true);\n\n        // decrease \"startup\" latency in FFMPEG (see:\n        // https://trac.ffmpeg.org/wiki/StreamingGuide)\n        recorder.setVideoOption(\"tune\", \"zerolatency\");\n        // tradeoff between quality and encode speed\n        // possible values are ultrafast,superfast, veryfast, faster, fast,\n        // medium, slow, slower, veryslow\n        // ultrafast offers us the least amount of compression (lower encoder\n        // CPU) at the cost of a larger stream size\n        // at the other end, veryslow provides the best compression (high\n        // encoder CPU) while lowering the stream size\n        // (see: https://trac.ffmpeg.org/wiki/Encode/H.264)\n        recorder.setVideoOption(\"preset\", \"ultrafast\");\n        // Constant Rate Factor (see: https://trac.ffmpeg.org/wiki/Encode/H.264)\n        recorder.setVideoOption(\"crf\", \"28\");\n        // 2000 kb/s, reasonable \"sane\" area for 720\n        recorder.setVideoBitrate(2000000);\n        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);\n        recorder.setFormat(\"flv\");\n        // FPS (frames per second)\n        recorder.setFrameRate(FRAME_RATE);\n        // Key frame interval, in our case every 2 seconds -> 30 (fps) * 2 = 60\n        // (gop length)\n        recorder.setGopSize(GOP_LENGTH_IN_FRAMES);\n\n        // We don't want variable bitrate audio\n        recorder.setAudioOption(\"crf\", \"0\");\n        // Highest quality\n        recorder.setAudioQuality(0);\n        // 192 Kbps\n        recorder.setAudioBitrate(192000);\n        recorder.setSampleRate(44100);\n        recorder.setAudioChannels(2);\n        recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);\n\n        // Jack 'n coke... do it...\n        recorder.start();\n\n        // Thread for audio capture, this could be in a nested private class if you prefer...\n        new Thread(new Runnable() {\n            @Override\n            public void run()\n            {\n                // Pick a format...\n                // NOTE: It is better to enumerate the formats that the system supports,\n                // because getLine() can error out with any particular format...\n                // For us: 44.1 sample rate, 16 bits, stereo, signed, little endian\n                AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);\n\n                // Get TargetDataLine with that format\n                Mixer.Info[] minfoSet = AudioSystem.getMixerInfo();\n                Mixer mixer = AudioSystem.getMixer(minfoSet[AUDIO_DEVICE_INDEX]);\n                DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);\n\n                try\n                {\n                    // Open and start capturing audio\n                    // It's possible to have more control over the chosen audio device with this line:\n                    // TargetDataLine line = (TargetDataLine)mixer.getLine(dataLineInfo);\n                    final TargetDataLine line = (TargetDataLine)AudioSystem.getLine(dataLineInfo);\n                    line.open(audioFormat);\n                    line.start();\n\n                    final int sampleRate = (int) audioFormat.getSampleRate();\n                    final int numChannels = audioFormat.getChannels();\n\n                    // Let's initialize our audio buffer...\n                    final int audioBufferSize = sampleRate * numChannels;\n                    final byte[] audioBytes = new byte[audioBufferSize];\n\n                    // Using a ScheduledThreadPoolExecutor vs a while loop with\n                    // a Thread.sleep will allow\n                    // us to get around some OS specific timing issues, and keep\n                    // to a more precise\n                    // clock as the fixed rate accounts for garbage collection\n                    // time, etc\n                    // a similar approach could be used for the webcam capture\n                    // as well, if you wish\n                    ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);\n                    exec.scheduleAtFixedRate(new Runnable() {\n                        @Override\n                        public void run()\n                        {\n                            try\n                            {\n                                // Read from the line... non-blocking\n                                int nBytesRead = 0;\n                                while (nBytesRead == 0) {\n                                    nBytesRead = line.read(audioBytes, 0, line.available());\n                                }\n\n                                // Since we specified 16 bits in the AudioFormat,\n                                // we need to convert our read byte[] to short[]\n                                // (see source from FFmpegFrameRecorder.recordSamples for AV_SAMPLE_FMT_S16)\n                                // Let's initialize our short[] array\n                                int nSamplesRead = nBytesRead / 2;\n                                short[] samples = new short[nSamplesRead];\n\n                                // Let's wrap our short[] into a ShortBuffer and\n                                // pass it to recordSamples\n                                ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);\n                                ShortBuffer sBuff = ShortBuffer.wrap(samples, 0, nSamplesRead);\n\n                                // recorder is instance of\n                                // org.bytedeco.javacv.FFmpegFrameRecorder\n                                recorder.recordSamples(sampleRate, numChannels, sBuff);\n                            } \n                            catch (org.bytedeco.javacv.FrameRecorder.Exception e)\n                            {\n                                e.printStackTrace();\n                            }\n                        }\n                    }, 0, (long) 1000 / FRAME_RATE, TimeUnit.MILLISECONDS);\n                } \n                catch (LineUnavailableException e1)\n                {\n                    e1.printStackTrace();\n                }\n            }\n        }).start();\n\n        // A really nice hardware accelerated component for our preview...\n        final CanvasFrame cFrame = new CanvasFrame(\"Capture Preview\", CanvasFrame.getDefaultGamma() / grabber.getGamma());\n\n        Frame capturedFrame = null;\n\n        // While we are capturing...\n        while ((capturedFrame = grabber.grab()) != null)\n        {\n            if (cFrame.isVisible())\n            {\n                // Show our frame in the preview\n                cFrame.showImage(capturedFrame);\n            }\n\n            // Let's define our start time...\n            // This needs to be initialized as close to when we'll use it as\n            // possible,\n            // as the delta from assignment to computed time could be too high\n            if (startTime == 0)\n                startTime = System.currentTimeMillis();\n\n            // Create timestamp for this frame\n            videoTS = 1000 * (System.currentTimeMillis() - startTime);\n\n            // Check for AV drift\n            if (videoTS > recorder.getTimestamp())\n            {\n                System.out.println(\n                        \"Lip-flap correction: \" \n                        + videoTS + \" : \"\n                        + recorder.getTimestamp() + \" -> \"\n                        + (videoTS - recorder.getTimestamp()));\n\n                // We tell the recorder to write this frame at this timestamp\n                recorder.setTimestamp(videoTS);\n            }\n\n            // Send the frame to the org.bytedeco.javacv.FFmpegFrameRecorder\n            recorder.record(capturedFrame);\n        }\n\n        cFrame.dispose();\n        recorder.stop();\n        grabber.stop();\n    }\n}\n"
  },
  {
    "path": "samples/YOLONet.java",
    "content": "/*\nMIT License\n\nCopyright (c) 2021 Florian Bruggisser\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n/*\nYOLONet Example Information\n---------------------------\n\nThis is a basic implementation of a YOLO object detection network inference example.\nIt works with most variants of YOLO (YOLOv2, YOLOv2-tiny, YOLOv3, YOLOv3-tiny, YOLOv3-tiny-prn, YOLOv4, YOLOv4-tiny).\nYOLO9000 is not support by OpenCV DNN.\n\nTo run the example download the following files and place them in the root folder of your project:\n\n    YOLOv4 Configuration: https://raw.githubusercontent.com/AlexeyAB/darknet/master/cfg/yolov4.cfg\n    YOLOv4 Weights: https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.weights\n    COCO Names: https://raw.githubusercontent.com/AlexeyAB/darknet/master/data/coco.names\n    Dog Demo Image: https://raw.githubusercontent.com/AlexeyAB/darknet/master/data/dog.jpg\n\nFor faster inferencing CUDA is highly recommended.\nOn CPU it is recommended to decrease the width & height of the network or use the tiny variants.\n */\n\nimport org.bytedeco.javacpp.FloatPointer;\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.indexer.FloatIndexer;\nimport org.bytedeco.opencv.global.opencv_dnn;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_dnn.Net;\nimport org.bytedeco.opencv.opencv_text.FloatVector;\nimport org.bytedeco.opencv.opencv_text.IntVector;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.bytedeco.opencv.global.opencv_core.CV_32F;\nimport static org.bytedeco.opencv.global.opencv_core.getCudaEnabledDeviceCount;\nimport static org.bytedeco.opencv.global.opencv_dnn.*;\nimport static org.bytedeco.opencv.global.opencv_highgui.imshow;\nimport static org.bytedeco.opencv.global.opencv_highgui.waitKey;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.imread;\nimport static org.bytedeco.opencv.global.opencv_imgproc.LINE_8;\nimport static org.bytedeco.opencv.global.opencv_imgproc.rectangle;\n\npublic class YOLONet {\n\n    public static void main(String[] args) {\n        Mat image = imread(\"dog.jpg\");\n\n        YOLONet yolo = new YOLONet(\n                \"yolov4.cfg\",\n                \"yolov4.weights\",\n                \"coco.names\",\n                608, 608);\n        yolo.setup();\n\n        List<ObjectDetectionResult> results = yolo.predict(image);\n\n        System.out.printf(\"Detected %d objects:\\n\", results.size());\n        for(ObjectDetectionResult result : results) {\n            System.out.printf(\"\\t%s - %.2f%%\\n\", result.className, result.confidence * 100f);\n\n            // annotate on image\n            rectangle(image,\n                    new Point(result.x, result.y),\n                    new Point(result.x + result.width, result.y + result.height),\n                    Scalar.MAGENTA, 2, LINE_8, 0);\n        }\n\n        imshow(\"YOLO\", image);\n        waitKey();\n    }\n\n    private Path configPath;\n    private Path weightsPath;\n    private Path namesPath;\n    private int width;\n    private int height;\n\n    private float confidenceThreshold = 0.5f;\n    private float nmsThreshold = 0.4f;\n\n    private Net net;\n    private StringVector outNames;\n\n    private List<String> names;\n\n    /**\n     * Creates a new YOLO network.\n     * @param configPath Path to the configuration file.\n     * @param weightsPath Path to the weights file.\n     * @param namesPath Path to the names file.\n     * @param width Width of the network as defined in the configuration.\n     * @param height Height of the network as defined in the configuration.\n     */\n    public YOLONet(String configPath, String weightsPath, String namesPath, int width, int height) {\n        this.configPath = Paths.get(configPath);\n        this.weightsPath = Paths.get(weightsPath);\n        this.namesPath = Paths.get(namesPath);\n        this.width = width;\n        this.height = height;\n    }\n\n    /**\n     * Initialises the network.\n     *\n     * @return True if the network initialisation was successful.\n     */\n    public boolean setup() {\n        net = readNetFromDarknet(\n                configPath.toAbsolutePath().toString(),\n                weightsPath.toAbsolutePath().toString());\n\n        // setup output layers\n        outNames = net.getUnconnectedOutLayersNames();\n\n        // enable cuda backend if available\n        if (getCudaEnabledDeviceCount() > 0) {\n            net.setPreferableBackend(opencv_dnn.DNN_BACKEND_CUDA);\n            net.setPreferableTarget(opencv_dnn.DNN_TARGET_CUDA);\n        }\n\n        // read names file\n        try {\n            names = Files.readAllLines(namesPath);\n        } catch (IOException e) {\n            System.err.println(\"Could not read names file!\");\n            e.printStackTrace();\n        }\n\n        return !net.empty();\n    }\n\n    /**\n     * Runs the object detection on the frame.\n     *\n     * @param frame Input frame.\n     * @return List of objects that have been detected.\n     */\n    public List<ObjectDetectionResult> predict(Mat frame) {\n        Mat inputBlob = blobFromImage(frame,\n                1 / 255.0,\n                new Size(width, height),\n                new Scalar(0.0),\n                true, false, CV_32F);\n\n        net.setInput(inputBlob);\n\n        // run detection\n        MatVector outs = new MatVector(outNames.size());\n        net.forward(outs, outNames);\n\n        // evaluate result\n        List<ObjectDetectionResult> result = postprocess(frame, outs);\n\n        // cleanup\n        outs.releaseReference();\n        inputBlob.release();\n\n        return result;\n    }\n\n    /**\n     * Remove the bounding boxes with low confidence using non-maxima suppression\n     *\n     * @param frame Input frame\n     * @param outs  Network outputs\n     * @return List of objects\n     */\n    private List<ObjectDetectionResult> postprocess(Mat frame, MatVector outs) {\n        final IntVector classIds = new IntVector();\n        final FloatVector confidences = new FloatVector();\n        final RectVector boxes = new RectVector();\n\n        for (int i = 0; i < outs.size(); ++i) {\n            // extract the bounding boxes that have a high enough score\n            // and assign their highest confidence class prediction.\n            Mat result = outs.get(i);\n            FloatIndexer data = result.createIndexer();\n\n            for (int j = 0; j < result.rows(); j++) {\n                // minMaxLoc implemented in java because it is 1D\n                int maxIndex = -1;\n                float maxScore = Float.MIN_VALUE;\n                for (int k = 5; k < result.cols(); k++) {\n                    float score = data.get(j, k);\n                    if (score > maxScore) {\n                        maxScore = score;\n                        maxIndex = k - 5;\n                    }\n                }\n\n                if (maxScore > confidenceThreshold) {\n                    int centerX = (int) (data.get(j, 0) * frame.cols());\n                    int centerY = (int) (data.get(j, 1) * frame.rows());\n                    int width = (int) (data.get(j, 2) * frame.cols());\n                    int height = (int) (data.get(j, 3) * frame.rows());\n                    int left = centerX - width / 2;\n                    int top = centerY - height / 2;\n\n                    classIds.push_back(maxIndex);\n                    confidences.push_back(maxScore);\n\n                    boxes.push_back(new Rect(left, top, width, height));\n                }\n            }\n\n            data.release();\n            result.release();\n        }\n\n        // remove overlapping bounding boxes with NMS\n        IntPointer indices = new IntPointer(confidences.size());\n        FloatPointer confidencesPointer = new FloatPointer(confidences.size());\n        confidencesPointer.put(confidences.get());\n\n        NMSBoxes(boxes, confidencesPointer, confidenceThreshold, nmsThreshold, indices, 1.f, 0);\n\n        // create result list\n        List<ObjectDetectionResult> detections = new ArrayList<>();\n        for (int i = 0; i < indices.limit(); ++i) {\n            final int idx = indices.get(i);\n            final Rect box = boxes.get(idx);\n\n            final int clsId = classIds.get(idx);\n\n            detections.add(new ObjectDetectionResult() {{\n                classId = clsId;\n                className = names.get(clsId);\n                confidence = confidences.get(idx);\n                x = box.x();\n                y = box.y();\n                width = box.width();\n                height = box.height();\n            }});\n\n            box.releaseReference();\n        }\n\n        // cleanup\n        indices.releaseReference();\n        confidencesPointer.releaseReference();\n        classIds.releaseReference();\n        confidences.releaseReference();\n        boxes.releaseReference();\n\n        return detections;\n    }\n\n    /**\n     * Dataclass for object detection result.\n     */\n    public static class ObjectDetectionResult {\n        public int classId;\n        public String className;\n\n        public float confidence;\n\n        public int x;\n        public int y;\n        public int width;\n        public int height;\n    }\n}\n"
  },
  {
    "path": "samples/haarcascade_frontalface_alt2.xml",
    "content": "<?xml version=\"1.0\"?>\n<!--\n    Tree-based 20x20 gentle adaboost frontal face detector.\n    Created by Rainer Lienhart.\n\n////////////////////////////////////////////////////////////////////////////////////////\n\n  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.\n\n  By downloading, copying, installing or using the software you agree to this license.\n  If you do not agree to this license, do not download, install,\n  copy or use the software.\n\n\n                        Intel License Agreement\n                For Open Source Computer Vision Library\n\n Copyright (C) 2000, Intel Corporation, all rights reserved.\n Third party copyrights are property of their respective owners.\n\n Redistribution and use in source and binary forms, with or without modification,\n are permitted provided that the following conditions are met:\n\n   * Redistribution's of source code must retain the above copyright notice,\n     this list of conditions and the following disclaimer.\n\n   * Redistribution's in binary form must reproduce the above copyright notice,\n     this list of conditions and the following disclaimer in the documentation\n     and/or other materials provided with the distribution.\n\n   * The name of Intel Corporation may not be used to endorse or promote products\n     derived from this software without specific prior written permission.\n\n This software is provided by the copyright holders and contributors \"as is\" and\n any express or implied warranties, including, but not limited to, the implied\n warranties of merchantability and fitness for a particular purpose are disclaimed.\n In no event shall the Intel Corporation or contributors be liable for any direct,\n indirect, incidental, special, exemplary, or consequential damages\n (including, but not limited to, procurement of substitute goods or services;\n loss of use, data, or profits; or business interruption) however caused\n and on any theory of liability, whether in contract, strict liability,\n or tort (including negligence or otherwise) arising in any way out of\n the use of this software, even if advised of the possibility of such damage.\n-->\n<opencv_storage>\n<cascade type_id=\"opencv-cascade-classifier\"><stageType>BOOST</stageType>\n  <featureType>HAAR</featureType>\n  <height>20</height>\n  <width>20</width>\n  <stageParams>\n    <maxWeakCount>109</maxWeakCount></stageParams>\n  <featureParams>\n    <maxCatCount>0</maxCatCount></featureParams>\n  <stageNum>20</stageNum>\n  <stages>\n    <_>\n      <maxWeakCount>3</maxWeakCount>\n      <stageThreshold>3.5069230198860168e-01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 0 4.3272329494357109e-03 -1 -2 1 1.3076160103082657e-02</internalNodes>\n          <leafValues>\n            3.8381900638341904e-02 8.9652568101882935e-01\n            2.6293140649795532e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2 5.2434601821005344e-04 -1 -2 3 4.4573000632226467e-03</internalNodes>\n          <leafValues>\n            1.0216630250215530e-01 1.2384019792079926e-01\n            6.9103831052780151e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 4 -9.2708261217921972e-04 -1 -2 5 3.3989109215326607e-04</internalNodes>\n          <leafValues>\n            1.9536970555782318e-01 2.1014410257339478e-01\n            8.2586747407913208e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>9</maxWeakCount>\n      <stageThreshold>3.4721779823303223e+00</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 6 2.3025739938020706e-03 -1 -2 7 4.4174338690936565e-03</internalNodes>\n          <leafValues>\n            1.0183759778738022e-01 8.2190579175949097e-01\n            1.9565549492835999e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 8 2.2203210741281509e-02 -1 -2 9 -1.7283110355492681e-04</internalNodes>\n          <leafValues>\n            2.2054070234298706e-01 7.3263257741928101e-02\n            5.9314841032028198e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 10 4.3567270040512085e-03 -1 -2 11\n            -2.6032889727503061e-03</internalNodes>\n          <leafValues>\n            1.8441149592399597e-01 4.0322139859199524e-01\n            8.0665212869644165e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 12 1.7309630056843162e-03 -1 -2 13\n            -7.8146401792764664e-03</internalNodes>\n          <leafValues>\n            2.5483280420303345e-01 6.0570698976516724e-01\n            2.7790638804435730e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 14 -8.7343417108058929e-03 -1 -2 15\n            9.4522320432588458e-04</internalNodes>\n          <leafValues>\n            2.8899800777435303e-01 7.6165872812271118e-01\n            3.4956431388854980e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 16 4.9414858222007751e-02 -1 -2 17\n            4.4891750440001488e-03</internalNodes>\n          <leafValues>\n            8.1516528129577637e-01 2.8087830543518066e-01\n            6.0277748107910156e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 18 6.0313619673252106e-02 -1 -2 19\n            -1.0762850288301706e-03</internalNodes>\n          <leafValues>\n            7.6075017452239990e-01 4.4440358877182007e-01\n            1.4373120665550232e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 20 -9.5083238556981087e-03 -1 -2 21\n            7.6601309701800346e-03</internalNodes>\n          <leafValues>\n            5.3181701898574829e-01 5.4110521078109741e-01\n            2.1806870400905609e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 22 7.6467678882181644e-03 -1 -2 23\n            -8.4662932204082608e-04</internalNodes>\n          <leafValues>\n            1.1589600145816803e-01 2.3406790196895599e-01\n            5.9903818368911743e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>14</maxWeakCount>\n      <stageThreshold>5.9844889640808105e+00</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            1 0 24 -4.8506218008697033e-03 -1 -2 25\n            -4.6141650527715683e-03</internalNodes>\n          <leafValues>\n            1.8054960668087006e-01 2.1778939664363861e-01\n            8.0182367563247681e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 26 -2.4301309604197741e-03 -1 -2 27\n            4.1787960799410939e-04</internalNodes>\n          <leafValues>\n            1.1413549631834030e-01 1.2030939757823944e-01\n            6.1085307598114014e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 28 1.0010929545387626e-03 -1 -2 29\n            1.0577100329101086e-03</internalNodes>\n          <leafValues>\n            2.0799599587917328e-01 3.3020541071891785e-01\n            7.5110942125320435e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 30 1.2376549420878291e-03 -1 -2 31\n            3.5315038985572755e-04</internalNodes>\n          <leafValues>\n            2.7682220935821533e-01 1.6682930290699005e-01\n            5.8294767141342163e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 32 -1.1953660286962986e-02 -1 -2 33\n            1.4182999730110168e-03</internalNodes>\n          <leafValues>\n            1.5087880194187164e-01 4.3912279605865479e-01\n            7.6465952396392822e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 34 3.4642980899661779e-03 -1 -2 35\n            -1.4948950149118900e-02</internalNodes>\n          <leafValues>\n            2.6515561342239380e-01 2.2980530560016632e-01\n            5.4421657323837280e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 36 -1.0506849503144622e-03 -1 -2 37\n            -4.0782918222248554e-03</internalNodes>\n          <leafValues>\n            3.6228439211845398e-01 2.6012599468231201e-01\n            7.2336578369140625e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 38 5.4242828628048301e-04 -1 -2 39\n            -7.3204059153795242e-03</internalNodes>\n          <leafValues>\n            3.8496789336204529e-01 2.9655128717422485e-01\n            5.4803091287612915e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 40 1.1421289527788758e-03 -1 -2 41\n            1.1783400550484657e-03</internalNodes>\n          <leafValues>\n            4.1047701239585876e-01 7.2390240430831909e-01\n            2.7872839570045471e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 42 4.4077109545469284e-02 -1 -2 43\n            3.7900090683251619e-03</internalNodes>\n          <leafValues>\n            5.6405162811279297e-01 5.9475481510162354e-01\n            3.3120200037956238e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 44 -2.4291418958455324e-03 -1 -2 45\n            9.4262324273586273e-03</internalNodes>\n          <leafValues>\n            6.6032320261001587e-01 4.6806651353836060e-01\n            2.0643380284309387e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 46 8.0630257725715637e-03 -1 -2 47\n            5.2240812219679356e-03</internalNodes>\n          <leafValues>\n            5.2988511323928833e-01 5.2816027402877808e-01\n            1.9095499813556671e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 48 -7.0630568079650402e-03 -1 -2 49\n            5.6897541508078575e-03</internalNodes>\n          <leafValues>\n            1.3806459307670593e-01 5.4906368255615234e-01\n            1.2602810561656952e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 50 1.2472929665818810e-03 -1 -2 51\n            4.9543488770723343e-02</internalNodes>\n          <leafValues>\n            2.3726630210876465e-01 5.2401661872863770e-01\n            1.7692160606384277e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>19</maxWeakCount>\n      <stageThreshold>8.5117864608764648e+00</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            1 0 52 -4.9326149746775627e-03 -1 -2 53\n            2.7918140403926373e-05</internalNodes>\n          <leafValues>\n            1.9980649650096893e-01 2.2993800044059753e-01\n            7.3932111263275146e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 54 3.0876200180500746e-03 -1 -2 55\n            7.4669660534709692e-06</internalNodes>\n          <leafValues>\n            1.5338400006294250e-01 2.0368589460849762e-01\n            5.8549159765243530e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 56 1.8739729421213269e-03 -1 -2 57\n            9.3380251200869679e-04</internalNodes>\n          <leafValues>\n            2.0498959720134735e-01 3.2341998815536499e-01\n            7.3230141401290894e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 58 1.9151850137859583e-03 -1 -2 59\n            -5.9683797881007195e-03</internalNodes>\n          <leafValues>\n            3.0451491475105286e-01 2.9321339726448059e-01\n            5.6212961673736572e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 60 -7.2115601506084204e-04 -1 -2 61\n            -5.9663117863237858e-03</internalNodes>\n          <leafValues>\n            3.6580368876457214e-01 2.7121558785438538e-01\n            7.2263348102569580e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 62 3.0874179676175117e-02 -1 -2 63\n            -1.1099710129201412e-02</internalNodes>\n          <leafValues>\n            4.4198378920555115e-01 3.6129769682884216e-01\n            5.2514511346817017e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 64 2.1164179779589176e-03 -1 -2 65\n            -9.4317439943552017e-03</internalNodes>\n          <leafValues>\n            3.6286169290542603e-01 1.6010950505733490e-01\n            7.0522767305374146e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 66 -3.5266019403934479e-03 -1 -2 67\n            -1.6907559474930167e-03</internalNodes>\n          <leafValues>\n            1.3012880086898804e-01 1.7863239347934723e-01\n            5.5215299129486084e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 68 4.6470930101349950e-04 -1 -2 69\n            -1.0215570218861103e-02</internalNodes>\n          <leafValues>\n            3.4873831272125244e-01 2.6739910244941711e-01\n            6.6679191589355469e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 70 1.2634709710255265e-03 -1 -2 71\n            -1.1875299736857414e-02</internalNodes>\n          <leafValues>\n            3.4378638863563538e-01 5.9953361749649048e-01\n            3.4977179765701294e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 72 -1.0732339695096016e-02 -1 -2 73\n            7.1836481802165508e-03</internalNodes>\n          <leafValues>\n            2.1504899859428406e-01 6.2714362144470215e-01\n            2.5195419788360596e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 74 -2.8340889140963554e-02 -1 -2 75\n            -4.5813230099156499e-04</internalNodes>\n          <leafValues>\n            8.2411892712116241e-02 5.9100568294525146e-01\n            3.7052011489868164e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 76 4.2940340936183929e-03 -1 -2 77\n            1.0751079767942429e-02</internalNodes>\n          <leafValues>\n            1.5947279334068298e-01 5.9804809093475342e-01\n            2.8325080871582031e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 78 2.2465119138360023e-02 -1 -2 79\n            -5.7988539338111877e-02</internalNodes>\n          <leafValues>\n            7.8770911693572998e-01 1.5557409822940826e-01\n            5.2396571636199951e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 80 7.2110891342163086e-03 -1 -2 81\n            -4.8367571085691452e-02</internalNodes>\n          <leafValues>\n            6.6203659772872925e-01 1.4247199892997742e-01\n            4.4298338890075684e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 82 -1.4418059960007668e-02 -1 -2 83\n            -2.3156389594078064e-02</internalNodes>\n          <leafValues>\n            1.5885409712791443e-01 2.3757989704608917e-01\n            5.2171349525451660e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 84 7.6985340565443039e-03 -1 -2 85\n            -5.6248619221150875e-03</internalNodes>\n          <leafValues>\n            1.9417250156402588e-01 6.2784057855606079e-01\n            3.7460449337959290e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 86 -7.2936748620122671e-04 -1 -2 87\n            6.1783898854628205e-04</internalNodes>\n          <leafValues>\n            3.8409221172332764e-01 3.1064930558204651e-01\n            5.5378472805023193e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 88 -4.5803939428878948e-05 -1 -2 89\n            -1.4719359569426160e-05</internalNodes>\n          <leafValues>\n            3.4444490075111389e-01 2.7295520901679993e-01\n            6.4289510250091553e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>19</maxWeakCount>\n      <stageThreshold>8.4680156707763672e+00</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 90 -1.3469370314851403e-03 -1 -2 91\n            -2.4774789344519377e-03</internalNodes>\n          <leafValues>\n            1.6570860147476196e-01 2.2738510370254517e-01\n            6.9893497228622437e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 92 5.2632777951657772e-03 -1 -2 93\n            4.9075339920818806e-03</internalNodes>\n          <leafValues>\n            1.5120740234851837e-01 5.5644702911376953e-01\n            1.6054420173168182e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 94 -2.3254349362105131e-03 -1 -2 95\n            -1.4665479538962245e-03</internalNodes>\n          <leafValues>\n            1.8802590668201447e-01 3.1224989891052246e-01\n            7.1653962135314941e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 96 -1.2311690300703049e-01 -1 -2 97\n            2.2108340635895729e-03</internalNodes>\n          <leafValues>\n            3.8595831394195557e-01 2.4552939832210541e-01\n            5.6957101821899414e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 98 2.0661531016230583e-03 -1 -2 99\n            3.6130280932411551e-04</internalNodes>\n          <leafValues>\n            2.7165201306343079e-01 2.2933620214462280e-01\n            7.2086298465728760e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 100 7.9957872629165649e-02 -1 -2 101\n            2.6064720004796982e-03</internalNodes>\n          <leafValues>\n            7.8336209058761597e-01 5.5452322959899902e-01\n            2.5506898760795593e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 102 6.5699010156095028e-03 -1 -2 103\n            1.6259610420092940e-03</internalNodes>\n          <leafValues>\n            1.8193900585174561e-01 3.5298758745193481e-01\n            6.5528190135955811e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 104 3.6204981151968241e-03 -1 -2 105\n            -4.4391951523721218e-03</internalNodes>\n          <leafValues>\n            5.4623097181320190e-01 1.3598430156707764e-01\n            5.4158151149749756e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 106 -9.0540945529937744e-03 -1 -2 107\n            -4.6067481162026525e-04</internalNodes>\n          <leafValues>\n            1.1151199787855148e-01 5.8467197418212891e-01\n            2.5983488559722900e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 108 -5.6621041148900986e-03 -1 -2 109\n            5.1165837794542313e-03</internalNodes>\n          <leafValues>\n            1.6105690598487854e-01 5.3766787052154541e-01\n            1.7394550144672394e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 110 -2.1362339612096548e-03 -1 -2 111\n            -5.4809921421110630e-03</internalNodes>\n          <leafValues>\n            1.9020730257034302e-01 3.2720080018043518e-01\n            6.3648408651351929e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 112 -8.1061907112598419e-03 -1 -2 113\n            6.0048708692193031e-03</internalNodes>\n          <leafValues>\n            6.9148528575897217e-01 4.3273261189460754e-01\n            6.9638431072235107e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 114 -8.7028548121452332e-02 -1 -2 115\n            -4.7809639945626259e-03</internalNodes>\n          <leafValues>\n            8.5941338539123535e-01 9.7394466400146484e-02\n            4.5870301127433777e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 116 -2.2166660055518150e-03 -1 -2 117\n            1.3642730191349983e-03</internalNodes>\n          <leafValues>\n            2.5546258687973022e-01 3.3190909028053284e-01\n            5.9641027450561523e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 118 -9.0077864006161690e-03 -1 -2 119\n            -1.5494120307266712e-02</internalNodes>\n          <leafValues>\n            2.6665949821472168e-01 1.8481859564781189e-01\n            6.2459707260131836e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 120 -4.2165028862655163e-03 -1 -2 121\n            4.3249759823083878e-02</internalNodes>\n          <leafValues>\n            5.3799271583557129e-01 5.1830291748046875e-01\n            2.1704199910163879e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 122 2.8786511393263936e-04 -1 -2 123\n            1.2373150093480945e-03</internalNodes>\n          <leafValues>\n            2.6133841276168823e-01 2.7865320444107056e-01\n            5.9089881181716919e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 124 1.9528300035744905e-03 -1 -2 125\n            -1.4947060262784362e-03</internalNodes>\n          <leafValues>\n            2.6128691434860229e-01 5.9154129028320312e-01\n            3.4557819366455078e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 126 3.5878680646419525e-03 -1 -2 127\n            -2.5938691105693579e-03</internalNodes>\n          <leafValues>\n            1.5870520472526550e-01 1.2704110145568848e-01\n            5.9794288873672485e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>27</maxWeakCount>\n      <stageThreshold>1.2578499794006348e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 128 3.5810680128633976e-03 -1 -2 129\n            -2.8552350122481585e-03</internalNodes>\n          <leafValues>\n            1.9951049983501434e-01 7.3730701208114624e-01\n            2.9217371344566345e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 130 1.9758539274334908e-03 -1 -2 131\n            3.2583118882030249e-03</internalNodes>\n          <leafValues>\n            1.9564199447631836e-01 5.6920468807220459e-01\n            1.8390649557113647e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 132 2.3711679386906326e-04 -1 -2 133\n            2.5942500215023756e-03</internalNodes>\n          <leafValues>\n            2.1716670691967010e-01 2.7199891209602356e-01\n            7.1502441167831421e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 134 -2.5032449513673782e-02 -1 -2 135\n            6.3087949529290199e-03</internalNodes>\n          <leafValues>\n            1.8251839280128479e-01 5.6998378038406372e-01\n            3.5098528861999512e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 136 -3.2494920305907726e-03 -1 -2 137\n            -1.4885730110108852e-02</internalNodes>\n          <leafValues>\n            4.0239268541336060e-01 3.6040958762168884e-01\n            7.2919952869415283e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 138 8.0623216927051544e-03 -1 -2 139\n            2.7405679225921631e-02</internalNodes>\n          <leafValues>\n            6.4914900064468384e-01 5.5189931392669678e-01\n            2.6596811413764954e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 140 3.4368600696325302e-02 -1 -2 141\n            -2.7292970567941666e-02</internalNodes>\n          <leafValues>\n            6.7125129699707031e-01 1.6913780570030212e-01\n            4.3262779712677002e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 142 7.4452121043577790e-04 -1 -2 143\n            7.0336280623450875e-04</internalNodes>\n          <leafValues>\n            3.4051001071929932e-01 5.5167931318283081e-01\n            3.3113878965377808e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 144 -1.2275460362434387e-01 -1 -2 145\n            3.2559928949922323e-03</internalNodes>\n          <leafValues>\n            1.6753150522708893e-01 3.6157518625259399e-01\n            6.4207828044891357e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 146 -3.2090399414300919e-02 -1 -2 147\n            3.2957999501377344e-03</internalNodes>\n          <leafValues>\n            2.9210790991783142e-01 5.6130319833755493e-01\n            3.3578601479530334e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 148 -3.2273170072585344e-03 -1 -2 149\n            1.1171669466421008e-03</internalNodes>\n          <leafValues>\n            6.9706428050994873e-01 3.5411500930786133e-01\n            6.1440062522888184e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 150 -1.7279950901865959e-02 -1 -2 151\n            1.1741200461983681e-02</internalNodes>\n          <leafValues>\n            5.5371809005737305e-01 5.3419572114944458e-01\n            2.7571049332618713e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 152 4.6405228786170483e-03 -1 -2 153\n            -1.6913030296564102e-02</internalNodes>\n          <leafValues>\n            2.4895210564136505e-01 1.7119289934635162e-01\n            5.5239528417587280e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 154 1.0060169734060764e-02 -1 -2 155\n            -6.0715491417795420e-04</internalNodes>\n          <leafValues>\n            8.2734507322311401e-01 3.7793910503387451e-01\n            5.4762518405914307e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 156 -1.0865400545299053e-03 -1 -2 157\n            8.9362077414989471e-03</internalNodes>\n          <leafValues>\n            3.2965409755706787e-01 6.0628837347030640e-01\n            2.4342200160026550e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 158 -2.6372660067863762e-04 -1 -2 159\n            1.3110050000250340e-02</internalNodes>\n          <leafValues>\n            3.8140949606895447e-01 5.5176162719726562e-01\n            3.7268930673599243e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 160 -2.9806280508637428e-03 -1 -2 161\n            -4.1619571857154369e-03</internalNodes>\n          <leafValues>\n            1.2296640127897263e-01 7.2522747516632080e-01\n            4.9734550714492798e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 162 3.3842328935861588e-02 -1 -2 163\n            -1.2564560165628791e-03</internalNodes>\n          <leafValues>\n            5.3483128547668457e-01 5.8519148826599121e-01\n            4.3841668963432312e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 164 -1.9635230302810669e-02 -1 -2 165\n            -9.9625496659427881e-04</internalNodes>\n          <leafValues>\n            2.2978340089321136e-01 6.2959378957748413e-01\n            4.1315990686416626e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 166 -2.3127110674977303e-02 -1 -2 167\n            2.3525709286332130e-02</internalNodes>\n          <leafValues>\n            1.6954590380191803e-01 5.1741302013397217e-01\n            5.9519391506910324e-02</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 168 -1.9356520846486092e-02 -1 -2 169\n            -4.1787112131714821e-03</internalNodes>\n          <leafValues>\n            1.3572479784488678e-01 2.9966288805007935e-01\n            5.7916951179504395e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 170 3.1488779932260513e-03 -1 -2 171\n            7.3972279205918312e-03</internalNodes>\n          <leafValues>\n            6.5925890207290649e-01 5.3071719408035278e-01\n            3.7951210141181946e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 172 7.1955118983169086e-06 -1 -2 173\n            4.7114409506320953e-02</internalNodes>\n          <leafValues>\n            3.1283149123191833e-01 5.5378931760787964e-01\n            1.0273090004920959e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 174 7.2878710925579071e-03 -1 -2 175\n            -6.1887511983513832e-03</internalNodes>\n          <leafValues>\n            4.6608591079711914e-01 7.1588581800460815e-01\n            4.7244489192962646e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 176 2.9757320880889893e-03 -1 -2 177\n            -1.8449809867888689e-03</internalNodes>\n          <leafValues>\n            5.9345688670873642e-02 7.0273017883300781e-01\n            4.7187310457229614e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 178 1.0239540279144421e-04 -1 -2 179\n            2.4277009069919586e-03</internalNodes>\n          <leafValues>\n            5.8947342634201050e-01 4.8623558878898621e-01\n            5.2475881576538086e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 180 -6.4751312136650085e-02 -1 -2 181\n            3.9380151429213583e-04</internalNodes>\n          <leafValues>\n            6.9174712896347046e-01 4.6696171164512634e-01\n            2.3824059963226318e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>31</maxWeakCount>\n      <stageThreshold>1.4546750068664551e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 182 1.4397440245375037e-03 -1 -2 183\n            -5.4068560712039471e-04</internalNodes>\n          <leafValues>\n            2.7734708786010742e-01 7.4271547794342041e-01\n            2.4797350168228149e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 184 -7.1237959673453588e-06 -1 -2 185\n            -2.3661039303988218e-03</internalNodes>\n          <leafValues>\n            2.1995030343532562e-01 5.8899897336959839e-01\n            2.5957161188125610e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 186 1.7343269428238273e-03 -1 -2 187\n            1.5874590026214719e-03</internalNodes>\n          <leafValues>\n            1.8601259589195251e-01 4.1518709063529968e-01\n            7.1034741401672363e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 188 3.7285638973116875e-03 -1 -2 189\n            -1.2883819639682770e-01</internalNodes>\n          <leafValues>\n            2.5279670953750610e-01 1.3930009305477142e-01\n            5.2545148134231567e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 190 7.9412180930376053e-03 -1 -2 191\n            -1.2661729939281940e-02</internalNodes>\n          <leafValues>\n            2.4877290427684784e-01 2.7107000350952148e-01\n            6.6188377141952515e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 192 3.0146789868013002e-05 -1 -2 193\n            -1.6330160200595856e-02</internalNodes>\n          <leafValues>\n            3.8128259778022766e-01 2.3264320194721222e-01\n            5.2630108594894409e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 194 1.4622770322603174e-05 -1 -2 195\n            -2.0858660340309143e-02</internalNodes>\n          <leafValues>\n            4.2933320999145508e-01 1.6004039347171783e-01\n            6.7823147773742676e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 196 2.8194559272378683e-03 -1 -2 197\n            3.7899368908256292e-03</internalNodes>\n          <leafValues>\n            6.6792941093444824e-01 4.5877051353454590e-01\n            7.1762388944625854e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 198 3.5344641655683517e-02 -1 -2 199\n            -1.1571600334718823e-03</internalNodes>\n          <leafValues>\n            1.8640750646591187e-01 5.5382597446441650e-01\n            3.1504508852958679e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 200 -5.8742752298712730e-03 -1 -2 201\n            -1.5201780115603469e-05</internalNodes>\n          <leafValues>\n            2.8287911415100098e-01 5.8702242374420166e-01\n            3.7048238515853882e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 202 -2.2681879636365920e-04 -1 -2 203\n            3.7845689803361893e-03</internalNodes>\n          <leafValues>\n            4.2189309000968933e-01 6.6670012474060059e-01\n            2.4611820280551910e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 204 -8.5295992903411388e-05 -1 -2 205\n            -4.4394891709089279e-02</internalNodes>\n          <leafValues>\n            3.5575878620147705e-01 1.6655470430850983e-01\n            5.2348488569259644e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 206 1.0126030538231134e-03 -1 -2 207\n            -7.6327780261635780e-03</internalNodes>\n          <leafValues>\n            2.8846129775047302e-01 2.9693400859832764e-01\n            6.0801112651824951e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 208 4.0330411866307259e-03 -1 -2 209\n            1.3676689565181732e-01</internalNodes>\n          <leafValues>\n            4.5363900065422058e-01 5.1772642135620117e-01\n            1.4491820335388184e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 210 -5.0060478970408440e-03 -1 -2 211\n            -1.2475839816033840e-02</internalNodes>\n          <leafValues>\n            7.6169097423553467e-01 2.1597060561180115e-01\n            5.4601877927780151e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 212 -9.4012258341535926e-04 -1 -2 213\n            -1.2191980145871639e-02</internalNodes>\n          <leafValues>\n            3.9262959361076355e-01 3.4788811206817627e-01\n            5.5426627397537231e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 214 -5.4959481349214911e-04 -1 -2 215\n            -2.1802430273965001e-04</internalNodes>\n          <leafValues>\n            6.0642760992050171e-01 5.6974071264266968e-01\n            1.7797139286994934e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 216 6.9115799851715565e-03 -1 -2 217\n            -9.7631698008626699e-04</internalNodes>\n          <leafValues>\n            5.3793722391128540e-01 3.3278390765190125e-01\n            5.4615312814712524e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 218 -8.7870173156261444e-03 -1 -2 219\n            -1.6761029837653041e-03</internalNodes>\n          <leafValues>\n            2.1161609888076782e-01 6.6358232498168945e-01\n            4.3658590316772461e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 220 -5.5694948881864548e-02 -1 -2 221\n            -1.9844379276037216e-02</internalNodes>\n          <leafValues>\n            5.3874248266220093e-01 1.6028049588203430e-01\n            5.3304588794708252e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 222 -7.4751611100509763e-04 -1 -2 223\n            2.3032890632748604e-02</internalNodes>\n          <leafValues>\n            2.9174768924713135e-01 5.6081241369247437e-01\n            1.9979810714721680e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 224 -3.0700280331075191e-03 -1 -2 225\n            -1.1636839481070638e-03</internalNodes>\n          <leafValues>\n            3.9383140206336975e-01 5.7574361562728882e-01\n            4.2394569516181946e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 226 2.2464339435100555e-01 -1 -2 227\n            1.4412109740078449e-03</internalNodes>\n          <leafValues>\n            7.6765531301498413e-01 5.3538662195205688e-01\n            2.5147768855094910e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 228 -3.0011249706149101e-02 -1 -2 229\n            -5.3078960627317429e-02</internalNodes>\n          <leafValues>\n            2.3649039864540100e-01 2.3858639597892761e-01\n            5.4146647453308105e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 230 2.0800929050892591e-03 -1 -2 231\n            -4.0738182142376900e-03</internalNodes>\n          <leafValues>\n            6.5116149187088013e-01 6.0304141044616699e-01\n            3.5877010226249695e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 232 -1.9529370591044426e-02 -1 -2 233\n            -5.3309470415115356e-02</internalNodes>\n          <leafValues>\n            5.4235929250717163e-01 2.3609539866447449e-01\n            5.4017579555511475e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 234 -3.4849561750888824e-02 -1 -2 235\n            -1.2658450007438660e-01</internalNodes>\n          <leafValues>\n            2.8369858860969543e-01 1.8135160207748413e-01\n            5.4210460186004639e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 236 7.3325118137290701e-06 -1 -2 237\n            -1.1843870393931866e-02</internalNodes>\n          <leafValues>\n            3.9803659915924072e-01 2.6163849234580994e-01\n            5.2377301454544067e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 238 -4.8470678739249706e-03 -1 -2 239\n            8.1693977117538452e-03</internalNodes>\n          <leafValues>\n            2.4381080269813538e-01 5.3271460533142090e-01\n            8.1903767585754395e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 240 -6.4716790802776814e-03 -1 -2 241\n            -1.5188479665084742e-05</internalNodes>\n          <leafValues>\n            4.6796938776969910e-01 5.5639117956161499e-01\n            4.3675860762596130e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 242 3.0696711037307978e-03 -1 -2 243\n            -1.6296720423270017e-04</internalNodes>\n          <leafValues>\n            6.6643488407135010e-01 5.5946111679077148e-01\n            3.0427119135856628e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>39</maxWeakCount>\n      <stageThreshold>1.8572250366210938e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            1 0 244 -9.8275858908891678e-03 -1 -2 245\n            -4.1693858802318573e-03</internalNodes>\n          <leafValues>\n            2.1160189807415009e-01 6.9246852397918701e-01\n            3.0437770485877991e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 246 3.5341319744475186e-04 -1 -2 247\n            4.8054549843072891e-03</internalNodes>\n          <leafValues>\n            3.1832858920097351e-01 5.4565590620040894e-01\n            2.5222688913345337e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 248 2.1071180526632816e-04 -1 -2 249\n            -2.8318869881331921e-03</internalNodes>\n          <leafValues>\n            2.9026180505752563e-01 3.1304559111595154e-01\n            6.8849372863769531e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 250 -7.5633679443853907e-06 -1 -2 251\n            -8.2888139877468348e-04</internalNodes>\n          <leafValues>\n            2.9624658823013306e-01 3.0996260046958923e-01\n            5.7525151968002319e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 252 1.6209259629249573e-03 -1 -2 253\n            9.1338958591222763e-03</internalNodes>\n          <leafValues>\n            3.9931958913803101e-01 4.8273721337318420e-01\n            7.5378328561782837e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 254 -4.1212290525436401e-03 -1 -2 255\n            -2.5447290390729904e-03</internalNodes>\n          <leafValues>\n            2.6169270277023315e-01 3.1087028980255127e-01\n            5.4912358522415161e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 256 -6.2652782071381807e-04 -1 -2 257\n            -3.6596331483451650e-05</internalNodes>\n          <leafValues>\n            3.2396918535232544e-01 6.5174108743667603e-01\n            4.1789120435714722e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 258 1.3882719911634922e-02 -1 -2 259\n            1.0493700392544270e-03</internalNodes>\n          <leafValues>\n            6.7712038755416870e-01 4.1595110297203064e-01\n            5.6528919935226440e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 260 1.8215360119938850e-02 -1 -2 261\n            -1.1334580369293690e-02</internalNodes>\n          <leafValues>\n            7.6896011829376221e-01 2.8733238577842712e-01\n            4.9889329075813293e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 262 -4.1097560897469521e-03 -1 -2 263\n            4.2612891411408782e-04</internalNodes>\n          <leafValues>\n            5.4630082845687866e-01 3.6312350630760193e-01\n            5.5125522613525391e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 264 6.0301548801362514e-03 -1 -2 265\n            3.3587709185667336e-04</internalNodes>\n          <leafValues>\n            1.1437670141458511e-01 2.8910788893699646e-01\n            5.4473417997360229e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 266 6.2279507983475924e-04 -1 -2 267\n            -2.5837119668722153e-02</internalNodes>\n          <leafValues>\n            3.0234318971633911e-01 2.1670059859752655e-01\n            5.2781528234481812e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 268 2.1774910390377045e-02 -1 -2 269\n            1.7682299949228764e-03</internalNodes>\n          <leafValues>\n            3.2548341155052185e-01 5.2630507946014404e-01\n            7.5263291597366333e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 270 -1.3793810270726681e-02 -1 -2 271\n            -5.0852829590439796e-03</internalNodes>\n          <leafValues>\n            7.4103301763534546e-01 6.8366098403930664e-01\n            4.5790711045265198e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 272 6.1795017682015896e-03 -1 -2 273\n            1.0030319914221764e-02</internalNodes>\n          <leafValues>\n            7.4499362707138062e-01 4.8607799410820007e-01\n            2.3614570498466492e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 274 -6.4201927743852139e-03 -1 -2 275\n            -5.6961281225085258e-03</internalNodes>\n          <leafValues>\n            1.4673270285129547e-01 2.3478199541568756e-01\n            5.3233772516250610e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 276 -7.1498160250484943e-03 -1 -2 277\n            2.4450740311294794e-03</internalNodes>\n          <leafValues>\n            1.4770570397377014e-01 3.4985339641571045e-01\n            5.8035618066787720e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 278 -3.7503410130739212e-02 -1 -2 279\n            4.7799441381357610e-04</internalNodes>\n          <leafValues>\n            5.2595508098602295e-01 4.3628829717636108e-01\n            6.2089228630065918e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 280 -7.0806080475449562e-03 -1 -2 281\n            3.2818000763654709e-02</internalNodes>\n          <leafValues>\n            2.0394609868526459e-01 5.1983588933944702e-01\n            1.3711960613727570e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 282 6.5188988810405135e-04 -1 -2 283\n            4.6485587954521179e-03</internalNodes>\n          <leafValues>\n            6.3234299421310425e-01 4.7201630473136902e-01\n            6.5670871734619141e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 284 -1.9827929791063070e-03 -1 -2 285\n            -1.6011310508474708e-03</internalNodes>\n          <leafValues>\n            6.0530602931976318e-01 5.0905191898345947e-01\n            3.1169331073760986e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 286 -3.0539939180016518e-03 -1 -2 287\n            4.3212040327489376e-04</internalNodes>\n          <leafValues>\n            3.4298041462898254e-01 3.8384029269218445e-01\n            5.7755982875823975e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 288 -2.7452120557427406e-02 -1 -2 289\n            9.3099439982324839e-04</internalNodes>\n          <leafValues>\n            2.1434690058231354e-01 5.9529662132263184e-01\n            3.7601581215858459e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 290 6.7144189961254597e-03 -1 -2 291\n            -3.3701690845191479e-03</internalNodes>\n          <leafValues>\n            5.6926268339157104e-01 5.7843041419982910e-01\n            3.9742821455001831e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 292 -1.8903959542512894e-02 -1 -2 293\n            -6.5850871615111828e-03</internalNodes>\n          <leafValues>\n            1.8188929557800293e-01 6.8491101264953613e-01\n            4.3515840172767639e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 294 5.8810501359403133e-03 -1 -2 295\n            8.0092082498595119e-04</internalNodes>\n          <leafValues>\n            2.7266609668731689e-01 4.2364311218261719e-01\n            5.8446758985519409e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 296 1.8510579830035567e-03 -1 -2 297\n            6.3273650594055653e-03</internalNodes>\n          <leafValues>\n            3.3713209629058838e-01 5.2702218294143677e-01\n            8.0536508560180664e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 298 -3.3820930402725935e-03 -1 -2 299\n            -1.9292969955131412e-03</internalNodes>\n          <leafValues>\n            2.8660181164741516e-01 5.8889460563659668e-01\n            3.8957870006561279e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 300 1.4995220117270947e-02 -1 -2 301\n            -2.6330750435590744e-02</internalNodes>\n          <leafValues>\n            2.1778169274330139e-01 1.7753170430660248e-01\n            5.6714701652526855e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 302 -4.1734222322702408e-03 -1 -2 303\n            2.7268350124359131e-02</internalNodes>\n          <leafValues>\n            4.6529620885848999e-01 4.7683110833168030e-01\n            5.6952387094497681e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 304 9.8880263976752758e-04 -1 -2 305\n            -1.0528849670663476e-03</internalNodes>\n          <leafValues>\n            3.3974018692970276e-01 6.2500411272048950e-01\n            4.2884120345115662e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 306 5.2288072183728218e-03 -1 -2 307\n            3.0395459383726120e-02</internalNodes>\n          <leafValues>\n            5.3477621078491211e-01 4.1155189275741577e-01\n            5.6607538461685181e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 308 -7.9113930463790894e-02 -1 -2 309\n            1.8231669440865517e-02</internalNodes>\n          <leafValues>\n            7.8813230991363525e-01 3.6043399572372437e-01\n            5.5695050954818726e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 310 5.2288072183728218e-03 -1 -2 311\n            4.3922828626818955e-04</internalNodes>\n          <leafValues>\n            5.4166442155838013e-01 5.5071568489074707e-01\n            3.8822770118713379e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 312 -8.6501962505280972e-04 -1 -2 313\n            1.0326979681849480e-03</internalNodes>\n          <leafValues>\n            3.1858509778976440e-01 5.5783641338348389e-01\n            3.2192459702491760e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 314 -7.2997747920453548e-03 -1 -2 315\n            -9.3629042385146022e-04</internalNodes>\n          <leafValues>\n            7.0732331275939941e-01 5.5580157041549683e-01\n            4.6138420701026917e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 316 -6.0483231209218502e-03 -1 -2 317\n            6.7529221996665001e-03</internalNodes>\n          <leafValues>\n            6.8692898750305176e-01 4.8703178763389587e-01\n            2.6503708958625793e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 318 5.3078029304742813e-02 -1 -2 319\n            -1.0225810110569000e-03</internalNodes>\n          <leafValues>\n            5.2815151214599609e-01 6.0858821868896484e-01\n            4.3048679828643799e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 320 3.1270649284124374e-02 -1 -2 321\n            -6.3522169366478920e-03</internalNodes>\n          <leafValues>\n            5.4458320140838623e-01 5.3283357620239258e-01\n            2.3643240332603455e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>45</maxWeakCount>\n      <stageThreshold>2.1578119277954102e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            1 0 322 -6.2215630896389484e-03 -1 -2 323\n            2.1097389981150627e-03</internalNodes>\n          <leafValues>\n            2.6255810260772705e-01 1.5649929642677307e-01\n            6.7928832769393921e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 324 1.0845859535038471e-02 -1 -2 325\n            6.4230401767417789e-04</internalNodes>\n          <leafValues>\n            3.4858089685440063e-01 3.6982551217079163e-01\n            5.9216582775115967e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 326 7.3311722371727228e-04 -1 -2 327\n            1.0134200565516949e-03</internalNodes>\n          <leafValues>\n            3.0070841312408447e-01 3.6249229311943054e-01\n            7.0724260807037354e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 328 1.1093559674918652e-02 -1 -2 329\n            -7.9127531498670578e-03</internalNodes>\n          <leafValues>\n            4.4167020916938782e-01 3.0287081003189087e-01\n            5.4173761606216431e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 330 1.2905309908092022e-02 -1 -2 331\n            -4.2430912144482136e-03</internalNodes>\n          <leafValues>\n            4.3745040893554688e-01 4.4015899300575256e-01\n            7.5651907920837402e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 332 -2.1304309484548867e-04 -1 -2 333\n            -2.2308640182018280e-03</internalNodes>\n          <leafValues>\n            2.3107869923114777e-01 3.5681959986686707e-01\n            5.7499992847442627e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 334 2.6400520000606775e-03 -1 -2 335\n            7.5101032853126526e-02</internalNodes>\n          <leafValues>\n            3.5936889052391052e-01 6.3635677099227905e-01\n            2.3270289599895477e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 336 -7.7012968249619007e-03 -1 -2 337\n            1.5588370151817799e-03</internalNodes>\n          <leafValues>\n            7.0746237039566040e-01 5.7002371549606323e-01\n            3.5904508829116821e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 338 -4.7687938786111772e-04 -1 -2 339\n            8.4234727546572685e-04</internalNodes>\n          <leafValues>\n            2.8054410219192505e-01 4.1254189610481262e-01\n            6.1779958009719849e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 340 -1.2825109995901585e-02 -1 -2 341\n            -6.5156567143276334e-04</internalNodes>\n          <leafValues>\n            5.4030781984329224e-01 5.6336438655853271e-01\n            3.3565390110015869e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 342 -1.2006159871816635e-02 -1 -2 343\n            1.3213419588282704e-03</internalNodes>\n          <leafValues>\n            7.1095108985900879e-01 4.9038508534431458e-01\n            2.8245830535888672e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 344 -2.0307440310716629e-02 -1 -2 345\n            4.0180929936468601e-03</internalNodes>\n          <leafValues>\n            1.8913699686527252e-01 5.3779661655426025e-01\n            3.1194949150085449e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 346 4.5315311290323734e-03 -1 -2 347\n            -4.4381739571690559e-03</internalNodes>\n          <leafValues>\n            7.2067582607269287e-01 1.8546679615974426e-01\n            4.9817329645156860e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 348 1.5692010056227446e-03 -1 -2 349\n            -4.9516442231833935e-03</internalNodes>\n          <leafValues>\n            2.6382741332054138e-01 6.8710672855377197e-01\n            4.7146868705749512e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 350 -2.7429679408669472e-02 -1 -2 351\n            1.4181969454512000e-03</internalNodes>\n          <leafValues>\n            1.5482850372791290e-01 4.3768429756164551e-01\n            6.3273680210113525e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 352 -1.3078940100967884e-02 -1 -2 353\n            -3.5092779435217381e-03</internalNodes>\n          <leafValues>\n            3.1668141484260559e-01 6.1997437477111816e-01\n            4.3796870112419128e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 354 1.8920730799436569e-02 -1 -2 355\n            2.1683350205421448e-03</internalNodes>\n          <leafValues>\n            1.4707140624523163e-01 5.8094590902328491e-01\n            3.4319490194320679e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 356 1.6401590546593070e-03 -1 -2 357\n            1.4005920093040913e-04</internalNodes>\n          <leafValues>\n            3.9594578742980957e-01 3.2400250434875488e-01\n            5.6466472148895264e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 358 -3.3137591090053320e-03 -1 -2 359\n            -2.9459029901772738e-03</internalNodes>\n          <leafValues>\n            4.2745280265808105e-01 3.3416679501533508e-01\n            6.6279602050781250e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 360 1.3612229668069631e-04 -1 -2 361\n            6.0512032359838486e-04</internalNodes>\n          <leafValues>\n            4.0469279885292053e-01 5.4840582609176636e-01\n            3.5699409246444702e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 362 -1.7513990402221680e-02 -1 -2 363\n            -1.8735030665993690e-02</internalNodes>\n          <leafValues>\n            1.8241509795188904e-01 7.9718202352523804e-01\n            5.0685691833496094e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 364 1.2065649963915348e-02 -1 -2 365\n            -2.6544178836047649e-03</internalNodes>\n          <leafValues>\n            2.1670070290565491e-01 6.5841788053512573e-01\n            4.6282431483268738e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 366 1.4501289697363973e-03 -1 -2 367\n            1.0954019613564014e-02</internalNodes>\n          <leafValues>\n            2.0902520418167114e-01 5.1123052835464478e-01\n            7.7845758199691772e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 368 1.5771709382534027e-02 -1 -2 369\n            -1.4252689667046070e-02</internalNodes>\n          <leafValues>\n            5.1323592662811279e-01 1.7424149811267853e-01\n            5.2671480178833008e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 370 3.0411860279855318e-05 -1 -2 371\n            2.3486299440264702e-02</internalNodes>\n          <leafValues>\n            3.4184479713439941e-01 5.6312650442123413e-01\n            2.0063939690589905e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 372 5.2205449901521206e-03 -1 -2 373\n            -2.5812430307269096e-02</internalNodes>\n          <leafValues>\n            6.2496489286422729e-01 3.2032281160354614e-01\n            5.1993298530578613e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 374 -1.9526650430634618e-03 -1 -2 375\n            -8.1470049917697906e-03</internalNodes>\n          <leafValues>\n            6.1407059431076050e-01 6.5928959846496582e-01\n            3.7111249566078186e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 376 3.2962448894977570e-03 -1 -2 377\n            -1.3961310032755136e-03</internalNodes>\n          <leafValues>\n            2.9521119594573975e-01 3.3208039402961731e-01\n            5.5284148454666138e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 378 -4.1055441834032536e-03 -1 -2 379\n            -1.0888779535889626e-02</internalNodes>\n          <leafValues>\n            1.7105500400066376e-01 3.3594349026679993e-01\n            5.6749051809310913e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 380 -7.6768421567976475e-03 -1 -2 381\n            -9.7729787230491638e-03</internalNodes>\n          <leafValues>\n            4.7732418775558472e-01 8.0810451507568359e-01\n            4.8458281159400940e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 382 6.0439710505306721e-03 -1 -2 383\n            -4.6134641161188483e-04</internalNodes>\n          <leafValues>\n            6.7840021848678589e-01 5.5146390199661255e-01\n            3.6423599720001221e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 384 5.7992361485958099e-02 -1 -2 385\n            5.9384980704635382e-04</internalNodes>\n          <leafValues>\n            1.2544350326061249e-01 4.4248789548873901e-01\n            5.7284617424011230e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 386 -6.2353480607271194e-03 -1 -2 387\n            -1.2784929946064949e-02</internalNodes>\n          <leafValues>\n            2.8050419688224792e-01 1.9509120285511017e-01\n            5.6529247760772705e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 388 4.1973669431172311e-04 -1 -2 389\n            8.0646801507100463e-04</internalNodes>\n          <leafValues>\n            6.1664837598800659e-01 4.5265799760818481e-01\n            5.9444868564605713e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 390 -1.6339010326191783e-03 -1 -2 391\n            -4.8299999907612801e-03</internalNodes>\n          <leafValues>\n            4.0869420766830444e-01 2.7935269474983215e-01\n            6.4449352025985718e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 392 -6.3992068171501160e-03 -1 -2 393\n            1.0819199681282043e-01</internalNodes>\n          <leafValues>\n            5.6716561317443848e-01 5.3118121623992920e-01\n            2.6143568754196167e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 394 6.5056560561060905e-04 -1 -2 395\n            2.0611250773072243e-02</internalNodes>\n          <leafValues>\n            2.9967740178108215e-01 4.4899430871009827e-01\n            6.8882799148559570e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 396 -2.5129050016403198e-02 -1 -2 397\n            1.7922939732670784e-03</internalNodes>\n          <leafValues>\n            5.1968640089035034e-01 3.4669959545135498e-01\n            5.5335879325866699e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 398 1.5626220265403390e-03 -1 -2 399\n            -6.1898730928078294e-04</internalNodes>\n          <leafValues>\n            3.0814400315284729e-01 2.6938709616661072e-01\n            5.5444890260696411e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 400 4.8111421056091785e-03 -1 -2 401\n            2.2484229411929846e-03</internalNodes>\n          <leafValues>\n            5.5878478288650513e-01 4.6721130609512329e-01\n            6.0908252000808716e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 402 -3.0147239565849304e-02 -1 -2 403\n            2.7548679709434509e-01</internalNodes>\n          <leafValues>\n            9.0275919437408447e-01 4.7198349237442017e-01\n            2.1969200670719147e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 404 3.6894630175083876e-03 -1 -2 405\n            7.2957701049745083e-03</internalNodes>\n          <leafValues>\n            6.2730091810226440e-01 4.8392179608345032e-01\n            6.9090622663497925e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 406 -5.6211069226264954e-02 -1 -2 407\n            -2.6478560175746679e-03</internalNodes>\n          <leafValues>\n            1.7384879291057587e-01 6.3041448593139648e-01\n            4.4743019342422485e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 408 -1.4534000074490905e-03 -1 -2 409\n            2.8540920466184616e-03</internalNodes>\n          <leafValues>\n            5.3025382757186890e-01 5.3383970260620117e-01\n            3.7968829274177551e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 410 5.8243022067472339e-04 -1 -2 411\n            9.2509482055902481e-04</internalNodes>\n          <leafValues>\n            3.2698369026184082e-01 4.5548120141029358e-01\n            6.3583481311798096e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>47</maxWeakCount>\n      <stageThreshold>2.2585290908813477e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 412 1.9806440919637680e-02 -1 -2 413\n            7.0395611692219973e-04</internalNodes>\n          <leafValues>\n            2.8097251057624817e-01 3.1198260188102722e-01\n            7.0903062820434570e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 414 2.5563780218362808e-03 -1 -2 415\n            1.0824160417541862e-03</internalNodes>\n          <leafValues>\n            2.9819479584693909e-01 3.0205601453781128e-01\n            5.8088111877441406e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 416 -9.2893769033253193e-04 -1 -2 417\n            -1.8009729683399200e-02</internalNodes>\n          <leafValues>\n            3.7381029129028320e-01 2.1631260216236115e-01\n            6.6192537546157837e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 418 2.3500190582126379e-03 -1 -2 419\n            8.1822491483762860e-04</internalNodes>\n          <leafValues>\n            2.9104039072990417e-01 5.5786228179931641e-01\n            3.3666279911994934e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 420 6.2095321482047439e-04 -1 -2 421\n            9.6780969761312008e-04</internalNodes>\n          <leafValues>\n            4.0724259614944458e-01 6.8595957756042480e-01\n            3.1054618954658508e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 422 4.8000211245380342e-04 -1 -2 423\n            9.0538640506565571e-05</internalNodes>\n          <leafValues>\n            3.3373329043388367e-01 3.3709588646888733e-01\n            5.4512107372283936e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 424 -4.3914798647165298e-02 -1 -2 425\n            -5.6501338258385658e-03</internalNodes>\n          <leafValues>\n            2.6256701350212097e-01 6.0504627227783203e-01\n            3.2324150204658508e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 426 3.8661491125822067e-03 -1 -2 427\n            -6.3069426687434316e-05</internalNodes>\n          <leafValues>\n            3.2626131176948547e-01 5.8173078298568726e-01\n            4.1643899679183960e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 428 5.2533738315105438e-02 -1 -2 429\n            1.3818660518154502e-03</internalNodes>\n          <leafValues>\n            7.0953989028930664e-01 5.2928757667541504e-01\n            2.5413888692855835e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 430 -8.9264067355543375e-04 -1 -2 431\n            8.5579507052898407e-02</internalNodes>\n          <leafValues>\n            4.0853410959243774e-01 5.2632361650466919e-01\n            3.0032029747962952e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 432 -1.8343339615967125e-04 -1 -2 433\n            -9.7924815490841866e-03</internalNodes>\n          <leafValues>\n            4.0292051434516907e-01 3.5213199257850647e-01\n            6.6640049219131470e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 434 1.4428620226681232e-02 -1 -2 435\n            -4.5687001198530197e-02</internalNodes>\n          <leafValues>\n            4.5935660600662231e-01 1.4747560024261475e-01\n            5.1786321401596069e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 436 -2.5763090234249830e-03 -1 -2 437\n            -3.8301859050989151e-02</internalNodes>\n          <leafValues>\n            1.8372780084609985e-01 8.0826580524444580e-01\n            5.1666879653930664e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 438 2.8978290501981974e-03 -1 -2 439\n            -2.5165060069411993e-03</internalNodes>\n          <leafValues>\n            4.7980138659477234e-01 3.3462959527969360e-01\n            5.4444491863250732e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 440 5.6281982688233256e-04 -1 -2 441\n            3.6684391088783741e-03</internalNodes>\n          <leafValues>\n            3.5890269279479980e-01 5.9831297397613525e-01\n            2.9839640855789185e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 442 2.1319789811968803e-03 -1 -2 443\n            7.6037310063838959e-03</internalNodes>\n          <leafValues>\n            6.1632239818572998e-01 5.2171301841735840e-01\n            2.0541590452194214e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 444 -1.1668079969240353e-04 -1 -2 445\n            3.1659509986639023e-03</internalNodes>\n          <leafValues>\n            3.4466689825057983e-01 5.5974847078323364e-01\n            2.6737868785858154e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 446 -2.2569499909877777e-02 -1 -2 447\n            2.7129601221531630e-04</internalNodes>\n          <leafValues>\n            6.9002681970596313e-01 4.4866389036178589e-01\n            5.5087852478027344e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 448 -1.5434459783136845e-02 -1 -2 449\n            -8.4861656650900841e-03</internalNodes>\n          <leafValues>\n            2.0483230054378510e-01 1.2549529969692230e-01\n            5.0603562593460083e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 450 -1.1807470023632050e-01 -1 -2 451\n            -1.2300079688429832e-03</internalNodes>\n          <leafValues>\n            6.7633062601089478e-02 5.6607007980346680e-01\n            4.2922011017799377e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 452 -7.0290351286530495e-03 -1 -2 453\n            8.9325206354260445e-03</internalNodes>\n          <leafValues>\n            7.1364039182662964e-01 4.3388760089874268e-01\n            7.0608752965927124e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 454 -4.7735981643199921e-02 -1 -2 455\n            -4.4155579060316086e-02</internalNodes>\n          <leafValues>\n            5.2686852216720581e-01 2.5805801153182983e-01\n            5.4069608449935913e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 456 -2.5983480736613274e-02 -1 -2 457\n            -4.7885831445455551e-03</internalNodes>\n          <leafValues>\n            1.9050540030002594e-01 2.5518929958343506e-01\n            5.3390771150588989e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 458 6.7423451691865921e-03 -1 -2 459\n            1.1654750443994999e-02</internalNodes>\n          <leafValues>\n            4.6933099627494812e-01 5.2619642019271851e-01\n            3.1454348564147949e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 460 -5.6982729583978653e-03 -1 -2 461\n            -7.2983349673449993e-03</internalNodes>\n          <leafValues>\n            1.7568530142307281e-01 7.7747297286987305e-01\n            5.1242929697036743e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 462 7.9091778025031090e-03 -1 -2 463\n            -1.5874979726504534e-04</internalNodes>\n          <leafValues>\n            5.2845597267150879e-01 3.8878020644187927e-01\n            5.5011737346649170e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 464 -6.2235877849161625e-03 -1 -2 465\n            1.3308860361576080e-03</internalNodes>\n          <leafValues>\n            2.4898290634155273e-01 4.2621460556983948e-01\n            5.9350621700286865e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 466 5.2055278792977333e-03 -1 -2 467\n            1.4065169729292393e-02</internalNodes>\n          <leafValues>\n            2.5452229380607605e-01 4.8519900441169739e-01\n            7.0214188098907471e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 468 -6.7384149879217148e-03 -1 -2 469\n            3.3406780567020178e-03</internalNodes>\n          <leafValues>\n            7.1432709693908691e-01 5.1757252216339111e-01\n            2.8086438775062561e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 470 -1.1880699545145035e-02 -1 -2 471\n            1.4226379571482539e-03</internalNodes>\n          <leafValues>\n            5.1732218265533447e-01 4.5028659701347351e-01\n            5.7956951856613159e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 472 2.9858129564672709e-03 -1 -2 473\n            -2.0481580868363380e-03</internalNodes>\n          <leafValues>\n            1.9151160120964050e-01 6.5024322271347046e-01\n            4.5593151450157166e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 474 1.7122729914262891e-03 -1 -2 475\n            -1.6980869695544243e-02</internalNodes>\n          <leafValues>\n            5.3762471675872803e-01 7.0562332868576050e-01\n            4.9146059155464172e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 476 -1.1290470138192177e-03 -1 -2 477\n            2.8620059601962566e-03</internalNodes>\n          <leafValues>\n            2.6787060499191284e-01 4.4108539819717407e-01\n            6.3683199882507324e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 478 -3.8065758999437094e-03 -1 -2 479\n            5.9090270660817623e-03</internalNodes>\n          <leafValues>\n            2.7635639905929565e-01 4.8673018813133240e-01\n            6.7287760972976685e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 480 1.1004370171576738e-03 -1 -2 481\n            -2.3396299220621586e-03</internalNodes>\n          <leafValues>\n            4.0705141425132751e-01 2.6049488782882690e-01\n            6.1548602581024170e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 482 -3.6068160552531481e-03 -1 -2 483\n            4.0831189602613449e-02</internalNodes>\n          <leafValues>\n            5.7319998741149902e-01 4.9733769893646240e-01\n            7.3870068788528442e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 484 -7.1082250215113163e-03 -1 -2 485\n            -9.3759730225428939e-04</internalNodes>\n          <leafValues>\n            6.9847512245178223e-01 2.6911678910255432e-01\n            4.7417798638343811e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 486 -1.6740820137783885e-03 -1 -2 487\n            8.8287703692913055e-02</internalNodes>\n          <leafValues>\n            3.5510140657424927e-01 5.2446138858795166e-01\n            2.0966500043869019e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 488 8.2009629113599658e-04 -1 -2 489\n            -7.6624617213383317e-04</internalNodes>\n          <leafValues>\n            4.1310968995094299e-01 4.6202930808067322e-01\n            6.7754101753234863e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 490 6.5769668435677886e-04 -1 -2 491\n            -2.1304790861904621e-03</internalNodes>\n          <leafValues>\n            5.6282752752304077e-01 5.5768597126007080e-01\n            4.5776501297950745e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 492 -3.7317050737328827e-04 -1 -2 493\n            -1.1172230355441570e-02</internalNodes>\n          <leafValues>\n            4.9592560529708862e-01 5.6256359815597534e-01\n            2.0471079647541046e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 494 4.3435219675302505e-02 -1 -2 495\n            9.6736161503940821e-04</internalNodes>\n          <leafValues>\n            2.2421480715274811e-01 4.5333439111709595e-01\n            6.1999320983886719e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 496 -3.1452889088541269e-03 -1 -2 497\n            1.5233129961416125e-03</internalNodes>\n          <leafValues>\n            6.6627562046051025e-01 5.0079882144927979e-01\n            2.3849929869174957e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 498 2.0854279864579439e-03 -1 -2 499\n            3.6098200827836990e-02</internalNodes>\n          <leafValues>\n            3.7535008788108826e-01 5.1771712303161621e-01\n            1.6344930231571198e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 500 1.6179570229724050e-03 -1 -2 501\n            -6.2132300809025764e-04</internalNodes>\n          <leafValues>\n            2.5873818993568420e-01 6.2995338439941406e-01\n            4.6587899327278137e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 502 7.1878539165481925e-04 -1 -2 503\n            -3.9339520037174225e-02</internalNodes>\n          <leafValues>\n            3.3540761470794678e-01 2.1541289985179901e-01\n            5.2357137203216553e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 504 -1.0988829890266061e-03 -1 -2 505\n            2.1191420964896679e-03</internalNodes>\n          <leafValues>\n            6.4688968658447266e-01 2.8930890560150146e-01\n            5.2548158168792725e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>53</maxWeakCount>\n      <stageThreshold>2.5609300613403320e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 506 5.2359891124069691e-03 -1 -2 507\n            -2.2169889416545630e-03</internalNodes>\n          <leafValues>\n            3.2997110486030579e-01 7.0415931940078735e-01\n            3.2354658842086792e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 508 -8.2303592935204506e-03 -1 -2 509\n            -8.2303592935204506e-03</internalNodes>\n          <leafValues>\n            4.9611708521842957e-01 7.1280431747436523e-01\n            4.9611708521842957e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 510 4.5343261444941163e-04 -1 -2 511\n            -4.1777061414904892e-04</internalNodes>\n          <leafValues>\n            3.2084721326828003e-01 6.6139167547225952e-01\n            3.5513329505920410e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 512 2.7823769487440586e-03 -1 -2 513\n            -6.0361868236213923e-05</internalNodes>\n          <leafValues>\n            3.7101349234580994e-01 5.7463937997817993e-01\n            3.8948801159858704e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 514 3.5061789676547050e-03 -1 -2 515\n            1.7013119941111654e-04</internalNodes>\n          <leafValues>\n            3.0541029572486877e-01 2.8855779767036438e-01\n            6.4877450466156006e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 516 -2.3378930054605007e-03 -1 -2 517\n            -2.1369170863181353e-03</internalNodes>\n          <leafValues>\n            3.1744310259819031e-01 3.8209199905395508e-01\n            5.2328932285308838e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 518 1.0250400518998504e-03 -1 -2 519\n            -4.4726220949087292e-05</internalNodes>\n          <leafValues>\n            3.6227950453758240e-01 6.5389591455459595e-01\n            4.0036809444427490e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 520 5.7102291611954570e-04 -1 -2 521\n            5.7743012439459562e-04</internalNodes>\n          <leafValues>\n            3.8931730389595032e-01 5.6145328283309937e-01\n            3.6876440048217773e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 522 7.9692091094329953e-04 -1 -2 523\n            3.5945948911830783e-04</internalNodes>\n          <leafValues>\n            6.4430278539657593e-01 3.3808529376983643e-01\n            5.8246481418609619e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 524 4.3973900028504431e-04 -1 -2 525\n            -8.9061429025605321e-04</internalNodes>\n          <leafValues>\n            3.9387670159339905e-01 3.4279710054397583e-01\n            5.5156987905502319e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 526 5.4110242053866386e-03 -1 -2 527\n            -8.5764907998964190e-04</internalNodes>\n          <leafValues>\n            3.8035380840301514e-01 6.4395052194595337e-01\n            4.1683459281921387e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 528 -2.2000649943947792e-02 -1 -2 529\n            -7.8731682151556015e-03</internalNodes>\n          <leafValues>\n            6.6546010971069336e-01 4.1827228665351868e-01\n            5.6047242879867554e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 530 -2.7444459497928619e-02 -1 -2 531\n            1.9792269449681044e-03</internalNodes>\n          <leafValues>\n            6.5868628025054932e-01 3.2449120283126831e-01\n            4.8828700184822083e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 532 -5.6783691979944706e-03 -1 -2 533\n            1.5057219570735469e-05</internalNodes>\n          <leafValues>\n            2.2290790081024170e-01 4.1072851419448853e-01\n            5.7475912570953369e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 534 -5.4136710241436958e-03 -1 -2 535\n            5.3679239936172962e-03</internalNodes>\n          <leafValues>\n            2.0657970011234283e-01 4.9264231324195862e-01\n            7.1394848823547363e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 536 -3.1426660716533661e-03 -1 -2 537\n            1.0907390154898167e-02</internalNodes>\n          <leafValues>\n            6.7800867557525635e-01 5.2149301767349243e-01\n            1.1439959704875946e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 538 5.8436761610209942e-03 -1 -2 539\n            9.0507230197545141e-05</internalNodes>\n          <leafValues>\n            1.9375260174274445e-01 3.8125771284103394e-01\n            5.5141878128051758e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 540 -1.6345789656043053e-02 -1 -2 541\n            1.5987500082701445e-03</internalNodes>\n          <leafValues>\n            2.4740239977836609e-01 4.8177829384803772e-01\n            5.9230798482894897e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 542 -4.0257978253066540e-03 -1 -2 543\n            -6.7750471644103527e-03</internalNodes>\n          <leafValues>\n            7.5082087516784668e-01 2.8798109292984009e-01\n            5.1996952295303345e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 544 -3.2470689620822668e-03 -1 -2 545\n            1.5409620245918632e-03</internalNodes>\n          <leafValues>\n            3.0449101328849792e-01 4.0634828805923462e-01\n            5.6765627861022949e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 546 -1.2858119793236256e-02 -1 -2 547\n            -1.4824670506641269e-04</internalNodes>\n          <leafValues>\n            9.6717558801174164e-02 4.5378330349922180e-01\n            6.1153751611709595e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 548 -9.0210810303688049e-03 -1 -2 549\n            -2.8795029968023300e-02</internalNodes>\n          <leafValues>\n            4.8077508807182312e-01 3.4037950634956360e-01\n            5.2555292844772339e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 550 9.0210810303688049e-03 -1 -2 551\n            7.4121179059147835e-03</internalNodes>\n          <leafValues>\n            7.5058358907699585e-01 5.4554468393325806e-01\n            3.2260689139366150e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 552 -3.7217529024928808e-03 -1 -2 553\n            1.9865889847278595e-01</internalNodes>\n          <leafValues>\n            2.3118489980697632e-01 5.2710479497909546e-01\n            1.4699299633502960e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 554 1.5208719560177997e-05 -1 -2 555\n            -3.9089918136596680e-03</internalNodes>\n          <leafValues>\n            3.6781388521194458e-01 7.1319299936294556e-01\n            4.9938669800758362e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 556 2.5106288958340883e-03 -1 -2 557\n            2.3921660613268614e-04</internalNodes>\n          <leafValues>\n            5.3120541572570801e-01 4.6893781423568726e-01\n            5.7140219211578369e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 558 6.9443131797015667e-03 -1 -2 559\n            1.2065629707649350e-03</internalNodes>\n          <leafValues>\n            6.9487977027893066e-01 4.0045049786567688e-01\n            5.8748817443847656e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 560 2.5106288958340883e-03 -1 -2 561\n            1.7514040227979422e-03</internalNodes>\n          <leafValues>\n            5.3295719623565674e-01 5.5458492040634155e-01\n            3.4495818614959717e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 562 -4.1978210210800171e-03 -1 -2 563\n            1.3092850567772985e-03</internalNodes>\n          <leafValues>\n            1.2171830236911774e-01 5.3750497102737427e-01\n            3.4156250953674316e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 564 6.7396182566881180e-04 -1 -2 565\n            -1.0530710220336914e-02</internalNodes>\n          <leafValues>\n            4.1951790452003479e-01 3.4607538580894470e-01\n            5.1558601856231689e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 566 -4.0672299265861511e-01 -1 -2 567\n            -2.6314549148082733e-02</internalNodes>\n          <leafValues>\n            5.8065678924322128e-02 1.4734490215778351e-01\n            5.5593782663345337e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 568 2.2557149641215801e-03 -1 -2 569\n            1.2154860422015190e-02</internalNodes>\n          <leafValues>\n            5.4777151346206665e-01 4.2077910900115967e-01\n            5.6218808889389038e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 570 -1.8436539918184280e-02 -1 -2 571\n            5.3676147945225239e-04</internalNodes>\n          <leafValues>\n            6.4471471309661865e-01 2.7651271224021912e-01\n            4.8885959386825562e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 572 -2.6265541091561317e-03 -1 -2 573\n            -5.1119807176291943e-04</internalNodes>\n          <leafValues>\n            5.2646911144256592e-01 5.7853102684020996e-01\n            4.2911028861999512e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 574 4.1454841266386211e-04 -1 -2 575\n            -5.5028748465701938e-04</internalNodes>\n          <leafValues>\n            3.4554108977317810e-01 6.0269188880920410e-01\n            4.1438931226730347e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 576 -1.0347720235586166e-03 -1 -2 577\n            -3.3966631162911654e-03</internalNodes>\n          <leafValues>\n            6.0952937602996826e-01 6.1082822084426880e-01\n            4.7077208757400513e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 578 3.1795909162610769e-03 -1 -2 579\n            -1.6528950072824955e-04</internalNodes>\n          <leafValues>\n            3.2443669438362122e-01 3.8307571411132812e-01\n            5.7343262434005737e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 580 8.3725210279226303e-03 -1 -2 581\n            -2.5799809955060482e-03</internalNodes>\n          <leafValues>\n            6.6109192371368408e-01 6.1393070220947266e-01\n            4.6861499547958374e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 582 9.0194388758391142e-04 -1 -2 583\n            3.6952210939489305e-04</internalNodes>\n          <leafValues>\n            3.5200220346450806e-01 2.5787541270256042e-01\n            5.4672420024871826e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 584 9.9746137857437134e-04 -1 -2 585\n            -3.6688039544969797e-03</internalNodes>\n          <leafValues>\n            4.8201468586921692e-01 5.7101500034332275e-01\n            4.8319110274314880e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 586 -8.9501030743122101e-04 -1 -2 587\n            5.1904921419918537e-03</internalNodes>\n          <leafValues>\n            6.1336791515350342e-01 4.9285829067230225e-01\n            2.5813090801239014e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 588 4.2274440056644380e-04 -1 -2 589\n            8.5176713764667511e-03</internalNodes>\n          <leafValues>\n            4.4711241126060486e-01 5.1610249280929565e-01\n            3.3165338635444641e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 590 -3.6623608320951462e-02 -1 -2 591\n            -4.1103712283074856e-03</internalNodes>\n          <leafValues>\n            9.2606216669082642e-02 8.5221147537231445e-01\n            5.1379078626632690e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 592 -6.6017331555485725e-03 -1 -2 593\n            2.5578640401363373e-02</internalNodes>\n          <leafValues>\n            5.4590600728988647e-01 5.2193528413772583e-01\n            1.9271859526634216e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 594 1.1447439901530743e-02 -1 -2 595\n            7.2427501436322927e-04</internalNodes>\n          <leafValues>\n            1.9160020351409912e-01 5.2315711975097656e-01\n            3.5353401303291321e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 596 9.7127500921487808e-03 -1 -2 597\n            -1.1337569914758205e-02</internalNodes>\n          <leafValues>\n            6.4641010761260986e-01 7.3830378055572510e-01\n            4.9647438526153564e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 598 -8.1453882157802582e-03 -1 -2 599\n            -8.5570756345987320e-03</internalNodes>\n          <leafValues>\n            3.6117058992385864e-01 3.4219071269035339e-01\n            5.9435117244720459e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 600 2.2993308957666159e-03 -1 -2 601\n            3.8430930580943823e-03</internalNodes>\n          <leafValues>\n            4.5501041412353516e-01 4.7168621420860291e-01\n            6.6561907529830933e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 602 -9.9116540513932705e-04 -1 -2 603\n            2.5496469810605049e-02</internalNodes>\n          <leafValues>\n            4.5927169919013977e-01 6.5634012222290039e-01\n            1.2588350474834442e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 604 -1.5748359262943268e-02 -1 -2 605\n            -1.8046120181679726e-02</internalNodes>\n          <leafValues>\n            5.2395021915435791e-01 8.0158519744873047e-01\n            5.0079578161239624e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 606 1.0323390364646912e-02 -1 -2 607\n            1.6452240524813533e-03</internalNodes>\n          <leafValues>\n            2.2748200595378876e-01 4.3519461154937744e-01\n            5.8676278591156006e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 608 1.5881149098277092e-02 -1 -2 609\n            1.0586519725620747e-02</internalNodes>\n          <leafValues>\n            4.4650518894195557e-01 4.5444580912590027e-01\n            5.7071107625961304e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 610 -2.1531689912080765e-02 -1 -2 611\n            5.2480469457805157e-03</internalNodes>\n          <leafValues>\n            6.5276437997817993e-01 3.4447279572486877e-01\n            5.3246361017227173e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>67</maxWeakCount>\n      <stageThreshold>3.2647129058837891e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 612 1.8219340126961470e-03 -1 -2 613\n            8.1313941627740860e-03</internalNodes>\n          <leafValues>\n            3.1087881326675415e-01 3.1332370638847351e-01\n            6.6458672285079956e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 614 1.7055979697033763e-03 -1 -2 615\n            -7.4483548814896494e-05</internalNodes>\n          <leafValues>\n            2.6401311159133911e-01 5.6472051143646240e-01\n            3.4853729605674744e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 616 3.8342390325851738e-04 -1 -2 617\n            3.1868910882622004e-03</internalNodes>\n          <leafValues>\n            3.1406548619270325e-01 6.4891988039016724e-01\n            3.8877290487289429e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 618 1.6044320166110992e-01 -1 -2 619\n            -6.7285560071468353e-03</internalNodes>\n          <leafValues>\n            7.2165298461914062e-01 1.6531379520893097e-01\n            5.1398259401321411e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 620 7.2638481469766703e-06 -1 -2 621\n            5.5551197146996856e-04</internalNodes>\n          <leafValues>\n            3.1406199932098389e-01 5.9936988353729248e-01\n            3.3173981308937073e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 622 -1.0822320356965065e-02 -1 -2 623\n            -4.5834020711481571e-03</internalNodes>\n          <leafValues>\n            2.6529380679130554e-01 1.8495689332485199e-01\n            5.3139579296112061e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 624 -3.0205070506781340e-03 -1 -2 625\n            7.7864617109298706e-02</internalNodes>\n          <leafValues>\n            4.0400999784469604e-01 6.1581897735595703e-01\n            1.7864869534969330e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 626 2.6494380086660385e-02 -1 -2 627\n            3.6912109702825546e-02</internalNodes>\n          <leafValues>\n            4.5110899209976196e-01 4.5282199978828430e-01\n            5.9722828865051270e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 628 5.7857790961861610e-03 -1 -2 629\n            9.3849771656095982e-04</internalNodes>\n          <leafValues>\n            2.5338920950889587e-01 3.4104120731353760e-01\n            5.9236437082290649e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 630 -1.1003199964761734e-02 -1 -2 631\n            -1.1737640015780926e-03</internalNodes>\n          <leafValues>\n            6.9580441713333130e-01 3.8510841131210327e-01\n            5.4081892967224121e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 632 -3.6596669815480709e-03 -1 -2 633\n            -2.4822750128805637e-03</internalNodes>\n          <leafValues>\n            2.0093089342117310e-01 6.2953931093215942e-01\n            4.3950408697128296e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 634 -4.4606071896851063e-03 -1 -2 635\n            -3.5969649907201529e-03</internalNodes>\n          <leafValues>\n            2.4052999913692474e-01 5.4501742124557495e-01\n            3.7823578715324402e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 636 -3.6222559865564108e-03 -1 -2 637\n            1.2059339787811041e-03</internalNodes>\n          <leafValues>\n            3.0338969826698303e-01 4.6337789297103882e-01\n            6.3359522819519043e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 638 4.3124938383698463e-03 -1 -2 639\n            -4.4961250387132168e-03</internalNodes>\n          <leafValues>\n            6.5988260507583618e-01 6.6216969490051270e-01\n            4.7552469372749329e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 640 -1.3860689941793680e-03 -1 -2 641\n            -5.1588460337370634e-04</internalNodes>\n          <leafValues>\n            2.8012010455131531e-01 3.8294890522956848e-01\n            5.6236267089843750e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 642 7.0330002927221358e-05 -1 -2 643\n            -2.0976549421902746e-04</internalNodes>\n          <leafValues>\n            4.5363429188728333e-01 5.6081390380859375e-01\n            4.2657798528671265e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 644 1.3642259873449802e-03 -1 -2 645\n            1.5483660390600562e-03</internalNodes>\n          <leafValues>\n            2.6370918750762939e-01 4.1707509756088257e-01\n            5.9329879283905029e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 646 1.9179609417915344e-01 -1 -2 647\n            -4.4776909053325653e-03</internalNodes>\n          <leafValues>\n            5.2567642927169800e-01 6.6326218843460083e-01\n            4.8925888538360596e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 648 -1.2649179995059967e-01 -1 -2 649\n            6.5253327193204314e-05</internalNodes>\n          <leafValues>\n            1.4997789263725281e-01 4.2333200573921204e-01\n            5.7560402154922485e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 650 4.1856421157717705e-03 -1 -2 651\n            2.7478230185806751e-04</internalNodes>\n          <leafValues>\n            5.2888268232345581e-01 4.5240178704261780e-01\n            5.6041252613067627e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 652 -2.2906810045242310e-03 -1 -2 653\n            1.6744500026106834e-03</internalNodes>\n          <leafValues>\n            5.5782741308212280e-01 3.3230578899383545e-01\n            5.5587881803512573e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 654 1.2349759927019477e-03 -1 -2 655\n            -8.7158754467964172e-03</internalNodes>\n          <leafValues>\n            3.6539471149444580e-01 1.9245339930057526e-01\n            5.3136497735977173e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 656 4.6613621525466442e-03 -1 -2 657\n            -8.5815992206335068e-03</internalNodes>\n          <leafValues>\n            2.0277309417724609e-01 7.6360601186752319e-01\n            5.1408261060714722e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 658 1.4352120459079742e-02 -1 -2 659\n            -7.7948719263076782e-03</internalNodes>\n          <leafValues>\n            5.2529758214950562e-01 2.6329371333122253e-01\n            5.3286892175674438e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 660 -3.4155680332332850e-03 -1 -2 661\n            -4.2639090679585934e-03</internalNodes>\n          <leafValues>\n            2.4160879850387573e-01 3.9365449547767639e-01\n            5.4787421226501465e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 662 8.7177697569131851e-03 -1 -2 663\n            -3.2232629600912333e-03</internalNodes>\n          <leafValues>\n            4.7881990671157837e-01 3.6316120624542236e-01\n            5.2883160114288330e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 664 -4.2188368737697601e-02 -1 -2 665\n            1.9875749945640564e-02</internalNodes>\n          <leafValues>\n            6.9311392307281494e-01 4.5201000571250916e-01\n            6.8550550937652588e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 666 -3.1134510412812233e-02 -1 -2 667\n            5.7032387703657150e-03</internalNodes>\n          <leafValues>\n            5.3004240989685059e-01 5.6068921089172363e-01\n            4.2306229472160339e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 668 5.2733682096004486e-03 -1 -2 669\n            -3.1231069006025791e-03</internalNodes>\n          <leafValues>\n            3.2472288608551025e-01 1.9856959581375122e-01\n            5.3498727083206177e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 670 4.6453849063254893e-04 -1 -2 671\n            3.0355889350175858e-02</internalNodes>\n          <leafValues>\n            4.2075088620185852e-01 5.1534587144851685e-01\n            3.1181010603904724e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 672 -4.2992769740521908e-03 -1 -2 673\n            1.9509199773892760e-04</internalNodes>\n          <leafValues>\n            3.2745069265365601e-01 5.9530782699584961e-01\n            4.2255210876464844e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 674 -7.7784480527043343e-03 -1 -2 675\n            1.6917599365115166e-02</internalNodes>\n          <leafValues>\n            7.2111797332763672e-01 4.9365919828414917e-01\n            7.0302772521972656e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 676 -5.1948569715023041e-02 -1 -2 677\n            -5.4751220159232616e-03</internalNodes>\n          <leafValues>\n            1.4255349338054657e-01 6.0593318939208984e-01\n            4.3939951062202454e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 678 1.5210839592327829e-05 -1 -2 679\n            1.0235579684376717e-03</internalNodes>\n          <leafValues>\n            4.4888499379158020e-01 4.2565500736236572e-01\n            5.7954382896423340e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 680 -1.0427719826111570e-04 -1 -2 681\n            8.7853781878948212e-03</internalNodes>\n          <leafValues>\n            4.2460399866104126e-01 4.9580091238021851e-01\n            6.7594307661056519e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 682 3.4012699034065008e-03 -1 -2 683\n            5.8582378551363945e-04</internalNodes>\n          <leafValues>\n            5.4234808683395386e-01 3.6365428566932678e-01\n            5.4643487930297852e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 684 -2.2973360028117895e-03 -1 -2 685\n            -1.4330189675092697e-02</internalNodes>\n          <leafValues>\n            2.5488188862800598e-01 6.5876567363739014e-01\n            4.5328021049499512e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 686 9.8565965890884399e-04 -1 -2 687\n            -4.6640761196613312e-02</internalNodes>\n          <leafValues>\n            3.8227710127830505e-01 3.0773219466209412e-01\n            5.2441328763961792e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 688 -1.1907300353050232e-01 -1 -2 689\n            1.9333280622959137e-02</internalNodes>\n          <leafValues>\n            1.0338629782199860e-01 5.5547451972961426e-01\n            3.2213169336318970e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 690 3.1427849084138870e-02 -1 -2 691\n            2.0082130504306406e-04</internalNodes>\n          <leafValues>\n            4.6823790669441223e-01 5.3730702400207520e-01\n            3.8006669282913208e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 692 -6.2584900297224522e-03 -1 -2 693\n            8.2861045375466347e-03</internalNodes>\n          <leafValues>\n            1.7992070317268372e-01 5.0950688123703003e-01\n            7.5446051359176636e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 694 2.0529709290713072e-03 -1 -2 695\n            3.2524869311600924e-03</internalNodes>\n          <leafValues>\n            5.6286448240280151e-01 4.8016890883445740e-01\n            5.8021020889282227e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 696 -3.1884901225566864e-02 -1 -2 697\n            1.8379340181127191e-03</internalNodes>\n          <leafValues>\n            1.7427450418472290e-01 3.4665969014167786e-01\n            5.1071548461914062e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 698 -4.8512680223211646e-04 -1 -2 699\n            -2.5407879147678614e-03</internalNodes>\n          <leafValues>\n            5.3260862827301025e-01 6.3427752256393433e-01\n            4.9926930665969849e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 700 -5.1559060811996460e-03 -1 -2 701\n            -4.4968750327825546e-02</internalNodes>\n          <leafValues>\n            3.4334290027618408e-01 1.8681369721889496e-01\n            5.2154648303985596e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 702 5.8984281495213509e-03 -1 -2 703\n            3.2763120252639055e-03</internalNodes>\n          <leafValues>\n            6.2293052673339844e-01 4.9357721209526062e-01\n            7.2179448604583740e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 704 -1.0161520185647532e-04 -1 -2 705\n            -1.6290300118271261e-04</internalNodes>\n          <leafValues>\n            5.0079762935638428e-01 6.0241490602493286e-01\n            2.3295080661773682e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 706 9.0541364625096321e-03 -1 -2 707\n            3.5398490726947784e-02</internalNodes>\n          <leafValues>\n            4.5104169845581055e-01 5.1419967412948608e-01\n            2.8602918982505798e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 708 5.6469351984560490e-03 -1 -2 709\n            -2.4807190056890249e-03</internalNodes>\n          <leafValues>\n            4.7049251198768616e-01 4.1798511147499084e-01\n            6.7266470193862915e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 710 -4.1088787838816643e-03 -1 -2 711\n            -2.0714469719678164e-03</internalNodes>\n          <leafValues>\n            5.8098018169403076e-01 6.0747838020324707e-01\n            4.5240598917007446e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 712 -2.8939060866832733e-03 -1 -2 713\n            1.3467279495671391e-03</internalNodes>\n          <leafValues>\n            3.3835199475288391e-01 5.6969100236892700e-01\n            3.9708450436592102e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 714 -9.0779133141040802e-02 -1 -2 715\n            -8.3171762526035309e-02</internalNodes>\n          <leafValues>\n            1.5027019381523132e-01 7.5736707448959351e-01\n            4.9364370107650757e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 716 -1.4107000315561891e-03 -1 -2 717\n            5.5668760091066360e-02</internalNodes>\n          <leafValues>\n            3.3909329771995544e-01 5.0250971317291260e-01\n            7.4220830202102661e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 718 5.7701539248228073e-02 -1 -2 719\n            -4.2503291368484497e-01</internalNodes>\n          <leafValues>\n            5.1973718404769897e-01 9.7346916794776917e-02\n            5.1857399940490723e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 720 -4.4380719191394746e-04 -1 -2 721\n            1.7924769781529903e-04</internalNodes>\n          <leafValues>\n            3.6493501067161560e-01 5.6192791461944580e-01\n            3.7602970004081726e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 722 5.0382469780743122e-03 -1 -2 723\n            1.5191170386970043e-02</internalNodes>\n          <leafValues>\n            6.3284450769424438e-01 4.9360820651054382e-01\n            7.4265247583389282e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 724 -1.2300389818847179e-02 -1 -2 725\n            1.5168030513450503e-03</internalNodes>\n          <leafValues>\n            1.3893499970436096e-01 5.0919622182846069e-01\n            3.4826481342315674e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 726 9.5754547510296106e-04 -1 -2 727\n            -1.8962200731039047e-02</internalNodes>\n          <leafValues>\n            6.0363167524337769e-01 2.3191730678081512e-01\n            5.1166528463363647e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 728 -2.2272260859608650e-02 -1 -2 729\n            -2.5145230814814568e-02</internalNodes>\n          <leafValues>\n            6.5550220012664795e-01 1.3260710239410400e-01\n            4.6740341186523438e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 730 1.9533900544047356e-02 -1 -2 731\n            -1.1231349781155586e-03</internalNodes>\n          <leafValues>\n            5.1820272207260132e-01 6.3182431459426880e-01\n            4.8255190253257751e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 732 -1.4861139934509993e-03 -1 -2 733\n            3.5002888762392104e-04</internalNodes>\n          <leafValues>\n            2.9186710715293884e-01 5.6213712692260742e-01\n            4.2492130398750305e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 734 -1.1231349781155586e-03 -1 -2 735\n            1.0409739799797535e-02</internalNodes>\n          <leafValues>\n            4.8137450218200684e-01 5.1840060949325562e-01\n            2.0512230694293976e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 736 -8.7832562625408173e-02 -1 -2 737\n            1.6584879485890269e-03</internalNodes>\n          <leafValues>\n            1.1799219995737076e-01 4.9878111481666565e-01\n            6.9737559556961060e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 738 -2.3008750285953283e-03 -1 -2 739\n            3.3026169985532761e-02</internalNodes>\n          <leafValues>\n            5.3398311138153076e-01 5.0332891941070557e-01\n            6.8519067764282227e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 740 -1.3585069682449102e-03 -1 -2 741\n            7.8067491995170712e-04</internalNodes>\n          <leafValues>\n            3.0028221011161804e-01 4.5930838584899902e-01\n            6.4400452375411987e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 742 -1.8025759607553482e-02 -1 -2 743\n            1.2354910140857100e-03</internalNodes>\n          <leafValues>\n            5.3112912178039551e-01 4.7291061282157898e-01\n            5.7214611768722534e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 744 -9.2583027435466647e-04 -1 -2 745\n            8.0123997759073973e-04</internalNodes>\n          <leafValues>\n            3.6623328924179077e-01 5.3619897365570068e-01\n            3.0086329579353333e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>63</maxWeakCount>\n      <stageThreshold>3.0672130584716797e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 746 2.4914839304983616e-03 -1 -2 747\n            -5.0488598644733429e-02</internalNodes>\n          <leafValues>\n            3.4223890304565430e-01 7.7034580707550049e-01\n            4.5163908600807190e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 748 -7.7838351717218757e-04 -1 -2 749\n            2.3572890495415777e-04</internalNodes>\n          <leafValues>\n            3.2563421130180359e-01 3.4065559506416321e-01\n            5.8970272541046143e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 750 4.5575071126222610e-03 -1 -2 751\n            8.1241987645626068e-03</internalNodes>\n          <leafValues>\n            4.3065789341926575e-01 7.1495872735977173e-01\n            4.3456849455833435e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 752 -4.4612158671952784e-04 -1 -2 753\n            -2.8972938889637589e-04</internalNodes>\n          <leafValues>\n            3.2959741353988647e-01 5.8456200361251831e-01\n            3.5266879200935364e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 754 7.1604831646254752e-06 -1 -2 755\n            -3.8497708737850189e-04</internalNodes>\n          <leafValues>\n            4.0819549560546875e-01 4.2031130194664001e-01\n            6.6341269016265869e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 756 1.9489860278554261e-04 -1 -2 757\n            -1.7083849757909775e-02</internalNodes>\n          <leafValues>\n            3.9424669742584229e-01 2.2940720617771149e-01\n            5.2389609813690186e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 758 8.3513697609305382e-04 -1 -2 759\n            7.5499608647078276e-04</internalNodes>\n          <leafValues>\n            3.0260318517684937e-01 6.0321962833404541e-01\n            3.4124588966369629e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 760 8.0216713249683380e-03 -1 -2 761\n            -3.8930509239435196e-02</internalNodes>\n          <leafValues>\n            7.3062407970428467e-01 3.5993251204490662e-01\n            5.2343809604644775e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 762 -7.0348767621908337e-05 -1 -2 763\n            -8.5350573062896729e-03</internalNodes>\n          <leafValues>\n            3.4937581419944763e-01 2.7461090683937073e-01\n            5.6265860795974731e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 764 1.0854450054466724e-02 -1 -2 765\n            4.5329501153901219e-04</internalNodes>\n          <leafValues>\n            5.2822262048721313e-01 4.5220491290092468e-01\n            6.0543018579483032e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 766 1.8117150466423482e-04 -1 -2 767\n            4.6641560038551688e-04</internalNodes>\n          <leafValues>\n            3.3068621158599854e-01 1.4550000429153442e-01\n            5.3849279880523682e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 768 -8.4854792803525925e-03 -1 -2 769\n            -1.8934309482574463e-02</internalNodes>\n          <leafValues>\n            4.8141559958457947e-01 3.5637411475181580e-01\n            5.4051452875137329e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 770 4.9814549274742603e-03 -1 -2 771\n            3.4286780282855034e-03</internalNodes>\n          <leafValues>\n            6.9577431678771973e-01 5.0508928298950195e-01\n            2.3169949650764465e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 772 4.4203791185282171e-04 -1 -2 773\n            2.3822550429031253e-04</internalNodes>\n          <leafValues>\n            6.0185819864273071e-01 4.7550821304321289e-01\n            5.5852377414703369e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 774 -6.4261639490723610e-03 -1 -2 775\n            9.9637769162654877e-03</internalNodes>\n          <leafValues>\n            2.2824659943580627e-01 4.0405881404876709e-01\n            5.6501698493957520e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 776 1.3654050417244434e-02 -1 -2 777\n            -9.9892877042293549e-03</internalNodes>\n          <leafValues>\n            5.2677392959594727e-01 6.7940497398376465e-01\n            4.7970339655876160e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 778 3.6558631807565689e-02 -1 -2 779\n            4.8999379941960797e-05</internalNodes>\n          <leafValues>\n            8.8425733149051666e-02 4.0207880735397339e-01\n            5.4573321342468262e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 780 1.3654050417244434e-02 -1 -2 781\n            1.8802779959514737e-03</internalNodes>\n          <leafValues>\n            5.2676129341125488e-01 4.8060521483421326e-01\n            6.3943648338317871e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 782 -1.3654050417244434e-02 -1 -2 783\n            1.2778700329363346e-03</internalNodes>\n          <leafValues>\n            1.7248100042343140e-01 4.4798240065574646e-01\n            6.3100087642669678e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 784 9.8843395244330168e-04 -1 -2 785\n            1.4511500012304168e-05</internalNodes>\n          <leafValues>\n            5.9481692314147949e-01 4.8541748523712158e-01\n            5.3093612194061279e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 786 -2.2775429533794522e-04 -1 -2 787\n            -1.4753740280866623e-02</internalNodes>\n          <leafValues>\n            3.1836318969726562e-01 3.0849760770797729e-01\n            5.3520262241363525e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 788 -3.4148250706493855e-03 -1 -2 789\n            7.5806681998074055e-03</internalNodes>\n          <leafValues>\n            6.1153268814086914e-01 4.9516460299491882e-01\n            7.0613312721252441e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 790 -5.7734688743948936e-03 -1 -2 791\n            7.4033669079653919e-05</internalNodes>\n          <leafValues>\n            3.7542209029197693e-01 4.1155171394348145e-01\n            5.8894449472427368e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 792 -8.2278084009885788e-03 -1 -2 793\n            5.3380909375846386e-03</internalNodes>\n          <leafValues>\n            9.5610566437244415e-02 5.3005087375640869e-01\n            3.9618980884552002e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 794 -2.7049109339714050e-03 -1 -2 795\n            7.7341338619589806e-03</internalNodes>\n          <leafValues>\n            6.4818692207336426e-01 5.1104402542114258e-01\n            3.1215190887451172e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 796 1.0886609554290771e-02 -1 -2 797\n            1.1038660071790218e-02</internalNodes>\n          <leafValues>\n            4.8014289140701294e-01 5.4297101497650146e-01\n            4.1623631119728088e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 798 -1.0054199956357479e-02 -1 -2 799\n            7.7072880230844021e-03</internalNodes>\n          <leafValues>\n            7.3293352127075195e-01 5.3568720817565918e-01\n            3.4555470943450928e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 800 -5.8278098003938794e-04 -1 -2 801\n            -2.5739220436662436e-03</internalNodes>\n          <leafValues>\n            3.6550220847129822e-01 3.7767601013183594e-01\n            5.3917747735977173e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 802 -7.0167761296033859e-03 -1 -2 803\n            -1.7727289814502001e-03</internalNodes>\n          <leafValues>\n            4.0393048524856567e-01 6.9504439830780029e-01\n            4.9811169505119324e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 804 -1.6318289563059807e-02 -1 -2 805\n            -1.1663000099360943e-02</internalNodes>\n          <leafValues>\n            5.2967327833175659e-01 5.8426398038864136e-01\n            4.7895029187202454e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 806 2.5881489273160696e-03 -1 -2 807\n            -3.7328999023884535e-03</internalNodes>\n          <leafValues>\n            6.0921788215637207e-01 6.7217427492141724e-01\n            4.0668940544128418e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 808 -1.4355930034071207e-03 -1 -2 809\n            1.8340899841859937e-03</internalNodes>\n          <leafValues>\n            3.5850879549980164e-01 5.3711581230163574e-01\n            4.0335071086883545e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 810 1.2280289828777313e-01 -1 -2 811\n            5.0228700041770935e-02</internalNodes>\n          <leafValues>\n            1.5475720167160034e-01 5.4338437318801880e-01\n            8.4292672574520111e-02</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 812 -2.1437000483274460e-02 -1 -2 813\n            -3.1009620055556297e-02</internalNodes>\n          <leafValues>\n            4.8600539565086365e-01 1.8330100178718567e-01\n            5.2075541019439697e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 814 -1.2973720207810402e-02 -1 -2 815\n            1.5818020328879356e-03</internalNodes>\n          <leafValues>\n            7.0482409000396729e-01 4.1705870628356934e-01\n            5.8651638031005859e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 816 -9.7806248813867569e-03 -1 -2 817\n            1.1735740117728710e-03</internalNodes>\n          <leafValues>\n            5.3079181909561157e-01 5.5224531888961792e-01\n            3.5071650147438049e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 818 1.4651629608124495e-03 -1 -2 819\n            2.3532148916274309e-03</internalNodes>\n          <leafValues>\n            3.0426511168479919e-01 5.3393232822418213e-01\n            2.8062361478805542e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 820 -6.1809681355953217e-03 -1 -2 821\n            6.5688649192452431e-04</internalNodes>\n          <leafValues>\n            6.4101332426071167e-01 5.6208711862564087e-01\n            4.3903189897537231e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 822 2.6228010654449463e-02 -1 -2 823\n            -1.7958110198378563e-02</internalNodes>\n          <leafValues>\n            6.4455568790435791e-01 2.0027139782905579e-01\n            4.6246650815010071e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 824 -7.6468721963465214e-03 -1 -2 825\n            -2.7482809964567423e-03</internalNodes>\n          <leafValues>\n            5.2632009983062744e-01 5.8739811182022095e-01\n            4.8366001248359680e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 826 1.3851850293576717e-02 -1 -2 827\n            2.6369190309196711e-03</internalNodes>\n          <leafValues>\n            1.5661309659481049e-01 4.2701789736747742e-01\n            5.8066600561141968e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 828 -3.1513599678874016e-03 -1 -2 829\n            -1.4788460248382762e-05</internalNodes>\n          <leafValues>\n            6.2158662080764771e-01 5.5766427516937256e-01\n            4.1220021247863770e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 830 -7.3676988482475281e-02 -1 -2 831\n            -3.0912780202925205e-03</internalNodes>\n          <leafValues>\n            1.5367099642753601e-01 6.3442689180374146e-01\n            4.5074120163917542e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 832 7.9240966588258743e-03 -1 -2 833\n            8.5778040811419487e-03</internalNodes>\n          <leafValues>\n            5.4579752683639526e-01 5.4016572237014771e-01\n            3.8907998800277710e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 834 5.5403169244527817e-03 -1 -2 835\n            -1.1886510037584230e-04</internalNodes>\n          <leafValues>\n            3.5556110739707947e-01 5.8367502689361572e-01\n            4.2743161320686340e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 836 -1.8408369272947311e-02 -1 -2 837\n            -2.3490579333156347e-03</internalNodes>\n          <leafValues>\n            5.8604401350021362e-01 4.4989579916000366e-01\n            5.4981988668441772e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 838 -7.6157399453222752e-03 -1 -2 839\n            -3.3190969843417406e-03</internalNodes>\n          <leafValues>\n            4.1009929776191711e-01 6.7013788223266602e-01\n            4.3530011177062988e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 840 -9.4642979092895985e-04 -1 -2 841\n            8.7858550250530243e-03</internalNodes>\n          <leafValues>\n            5.3911769390106201e-01 5.5040502548217773e-01\n            3.9909350872039795e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 842 1.6395459533669055e-04 -1 -2 843\n            -2.3508940357714891e-03</internalNodes>\n          <leafValues>\n            3.5929331183433533e-01 4.0341728925704956e-01\n            5.8060771226882935e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 844 7.5449963333085179e-05 -1 -2 845\n            2.7018489316105843e-02</internalNodes>\n          <leafValues>\n            5.4123848676681519e-01 4.9449229240417480e-01\n            5.5894362926483154e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 846 8.4561208495870233e-04 -1 -2 847\n            -1.1687109945341945e-03</internalNodes>\n          <leafValues>\n            5.8092182874679565e-01 4.7469571232795715e-01\n            2.8458958864212036e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 848 2.2897500544786453e-02 -1 -2 849\n            7.0879262685775757e-01</internalNodes>\n          <leafValues>\n            2.4144110083580017e-01 5.1957648992538452e-01\n            1.0300920158624649e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 850 3.7483830004930496e-02 -1 -2 851\n            1.2827500468119979e-03</internalNodes>\n          <leafValues>\n            1.8146389722824097e-01 4.2460718750953674e-01\n            5.7079732418060303e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 852 -5.1718312315642834e-03 -1 -2 853\n            2.7545939665287733e-03</internalNodes>\n          <leafValues>\n            6.1433231830596924e-01 5.2056711912155151e-01\n            4.2204418778419495e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 854 -3.6072919610887766e-03 -1 -2 855\n            -2.5258748792111874e-04</internalNodes>\n          <leafValues>\n            3.1825920939445496e-01 5.7104682922363281e-01\n            4.2260938882827759e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 856 -7.0514748804271221e-03 -1 -2 857\n            -5.4323761723935604e-03</internalNodes>\n          <leafValues>\n            5.1628297567367554e-01 2.6662889122962952e-01\n            5.2146798372268677e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 858 -1.4652940080850385e-05 -1 -2 859\n            -1.8556920113041997e-03</internalNodes>\n          <leafValues>\n            3.9817610383033752e-01 3.3227631449699402e-01\n            5.7058340311050415e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 860 4.7609540633857250e-03 -1 -2 861\n            1.5676260227337480e-03</internalNodes>\n          <leafValues>\n            6.6365581750869751e-01 5.5055677890777588e-01\n            4.4206619262695312e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 862 5.4239919409155846e-03 -1 -2 863\n            -6.4692399464547634e-03</internalNodes>\n          <leafValues>\n            5.9599381685256958e-01 5.3695940971374512e-01\n            3.7443399429321289e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 864 -7.8038539504632354e-04 -1 -2 865\n            4.5086450874805450e-02</internalNodes>\n          <leafValues>\n            4.1035950183868408e-01 5.1775068044662476e-01\n            1.8781000375747681e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 866 -5.1405387930572033e-03 -1 -2 867\n            -2.1236129105091095e-02</internalNodes>\n          <leafValues>\n            2.3528920114040375e-01 1.7087510228157043e-01\n            5.4249739646911621e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 868 -2.3763340432196856e-03 -1 -2 869\n            5.4122589528560638e-02</internalNodes>\n          <leafValues>\n            5.8365309238433838e-01 5.1174330711364746e-01\n            1.8659310042858124e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 870 -5.3492980077862740e-04 -1 -2 871\n            -5.8454048121348023e-04</internalNodes>\n          <leafValues>\n            5.1086932420730591e-01 4.7754910588264465e-01\n            2.4398539960384369e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>71</maxWeakCount>\n      <stageThreshold>3.4677078247070312e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 872 3.0031939968466759e-03 -1 -2 873\n            6.9161207647994161e-04</internalNodes>\n          <leafValues>\n            3.3496499061584473e-01 4.5183679461479187e-01\n            7.2893542051315308e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 874 1.1212790384888649e-02 -1 -2 875\n            -7.6108198845759034e-04</internalNodes>\n          <leafValues>\n            2.9508009552955627e-01 5.6690549850463867e-01\n            2.8308510780334473e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 876 1.1984579759882763e-04 -1 -2 877\n            -1.9725349557120353e-04</internalNodes>\n          <leafValues>\n            4.0905779600143433e-01 6.9514942169189453e-01\n            4.6378681063652039e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 878 -5.5180420167744160e-03 -1 -2 879\n            1.2148249661549926e-03</internalNodes>\n          <leafValues>\n            3.1676751375198364e-01 3.3167061209678650e-01\n            5.3963977098464966e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 880 -4.2497441172599792e-03 -1 -2 881\n            -9.4915721565485001e-03</internalNodes>\n          <leafValues>\n            2.6005738973617554e-01 7.4842947721481323e-01\n            5.0731921195983887e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 882 6.5378600265830755e-04 -1 -2 883\n            -4.9741100519895554e-04</internalNodes>\n          <leafValues>\n            3.9520108699798584e-01 5.8802747726440430e-01\n            3.5521200299263000e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 884 -4.3079249560832977e-02 -1 -2 885\n            -5.1999092102050781e-04</internalNodes>\n          <leafValues>\n            2.4348780512809753e-01 3.1955629587173462e-01\n            5.5854547023773193e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 886 -4.5451628975570202e-03 -1 -2 887\n            -7.9610403627157211e-03</internalNodes>\n          <leafValues>\n            4.8452898859977722e-01 3.8011810183525085e-01\n            5.3585118055343628e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 888 -3.1919340835884213e-04 -1 -2 889\n            -1.9223889335989952e-02</internalNodes>\n          <leafValues>\n            4.3563291430473328e-01 2.6130661368370056e-01\n            6.1554962396621704e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 890 -1.3076990144327283e-03 -1 -2 891\n            1.9825039431452751e-02</internalNodes>\n          <leafValues>\n            5.9420621395111084e-01 4.9454280734062195e-01\n            7.3848551511764526e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 892 -2.2013280540704727e-03 -1 -2 893\n            -7.8596705570816994e-03</internalNodes>\n          <leafValues>\n            2.2144819796085358e-01 3.6009770631790161e-01\n            5.2985501289367676e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 894 1.4142199652269483e-03 -1 -2 895\n            -1.1232759803533554e-02</internalNodes>\n          <leafValues>\n            5.7765662670135498e-01 6.9344568252563477e-01\n            4.8272070288658142e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 896 2.9746301006525755e-03 -1 -2 897\n            5.3283828310668468e-04</internalNodes>\n          <leafValues>\n            3.2166770100593567e-01 3.9625000953674316e-01\n            5.6803637742996216e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 898 1.0105259716510773e-02 -1 -2 899\n            -1.1653699912130833e-02</internalNodes>\n          <leafValues>\n            7.5674182176589966e-01 6.5235567092895508e-01\n            5.0270539522171021e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 900 -7.0609981194138527e-03 -1 -2 901\n            2.2343141026794910e-03</internalNodes>\n          <leafValues>\n            2.5387701392173767e-01 4.3872770667076111e-01\n            6.1776322126388550e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 902 -2.9802279546856880e-02 -1 -2 903\n            1.1611840454861522e-03</internalNodes>\n          <leafValues>\n            5.2011400461196899e-01 4.6479099988937378e-01\n            6.1842548847198486e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 904 9.4824447296559811e-04 -1 -2 905\n            4.1284630424343050e-04</internalNodes>\n          <leafValues>\n            3.0409941077232361e-01 4.5188081264495850e-01\n            6.2457829713821411e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 906 -3.1203540042042732e-02 -1 -2 907\n            2.7652881108224392e-03</internalNodes>\n          <leafValues>\n            2.7889358997344971e-01 4.6985000371932983e-01\n            6.5024542808532715e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 908 2.5644779205322266e-02 -1 -2 909\n            -7.5331530533730984e-03</internalNodes>\n          <leafValues>\n            1.8051710724830627e-01 3.2080689072608948e-01\n            5.5220228433609009e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 910 3.2047149725258350e-03 -1 -2 911\n            -2.4282479716930538e-04</internalNodes>\n          <leafValues>\n            6.4369338750839233e-01 5.6767052412033081e-01\n            4.5091038942337036e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 912 -6.1979342717677355e-04 -1 -2 913\n            -8.0101029016077518e-04</internalNodes>\n          <leafValues>\n            3.1221461296081543e-01 2.9651939868927002e-01\n            5.2304947376251221e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 914 -9.1816839994862676e-04 -1 -2 915\n            1.2239529751241207e-03</internalNodes>\n          <leafValues>\n            5.4647117853164673e-01 4.6185028553009033e-01\n            5.6795489788055420e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 916 -6.8743730662390590e-04 -1 -2 917\n            -1.8252469599246979e-03</internalNodes>\n          <leafValues>\n            5.4308801889419556e-01 5.4336231946945190e-01\n            3.3852210640907288e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 918 -7.4570789001882076e-03 -1 -2 919\n            5.3775748237967491e-03</internalNodes>\n          <leafValues>\n            5.2655947208404541e-01 4.8572158813476562e-01\n            6.8151241540908813e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 920 3.7602309603244066e-03 -1 -2 921\n            8.7752222316339612e-04</internalNodes>\n          <leafValues>\n            2.8321608901023865e-01 3.9668309688568115e-01\n            5.5124807357788086e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 922 5.5084479972720146e-03 -1 -2 923\n            -7.5949047459289432e-04</internalNodes>\n          <leafValues>\n            6.7846202850341797e-01 3.9065030217170715e-01\n            5.4572027921676636e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 924 1.6352660022675991e-03 -1 -2 925\n            -1.2750849418807775e-04</internalNodes>\n          <leafValues>\n            3.6402040719985962e-01 5.8297240734100342e-01\n            4.1949799656867981e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 926 2.2067610174417496e-02 -1 -2 927\n            -1.9203789532184601e-02</internalNodes>\n          <leafValues>\n            4.6067029237747192e-01 3.2614830136299133e-01\n            5.2360808849334717e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 928 -1.2998109683394432e-02 -1 -2 929\n            -3.1332690268754959e-03</internalNodes>\n          <leafValues>\n            7.0221120119094849e-01 2.8704708814620972e-01\n            5.0764769315719604e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 930 -5.2937557920813560e-03 -1 -2 931\n            2.1857069805264473e-03</internalNodes>\n          <leafValues>\n            4.7095209360122681e-01 4.7082918882369995e-01\n            6.1698418855667114e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 932 -4.5750709250569344e-03 -1 -2 933\n            -4.5152138918638229e-02</internalNodes>\n          <leafValues>\n            3.1142529845237732e-01 1.8514350056648254e-01\n            5.5048149824142456e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 934 -2.7783559635281563e-03 -1 -2 935\n            -2.5752480141818523e-03</internalNodes>\n          <leafValues>\n            4.9373480677604675e-01 6.1529481410980225e-01\n            4.7354999184608459e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 936 1.1614130344241858e-03 -1 -2 937\n            2.3350189439952374e-03</internalNodes>\n          <leafValues>\n            6.5105718374252319e-01 4.0883418917655945e-01\n            5.6841522455215454e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 938 3.8499289657920599e-03 -1 -2 939\n            2.4529630318284035e-03</internalNodes>\n          <leafValues>\n            3.0258288979530334e-01 5.2325028181076050e-01\n            2.0176209509372711e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 940 3.6731390282511711e-03 -1 -2 941\n            2.1937100682407618e-03</internalNodes>\n          <leafValues>\n            6.4284259080886841e-01 4.3288651108741760e-01\n            6.4205098152160645e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 942 -6.4666871912777424e-03 -1 -2 943\n            -5.7186251506209373e-03</internalNodes>\n          <leafValues>\n            5.2540659904479980e-01 2.4909840524196625e-01\n            5.2876192331314087e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 944 9.9941878579556942e-04 -1 -2 945\n            -7.8276498243212700e-04</internalNodes>\n          <leafValues>\n            3.3297958970069885e-01 3.5983449220657349e-01\n            5.4983407258987427e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 946 4.3231188319623470e-03 -1 -2 947\n            4.0838290005922318e-03</internalNodes>\n          <leafValues>\n            4.8187050223350525e-01 5.2663302421569824e-01\n            3.1057891249656677e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 948 3.0515898833982646e-04 -1 -2 949\n            1.2640280183404684e-03</internalNodes>\n          <leafValues>\n            3.9952918887138367e-01 3.2284379005432129e-01\n            5.8192151784896851e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 950 -1.0152660310268402e-02 -1 -2 951\n            -2.6863690000027418e-03</internalNodes>\n          <leafValues>\n            8.0260711908340454e-01 3.8756170868873596e-01\n            5.4665708541870117e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 952 -9.0515613555908203e-03 -1 -2 953\n            -6.3204211182892323e-03</internalNodes>\n          <leafValues>\n            4.3720579147338867e-01 1.1265510320663452e-01\n            6.3954162597656250e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 954 2.6117300149053335e-03 -1 -2 955\n            1.4339019544422626e-02</internalNodes>\n          <leafValues>\n            5.4239892959594727e-01 4.9792730808258057e-01\n            6.0422360897064209e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 956 2.8452780097723007e-03 -1 -2 957\n            1.4783289771003183e-05</internalNodes>\n          <leafValues>\n            3.4910920262336731e-01 4.1950678825378418e-01\n            5.7759660482406616e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 958 8.1814555451273918e-03 -1 -2 959\n            6.6321990452706814e-03</internalNodes>\n          <leafValues>\n            4.8859870433807373e-01 5.4444682598114014e-01\n            4.4209951162338257e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 960 -2.2483461070805788e-03 -1 -2 961\n            1.2374560348689556e-02</internalNodes>\n          <leafValues>\n            6.6997921466827393e-01 4.4786059856414795e-01\n            6.5648937225341797e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 962 -6.6516688093543053e-03 -1 -2 963\n            -8.5750613361597061e-03</internalNodes>\n          <leafValues>\n            5.5118787288665771e-01 4.0174451470375061e-01\n            5.4055362939834595e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 964 6.5078441984951496e-03 -1 -2 965\n            2.8675209730863571e-02</internalNodes>\n          <leafValues>\n            2.2943930327892303e-01 5.1779001951217651e-01\n            3.5677561163902283e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 966 7.0673860609531403e-03 -1 -2 967\n            1.2367829913273454e-03</internalNodes>\n          <leafValues>\n            5.5646997690200806e-01 3.6276981234550476e-01\n            5.5724138021469116e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 968 7.4818679131567478e-03 -1 -2 969\n            4.7109839506447315e-03</internalNodes>\n          <leafValues>\n            6.7849111557006836e-01 4.1212528944015503e-01\n            6.0722357034683228e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 970 -6.9405790418386459e-03 -1 -2 971\n            3.3302098512649536e-02</internalNodes>\n          <leafValues>\n            5.4597669839859009e-01 5.2767068147659302e-01\n            2.3749159276485443e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 972 3.6104630678892136e-02 -1 -2 973\n            1.9674649462103844e-02</internalNodes>\n          <leafValues>\n            7.2492793202400208e-02 4.6263459324836731e-01\n            8.2089632749557495e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 974 3.4766150638461113e-03 -1 -2 975\n            1.3987369602546096e-03</internalNodes>\n          <leafValues>\n            5.2087318897247314e-01 5.4844141006469727e-01\n            4.2300349473953247e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 976 4.0974249131977558e-03 -1 -2 977\n            2.6973790954798460e-03</internalNodes>\n          <leafValues>\n            2.7805531024932861e-01 5.4038310050964355e-01\n            3.7909889221191406e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 978 -5.6591699831187725e-03 -1 -2 979\n            3.9460969856008887e-04</internalNodes>\n          <leafValues>\n            4.7983360290527344e-01 3.7669500708580017e-01\n            5.4292291402816772e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 980 2.1750570740550756e-03 -1 -2 981\n            1.4614439569413662e-03</internalNodes>\n          <leafValues>\n            6.2071627378463745e-01 3.3579450845718384e-01\n            5.1426321268081665e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 982 -5.3006567759439349e-04 -1 -2 983\n            1.4869309961795807e-01</internalNodes>\n          <leafValues>\n            5.3446400165557861e-01 5.1596081256866455e-01\n            2.5618231296539307e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 984 -5.8816498494707048e-05 -1 -2 985\n            -1.6275369562208652e-03</internalNodes>\n          <leafValues>\n            5.1230919361114502e-01 6.0176461935043335e-01\n            3.1093719601631165e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 986 -1.2881809845566750e-02 -1 -2 987\n            9.4982917653396726e-04</internalNodes>\n          <leafValues>\n            2.7122870087623596e-01 5.4424422979354858e-01\n            4.0288880467414856e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 988 -1.2315999716520309e-02 -1 -2 989\n            9.0286601334810257e-03</internalNodes>\n          <leafValues>\n            4.7360658645629883e-01 7.4514347314834595e-01\n            3.4879919886589050e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 990 -8.6876116693019867e-02 -1 -2 991\n            -1.5107560102478601e-05</internalNodes>\n          <leafValues>\n            2.2903330624103546e-01 5.5178898572921753e-01\n            4.3931490182876587e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 992 -1.7457660287618637e-02 -1 -2 993\n            -2.5219470262527466e-03</internalNodes>\n          <leafValues>\n            9.0167902410030365e-02 6.2335401773452759e-01\n            4.7894591093063354e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 994 1.0656520025804639e-03 -1 -2 995\n            -4.2540300637483597e-03</internalNodes>\n          <leafValues>\n            5.4896962642669678e-01 5.5798089504241943e-01\n            4.3758779764175415e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 996 -9.0349102392792702e-03 -1 -2 997\n            -1.5230999561026692e-03</internalNodes>\n          <leafValues>\n            3.5791561007499695e-01 5.6136602163314819e-01\n            3.9390438795089722e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 998 2.8441150207072496e-03 -1 -2 999\n            -3.2824429217725992e-03</internalNodes>\n          <leafValues>\n            3.9015549421310425e-01 4.5286190509796143e-01\n            5.4413431882858276e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1000 3.2161718991119415e-05 -1 -2 1001\n            3.0118400900391862e-05</internalNodes>\n          <leafValues>\n            5.8031117916107178e-01 3.3368501067161560e-01\n            5.5048561096191406e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1002 -5.6150099262595177e-03 -1 -2 1003\n            -1.7389209941029549e-02</internalNodes>\n          <leafValues>\n            6.1247891187667847e-01 8.7271630764007568e-02\n            5.2045881748199463e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1004 -4.4361080654198304e-05 -1 -2 1005\n            1.0354899859521538e-04</internalNodes>\n          <leafValues>\n            3.9353290200233459e-01 5.9188538789749146e-01\n            4.1196140646934509e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1006 1.5939630102366209e-03 -1 -2 1007\n            2.5440789759159088e-03</internalNodes>\n          <leafValues>\n            4.8396238684654236e-01 4.7873649001121521e-01\n            6.3606631755828857e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1008 1.5083180187502876e-05 -1 -2 1009\n            -9.9282202427275479e-05</internalNodes>\n          <leafValues>\n            4.2311170697212219e-01 4.2745891213417053e-01\n            6.0940480232238770e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1010 5.5371708003804088e-04 -1 -2 1011\n            1.9186759600415826e-03</internalNodes>\n          <leafValues>\n            4.2719879746437073e-01 4.4971078634262085e-01\n            5.5491220951080322e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1012 -5.0764222396537662e-04 -1 -2 1013\n            1.7236480489373207e-03</internalNodes>\n          <leafValues>\n            5.4771959781646729e-01 2.8829228878021240e-01\n            5.6151270866394043e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>75</maxWeakCount>\n      <stageThreshold>3.6726501464843750e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 1014 1.3092169538140297e-02 -1 -2 1015\n            4.1446479735895991e-04</internalNodes>\n          <leafValues>\n            3.3388701081275940e-01 3.0993521213531494e-01\n            6.6774922609329224e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1016 2.1835729479789734e-02 -1 -2 1017\n            4.8323940485715866e-02</internalNodes>\n          <leafValues>\n            4.3690490722656250e-01 4.3017241358757019e-01\n            6.1538851261138916e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1018 1.6091950237751007e-03 -1 -2 1019\n            1.3469760306179523e-03</internalNodes>\n          <leafValues>\n            3.3873260021209717e-01 6.2487137317657471e-01\n            3.5941308736801147e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1020 1.7729059618432075e-04 -1 -2 1021\n            3.6743620876222849e-04</internalNodes>\n          <leafValues>\n            3.8684248924255371e-01 4.4093450903892517e-01\n            5.4764741659164429e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1022 -1.2352119665592909e-03 -1 -2 1023\n            1.1705530341714621e-03</internalNodes>\n          <leafValues>\n            3.2601711153984070e-01 4.1113489866256714e-01\n            6.0881638526916504e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1024 -2.9695429475395940e-05 -1 -2 1025\n            2.7050738572143018e-04</internalNodes>\n          <leafValues>\n            4.2694228887557983e-01 4.3064668774604797e-01\n            5.8105140924453735e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1026 -7.9626210208516568e-05 -1 -2 1027\n            3.3152441028505564e-04</internalNodes>\n          <leafValues>\n            3.6691430211067200e-01 4.6106639504432678e-01\n            6.2905901670455933e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1028 -5.2305828779935837e-02 -1 -2 1029\n            2.6880469173192978e-02</internalNodes>\n          <leafValues>\n            5.3286898136138916e-01 5.2132612466812134e-01\n            3.2312199473381042e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1030 -2.4203000066336244e-04 -1 -2 1031\n            -1.6424639616161585e-03</internalNodes>\n          <leafValues>\n            3.5685700178146362e-01 3.4406611323356628e-01\n            5.6256049871444702e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1032 -2.6830288697965443e-04 -1 -2 1033\n            -2.2649629972875118e-03</internalNodes>\n          <leafValues>\n            4.5611730217933655e-01 5.3213518857955933e-01\n            3.6741548776626587e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1034 1.5627209097146988e-02 -1 -2 1035\n            1.6211320459842682e-01</internalNodes>\n          <leafValues>\n            2.0293539762496948e-01 5.5630332231521606e-01\n            2.6188498735427856e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1036 -3.7391691002994776e-03 -1 -2 1037\n            -2.0878419745713472e-03</internalNodes>\n          <leafValues>\n            6.0621947050094604e-01 5.9507638216018677e-01\n            4.5451170206069946e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1038 2.3334210272878408e-03 -1 -2 1039\n            6.5116386394947767e-05</internalNodes>\n          <leafValues>\n            6.4355242252349854e-01 3.5207340121269226e-01\n            5.1797789335250854e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1040 7.4625718407332897e-03 -1 -2 1041\n            -2.2032689303159714e-02</internalNodes>\n          <leafValues>\n            5.3266882896423340e-01 3.4919810295104980e-01\n            5.4292368888854980e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1042 -8.3081610500812531e-03 -1 -2 1043\n            -4.3259368976578116e-04</internalNodes>\n          <leafValues>\n            2.0840230584144592e-01 3.9652720093727112e-01\n            5.4254537820816040e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1044 -3.2209228724241257e-02 -1 -2 1045\n            -9.0424838708713651e-04</internalNodes>\n          <leafValues>\n            5.3064119815826416e-01 5.4503858089447021e-01\n            4.2566969990730286e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1046 2.2727500181645155e-03 -1 -2 1047\n            5.9820008464157581e-03</internalNodes>\n          <leafValues>\n            5.9686112403869629e-01 4.7581401467323303e-01\n            3.1509441137313843e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1048 -5.8856618124991655e-04 -1 -2 1049\n            -8.8227191008627415e-04</internalNodes>\n          <leafValues>\n            4.8477488756179810e-01 5.4263162612915039e-01\n            4.3383410573005676e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1050 -7.4473457061685622e-05 -1 -2 1051\n            3.9148979703895748e-04</internalNodes>\n          <leafValues>\n            4.2875099182128906e-01 6.3451850414276123e-01\n            4.1018518805503845e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1052 -3.6939629353582859e-03 -1 -2 1053\n            -1.1207849718630314e-02</internalNodes>\n          <leafValues>\n            4.8491048812866211e-01 4.1463369131088257e-01\n            5.4712641239166260e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1054 -1.0337409563362598e-02 -1 -2 1055\n            3.6883640568703413e-03</internalNodes>\n          <leafValues>\n            2.8771838545799255e-01 5.1019018888473511e-01\n            7.2169512510299683e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1056 -3.8984280545264482e-03 -1 -2 1057\n            -5.9986729174852371e-03</internalNodes>\n          <leafValues>\n            5.2761822938919067e-01 6.6184598207473755e-01\n            4.8416310548782349e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1058 4.5043681748211384e-03 -1 -2 1059\n            1.7799530178308487e-02</internalNodes>\n          <leafValues>\n            1.8741579353809357e-01 4.6169349551200867e-01\n            7.0889657735824585e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1060 -1.8462570384144783e-02 -1 -2 1061\n            1.4931300029275008e-05</internalNodes>\n          <leafValues>\n            3.0019798874855042e-01 4.5618081092834473e-01\n            5.6107878684997559e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1062 -8.6021229624748230e-02 -1 -2 1063\n            -6.0818758356617764e-05</internalNodes>\n          <leafValues>\n            2.3417009413242340e-01 5.6722861528396606e-01\n            4.1999641060829163e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1064 1.2670679716393352e-03 -1 -2 1065\n            1.3699879636988044e-03</internalNodes>\n          <leafValues>\n            6.2074822187423706e-01 5.3949588537216187e-01\n            3.8238629698753357e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1066 3.3162781037390232e-03 -1 -2 1067\n            -1.4532039640471339e-03</internalNodes>\n          <leafValues>\n            7.0616811513900757e-01 3.0655130743980408e-01\n            4.8273730278015137e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1068 -7.1492061018943787e-02 -1 -2 1069\n            1.9857978913933039e-03</internalNodes>\n          <leafValues>\n            5.1931220293045044e-01 4.6424350142478943e-01\n            5.8076947927474976e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1070 6.2516499310731888e-03 -1 -2 1071\n            2.7005500160157681e-03</internalNodes>\n          <leafValues>\n            2.9498139023780823e-01 4.5858868956565857e-01\n            6.0223537683486938e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1072 1.1130389757454395e-02 -1 -2 1073\n            1.5092849731445312e-02</internalNodes>\n          <leafValues>\n            4.3578410148620605e-01 4.5615398883819580e-01\n            6.1190617084503174e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1074 -2.7943300083279610e-02 -1 -2 1075\n            4.4036991312168539e-05</internalNodes>\n          <leafValues>\n            6.5371441841125488e-01 3.4747231006622314e-01\n            5.3369677066802979e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1076 -1.2232770211994648e-02 -1 -2 1077\n            -6.8591412855312228e-04</internalNodes>\n          <leafValues>\n            3.7316760420799255e-01 5.7172292470932007e-01\n            4.7933790087699890e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1078 -3.8992990739643574e-03 -1 -2 1079\n            4.9113907152786851e-04</internalNodes>\n          <leafValues>\n            4.0564361214637756e-01 6.1740481853485107e-01\n            4.4717541337013245e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1080 8.2117747515439987e-03 -1 -2 1081\n            -4.5564480125904083e-02</internalNodes>\n          <leafValues>\n            6.1796981096267700e-01 2.2854949533939362e-01\n            5.2495658397674561e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1082 -5.3631910122931004e-03 -1 -2 1083\n            -1.2274970300495625e-02</internalNodes>\n          <leafValues>\n            1.7849500477313995e-01 7.2619527578353882e-01\n            4.5503988862037659e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1084 5.4185991175472736e-03 -1 -2 1085\n            8.1846961984410882e-04</internalNodes>\n          <leafValues>\n            5.2529907226562500e-01 5.4452222585678101e-01\n            3.2722181081771851e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1086 4.1358140297234058e-03 -1 -2 1087\n            3.9578010910190642e-04</internalNodes>\n          <leafValues>\n            7.0138317346572876e-01 4.9659439921379089e-01\n            3.2955980300903320e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1088 4.6887691132724285e-03 -1 -2 1089\n            -1.8255440518260002e-02</internalNodes>\n          <leafValues>\n            5.3626418113708496e-01 6.4961087703704834e-01\n            4.7571370005607605e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1090 -6.2736468389630318e-03 -1 -2 1091\n            2.4320168886333704e-03</internalNodes>\n          <leafValues>\n            2.3437410593032837e-01 4.6201181411743164e-01\n            6.8984192609786987e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1092 -4.9617629498243332e-02 -1 -2 1093\n            1.1701210169121623e-03</internalNodes>\n          <leafValues>\n            2.1007199585437775e-01 4.6215289831161499e-01\n            5.7971358299255371e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1094 -4.5237291604280472e-02 -1 -2 1095\n            4.7563421539962292e-03</internalNodes>\n          <leafValues>\n            2.1182620525360107e-01 4.8846149444580078e-01\n            6.8724989891052246e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1096 -1.4835969544947147e-02 -1 -2 1097\n            7.7436608262360096e-04</internalNodes>\n          <leafValues>\n            5.2751058340072632e-01 4.1723209619522095e-01\n            5.4911398887634277e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1098 1.4835969544947147e-02 -1 -2 1099\n            -8.0892542609944940e-04</internalNodes>\n          <leafValues>\n            2.1248769760131836e-01 5.4952150583267212e-01\n            4.2077958583831787e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1100 7.7517668250948191e-04 -1 -2 1101\n            -6.7618978209793568e-03</internalNodes>\n          <leafValues>\n            3.3219420909881592e-01 2.2129580378532410e-01\n            5.2326530218124390e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1102 -4.0135860443115234e-02 -1 -2 1103\n            -3.3651469275355339e-03</internalNodes>\n          <leafValues>\n            1.1017960309982300e-01 3.8101008534431458e-01\n            5.6172919273376465e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1104 7.4713007779791951e-04 -1 -2 1105\n            -4.2727389372885227e-03</internalNodes>\n          <leafValues>\n            5.7950568199157715e-01 6.3922691345214844e-01\n            4.7114381194114685e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1106 3.6202510818839073e-03 -1 -2 1107\n            4.7307618660852313e-04</internalNodes>\n          <leafValues>\n            3.4098839759826660e-01 3.6593028903007507e-01\n            5.3881710767745972e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1108 3.3094909042119980e-02 -1 -2 1109\n            -1.1544119566679001e-02</internalNodes>\n          <leafValues>\n            7.1703857183456421e-01 6.3868182897567749e-01\n            4.6813040971755981e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1110 -7.4234469793736935e-03 -1 -2 1111\n            -4.2252950370311737e-03</internalNodes>\n          <leafValues>\n            3.2637009024620056e-01 5.7678192853927612e-01\n            4.3464180827140808e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1112 1.8133109435439110e-02 -1 -2 1113\n            7.0903049781918526e-03</internalNodes>\n          <leafValues>\n            4.6978279948234558e-01 4.4373890757560730e-01\n            6.0616689920425415e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1114 -1.3272940181195736e-02 -1 -2 1115\n            1.4632199599873275e-04</internalNodes>\n          <leafValues>\n            6.5585112571716309e-01 3.3763539791107178e-01\n            5.0916552543640137e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1116 -3.5790191031992435e-03 -1 -2 1117\n            -4.6997101162560284e-04</internalNodes>\n          <leafValues>\n            2.9478839039802551e-01 5.5569821596145630e-01\n            4.6654561161994934e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1118 -4.8179440200328827e-02 -1 -2 1119\n            -9.2581362696364522e-04</internalNodes>\n          <leafValues>\n            7.3383557796478271e-01 3.5438719391822815e-01\n            5.2851498126983643e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1120 -1.4780730009078979e-02 -1 -2 1121\n            -1.0027450323104858e-01</internalNodes>\n          <leafValues>\n            1.9444419443607330e-01 9.9049292504787445e-02\n            5.1398539543151855e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1122 -9.3848101096227765e-04 -1 -2 1123\n            -2.8861360624432564e-03</internalNodes>\n          <leafValues>\n            5.8271098136901855e-01 3.4414279460906982e-01\n            5.1488387584686279e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1124 -4.3682761490345001e-02 -1 -2 1125\n            2.6115700602531433e-03</internalNodes>\n          <leafValues>\n            5.2079981565475464e-01 4.8355031013488770e-01\n            6.3222199678421021e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1126 4.3682761490345001e-02 -1 -2 1127\n            1.7179530113935471e-03</internalNodes>\n          <leafValues>\n            1.3645380735397339e-01 4.5373201370239258e-01\n            6.0667508840560913e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1128 -3.3964909613132477e-02 -1 -2 1129\n            -1.0993590112775564e-03</internalNodes>\n          <leafValues>\n            4.9683749675750732e-01 5.8316808938980103e-01\n            4.6882399916648865e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1130 5.4301079362630844e-02 -1 -2 1131\n            1.0993590112775564e-03</internalNodes>\n          <leafValues>\n            7.5682890415191650e-01 4.3301481008529663e-01\n            5.7684689760208130e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1132 -1.4954120160837192e-05 -1 -2 1133\n            3.1415868550539017e-02</internalNodes>\n          <leafValues>\n            4.4432818889617920e-01 5.2744728326797485e-01\n            3.0378559231758118e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1134 1.0831849649548531e-02 -1 -2 1135\n            8.6545711383223534e-04</internalNodes>\n          <leafValues>\n            3.5817208886146545e-01 5.9375840425491333e-01\n            4.2946299910545349e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1136 2.2743160370737314e-03 -1 -2 1137\n            3.9340821094810963e-03</internalNodes>\n          <leafValues>\n            5.9545767307281494e-01 4.7922229766845703e-01\n            5.8561331033706665e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1138 8.1451907753944397e-03 -1 -2 1139\n            -5.2763288840651512e-03</internalNodes>\n          <leafValues>\n            3.5734778642654419e-01 4.0260228514671326e-01\n            5.7647430896759033e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1140 -8.3787851035594940e-03 -1 -2 1141\n            1.5621910570189357e-03</internalNodes>\n          <leafValues>\n            4.9813330173492432e-01 4.7365880012512207e-01\n            5.5836081504821777e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1142 3.2318739686161280e-03 -1 -2 1143\n            6.6804019734263420e-03</internalNodes>\n          <leafValues>\n            6.1674368381500244e-01 4.1314241290092468e-01\n            6.2806951999664307e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1144 -3.3396480139344931e-03 -1 -2 1145\n            -2.0933480560779572e-01</internalNodes>\n          <leafValues>\n            3.4463581442832947e-01 1.0386580228805542e-01\n            5.2044892311096191e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1146 6.3805822283029556e-03 -1 -2 1147\n            -6.0137799009680748e-03</internalNodes>\n          <leafValues>\n            2.1674020588397980e-01 6.7383992671966553e-01\n            4.8966509103775024e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1148 -8.1756077706813812e-03 -1 -2 1149\n            6.3951779156923294e-04</internalNodes>\n          <leafValues>\n            5.1779150962829590e-01 4.8196458816528320e-01\n            5.4644381999969482e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1150 1.0127760469913483e-03 -1 -2 1151\n            4.9784599104896188e-04</internalNodes>\n          <leafValues>\n            3.4235960245132446e-01 4.4884610176086426e-01\n            5.9126710891723633e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1152 1.3596490316558629e-04 -1 -2 1153\n            1.3571660034358501e-02</internalNodes>\n          <leafValues>\n            5.5688631534576416e-01 5.1610678434371948e-01\n            1.7130009829998016e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1154 3.0259079721872695e-05 -1 -2 1155\n            -3.2625840976834297e-03</internalNodes>\n          <leafValues>\n            4.9162039160728455e-01 6.4046627283096313e-01\n            2.8590849041938782e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1156 -1.9217010412830859e-04 -1 -2 1157\n            2.1993879228830338e-02</internalNodes>\n          <leafValues>\n            5.4592829942703247e-01 4.7157138586044312e-01\n            5.6900751590728760e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1158 7.8907777788117528e-04 -1 -2 1159\n            5.0893891602754593e-04</internalNodes>\n          <leafValues>\n            3.2798269391059875e-01 4.3020078539848328e-01\n            5.6960451602935791e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1160 1.1662710312521085e-04 -1 -2 1161\n            8.0604078248143196e-03</internalNodes>\n          <leafValues>\n            5.3872352838516235e-01 5.0214231014251709e-01\n            5.9653222560882568e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1162 9.5925969071686268e-04 -1 -2 1163\n            -1.9526129588484764e-02</internalNodes>\n          <leafValues>\n            3.4734940528869629e-01 6.4755451679229736e-01\n            4.6437820792198181e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>78</maxWeakCount>\n      <stageThreshold>3.8236038208007812e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 1164 4.1242439299821854e-02 -1 -2 1165\n            1.5626709908246994e-02</internalNodes>\n          <leafValues>\n            3.3933150768280029e-01 5.1041001081466675e-01\n            7.7728152275085449e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1166 2.9947189614176750e-04 -1 -2 1167\n            -1.0037609608843923e-03</internalNodes>\n          <leafValues>\n            3.6646738648414612e-01 5.4056507349014282e-01\n            3.9262050390243530e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1168 6.8128242855891585e-04 -1 -2 1169\n            1.3098999625071883e-04</internalNodes>\n          <leafValues>\n            4.2515191435813904e-01 4.1351449489593506e-01\n            6.9257462024688721e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1170 3.1696720980107784e-03 -1 -2 1171\n            -2.0587369799613953e-03</internalNodes>\n          <leafValues>\n            3.4558731317520142e-01 2.2341939806938171e-01\n            5.2861189842224121e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1172 -4.6395038953050971e-04 -1 -2 1173\n            3.5089480224996805e-03</internalNodes>\n          <leafValues>\n            4.2065200209617615e-01 6.5029817819595337e-01\n            4.1175979375839233e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1174 -2.3975980002433062e-03 -1 -2 1175\n            1.0901279747486115e-03</internalNodes>\n          <leafValues>\n            3.6733010411262512e-01 2.9062381386756897e-01\n            5.4451119899749756e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1176 -1.6524370585102588e-04 -1 -2 1177\n            -4.1602319106459618e-04</internalNodes>\n          <leafValues>\n            4.2335158586502075e-01 3.8863611221313477e-01\n            6.2691658735275269e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1178 -2.3739910102449358e-04 -1 -2 1179\n            2.4739760905504227e-02</internalNodes>\n          <leafValues>\n            5.5244511365890503e-01 4.9600958824157715e-01\n            5.3734910488128662e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1180 -1.5342839993536472e-02 -1 -2 1181\n            1.1540469713509083e-02</internalNodes>\n          <leafValues>\n            6.8494051694869995e-01 4.0372350811958313e-01\n            6.7869400978088379e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1182 6.4230621792376041e-03 -1 -2 1183\n            1.2977809645235538e-02</internalNodes>\n          <leafValues>\n            3.8146761059761047e-01 5.5270588397979736e-01\n            3.7449559569358826e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1184 1.1063399724662304e-03 -1 -2 1185\n            1.3743690215051174e-03</internalNodes>\n          <leafValues>\n            3.5209289193153381e-01 5.6419032812118530e-01\n            3.0750259757041931e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1186 1.6233779489994049e-02 -1 -2 1187\n            -8.1519351806491613e-04</internalNodes>\n          <leafValues>\n            4.8888280987739563e-01 5.4563212394714355e-01\n            4.7435501217842102e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1188 -9.0782493352890015e-02 -1 -2 1189\n            1.1665210127830505e-02</internalNodes>\n          <leafValues>\n            2.9252481460571289e-01 4.6884548664093018e-01\n            6.2303477525711060e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1190 -2.3286409676074982e-02 -1 -2 1191\n            2.1559339947998524e-03</internalNodes>\n          <leafValues>\n            6.8958431482315063e-01 5.3558021783828735e-01\n            3.4234660863876343e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1192 -4.3167220428586006e-03 -1 -2 1193\n            1.5610599657520652e-03</internalNodes>\n          <leafValues>\n            5.9370762109756470e-01 4.7086599469184875e-01\n            2.7369970083236694e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1194 1.4076639898121357e-02 -1 -2 1195\n            7.1018589660525322e-03</internalNodes>\n          <leafValues>\n            5.2871561050415039e-01 5.3361928462982178e-01\n            3.2248139381408691e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1196 -4.8221647739410400e-03 -1 -2 1197\n            -5.3852899000048637e-03</internalNodes>\n          <leafValues>\n            2.9839101433753967e-01 5.6239992380142212e-01\n            4.2959120869636536e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1198 7.3483278974890709e-03 -1 -2 1199\n            -3.5707519855350256e-03</internalNodes>\n          <leafValues>\n            6.8139612674713135e-01 5.8579689264297485e-01\n            4.6034291386604309e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1200 2.3340100888162851e-03 -1 -2 1201\n            4.7432780265808105e-03</internalNodes>\n          <leafValues>\n            2.7448511123657227e-01 5.0475269556045532e-01\n            2.3627419769763947e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1202 6.5055489540100098e-03 -1 -2 1203\n            1.2589249759912491e-02</internalNodes>\n          <leafValues>\n            5.2422481775283813e-01 4.8236909508705139e-01\n            6.7525368928909302e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1204 -6.3358368352055550e-03 -1 -2 1205\n            -5.7639651931822300e-03</internalNodes>\n          <leafValues>\n            1.7346349358558655e-01 6.3543808460235596e-01\n            4.5874750614166260e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1206 1.3599749654531479e-03 -1 -2 1207\n            2.8404260054230690e-02</internalNodes>\n          <leafValues>\n            4.5803809165954590e-01 5.1763808727264404e-01\n            1.2043850123882294e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1208 -9.2958156019449234e-03 -1 -2 1209\n            -1.1800320353358984e-03</internalNodes>\n          <leafValues>\n            2.3379570245742798e-01 3.9028140902519226e-01\n            5.6529301404953003e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1210 -2.0948140881955624e-03 -1 -2 1211\n            4.1679958812892437e-03</internalNodes>\n          <leafValues>\n            5.5120289325714111e-01 5.4559761285781860e-01\n            4.7989490628242493e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1212 5.4458891972899437e-03 -1 -2 1213\n            -1.2766510481014848e-03</internalNodes>\n          <leafValues>\n            6.1270868778228760e-01 5.3171318769454956e-01\n            3.8509321212768555e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1214 5.9404270723462105e-04 -1 -2 1215\n            4.2309608310461044e-02</internalNodes>\n          <leafValues>\n            5.4464370012283325e-01 5.2346438169479370e-01\n            2.2130440175533295e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1216 5.6189671158790588e-03 -1 -2 1217\n            7.2401198558509350e-03</internalNodes>\n          <leafValues>\n            4.9161979556083679e-01 1.4714759588241577e-01\n            4.8528939485549927e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1218 -4.5610670931637287e-03 -1 -2 1219\n            4.5506159949582070e-05</internalNodes>\n          <leafValues>\n            2.7737739682197571e-01 4.6264618635177612e-01\n            5.7680791616439819e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1220 -6.1903791502118111e-03 -1 -2 1221\n            8.1186462193727493e-04</internalNodes>\n          <leafValues>\n            1.6442899405956268e-01 4.7785910964012146e-01\n            6.2618649005889893e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1222 1.3779809698462486e-02 -1 -2 1223\n            1.1290319962427020e-03</internalNodes>\n          <leafValues>\n            5.2573078870773315e-01 5.4980480670928955e-01\n            3.9831069111824036e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1224 -1.0610350000206381e-04 -1 -2 1225\n            1.6695790691301227e-04</internalNodes>\n          <leafValues>\n            4.0335190296173096e-01 4.1493400931358337e-01\n            5.7953411340713501e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1226 1.1290319962427020e-03 -1 -2 1227\n            -1.2019349634647369e-01</internalNodes>\n          <leafValues>\n            3.9341148734092712e-01 7.3400482535362244e-02\n            5.2025860548019409e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1228 -1.5230740420520306e-02 -1 -2 1229\n            3.5759829916059971e-03</internalNodes>\n          <leafValues>\n            3.7495058774948120e-01 5.0781500339508057e-01\n            6.6060662269592285e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1230 1.3479460030794144e-02 -1 -2 1231\n            -2.1162950433790684e-03</internalNodes>\n          <leafValues>\n            4.5477110147476196e-01 3.3110061287879944e-01\n            5.3842592239379883e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1232 -1.7877709120512009e-02 -1 -2 1233\n            1.0931970318779349e-03</internalNodes>\n          <leafValues>\n            6.5132528543472290e-01 5.2647650241851807e-01\n            3.4569910168647766e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1234 -3.0553159303963184e-03 -1 -2 1235\n            3.6365049891173840e-03</internalNodes>\n          <leafValues>\n            6.2686139345169067e-01 5.3992128372192383e-01\n            4.3453970551490784e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1236 9.7896481747739017e-05 -1 -2 1237\n            -3.2714448752813041e-04</internalNodes>\n          <leafValues>\n            3.8356059789657593e-01 3.3376678824424744e-01\n            5.5391657352447510e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1238 4.3425030889920890e-04 -1 -2 1239\n            1.4005579985678196e-02</internalNodes>\n          <leafValues>\n            5.7882702350616455e-01 5.2750778198242188e-01\n            2.7011251449584961e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1240 -9.2654931358993053e-04 -1 -2 1241\n            3.9504268206655979e-03</internalNodes>\n          <leafValues>\n            5.8522802591323853e-01 4.7283369302749634e-01\n            3.3139181137084961e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1242 -5.8086868375539780e-04 -1 -2 1243\n            -1.2018020264804363e-02</internalNodes>\n          <leafValues>\n            4.2588108777999878e-01 5.6097871065139771e-01\n            4.8951920866966248e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1244 -1.4521540701389313e-01 -1 -2 1245\n            -6.6049019806087017e-03</internalNodes>\n          <leafValues>\n            4.3894480913877487e-02 4.2291709780693054e-01\n            5.6162929534912109e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1246 -3.4909751266241074e-02 -1 -2 1247\n            3.7478420417755842e-03</internalNodes>\n          <leafValues>\n            4.7881281375885010e-01 4.8002821207046509e-01\n            5.8013892173767090e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1248 3.3038031309843063e-02 -1 -2 1249\n            3.6872599739581347e-03</internalNodes>\n          <leafValues>\n            7.0781761407852173e-01 4.4496241211891174e-01\n            5.9577310085296631e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1250 -4.5311939902603626e-03 -1 -2 1251\n            4.1058510541915894e-03</internalNodes>\n          <leafValues>\n            4.1770470142364502e-01 5.3729480504989624e-01\n            3.7369269132614136e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1252 -8.7599847465753555e-03 -1 -2 1253\n            -2.3003309965133667e-02</internalNodes>\n          <leafValues>\n            6.6588079929351807e-01 2.6479220390319824e-01\n            5.1018178462982178e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1254 5.3664818406105042e-03 -1 -2 1255\n            3.8971770554780960e-02</internalNodes>\n          <leafValues>\n            4.5486348867416382e-01 5.1570618152618408e-01\n            3.4364390373229980e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1256 -2.7767190709710121e-02 -1 -2 1257\n            -9.8894089460372925e-03</internalNodes>\n          <leafValues>\n            2.3543910682201385e-01 6.8877410888671875e-01\n            5.1110517978668213e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1258 -3.2073140610009432e-03 -1 -2 1259\n            -6.7484978353604674e-04</internalNodes>\n          <leafValues>\n            5.4388678073883057e-01 5.4511487483978271e-01\n            4.8313531279563904e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1260 -5.1947520114481449e-03 -1 -2 1261\n            -2.6169899501837790e-04</internalNodes>\n          <leafValues>\n            2.1134190261363983e-01 5.2736818790435791e-01\n            3.9925870299339294e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1262 2.2421479225158691e-03 -1 -2 1263\n            -1.2139769969508052e-03</internalNodes>\n          <leafValues>\n            4.6882608532905579e-01 5.5042350292205811e-01\n            4.3848711252212524e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1264 -2.9469770379364491e-03 -1 -2 1265\n            -3.9291830034926534e-04</internalNodes>\n          <leafValues>\n            3.8928470015525818e-01 6.0017228126525879e-01\n            4.5616629719734192e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1266 6.2550729513168335e-01 -1 -2 1267\n            9.7744520753622055e-03</internalNodes>\n          <leafValues>\n            6.8125613033771515e-02 4.8130258917808533e-01\n            5.6206572055816650e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1268 9.4378247857093811e-02 -1 -2 1269\n            -1.9560910295695066e-03</internalNodes>\n          <leafValues>\n            6.6632293164730072e-02 3.5882329940795898e-01\n            5.2954071760177612e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1270 9.0652769431471825e-03 -1 -2 1271\n            4.2138071148656309e-04</internalNodes>\n          <leafValues>\n            4.8226881027221680e-01 4.6703329682350159e-01\n            5.6831127405166626e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1272 -4.4220191193744540e-04 -1 -2 1273\n            -4.7313501127064228e-03</internalNodes>\n          <leafValues>\n            5.3607952594757080e-01 6.1372458934783936e-01\n            3.1880891323089600e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1274 1.5395509544759989e-03 -1 -2 1275\n            2.4315000046044588e-03</internalNodes>\n          <leafValues>\n            4.4877201318740845e-01 4.8941668868064880e-01\n            6.7166537046432495e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1276 -1.5581619925796986e-02 -1 -2 1277\n            1.0816920548677444e-03</internalNodes>\n          <leafValues>\n            3.3367419242858887e-01 4.7182199358940125e-01\n            5.9606271982192993e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1278 -2.2197659127414227e-03 -1 -2 1279\n            -9.3048671260476112e-04</internalNodes>\n          <leafValues>\n            3.5885548591613770e-01 6.2187129259109497e-01\n            4.8173001408576965e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1280 -4.7418707981705666e-03 -1 -2 1281\n            -6.2950369901955128e-03</internalNodes>\n          <leafValues>\n            2.5500270724296570e-01 6.7280787229537964e-01\n            5.0510638952255249e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1282 3.5216049291193485e-03 -1 -2 1283\n            -2.4289379362016916e-03</internalNodes>\n          <leafValues>\n            5.4019099473953247e-01 5.4194617271423340e-01\n            4.3471428751945496e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1284 -2.5261470582336187e-03 -1 -2 1285\n            -1.4817339833825827e-03</internalNodes>\n          <leafValues>\n            6.9706249237060547e-01 3.2634168863296509e-01\n            4.9178731441497803e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1286 -2.2474530339241028e-01 -1 -2 1287\n            2.8342509176582098e-03</internalNodes>\n          <leafValues>\n            7.2937291115522385e-03 4.5792299509048462e-01\n            5.3798812627792358e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1288 -2.0821610465645790e-02 -1 -2 1289\n            1.4896340144332498e-04</internalNodes>\n          <leafValues>\n            6.0240888595581055e-01 3.3361440896987915e-01\n            4.9628159403800964e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1290 -3.3524499740451574e-03 -1 -2 1291\n            -3.7279881536960602e-02</internalNodes>\n          <leafValues>\n            3.5587510466575623e-01 1.6985629498958588e-01\n            5.2089858055114746e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1292 1.3896770542487502e-04 -1 -2 1293\n            -3.1912620761431754e-04</internalNodes>\n          <leafValues>\n            5.5906862020492554e-01 5.8487337827682495e-01\n            3.7958368659019470e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1294 5.4003461264073849e-04 -1 -2 1295\n            3.8956850767135620e-03</internalNodes>\n          <leafValues>\n            5.6702882051467896e-01 5.1826947927474976e-01\n            3.3277091383934021e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1296 1.6084529925137758e-03 -1 -2 1297\n            -5.7474587811157107e-04</internalNodes>\n          <leafValues>\n            5.4104858636856079e-01 6.0226422548294067e-01\n            3.6446440219879150e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1298 1.3435039669275284e-02 -1 -2 1299\n            2.1368139423429966e-03</internalNodes>\n          <leafValues>\n            3.4412819147109985e-01 5.2924340963363647e-01\n            2.7470758557319641e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1300 1.4157629571855068e-02 -1 -2 1301\n            5.3884391672909260e-03</internalNodes>\n          <leafValues>\n            8.0278682708740234e-01 5.2223151922225952e-01\n            3.5867279767990112e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1302 8.8013410568237305e-03 -1 -2 1303\n            3.8858849438838661e-04</internalNodes>\n          <leafValues>\n            4.9003869295120239e-01 4.6810561418533325e-01\n            5.7219529151916504e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1304 -2.2143588867038488e-03 -1 -2 1305\n            -8.4642972797155380e-03</internalNodes>\n          <leafValues>\n            5.3888058662414551e-01 6.6755378246307373e-01\n            3.4484419226646423e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1306 1.5044390223920345e-02 -1 -2 1307\n            7.6346402056515217e-03</internalNodes>\n          <leafValues>\n            9.2396140098571777e-01 4.8848968744277954e-01\n            6.3060528039932251e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1308 3.3895121305249631e-04 -1 -2 1309\n            2.1157610171940178e-04</internalNodes>\n          <leafValues>\n            3.9974310994148254e-01 5.6639820337295532e-01\n            3.9729809761047363e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1310 -2.7514949440956116e-02 -1 -2 1311\n            5.1603060215711594e-02</internalNodes>\n          <leafValues>\n            5.2010637521743774e-01 5.1407301425933838e-01\n            1.2451309710741043e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1312 3.7510651163756847e-03 -1 -2 1313\n            -2.1457639522850513e-03</internalNodes>\n          <leafValues>\n            3.8020950555801392e-01 3.3094480633735657e-01\n            5.4745388031005859e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1314 -5.8178009930998087e-04 -1 -2 1315\n            -9.3638541875407100e-04</internalNodes>\n          <leafValues>\n            4.8926019668579102e-01 5.9373992681503296e-01\n            4.6646690368652344e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1316 4.1667491197586060e-02 -1 -2 1317\n            -6.7763780243694782e-03</internalNodes>\n          <leafValues>\n            7.0213532447814941e-01 3.2227510213851929e-01\n            5.0683951377868652e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1318 -2.9170580673962831e-03 -1 -2 1319\n            3.2789530814625323e-04</internalNodes>\n          <leafValues>\n            4.7177010774612427e-01 4.5093831419944763e-01\n            5.6511628627777100e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>91</maxWeakCount>\n      <stageThreshold>4.4682968139648438e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 1320 1.1729800142347813e-02 -1 -2 1321\n            1.1712179984897375e-03</internalNodes>\n          <leafValues>\n            3.8052248954772949e-01 3.1400179862976074e-01\n            6.8581461906433105e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1322 9.3555096536874771e-03 -1 -2 1323\n            1.6570610459893942e-03</internalNodes>\n          <leafValues>\n            6.8346732854843140e-01 2.9924729466438293e-01\n            5.4756778478622437e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1324 -1.3387809740379453e-03 -1 -2 1325\n            1.7580550047568977e-04</internalNodes>\n          <leafValues>\n            2.9414069652557373e-01 3.8969779014587402e-01\n            5.8729708194732666e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1326 -2.9473248869180679e-03 -1 -2 1327\n            8.3220899105072021e-03</internalNodes>\n          <leafValues>\n            3.5765719413757324e-01 5.2324008941650391e-01\n            3.2310879230499268e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1328 7.4366689659655094e-03 -1 -2 1329\n            -2.1322889369912446e-04</internalNodes>\n          <leafValues>\n            6.7156732082366943e-01 5.4705417156219482e-01\n            3.8633960485458374e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1330 -7.8024631366133690e-03 -1 -2 1331\n            5.6611228501424193e-04</internalNodes>\n          <leafValues>\n            2.7714601159095764e-01 4.6891361474990845e-01\n            5.8519637584686279e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1332 -9.2346500605344772e-03 -1 -2 1333\n            -1.4676499631605111e-05</internalNodes>\n          <leafValues>\n            2.7043971419334412e-01 5.6225502490997314e-01\n            3.5793170332908630e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1334 9.7007937729358673e-03 -1 -2 1335\n            -3.5320650786161423e-03</internalNodes>\n          <leafValues>\n            4.1738718748092651e-01 4.1950130462646484e-01\n            5.5494689941406250e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1336 2.1616410464048386e-02 -1 -2 1337\n            3.4567608963698149e-03</internalNodes>\n          <leafValues>\n            2.8573909401893616e-01 6.0245329141616821e-01\n            4.3775078654289246e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1338 2.2914320230484009e-02 -1 -2 1339\n            3.4328910987824202e-03</internalNodes>\n          <leafValues>\n            4.6893501281738281e-01 4.6646049618721008e-01\n            5.7625621557235718e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1340 -8.6510833352804184e-03 -1 -2 1341\n            1.4510039472952485e-03</internalNodes>\n          <leafValues>\n            6.3817399740219116e-01 3.7114879488945007e-01\n            5.5307507514953613e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1342 7.8191719949245453e-03 -1 -2 1343\n            2.0798550394829363e-04</internalNodes>\n          <leafValues>\n            5.2643620967864990e-01 3.7305128574371338e-01\n            5.4457312822341919e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1344 -3.9962218143045902e-03 -1 -2 1345\n            -1.5010139577498194e-05</internalNodes>\n          <leafValues>\n            2.4381700158119202e-01 5.3246712684631348e-01\n            3.6829888820648193e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1346 -4.2428788729012012e-03 -1 -2 1347\n            9.1374982148408890e-03</internalNodes>\n          <leafValues>\n            6.4814740419387817e-01 4.8961588740348816e-01\n            6.5588432550430298e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1348 8.8254585862159729e-03 -1 -2 1349\n            9.4092212384566665e-04</internalNodes>\n          <leafValues>\n            3.6138701438903809e-01 5.5028957128524780e-01\n            3.6325180530548096e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1350 -1.2503350153565407e-02 -1 -2 1351\n            8.6759645491838455e-03</internalNodes>\n          <leafValues>\n            2.2611320018768311e-01 4.9878901243209839e-01\n            6.8471962213516235e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1352 -1.0416760109364986e-02 -1 -2 1353\n            2.7432460337877274e-03</internalNodes>\n          <leafValues>\n            2.4462990462779999e-01 3.5115250945091248e-01\n            5.3998267650604248e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1354 -4.2385691776871681e-03 -1 -2 1355\n            1.8325870856642723e-02</internalNodes>\n          <leafValues>\n            6.8236732482910156e-01 4.8915800452232361e-01\n            7.1356189250946045e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1356 -2.4334540590643883e-02 -1 -2 1357\n            4.6469361404888332e-04</internalNodes>\n          <leafValues>\n            3.5225218534469604e-01 4.0498688817024231e-01\n            5.5158257484436035e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1358 3.4260009415447712e-03 -1 -2 1359\n            -2.5827318895608187e-03</internalNodes>\n          <leafValues>\n            4.1267699003219604e-01 2.8994289040565491e-01\n            5.3864318132400513e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1360 1.0545699624344707e-03 -1 -2 1361\n            -9.1257691383361816e-04</internalNodes>\n          <leafValues>\n            3.7713441252708435e-01 5.8273869752883911e-01\n            4.2675569653511047e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1362 2.6589010376483202e-03 -1 -2 1363\n            4.8598358407616615e-03</internalNodes>\n          <leafValues>\n            4.6881249547004700e-01 4.8539221286773682e-01\n            6.1636447906494141e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1364 8.0638676881790161e-03 -1 -2 1365\n            -7.5898370705544949e-03</internalNodes>\n          <leafValues>\n            1.7491950094699860e-01 6.8261897563934326e-01\n            4.8940700292587280e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1366 3.6368070868775249e-04 -1 -2 1367\n            6.2594950199127197e-02</internalNodes>\n          <leafValues>\n            4.6145960688591003e-01 5.1830172538757324e-01\n            2.6866960525512695e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1368 -4.9753207713365555e-03 -1 -2 1369\n            -2.0880119409412146e-03</internalNodes>\n          <leafValues>\n            1.7584669589996338e-01 6.3693821430206299e-01\n            4.9300441145896912e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1370 9.5644511748105288e-04 -1 -2 1371\n            -3.1721461564302444e-02</internalNodes>\n          <leafValues>\n            4.1393989324569702e-01 6.0455572605133057e-01\n            4.8163640499114990e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1372 1.2898689601570368e-03 -1 -2 1373\n            9.8405163735151291e-03</internalNodes>\n          <leafValues>\n            5.4508107900619507e-01 2.9240009188652039e-01\n            6.6996061801910400e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1374 1.2237089686095715e-03 -1 -2 1375\n            -8.4232585504651070e-03</internalNodes>\n          <leafValues>\n            6.2828367948532104e-01 5.9865701198577881e-01\n            4.8525801301002502e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1376 -7.2726322105154395e-04 -1 -2 1377\n            4.6842931769788265e-03</internalNodes>\n          <leafValues>\n            3.3400490880012512e-01 5.1689237356185913e-01\n            2.6794800162315369e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1378 -1.0379579616710544e-03 -1 -2 1379\n            9.1342730447649956e-03</internalNodes>\n          <leafValues>\n            5.9257918596267700e-01 5.4377281665802002e-01\n            4.3468001484870911e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1380 1.4971119817346334e-03 -1 -2 1381\n            1.5762320253998041e-03</internalNodes>\n          <leafValues>\n            4.1295009851455688e-01 4.5228740572929382e-01\n            6.5562921762466431e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1382 8.7496247142553329e-03 -1 -2 1383\n            -8.5103599121794105e-04</internalNodes>\n          <leafValues>\n            4.5320340991020203e-01 3.7859839200973511e-01\n            5.4169750213623047e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1384 -1.7325570806860924e-02 -1 -2 1385\n            -8.3266440778970718e-03</internalNodes>\n          <leafValues>\n            6.8842482566833496e-01 3.0913260579109192e-01\n            5.2436548471450806e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1386 1.5157909729168750e-05 -1 -2 1387\n            1.8041470320895314e-03</internalNodes>\n          <leafValues>\n            4.7657939791679382e-01 4.7253859043121338e-01\n            5.7165551185607910e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1388 3.0691560823470354e-03 -1 -2 1389\n            -5.2225510444259271e-05</internalNodes>\n          <leafValues>\n            2.1433599293231964e-01 5.6532102823257446e-01\n            4.3851110339164734e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1390 1.0072169970953837e-04 -1 -2 1391\n            1.3573700562119484e-04</internalNodes>\n          <leafValues>\n            5.9247761964797974e-01 4.5734488964080811e-01\n            5.7693827152252197e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1392 9.2137878527864814e-04 -1 -2 1393\n            3.0316581251099706e-04</internalNodes>\n          <leafValues>\n            5.9926092624664307e-01 3.6100810766220093e-01\n            5.0493258237838745e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1394 3.9582479745149612e-02 -1 -2 1395\n            4.7519680112600327e-02</internalNodes>\n          <leafValues>\n            1.5384890139102936e-01 5.2161407470703125e-01\n            1.4283910393714905e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1396 1.8871759995818138e-02 -1 -2 1397\n            -3.9876459049992263e-04</internalNodes>\n          <leafValues>\n            2.8255069255828857e-01 4.0350168943405151e-01\n            5.4377931356430054e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1398 4.6556600136682391e-04 -1 -2 1399\n            6.7090610973536968e-03</internalNodes>\n          <leafValues>\n            4.6689969301223755e-01 5.3313547372817993e-01\n            4.1365718841552734e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1400 -1.8931160448119044e-03 -1 -2 1401\n            -1.3056949712336063e-02</internalNodes>\n          <leafValues>\n            7.1551632881164551e-01 3.1178998947143555e-01\n            5.2084398269653320e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1402 -1.9484119547996670e-04 -1 -2 1403\n            1.5093220099515747e-05</internalNodes>\n          <leafValues>\n            4.6376588940620422e-01 4.5616531372070312e-01\n            5.4452341794967651e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1404 -7.1617960202274844e-06 -1 -2 1405\n            3.0164679628796875e-04</internalNodes>\n          <leafValues>\n            4.1931080818176270e-01 5.9662377834320068e-01\n            4.1005000472068787e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1406 4.4195181690156460e-03 -1 -2 1407\n            -7.3984181508421898e-03</internalNodes>\n          <leafValues>\n            4.8450559377670288e-01 6.2068462371826172e-01\n            4.9312090873718262e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1408 -7.8031201846897602e-03 -1 -2 1409\n            -1.0731429792940617e-02</internalNodes>\n          <leafValues>\n            5.2824628353118896e-01 9.1048341989517212e-01\n            3.4559220075607300e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1410 1.4246780192479491e-03 -1 -2 1411\n            -8.2717568147927523e-05</internalNodes>\n          <leafValues>\n            4.7085541486740112e-01 5.6516230106353760e-01\n            4.7310239076614380e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1412 4.4803409837186337e-03 -1 -2 1413\n            3.0789140146225691e-03</internalNodes>\n          <leafValues>\n            6.1758869886398315e-01 5.1395332813262939e-01\n            3.4230878949165344e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1414 -1.1310289846733212e-03 -1 -2 1415\n            -1.0410690447315574e-03</internalNodes>\n          <leafValues>\n            4.9182820320129395e-01 5.9420871734619141e-01\n            4.9230429530143738e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1416 1.1648540385067463e-03 -1 -2 1417\n            9.0057362103834748e-04</internalNodes>\n          <leafValues>\n            6.4052718877792358e-01 4.5043969154357910e-01\n            6.1920768022537231e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1418 6.8781538866460323e-03 -1 -2 1419\n            -3.5283900797367096e-02</internalNodes>\n          <leafValues>\n            5.3748130798339844e-01 2.2471010684967041e-01\n            5.2171707153320312e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1420 -1.3320200378075242e-03 -1 -2 1421\n            -2.3177571129053831e-03</internalNodes>\n          <leafValues>\n            2.5547030568122864e-01 3.7925159931182861e-01\n            5.2432268857955933e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1422 2.1332940377760679e-04 -1 -2 1423\n            1.3467900454998016e-02</internalNodes>\n          <leafValues>\n            3.8603371381759644e-01 5.3806877136230469e-01\n            4.1783639788627625e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1424 -1.2829169863834977e-03 -1 -2 1425\n            5.1571638323366642e-04</internalNodes>\n          <leafValues>\n            6.1336231231689453e-01 4.0285378694534302e-01\n            5.5368518829345703e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1426 3.9254198782145977e-03 -1 -2 1427\n            -3.3780589699745178e-02</internalNodes>\n          <leafValues>\n            5.2799212932586670e-01 2.3346750438213348e-01\n            5.1759117841720581e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1428 -3.7853721529245377e-02 -1 -2 1429\n            -4.0752900531515479e-04</internalNodes>\n          <leafValues>\n            1.0748530179262161e-01 5.3459298610687256e-01\n            4.1989380121231079e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1430 -3.1193809118121862e-03 -1 -2 1431\n            -1.5714969485998154e-02</internalNodes>\n          <leafValues>\n            3.8558250665664673e-01 3.3351901173591614e-01\n            5.2632021903991699e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1432 -7.8525702701881528e-04 -1 -2 1433\n            -2.8750501223839819e-04</internalNodes>\n          <leafValues>\n            5.8603972196578979e-01 5.4377847909927368e-01\n            3.7161049246788025e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1434 2.8016859665513039e-02 -1 -2 1435\n            -1.9018839811906219e-03</internalNodes>\n          <leafValues>\n            3.3307549357414246e-01 5.3665977716445923e-01\n            4.6937939524650574e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1436 2.0647559314966202e-02 -1 -2 1437\n            4.3002571910619736e-03</internalNodes>\n          <leafValues>\n            1.0069560259580612e-01 4.8160359263420105e-01\n            6.2156772613525391e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1438 1.3459140434861183e-02 -1 -2 1439\n            -1.0320040397346020e-02</internalNodes>\n          <leafValues>\n            5.4619538784027100e-01 4.5784530043601990e-01\n            5.4193097352981567e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1440 3.1990748643875122e-01 -1 -2 1441\n            9.2198798665776849e-04</internalNodes>\n          <leafValues>\n            2.0080469548702240e-01 5.1932811737060547e-01\n            3.9121940732002258e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1442 4.1852539288811386e-04 -1 -2 1443\n            3.5891108564101160e-04</internalNodes>\n          <leafValues>\n            4.2997440695762634e-01 4.3445029854774475e-01\n            5.5319738388061523e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1444 -2.0992439985275269e-01 -1 -2 1445\n            -4.9328152090311050e-03</internalNodes>\n          <leafValues>\n            1.0757210105657578e-01 5.7627969980239868e-01\n            4.5746439695358276e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1446 2.3409130517393351e-03 -1 -2 1447\n            4.7120270319283009e-03</internalNodes>\n          <leafValues>\n            7.4768078327178955e-01 5.2617651224136353e-01\n            4.5055508613586426e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1448 2.8713190928101540e-02 -1 -2 1449\n            -2.6156550738960505e-03</internalNodes>\n          <leafValues>\n            4.4071030616760254e-01 4.2442709207534790e-01\n            6.8929767608642578e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1450 -1.3558969832956791e-02 -1 -2 1451\n            -3.0331799644045532e-04</internalNodes>\n          <leafValues>\n            1.2522679567337036e-01 4.0777918696403503e-01\n            5.4428178071975708e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1452 -5.5601762142032385e-04 -1 -2 1453\n            2.4025330785661936e-03</internalNodes>\n          <leafValues>\n            5.3780037164688110e-01 3.1665799021720886e-01\n            5.2857381105422974e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1454 -3.4089901018887758e-03 -1 -2 1455\n            8.0019602319225669e-04</internalNodes>\n          <leafValues>\n            4.9052149057388306e-01 4.5227360725402832e-01\n            5.5806142091751099e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1456 2.1901070140302181e-03 -1 -2 1457\n            3.3745369873940945e-03</internalNodes>\n          <leafValues>\n            6.6126817464828491e-01 5.1077651977539062e-01\n            3.3869299292564392e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1458 8.0019602319225669e-04 -1 -2 1459\n            1.7346069216728210e-02</internalNodes>\n          <leafValues>\n            5.7075601816177368e-01 5.0160211324691772e-01\n            6.3064599037170410e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1460 -1.9568449351936579e-03 -1 -2 1461\n            -1.1229019612073898e-02</internalNodes>\n          <leafValues>\n            3.0178061127662659e-01 6.2938511371612549e-01\n            4.5204889774322510e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1462 -2.6608388870954514e-03 -1 -2 1463\n            -1.1615100316703320e-02</internalNodes>\n          <leafValues>\n            3.3440071344375610e-01 2.8253790736198425e-01\n            5.1509708166122437e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1464 -9.5248602330684662e-02 -1 -2 1465\n            7.3701781220734119e-03</internalNodes>\n          <leafValues>\n            1.3982650637626648e-01 5.2939987182617188e-01\n            2.3317280411720276e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1466 -1.4953900128602982e-02 -1 -2 1467\n            5.7038792874664068e-04</internalNodes>\n          <leafValues>\n            4.9404659867286682e-01 5.4665708541870117e-01\n            4.6267679333686829e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1468 5.8516198769211769e-03 -1 -2 1469\n            2.1150549582671374e-04</internalNodes>\n          <leafValues>\n            6.2700408697128296e-01 5.5081409215927124e-01\n            4.0618729591369629e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1470 -6.9679190346505493e-06 -1 -2 1471\n            -7.9677387839183211e-04</internalNodes>\n          <leafValues>\n            4.0965679287910461e-01 5.6155568361282349e-01\n            4.6668860316276550e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1472 1.9459480419754982e-02 -1 -2 1473\n            -1.1160830035805702e-02</internalNodes>\n          <leafValues>\n            2.3114809393882751e-01 3.0870118737220764e-01\n            5.5146622657775879e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1474 1.4056149870157242e-02 -1 -2 1475\n            -3.2958350493572652e-04</internalNodes>\n          <leafValues>\n            7.0050561428070068e-01 5.7974857091903687e-01\n            4.6916508674621582e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1476 -5.4636420682072639e-03 -1 -2 1477\n            5.8881669247057289e-05</internalNodes>\n          <leafValues>\n            5.9285950660705566e-01 3.7413978576660156e-01\n            5.1701688766479492e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1478 6.6343429498374462e-03 -1 -2 1479\n            4.5263409614562988e-02</internalNodes>\n          <leafValues>\n            5.4149878025054932e-01 5.1803272962570190e-01\n            1.5296840667724609e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1480 -8.0646127462387085e-03 -1 -2 1481\n            4.7389548853971064e-04</internalNodes>\n          <leafValues>\n            2.5154680013656616e-01 5.1219987869262695e-01\n            3.7259489297866821e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1482 1.4877359717502259e-05 -1 -2 1483\n            2.4321159347891808e-02</internalNodes>\n          <leafValues>\n            5.5324357748031616e-01 4.9607661366462708e-01\n            5.9833151102066040e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1484 6.9931396865285933e-05 -1 -2 1485\n            2.6287760119885206e-03</internalNodes>\n          <leafValues>\n            4.1639530658721924e-01 5.8801448345184326e-01\n            3.3996629714965820e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1486 3.8190539926290512e-03 -1 -2 1487\n            -2.5989150628447533e-02</internalNodes>\n          <leafValues>\n            7.8466212749481201e-01 3.2881140708923340e-01\n            5.1550877094268799e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1488 1.2062400346621871e-03 -1 -2 1489\n            -1.5557400183752179e-03</internalNodes>\n          <leafValues>\n            4.5960599184036255e-01 3.1269869208335876e-01\n            7.1833992004394531e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1490 -2.2691930644214153e-03 -1 -2 1491\n            2.3287249496206641e-04</internalNodes>\n          <leafValues>\n            5.2740061283111572e-01 4.8786661028862000e-01\n            5.6151527166366577e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1492 -5.5999699980020523e-03 -1 -2 1493\n            -1.0496189817786217e-02</internalNodes>\n          <leafValues>\n            5.1608121395111084e-01 5.7016140222549438e-01\n            3.2048508524894714e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1494 -1.4814930182183161e-05 -1 -2 1495\n            -6.4287078566849232e-04</internalNodes>\n          <leafValues>\n            5.5388379096984863e-01 5.3494292497634888e-01\n            4.4721511006355286e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1496 -1.8891949730459601e-04 -1 -2 1497\n            -9.0413521975278854e-03</internalNodes>\n          <leafValues>\n            5.0128370523452759e-01 2.5629359483718872e-01\n            4.5033830404281616e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1498 7.9534705728292465e-03 -1 -2 1499\n            -2.7908999472856522e-03</internalNodes>\n          <leafValues>\n            2.6304998993873596e-01 5.7565087080001831e-01\n            4.8548638820648193e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1500 3.2857100013643503e-03 -1 -2 1501\n            7.7063008211553097e-04</internalNodes>\n          <leafValues>\n            4.0847519040107727e-01 4.0733560919761658e-01\n            5.9202408790588379e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>97</maxWeakCount>\n      <stageThreshold>4.7763450622558594e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 1502 6.3021942973136902e-02 -1 -2 1503\n            -2.8374609537422657e-03</internalNodes>\n          <leafValues>\n            3.4193828701972961e-01 6.8295639753341675e-01\n            4.4045230746269226e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1504 4.6461950987577438e-02 -1 -2 1505\n            2.9152540490031242e-02</internalNodes>\n          <leafValues>\n            4.3917450308799744e-01 4.6010631322860718e-01\n            6.3579368591308594e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1506 -1.4000290320836939e-05 -1 -2 1507\n            -1.2757079675793648e-03</internalNodes>\n          <leafValues>\n            3.7300100922584534e-01 3.0938240885734558e-01\n            5.9013700485229492e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1508 1.3596529606729746e-03 -1 -2 1509\n            1.7991929780691862e-04</internalNodes>\n          <leafValues>\n            4.3375650048255920e-01 4.2175039649009705e-01\n            5.8468478918075562e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1510 -1.4166639630275313e-05 -1 -2 1511\n            6.0252390539972112e-05</internalNodes>\n          <leafValues>\n            4.0846911072731018e-01 5.0872868299484253e-01\n            7.2771841287612915e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1512 6.4320368692278862e-03 -1 -2 1513\n            4.6682319953106344e-04</internalNodes>\n          <leafValues>\n            2.9679030179977417e-01 4.1104629635810852e-01\n            5.5812197923660278e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1514 5.7436279021203518e-03 -1 -2 1515\n            3.2019240316003561e-03</internalNodes>\n          <leafValues>\n            4.2873099446296692e-01 4.2661958932876587e-01\n            6.4440459012985229e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1516 -5.7637941790744662e-04 -1 -2 1517\n            -3.7901920732110739e-03</internalNodes>\n          <leafValues>\n            4.0848249197006226e-01 3.1819209456443787e-01\n            5.2306932210922241e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1518 4.8914109356701374e-03 -1 -2 1519\n            4.6459292061626911e-03</internalNodes>\n          <leafValues>\n            3.5483568906784058e-01 5.6105977296829224e-01\n            2.6938489079475403e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1520 -6.8799369037151337e-03 -1 -2 1521\n            -1.8147470429539680e-02</internalNodes>\n          <leafValues>\n            6.2354081869125366e-01 2.8619819879531860e-01\n            5.2268481254577637e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1522 1.1409220314817503e-04 -1 -2 1523\n            -5.4334272863343358e-04</internalNodes>\n          <leafValues>\n            3.2578331232070923e-01 3.8829690217971802e-01\n            5.3411662578582764e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1524 -2.7602489572018385e-03 -1 -2 1525\n            -1.9730569329112768e-03</internalNodes>\n          <leafValues>\n            6.3539659976959229e-01 5.8807611465454102e-01\n            4.5930901169776917e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1526 2.4565239436924458e-03 -1 -2 1527\n            1.9392010290175676e-04</internalNodes>\n          <leafValues>\n            3.1340101361274719e-01 5.2771317958831787e-01\n            3.6041069030761719e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1528 7.8643016517162323e-02 -1 -2 1529\n            6.5276869572699070e-03</internalNodes>\n          <leafValues>\n            5.2903419733047485e-01 4.6544799208641052e-01\n            6.0449051856994629e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1530 -7.8716799616813660e-02 -1 -2 1531\n            5.7298499159514904e-03</internalNodes>\n          <leafValues>\n            2.5411269068717957e-01 4.3669191002845764e-01\n            5.8228862285614014e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1532 6.2386557692661881e-04 -1 -2 1533\n            -8.5267230868339539e-02</internalNodes>\n          <leafValues>\n            5.4726922512054443e-01 1.4616079628467560e-01\n            5.1818108558654785e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1534 4.0981110185384750e-02 -1 -2 1535\n            7.7135749161243439e-03</internalNodes>\n          <leafValues>\n            1.2701350450515747e-01 4.8326849937438965e-01\n            2.2235789895057678e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1536 -6.8663940764963627e-03 -1 -2 1537\n            1.4559639617800713e-02</internalNodes>\n          <leafValues>\n            5.9189289808273315e-01 4.7615069150924683e-01\n            5.7272237539291382e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1538 -1.0064310394227505e-02 -1 -2 1539\n            3.6274080630391836e-03</internalNodes>\n          <leafValues>\n            3.6367309093475342e-01 5.2717310190200806e-01\n            2.7405250072479248e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1540 -2.3421540390700102e-03 -1 -2 1541\n            -2.4686409160494804e-02</internalNodes>\n          <leafValues>\n            5.4977840185165405e-01 6.0598951578140259e-01\n            4.9603140354156494e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1542 1.9456120207905769e-04 -1 -2 1543\n            3.1714211218059063e-04</internalNodes>\n          <leafValues>\n            3.7694650888442993e-01 4.0623620152473450e-01\n            5.6682151556015015e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1544 2.0793990697711706e-03 -1 -2 1545\n            1.7982709687203169e-03</internalNodes>\n          <leafValues>\n            4.6186569333076477e-01 4.8675051331520081e-01\n            6.5184497833251953e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1546 -2.2287059982772917e-04 -1 -2 1547\n            3.2623921288177371e-04</internalNodes>\n          <leafValues>\n            5.6775957345962524e-01 3.7107339501380920e-01\n            5.6766051054000854e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1548 -6.6792681813240051e-02 -1 -2 1549\n            -1.4869889710098505e-03</internalNodes>\n          <leafValues>\n            2.5115218758583069e-01 3.8867509365081787e-01\n            5.2622538805007935e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1550 -5.0454870797693729e-03 -1 -2 1551\n            -4.8297587782144547e-03</internalNodes>\n          <leafValues>\n            6.5574729442596436e-01 5.9341061115264893e-01\n            4.2859220504760742e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1552 -1.0722599690780044e-03 -1 -2 1553\n            8.7901195511221886e-03</internalNodes>\n          <leafValues>\n            5.4260587692260742e-01 5.3513032197952271e-01\n            4.8342779278755188e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1554 -7.1750381030142307e-03 -1 -2 1555\n            1.1251230025663972e-03</internalNodes>\n          <leafValues>\n            2.0671689510345459e-01 5.1122522354125977e-01\n            3.4687140583992004e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1556 1.0634710080921650e-02 -1 -2 1557\n            -1.1763219721615314e-02</internalNodes>\n          <leafValues>\n            4.4790080189704895e-01 6.2539017200469971e-01\n            4.9689871072769165e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1558 9.2324063181877136e-02 -1 -2 1559\n            1.8991080578416586e-03</internalNodes>\n          <leafValues>\n            2.0313039422035217e-01 5.6187218427658081e-01\n            4.0465721487998962e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1560 -1.0510340332984924e-02 -1 -2 1561\n            -7.4531312566250563e-04</internalNodes>\n          <leafValues>\n            4.9432641267776489e-01 5.6134277582168579e-01\n            3.8453319668769836e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1562 8.0041000619530678e-03 -1 -2 1563\n            5.8110528625547886e-03</internalNodes>\n          <leafValues>\n            7.7598422765731812e-01 4.6247330307960510e-01\n            6.2862771749496460e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1564 -2.7918580919504166e-02 -1 -2 1565\n            2.1739399526268244e-03</internalNodes>\n          <leafValues>\n            2.4093140661716461e-01 5.3455048799514771e-01\n            3.5079580545425415e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1566 -4.0639587678015232e-03 -1 -2 1567\n            6.0017139185220003e-04</internalNodes>\n          <leafValues>\n            6.6471010446548462e-01 4.9985098838806152e-01\n            3.0221650004386902e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1568 1.9214770291000605e-03 -1 -2 1569\n            -1.3860830105841160e-02</internalNodes>\n          <leafValues>\n            5.9191507101058960e-01 6.3517677783966064e-01\n            4.9933108687400818e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1570 2.3006850853562355e-02 -1 -2 1571\n            -1.3857929734513164e-03</internalNodes>\n          <leafValues>\n            1.9023360311985016e-01 5.2533692121505737e-01\n            3.9858600497245789e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1572 1.2637410545721650e-03 -1 -2 1573\n            -1.4675210230052471e-02</internalNodes>\n          <leafValues>\n            4.6661040186882019e-01 3.8231649994850159e-01\n            5.3266328573226929e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1574 -2.9535070061683655e-03 -1 -2 1575\n            -1.7189770005643368e-03</internalNodes>\n          <leafValues>\n            7.0636558532714844e-01 3.8134628534317017e-01\n            5.2467352151870728e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1576 -4.2484089499339461e-04 -1 -2 1577\n            -8.5248658433556557e-04</internalNodes>\n          <leafValues>\n            4.7916388511657715e-01 4.4912180304527283e-01\n            5.3709012269973755e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1578 8.9034568518400192e-03 -1 -2 1579\n            1.4895649655954912e-05</internalNodes>\n          <leafValues>\n            2.0764739811420441e-01 4.4476351141929626e-01\n            5.6671631336212158e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1580 -4.7091601300053298e-04 -1 -2 1581\n            4.3084810022264719e-04</internalNodes>\n          <leafValues>\n            5.4650712013244629e-01 5.4932618141174316e-01\n            4.5807081460952759e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1582 -6.3893961487337947e-04 -1 -2 1583\n            -7.3733746830839664e-05</internalNodes>\n          <leafValues>\n            5.5015718936920166e-01 5.0857907533645630e-01\n            3.3056980371475220e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1584 -8.8991485536098480e-03 -1 -2 1585\n            -1.0253350250422955e-02</internalNodes>\n          <leafValues>\n            4.2764690518379211e-01 1.1232180148363113e-01\n            5.1527231931686401e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1586 -5.9637490659952164e-02 -1 -2 1587\n            2.1707199513912201e-02</internalNodes>\n          <leafValues>\n            7.3867720365524292e-01 4.9962919950485229e-01\n            1.3394139707088470e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1588 9.9107045680284500e-03 -1 -2 1589\n            -1.0998300276696682e-02</internalNodes>\n          <leafValues>\n            4.6790120005607605e-01 6.9286561012268066e-01\n            5.0120681524276733e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1590 7.4608891736716032e-04 -1 -2 1591\n            2.9539171373471618e-04</internalNodes>\n          <leafValues>\n            5.8335822820663452e-01 3.8263911008834839e-01\n            5.5663508176803589e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1592 5.0054129213094711e-02 -1 -2 1593\n            -7.2330660186707973e-03</internalNodes>\n          <leafValues>\n            3.0027210712432861e-01 5.9080427885055542e-01\n            5.0008708238601685e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1594 -2.6863380335271358e-03 -1 -2 1595\n            -1.0195849463343620e-03</internalNodes>\n          <leafValues>\n            3.9750349521636963e-01 3.6976858973503113e-01\n            5.7561928033828735e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1596 -2.0204920321702957e-02 -1 -2 1597\n            2.1340379025787115e-03</internalNodes>\n          <leafValues>\n            6.3752681016921997e-01 5.3632658720016479e-01\n            4.4331708550453186e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1598 -1.8348889425396919e-03 -1 -2 1599\n            -5.9489468112587929e-03</internalNodes>\n          <leafValues>\n            5.8289992809295654e-01 2.6806709170341492e-01\n            4.6428859233856201e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1600 -2.3030120064504445e-04 -1 -2 1601\n            5.0581009127199650e-03</internalNodes>\n          <leafValues>\n            5.4753202199935913e-01 5.3208339214324951e-01\n            4.6464928984642029e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1602 -5.1950011402368546e-04 -1 -2 1603\n            -6.8620947422459722e-04</internalNodes>\n          <leafValues>\n            5.2327448129653931e-01 4.9350860714912415e-01\n            3.1031179428100586e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1604 -7.4936267919838428e-03 -1 -2 1605\n            -1.5682930126786232e-02</internalNodes>\n          <leafValues>\n            2.8830468654632568e-01 3.6403131484985352e-01\n            5.3687548637390137e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1606 -3.2649750355631113e-03 -1 -2 1607\n            3.8463930832222104e-04</internalNodes>\n          <leafValues>\n            6.4686310291290283e-01 5.2596598863601685e-01\n            3.8314279913902283e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1608 4.4492390006780624e-03 -1 -2 1609\n            2.3118320852518082e-02</internalNodes>\n          <leafValues>\n            2.0868189632892609e-01 4.9785330891609192e-01\n            5.9612572193145752e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1610 2.0835159812122583e-03 -1 -2 1611\n            1.1513150529935956e-03</internalNodes>\n          <leafValues>\n            5.7464218139648438e-01 3.5868450999259949e-01\n            5.3634738922119141e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1612 3.6104708909988403e-02 -1 -2 1613\n            3.6256198654882610e-04</internalNodes>\n          <leafValues>\n            2.8331369161605835e-01 5.4777222871780396e-01\n            4.1105321049690247e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1614 -3.4635469783097506e-03 -1 -2 1615\n            -2.8796829283237457e-03</internalNodes>\n          <leafValues>\n            5.9903860092163086e-01 5.7252532243728638e-01\n            4.1495120525360107e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1616 -8.1119500100612640e-03 -1 -2 1617\n            4.5932079665362835e-03</internalNodes>\n          <leafValues>\n            5.3963518142700195e-01 5.3797042369842529e-01\n            3.8913029432296753e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1618 7.0014740340411663e-03 -1 -2 1619\n            8.0169539432972670e-04</internalNodes>\n          <leafValues>\n            3.7146711349487305e-01 5.5295670032501221e-01\n            3.7558048963546753e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1620 -8.6652329191565514e-03 -1 -2 1621\n            -2.7315050829201937e-03</internalNodes>\n          <leafValues>\n            5.0257730484008789e-01 5.8503222465515137e-01\n            4.6175739169120789e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1622 1.3301590224727988e-03 -1 -2 1623\n            -4.2648240923881531e-03</internalNodes>\n          <leafValues>\n            5.9377008676528931e-01 5.6453680992126465e-01\n            3.9376249909400940e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1624 6.3251499086618423e-03 -1 -2 1625\n            -3.0753740575164557e-03</internalNodes>\n          <leafValues>\n            5.1821058988571167e-01 3.0074161291122437e-01\n            5.1964038610458374e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1626 -7.3622138006612659e-04 -1 -2 1627\n            3.0082479497650638e-05</internalNodes>\n          <leafValues>\n            3.6975800991058350e-01 4.3275931477546692e-01\n            5.7158088684082031e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1628 -3.8722730241715908e-03 -1 -2 1629\n            6.2879058532416821e-04</internalNodes>\n          <leafValues>\n            3.4737130999565125e-01 5.4382592439651489e-01\n            4.4539061188697815e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1630 1.3411579420790076e-03 -1 -2 1631\n            -8.3681922405958176e-03</internalNodes>\n          <leafValues>\n            6.5117138624191284e-01 1.4432950317859650e-01\n            4.8881998658180237e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1632 9.3305751215666533e-04 -1 -2 1633\n            -1.0746510233730078e-03</internalNodes>\n          <leafValues>\n            3.9511090517044067e-01 3.9102658629417419e-01\n            5.3495037555694580e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1634 -1.8610050901770592e-02 -1 -2 1635\n            1.3651419430971146e-03</internalNodes>\n          <leafValues>\n            1.2757439911365509e-01 5.0382888317108154e-01\n            6.9513040781021118e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1636 7.3744421824812889e-03 -1 -2 1637\n            8.4163323044776917e-03</internalNodes>\n          <leafValues>\n            5.2534431219100952e-01 5.0112438201904297e-01\n            7.3113328218460083e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1638 5.1413988694548607e-03 -1 -2 1639\n            4.5847031287848949e-03</internalNodes>\n          <leafValues>\n            4.9535360932350159e-01 2.5355559587478638e-01\n            6.4624428749084473e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1640 2.8565239161252975e-02 -1 -2 1641\n            4.3958800961263478e-04</internalNodes>\n          <leafValues>\n            2.3307220637798309e-01 4.7022441029548645e-01\n            5.5445492267608643e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1642 3.1459458172321320e-02 -1 -2 1643\n            5.6011630222201347e-03</internalNodes>\n          <leafValues>\n            3.3689688891172409e-02 4.7871211171150208e-01\n            6.3383519649505615e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1644 7.1835669223219156e-04 -1 -2 1645\n            -5.5303089320659637e-03</internalNodes>\n          <leafValues>\n            5.4314869642257690e-01 4.1058328747749329e-01\n            5.4039907455444336e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1646 1.4129279879853129e-03 -1 -2 1647\n            2.5530709535814822e-04</internalNodes>\n          <leafValues>\n            3.1055399775505066e-01 4.2544719576835632e-01\n            5.4471540451049805e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1648 3.1966410460881889e-04 -1 -2 1649\n            5.0411392003297806e-03</internalNodes>\n          <leafValues>\n            6.1183619499206543e-01 5.2900421619415283e-01\n            4.2247870564460754e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1650 7.7617880888283253e-03 -1 -2 1651\n            2.9374631121754646e-03</internalNodes>\n          <leafValues>\n            4.3153458833694458e-01 6.6292631626129150e-01\n            3.0289649963378906e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1652 -1.6497720498591661e-03 -1 -2 1653\n            -5.8834417723119259e-03</internalNodes>\n          <leafValues>\n            5.4918527603149414e-01 3.1885540485382080e-01\n            5.1842892169952393e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1654 8.7459187489002943e-04 -1 -2 1655\n            -1.5308779664337635e-02</internalNodes>\n          <leafValues>\n            3.3288308978080750e-01 3.9236080646514893e-01\n            5.2351391315460205e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1656 3.2292451709508896e-02 -1 -2 1657\n            -4.3842519517056644e-04</internalNodes>\n          <leafValues>\n            5.9776467084884644e-01 4.5416879653930664e-01\n            5.3694289922714233e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1658 1.5429529594257474e-03 -1 -2 1659\n            -2.4733028840273619e-03</internalNodes>\n          <leafValues>\n            6.3181412220001221e-01 3.4906330704689026e-01\n            4.7590249776840210e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1660 2.0994939841330051e-03 -1 -2 1661\n            -5.7541108690202236e-03</internalNodes>\n          <leafValues>\n            5.8871978521347046e-01 5.9613317251205444e-01\n            4.8419830203056335e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1662 -1.0233130306005478e-02 -1 -2 1663\n            2.2554509341716766e-01</internalNodes>\n          <leafValues>\n            1.7054040729999542e-01 4.7793799638748169e-01\n            9.7879663109779358e-02</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1664 2.9666559770703316e-02 -1 -2 1665\n            -2.8518449980765581e-03</internalNodes>\n          <leafValues>\n            5.8222240209579468e-01 5.4596269130706787e-01\n            4.6100661158561707e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1666 9.7465328872203827e-04 -1 -2 1667\n            1.4044740055396687e-05</internalNodes>\n          <leafValues>\n            3.6703228950500488e-01 4.3023860454559326e-01\n            5.6917107105255127e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1668 -1.7579430714249611e-02 -1 -2 1669\n            -5.2381679415702820e-02</internalNodes>\n          <leafValues>\n            6.9173210859298706e-01 7.1100401878356934e-01\n            5.0601547956466675e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1670 -1.1242110282182693e-02 -1 -2 1671\n            -3.6728400737047195e-03</internalNodes>\n          <leafValues>\n            8.7691891193389893e-01 6.5191918611526489e-01\n            4.5460689067840576e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1672 3.5082760732620955e-03 -1 -2 1673\n            6.1679710634052753e-03</internalNodes>\n          <leafValues>\n            5.3298658132553101e-01 5.2204591035842896e-01\n            2.9535189270973206e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1674 -9.7009900491684675e-04 -1 -2 1675\n            -1.0957010090351105e-02</internalNodes>\n          <leafValues>\n            5.0486332178115845e-01 5.8373582363128662e-01\n            3.0200859904289246e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1676 -8.3272513002157211e-03 -1 -2 1677\n            2.9798380637657829e-05</internalNodes>\n          <leafValues>\n            3.1580638885498047e-01 4.3863898515701294e-01\n            5.4432111978530884e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1678 2.8244039276614785e-04 -1 -2 1679\n            -8.1364117795601487e-04</internalNodes>\n          <leafValues>\n            5.6253957748413086e-01 5.2811980247497559e-01\n            3.4014078974723816e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1680 1.8008040497079492e-03 -1 -2 1681\n            -6.9944779388606548e-03</internalNodes>\n          <leafValues>\n            3.4716591238975525e-01 4.4816970825195312e-01\n            5.3857702016830444e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1682 4.5625398342963308e-05 -1 -2 1683\n            -7.3189922841265798e-04</internalNodes>\n          <leafValues>\n            4.4925129413604736e-01 4.1673120856285095e-01\n            6.0211020708084106e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1684 -2.9980219551362097e-04 -1 -2 1685\n            -2.9060940505587496e-05</internalNodes>\n          <leafValues>\n            4.1484281420707703e-01 5.5920898914337158e-01\n            4.0732109546661377e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1686 -5.9742690064013004e-04 -1 -2 1687\n            1.4831830048933625e-04</internalNodes>\n          <leafValues>\n            6.0889142751693726e-01 5.2983051538467407e-01\n            3.7619501352310181e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1688 -2.9441029764711857e-03 -1 -2 1689\n            1.3741210103034973e-01</internalNodes>\n          <leafValues>\n            4.7160848975181580e-01 5.1013368368148804e-01\n            4.6746801584959030e-02</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1690 -8.8414177298545837e-02 -1 -2 1691\n            7.0610277354717255e-02</internalNodes>\n          <leafValues>\n            1.1818689852952957e-01 5.1190632581710815e-01\n            7.7784419059753418e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1692 -7.7188978902995586e-03 -1 -2 1693\n            1.5115399844944477e-02</internalNodes>\n          <leafValues>\n            1.8741349875926971e-01 4.9800279736518860e-01\n            7.0058178901672363e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1694 1.0671879863366485e-03 -1 -2 1695\n            7.0487911580130458e-04</internalNodes>\n          <leafValues>\n            4.4822388887405396e-01 6.2657529115676880e-01\n            4.4026550650596619e-01</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>90</maxWeakCount>\n      <stageThreshold>4.4251281738281250e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            1 0 1696 -9.8690733313560486e-02 -1 -2 1697\n            6.2373418360948563e-02</internalNodes>\n          <leafValues>\n            3.9994749426841736e-01 5.2477848529815674e-01\n            8.1935757398605347e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1698 1.9496519817039371e-03 -1 -2 1699\n            -8.9139147894456983e-04</internalNodes>\n          <leafValues>\n            3.5298168659210205e-01 5.8527278900146484e-01\n            3.2459780573844910e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1700 -5.5150408297777176e-04 -1 -2 1701\n            -1.1721949558705091e-03</internalNodes>\n          <leafValues>\n            3.8928169012069702e-01 4.3350520730018616e-01\n            6.5206241607666016e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1702 -7.4480642797425389e-04 -1 -2 1703\n            -2.6264840271323919e-03</internalNodes>\n          <leafValues>\n            4.0411350131034851e-01 5.6249821186065674e-01\n            3.9675250649452209e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1704 -3.9712688885629177e-04 -1 -2 1705\n            3.5984949208796024e-03</internalNodes>\n          <leafValues>\n            3.8561120629310608e-01 5.9978890419006348e-01\n            4.2416140437126160e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1706 5.3080618381500244e-03 -1 -2 1707\n            9.6319877775385976e-04</internalNodes>\n          <leafValues>\n            6.6601687669754028e-01 4.4813790917396545e-01\n            5.5834877490997314e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1708 5.0776469288393855e-04 -1 -2 1709\n            3.6223160568624735e-03</internalNodes>\n          <leafValues>\n            3.5354590415954590e-01 3.4098070859909058e-01\n            5.4206877946853638e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1710 -6.2061410397291183e-02 -1 -2 1711\n            6.4387189922854304e-04</internalNodes>\n          <leafValues>\n            1.9340839982032776e-01 4.0836268663406372e-01\n            5.4902219772338867e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1712 2.6239909231662750e-02 -1 -2 1713\n            8.1940297968685627e-04</internalNodes>\n          <leafValues>\n            2.2857080399990082e-01 4.6486678719520569e-01\n            6.0173559188842773e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1714 2.3833119485061616e-04 -1 -2 1715\n            -1.5869759954512119e-03</internalNodes>\n          <leafValues>\n            3.5980388522148132e-01 4.2596510052680969e-01\n            5.4764348268508911e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1716 -6.7263417877256870e-03 -1 -2 1717\n            1.1006110347807407e-02</internalNodes>\n          <leafValues>\n            6.5072381496429443e-01 5.1494097709655762e-01\n            3.3629849553108215e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1718 7.1445819921791553e-03 -1 -2 1719\n            -4.7233798541128635e-03</internalNodes>\n          <leafValues>\n            2.6729300618171692e-01 5.6521821022033691e-01\n            4.2981448769569397e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1720 9.8437406122684479e-03 -1 -2 1721\n            1.5124640412977897e-05</internalNodes>\n          <leafValues>\n            1.1518859863281250e-01 4.3735980987548828e-01\n            5.6121289730072021e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1722 3.9908871054649353e-02 -1 -2 1723\n            5.3903679363429546e-03</internalNodes>\n          <leafValues>\n            5.2046489715576172e-01 4.8134678602218628e-01\n            6.3612091541290283e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1724 -3.9908871054649353e-02 -1 -2 1725\n            5.3903679363429546e-03</internalNodes>\n          <leafValues>\n            1.5068709850311279e-01 4.5816949009895325e-01\n            6.2002408504486084e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1726 6.7005190066993237e-03 -1 -2 1727\n            -1.2623789720237255e-02</internalNodes>\n          <leafValues>\n            3.4322351217269897e-01 3.0882269144058228e-01\n            5.2267378568649292e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1728 1.1806610040366650e-02 -1 -2 1729\n            -3.4257229417562485e-03</internalNodes>\n          <leafValues>\n            7.1879392862319946e-01 3.1208148598670959e-01\n            5.0658440589904785e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1730 3.9385299896821380e-04 -1 -2 1731\n            3.4388188272714615e-02</internalNodes>\n          <leafValues>\n            4.7545841336250305e-01 5.2616578340530396e-01\n            3.3501741290092468e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1732 -7.5009986758232117e-02 -1 -2 1733\n            4.9022492021322250e-04</internalNodes>\n          <leafValues>\n            1.7134809494018555e-01 4.7258019447326660e-01\n            5.9564691781997681e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1734 -8.5525289177894592e-03 -1 -2 1735\n            1.3135520566720515e-04</internalNodes>\n          <leafValues>\n            6.5582227706909180e-01 4.8354008793830872e-01\n            5.5869138240814209e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1736 4.7948658466339111e-03 -1 -2 1737\n            2.0124691072851419e-03</internalNodes>\n          <leafValues>\n            2.6457059383392334e-01 3.6579450964927673e-01\n            5.1247721910476685e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1738 -1.1785479635000229e-01 -1 -2 1739\n            1.5575019642710686e-03</internalNodes>\n          <leafValues>\n            2.3856540024280548e-01 5.4904741048812866e-01\n            4.2747479677200317e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1740 -1.5573759563267231e-02 -1 -2 1741\n            -2.1854790393263102e-03</internalNodes>\n          <leafValues>\n            6.9389009475708008e-01 3.6459881067276001e-01\n            5.0925260782241821e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1742 2.9272339306771755e-03 -1 -2 1743\n            6.4663668163120747e-03</internalNodes>\n          <leafValues>\n            4.6858081221580505e-01 4.9734100699424744e-01\n            7.7260971069335938e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1744 -7.6140360906720161e-03 -1 -2 1745\n            4.1512572206556797e-03</internalNodes>\n          <leafValues>\n            6.8774658441543579e-01 4.7885251045227051e-01\n            6.9216579198837280e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1746 2.7711640577763319e-03 -1 -2 1747\n            -1.2836109846830368e-02</internalNodes>\n          <leafValues>\n            5.4818397760391235e-01 3.8001629710197449e-01\n            5.2044928073883057e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1748 -2.4380050599575043e-03 -1 -2 1749\n            2.1713329479098320e-03</internalNodes>\n          <leafValues>\n            2.5824350118637085e-01 4.9611631035804749e-01\n            3.2152029871940613e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1750 6.2800728483125567e-04 -1 -2 1751\n            -9.7982389852404594e-03</internalNodes>\n          <leafValues>\n            5.4604238271713257e-01 6.0465437173843384e-01\n            4.9399220943450928e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1752 7.3543828912079334e-03 -1 -2 1753\n            -1.4665040187537670e-02</internalNodes>\n          <leafValues>\n            5.2910941839218140e-01 5.4461228847503662e-01\n            3.5673621296882629e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1754 3.0244510620832443e-02 -1 -2 1755\n            -5.6660208851099014e-02</internalNodes>\n          <leafValues>\n            5.5183291435241699e-01 6.9309788942337036e-01\n            5.0933879613876343e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1756 -5.6967479176819324e-03 -1 -2 1757\n            3.0806770548224449e-02</internalNodes>\n          <leafValues>\n            3.2015261054039001e-01 4.9892461299896240e-01\n            2.2770540416240692e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1758 2.2748769260942936e-03 -1 -2 1759\n            2.0436900667846203e-03</internalNodes>\n          <leafValues>\n            4.8109310865402222e-01 5.2838671207427979e-01\n            3.2559248805046082e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1760 -8.6277956143021584e-03 -1 -2 1761\n            6.5113382879644632e-04</internalNodes>\n          <leafValues>\n            6.2665361166000366e-01 5.0971370935440063e-01\n            3.1919100880622864e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1762 8.8188261725008488e-04 -1 -2 1763\n            -1.4594909735023975e-02</internalNodes>\n          <leafValues>\n            4.5495858788490295e-01 2.6450389623641968e-01\n            5.1538681983947754e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1764 -1.2304580304771662e-03 -1 -2 1765\n            -2.1867299801670015e-04</internalNodes>\n          <leafValues>\n            6.1975848674774170e-01 5.4691988229751587e-01\n            4.2068558931350708e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1766 -1.0909959673881531e-03 -1 -2 1767\n            3.5210378700867295e-04</internalNodes>\n          <leafValues>\n            4.1407600045204163e-01 5.4766088724136353e-01\n            4.1550210118293762e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1768 -7.2563779540359974e-03 -1 -2 1769\n            1.4701850013807416e-03</internalNodes>\n          <leafValues>\n            7.1604692935943604e-01 5.2408081293106079e-01\n            3.7296628952026367e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1770 1.1472719779703766e-04 -1 -2 1771\n            3.0506469774991274e-03</internalNodes>\n          <leafValues>\n            4.0337988734245300e-01 5.2639859914779663e-01\n            3.5600930452346802e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1772 2.6269949739798903e-04 -1 -2 1773\n            -3.6365550477057695e-03</internalNodes>\n          <leafValues>\n            4.5697999000549316e-01 3.0425709486007690e-01\n            5.8682537078857422e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1774 -8.4893293678760529e-03 -1 -2 1775\n            5.8107408694922924e-03</internalNodes>\n          <leafValues>\n            4.9141570925712585e-01 4.9185299873352051e-01\n            6.2669628858566284e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1776 7.5583951547741890e-04 -1 -2 1777\n            -2.2017690353095531e-03</internalNodes>\n          <leafValues>\n            5.6332361698150635e-01 5.5539160966873169e-01\n            3.8276460766792297e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1778 2.7908938936889172e-03 -1 -2 1779\n            -1.8228569533675909e-03</internalNodes>\n          <leafValues>\n            5.4986977577209473e-01 4.3822830915451050e-01\n            5.4240328073501587e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1780 -7.2495508939027786e-03 -1 -2 1781\n            -6.8744522286579013e-04</internalNodes>\n          <leafValues>\n            2.8881219029426575e-01 3.4726551175117493e-01\n            5.0763708353042603e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1782 2.5174440816044807e-03 -1 -2 1783\n            -1.0151379741728306e-02</internalNodes>\n          <leafValues>\n            4.6612051129341125e-01 3.7447750568389893e-01\n            5.2940011024475098e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1784 -4.1399952024221420e-03 -1 -2 1785\n            -4.7078551724553108e-03</internalNodes>\n          <leafValues>\n            4.6604850888252258e-01 4.1750618815422058e-01\n            6.9163060188293457e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1786 4.1981041431427002e-02 -1 -2 1787\n            -1.4272999949753284e-02</internalNodes>\n          <leafValues>\n            2.0182150602340698e-01 7.5111979246139526e-01\n            5.0320839881896973e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1788 4.0869521908462048e-03 -1 -2 1789\n            1.7606799956411123e-03</internalNodes>\n          <leafValues>\n            2.5045138597488403e-01 3.3014011383056641e-01\n            5.2183371782302856e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1790 1.2550549581646919e-04 -1 -2 1791\n            -2.9503209516406059e-03</internalNodes>\n          <leafValues>\n            4.6144428849220276e-01 4.6199500560760498e-01\n            5.2470302581787109e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1792 -1.1312420247122645e-03 -1 -2 1793\n            -1.6983180539682508e-03</internalNodes>\n          <leafValues>\n            6.3143682479858398e-01 3.4013068675994873e-01\n            5.0555270910263062e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1794 -1.1457820422947407e-02 -1 -2 1795\n            -8.4962565451860428e-03</internalNodes>\n          <leafValues>\n            4.9399960041046143e-01 2.9654508829116821e-01\n            5.1943677663803101e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1796 1.1919089592993259e-02 -1 -2 1797\n            6.4416420646011829e-03</internalNodes>\n          <leafValues>\n            7.8869980573654175e-01 5.1069867610931396e-01\n            2.9671460390090942e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1798 -8.7857811013236642e-04 -1 -2 1799\n            -2.0312711130827665e-03</internalNodes>\n          <leafValues>\n            5.7143712043762207e-01 4.4812008738517761e-01\n            5.3849118947982788e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1800 -1.5262430533766747e-03 -1 -2 1801\n            4.2860880494117737e-03</internalNodes>\n          <leafValues>\n            6.1935687065124512e-01 4.3398851156234741e-01\n            7.6972991228103638e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1802 3.5010920837521553e-03 -1 -2 1803\n            1.2587670236825943e-02</internalNodes>\n          <leafValues>\n            3.1713891029357910e-01 5.2466988563537598e-01\n            4.2412081360816956e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1804 2.6207490009255707e-04 -1 -2 1805\n            4.4701730075757951e-05</internalNodes>\n          <leafValues>\n            4.2318999767303467e-01 4.1741389036178589e-01\n            5.9196037054061890e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1806 7.8084698179736733e-04 -1 -2 1807\n            8.8851212058216333e-04</internalNodes>\n          <leafValues>\n            4.2773890495300293e-01 3.7201610207557678e-01\n            5.2268189191818237e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1808 2.3369069676846266e-03 -1 -2 1809\n            1.6688359901309013e-03</internalNodes>\n          <leafValues>\n            5.4780668020248413e-01 3.6286789178848267e-01\n            6.1500048637390137e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1810 3.0844469438306987e-04 -1 -2 1811\n            3.4617560449987650e-03</internalNodes>\n          <leafValues>\n            4.7470751404762268e-01 4.5801380276679993e-01\n            5.5856817960739136e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1812 1.8961310386657715e-02 -1 -2 1813\n            1.7347310483455658e-01</internalNodes>\n          <leafValues>\n            5.2988010644912720e-01 3.6983850598335266e-01\n            8.4986197948455811e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1814 2.0020549709443003e-04 -1 -2 1815\n            1.0967060225084424e-03</internalNodes>\n          <leafValues>\n            5.5656617879867554e-01 4.7957131266593933e-01\n            6.2862598896026611e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1816 1.5107099898159504e-04 -1 -2 1817\n            -3.4463501069694757e-03</internalNodes>\n          <leafValues>\n            4.0524059534072876e-01 6.1730152368545532e-01\n            4.4142639636993408e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1818 8.5176620632410049e-03 -1 -2 1819\n            -3.5812109708786011e-02</internalNodes>\n          <leafValues>\n            3.5705709457397461e-01 3.1513288617134094e-01\n            5.2527028322219849e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1820 -2.1155400201678276e-02 -1 -2 1821\n            8.9890940580517054e-04</internalNodes>\n          <leafValues>\n            6.1247211694717407e-01 5.1699757575988770e-01\n            3.5962718725204468e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1822 -1.5613760333508253e-03 -1 -2 1823\n            6.7120860330760479e-04</internalNodes>\n          <leafValues>\n            4.9149879813194275e-01 4.5462110638618469e-01\n            5.3958117961883545e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1824 -2.1597029641270638e-02 -1 -2 1825\n            -2.4947229772806168e-02</internalNodes>\n          <leafValues>\n            1.9031339883804321e-01 6.9740772247314453e-01\n            4.9677160382270813e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1826 1.8725979607552290e-03 -1 -2 1827\n            6.3912719488143921e-03</internalNodes>\n          <leafValues>\n            4.7489479184150696e-01 5.1801782846450806e-01\n            2.9243218898773193e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1828 -9.1552399098873138e-03 -1 -2 1829\n            2.1715660113841295e-03</internalNodes>\n          <leafValues>\n            7.6658701896667480e-01 5.2155512571334839e-01\n            3.3657190203666687e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1830 1.2330369791015983e-03 -1 -2 1831\n            -4.0785901364870369e-04</internalNodes>\n          <leafValues>\n            6.2609577178955078e-01 4.5335099101066589e-01\n            5.3864890336990356e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1832 4.6437609125860035e-04 -1 -2 1833\n            -1.1600199650274590e-04</internalNodes>\n          <leafValues>\n            4.1034960746765137e-01 5.8303910493850708e-01\n            4.3041059374809265e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1834 -1.2718720361590385e-02 -1 -2 1835\n            8.9431880041956902e-05</internalNodes>\n          <leafValues>\n            2.1325829625129700e-01 4.8728910088539124e-01\n            5.4589152336120605e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1836 -3.3913689549081028e-04 -1 -2 1837\n            -1.8026340752840042e-02</internalNodes>\n          <leafValues>\n            3.9743649959564209e-01 7.5685507059097290e-01\n            5.0456118583679199e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1838 6.9179181009531021e-03 -1 -2 1839\n            -1.1839679791592062e-04</internalNodes>\n          <leafValues>\n            3.9662998914718628e-01 4.1980829834938049e-01\n            5.4358041286468506e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1840 -3.9474181830883026e-03 -1 -2 1841\n            6.0050919273635373e-05</internalNodes>\n          <leafValues>\n            6.3694578409194946e-01 5.2695667743682861e-01\n            3.8122430443763733e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1842 9.1423643752932549e-03 -1 -2 1843\n            2.1305440168362111e-04</internalNodes>\n          <leafValues>\n            4.1567629575729370e-01 3.5235330462455750e-01\n            5.3494542837142944e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1844 -2.0855850016232580e-04 -1 -2 1845\n            1.3130389852449298e-03</internalNodes>\n          <leafValues>\n            4.4033220410346985e-01 6.0581612586975098e-01\n            4.4682189822196960e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1846 -2.9134768992662430e-03 -1 -2 1847\n            2.9645769391208887e-03</internalNodes>\n          <leafValues>\n            4.8257058858871460e-01 4.8359981179237366e-01\n            6.0392779111862183e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1848 1.7772549763321877e-03 -1 -2 1849\n            -7.7136349864304066e-03</internalNodes>\n          <leafValues>\n            6.8718272447586060e-01 2.8422209620475769e-01\n            5.1454281806945801e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1850 5.1027478184551001e-04 -1 -2 1851\n            1.7460630042478442e-03</internalNodes>\n          <leafValues>\n            6.0244262218475342e-01 4.7566100955009460e-01\n            5.7211542129516602e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1852 3.8068278809078038e-04 -1 -2 1853\n            2.8228890150785446e-03</internalNodes>\n          <leafValues>\n            4.9310690164566040e-01 3.3116981387138367e-01\n            6.2275981903076172e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1854 -5.3000478073954582e-03 -1 -2 1855\n            4.4951299059903249e-05</internalNodes>\n          <leafValues>\n            5.2320927381515503e-01 3.9952319860458374e-01\n            5.3147977590560913e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1856 3.2752458937466145e-03 -1 -2 1857\n            -2.8162579983472824e-03</internalNodes>\n          <leafValues>\n            4.4816198945045471e-01 3.9079719781875610e-01\n            6.6716408729553223e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1858 1.4112279750406742e-03 -1 -2 1859\n            8.3062034100294113e-03</internalNodes>\n          <leafValues>\n            5.3570109605789185e-01 4.7709658741950989e-01\n            5.5700999498367310e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1860 2.2164839319884777e-03 -1 -2 1861\n            -4.9868631176650524e-03</internalNodes>\n          <leafValues>\n            4.9471241235733032e-01 5.2413070201873779e-01\n            2.5126549601554871e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1862 -3.6664260551333427e-03 -1 -2 1863\n            -1.0581229813396931e-02</internalNodes>\n          <leafValues>\n            4.6195539832115173e-01 6.3017189502716064e-01\n            4.9730318784713745e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1864 7.3366491124033928e-03 -1 -2 1865\n            -3.9318940252996981e-04</internalNodes>\n          <leafValues>\n            2.8709700703620911e-01 4.2528051137924194e-01\n            5.5792468786239624e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1866 -8.1375334411859512e-03 -1 -2 1867\n            2.4809150490909815e-03</internalNodes>\n          <leafValues>\n            5.7473158836364746e-01 5.2033740282058716e-01\n            3.9035668969154358e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1868 8.8749779388308525e-04 -1 -2 1869\n            -4.2194919660687447e-04</internalNodes>\n          <leafValues>\n            5.5343210697174072e-01 5.3380441665649414e-01\n            3.9258408546447754e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1870 -7.9790111631155014e-03 -1 -2 1871\n            1.1439629597589374e-03</internalNodes>\n          <leafValues>\n            4.1443160176277161e-01 4.7013729810714722e-01\n            5.2817362546920776e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1872 7.5542130507528782e-03 -1 -2 1873\n            1.0288399644196033e-03</internalNodes>\n          <leafValues>\n            2.5272560119628906e-01 5.6051462888717651e-01\n            4.2978560924530029e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1874 -1.7234670231118798e-03 -1 -2 1875\n            5.7586699724197388e-01</internalNodes>\n          <leafValues>\n            4.8396828770637512e-01 5.1105028390884399e-01\n            8.0489329993724823e-02</leafValues></_></weakClassifiers></_>\n    <_>\n      <maxWeakCount>109</maxWeakCount>\n      <stageThreshold>5.3755569458007812e+01</stageThreshold>\n      <weakClassifiers>\n        <_>\n          <internalNodes>\n            0 1 1876 6.6640521399676800e-03 -1 -2 1877\n            8.9905522763729095e-03</internalNodes>\n          <leafValues>\n            3.8289201259613037e-01 4.8584291338920593e-01\n            7.3549592494964600e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1878 5.7154200039803982e-03 -1 -2 1879\n            1.1257929727435112e-03</internalNodes>\n          <leafValues>\n            6.7232239246368408e-01 4.4295778870582581e-01\n            6.0707777738571167e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1880 -9.1789010912179947e-04 -1 -2 1881\n            -1.0492859873920679e-03</internalNodes>\n          <leafValues>\n            3.0763450264930725e-01 5.5936437845230103e-01\n            3.6510229110717773e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1882 3.5453929740469903e-05 -1 -2 1883\n            2.9015709878876805e-04</internalNodes>\n          <leafValues>\n            4.2779681086540222e-01 4.5835450291633606e-01\n            5.2846831083297729e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1884 1.6071660502348095e-04 -1 -2 1885\n            -5.2961107576265931e-04</internalNodes>\n          <leafValues>\n            3.7981921434402466e-01 3.8504371047019958e-01\n            5.9396880865097046e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1886 2.6682569296099246e-04 -1 -2 1887\n            -1.3492540165316314e-04</internalNodes>\n          <leafValues>\n            4.1230249404907227e-01 5.7605999708175659e-01\n            4.2376458644866943e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1888 -1.0841679759323597e-02 -1 -2 1889\n            1.2077829800546169e-02</internalNodes>\n          <leafValues>\n            3.9299210906028748e-01 5.7619231939315796e-01\n            2.7804449200630188e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1890 2.2128869313746691e-03 -1 -2 1891\n            -1.5266190283000469e-02</internalNodes>\n          <leafValues>\n            4.7945070266723633e-01 7.4055880308151245e-02\n            5.1535779237747192e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1892 6.7929533543065190e-05 -1 -2 1893\n            1.7633590323384851e-04</internalNodes>\n          <leafValues>\n            5.8587378263473511e-01 3.5676109790802002e-01\n            5.5989629030227661e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1894 8.1311381654813886e-04 -1 -2 1895\n            3.2630451023578644e-03</internalNodes>\n          <leafValues>\n            5.3468507528305054e-01 4.7825369238853455e-01\n            5.4567539691925049e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1896 -3.9503918960690498e-03 -1 -2 1897\n            -3.9864578866399825e-04</internalNodes>\n          <leafValues>\n            2.8318119049072266e-01 5.4852157831192017e-01\n            4.1596978902816772e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1898 -1.1432520113885403e-02 -1 -2 1899\n            5.3339172154664993e-03</internalNodes>\n          <leafValues>\n            5.6391012668609619e-01 4.5969840884208679e-01\n            5.9312427043914795e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1900 8.3193257451057434e-03 -1 -2 1901\n            -4.2479918920435011e-04</internalNodes>\n          <leafValues>\n            3.2306200265884399e-01 3.7952938675880432e-01\n            5.4086112976074219e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1902 -1.1189430207014084e-01 -1 -2 1903\n            -7.5553781352937222e-03</internalNodes>\n          <leafValues>\n            1.1322979629039764e-01 6.3393700122833252e-01\n            4.8387709259986877e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1904 -7.0337029173970222e-03 -1 -2 1905\n            -1.4833680354058743e-02</internalNodes>\n          <leafValues>\n            5.6652551889419556e-01 6.7514181137084961e-01\n            4.1409450769424438e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1906 8.7506724521517754e-03 -1 -2 1907\n            1.6645010327920318e-03</internalNodes>\n          <leafValues>\n            3.5612589120864868e-01 5.3472799062728882e-01\n            3.6497798562049866e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1908 9.4900820404291153e-03 -1 -2 1909\n            1.1133110383525491e-03</internalNodes>\n          <leafValues>\n            2.7546560764312744e-01 4.2259928584098816e-01\n            5.6291788816452026e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1910 9.4940755516290665e-03 -1 -2 1911\n            -1.5396620146930218e-03</internalNodes>\n          <leafValues>\n            4.9060368537902832e-01 4.0070518851280212e-01\n            5.3807091712951660e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1912 1.3434959948062897e-01 -1 -2 1913\n            -9.4940755516290665e-03</internalNodes>\n          <leafValues>\n            2.2146719694137573e-01 7.3531562089920044e-01\n            5.0050330162048340e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1914 2.0011790096759796e-02 -1 -2 1915\n            -1.8875009845942259e-03</internalNodes>\n          <leafValues>\n            3.3279061317443848e-01 3.9152890443801880e-01\n            5.4018497467041016e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1916 7.1842782199382782e-03 -1 -2 1917\n            1.6976969782263041e-03</internalNodes>\n          <leafValues>\n            7.1766048669815063e-01 4.5269781351089478e-01\n            6.0769128799438477e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1918 4.9219978973269463e-03 -1 -2 1919\n            1.1803199537098408e-02</internalNodes>\n          <leafValues>\n            2.5698339939117432e-01 4.9996379017829895e-01\n            5.9582281112670898e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1920 -9.7703449428081512e-03 -1 -2 1921\n            2.1174899302423000e-03</internalNodes>\n          <leafValues>\n            3.4590938687324524e-01 4.5151269435882568e-01\n            5.8297157287597656e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1922 9.4801411032676697e-03 -1 -2 1923\n            -2.6078789960592985e-03</internalNodes>\n          <leafValues>\n            4.8073920607566833e-01 3.4622168540954590e-01\n            5.2015948295593262e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1924 -5.7252747938036919e-03 -1 -2 1925\n            -8.2325618714094162e-03</internalNodes>\n          <leafValues>\n            6.5998530387878418e-01 2.8218281269073486e-01\n            5.1252847909927368e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1926 8.9571950957179070e-04 -1 -2 1927\n            -1.5021569561213255e-04</internalNodes>\n          <leafValues>\n            4.8838189244270325e-01 4.8299181461334229e-01\n            5.4287171363830566e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1928 4.8489659093320370e-04 -1 -2 1929\n            -9.6192650496959686e-02</internalNodes>\n          <leafValues>\n            4.4345989823341370e-01 2.2566360235214233e-01\n            5.9562277793884277e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1930 -1.1053519556298852e-03 -1 -2 1931\n            -1.0215040296316147e-01</internalNodes>\n          <leafValues>\n            4.5272240042686462e-01 2.8443491458892822e-01\n            5.1864528656005859e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1932 3.0147889629006386e-03 -1 -2 1933\n            7.6131648384034634e-03</internalNodes>\n          <leafValues>\n            3.8089990615844727e-01 5.7186990976333618e-01\n            4.2625638842582703e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1934 1.5197630273178220e-03 -1 -2 1935\n            -1.4197279699146748e-02</internalNodes>\n          <leafValues>\n            5.9427189826965332e-01 7.7311038970947266e-01\n            4.9976539611816406e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1936 -1.3818879611790180e-02 -1 -2 1937\n            -5.0701329018920660e-04</internalNodes>\n          <leafValues>\n            6.6811382770538330e-01 3.3056080341339111e-01\n            4.7499749064445496e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1938 -9.3537531793117523e-03 -1 -2 1939\n            -9.4771059229969978e-03</internalNodes>\n          <leafValues>\n            2.8609329462051392e-01 6.1888831853866577e-01\n            4.8421001434326172e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1940 1.6923650400713086e-03 -1 -2 1941\n            5.8652542065829039e-04</internalNodes>\n          <leafValues>\n            6.0702490806579590e-01 3.7826898694038391e-01\n            5.3681969642639160e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1942 -2.5826620403677225e-03 -1 -2 1943\n            -2.7307639829814434e-03</internalNodes>\n          <leafValues>\n            3.6902099847793579e-01 3.8571149110794067e-01\n            5.3181087970733643e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1944 2.1871570497751236e-02 -1 -2 1945\n            -1.5010299648565706e-05</internalNodes>\n          <leafValues>\n            2.3270089924335480e-01 5.5607229471206665e-01\n            4.3014100193977356e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1946 5.3583700209856033e-03 -1 -2 1947\n            5.0057549960911274e-03</internalNodes>\n          <leafValues>\n            6.7676377296447754e-01 5.1949042081832886e-01\n            3.6128538846969604e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1948 -1.9030070398002863e-03 -1 -2 1949\n            -7.8506693243980408e-03</internalNodes>\n          <leafValues>\n            3.2378450036048889e-01 1.1948519945144653e-01\n            4.9917238950729370e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1950 -2.7093670796602964e-03 -1 -2 1951\n            1.4138079714030027e-03</internalNodes>\n          <leafValues>\n            4.8549601435661316e-01 4.8723229765892029e-01\n            5.9035778045654297e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1952 9.0300198644399643e-03 -1 -2 1953\n            -9.7925681620836258e-04</internalNodes>\n          <leafValues>\n            6.5473157167434692e-01 5.8492732048034668e-01\n            4.5542308688163757e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1954 1.3984439428895712e-03 -1 -2 1955\n            8.3372107474133372e-04</internalNodes>\n          <leafValues>\n            4.0646260976791382e-01 5.3995430469512939e-01\n            4.1528099775314331e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1956 1.0551059618592262e-02 -1 -2 1957\n            8.8344102550763637e-05</internalNodes>\n          <leafValues>\n            1.7966809868812561e-01 4.2518630623817444e-01\n            5.4135227203369141e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1958 -4.1022308170795441e-02 -1 -2 1959\n            7.5065628625452518e-03</internalNodes>\n          <leafValues>\n            5.2281248569488525e-01 4.8537430167198181e-01\n            6.0934442281723022e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1960 4.1022308170795441e-02 -1 -2 1961\n            -5.3961377125233412e-04</internalNodes>\n          <leafValues>\n            2.2050240635871887e-01 5.6927317380905151e-01\n            4.4687569141387939e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1962 -6.8696036934852600e-02 -1 -2 1963\n            -1.8447940237820148e-03</internalNodes>\n          <leafValues>\n            1.4833140373229980e-01 6.2112838029861450e-01\n            4.9666011333465576e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1964 -6.0959919355809689e-03 -1 -2 1965\n            -4.2068301700055599e-03</internalNodes>\n          <leafValues>\n            2.2946719825267792e-01 6.4070910215377808e-01\n            4.7485628724098206e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1966 -7.1332789957523346e-04 -1 -2 1967\n            1.1756779998540878e-01</internalNodes>\n          <leafValues>\n            5.3549361228942871e-01 5.1369780302047729e-01\n            1.0595739819109440e-02</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1968 5.9354289987822995e-05 -1 -2 1969\n            -6.3173691742122173e-03</internalNodes>\n          <leafValues>\n            3.7118038535118103e-01 1.7120739817619324e-01\n            5.0617581605911255e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1970 1.4941499568521976e-02 -1 -2 1971\n            -2.0789399277418852e-03</internalNodes>\n          <leafValues>\n            6.7291188240051270e-01 4.4106459617614746e-01\n            5.4440277814865112e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1972 -7.0736219640821218e-04 -1 -2 1973\n            -3.1247111037373543e-03</internalNodes>\n          <leafValues>\n            5.5689108371734619e-01 5.0238692760467529e-01\n            3.5624051094055176e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1974 -7.8919378574937582e-04 -1 -2 1975\n            1.0179580189287663e-02</internalNodes>\n          <leafValues>\n            5.4567861557006836e-01 5.5451387166976929e-01\n            4.6223109960556030e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1976 -2.7506109327077866e-03 -1 -2 1977\n            1.0601329617202282e-02</internalNodes>\n          <leafValues>\n            4.9425360560417175e-01 2.9612338542938232e-01\n            5.9643387794494629e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1978 5.1466780714690685e-03 -1 -2 1979\n            7.6321147382259369e-02</internalNodes>\n          <leafValues>\n            5.4952287673950195e-01 5.1739591360092163e-01\n            2.9402169585227966e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1980 -1.5027689514681697e-03 -1 -2 1981\n            1.2266670353710651e-02</internalNodes>\n          <leafValues>\n            3.1062999367713928e-01 4.6511501073837280e-01\n            6.8466138839721680e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1982 -3.1118579208850861e-02 -1 -2 1983\n            2.8905589133501053e-02</internalNodes>\n          <leafValues>\n            5.2260571718215942e-01 5.1822441816329956e-01\n            2.7054280042648315e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1984 4.7598380595445633e-02 -1 -2 1985\n            3.0808549374341965e-02</internalNodes>\n          <leafValues>\n            1.1095120012760162e-01 4.9386250972747803e-01\n            1.4041109383106232e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1986 -2.1277810446918011e-04 -1 -2 1987\n            7.8969962894916534e-02</internalNodes>\n          <leafValues>\n            4.3923568725585938e-01 5.2165520191192627e-01\n            2.2941139340400696e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1988 -1.0257950052618980e-02 -1 -2 1989\n            1.2604889925569296e-03</internalNodes>\n          <leafValues>\n            6.1766529083251953e-01 5.2362227439880371e-01\n            3.3289659023284912e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1990 -3.3490460366010666e-02 -1 -2 1991\n            -5.9202767442911863e-04</internalNodes>\n          <leafValues>\n            4.8661869764328003e-01 4.1164070367813110e-01\n            5.3956401348114014e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1992 3.0320750738610514e-05 -1 -2 1993\n            -5.4369680583477020e-04</internalNodes>\n          <leafValues>\n            5.6107360124588013e-01 5.6213891506195068e-01\n            3.4612038731575012e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1994 -3.3490460366010666e-02 -1 -2 1995\n            -5.9202767442911863e-04</internalNodes>\n          <leafValues>\n            4.8967620730400085e-01 4.3054041266441345e-01\n            5.3407138586044312e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 1996 2.0550889894366264e-03 -1 -2 1997\n            -4.4353571720421314e-03</internalNodes>\n          <leafValues>\n            5.5449998378753662e-01 6.0385400056838989e-01\n            3.7465929985046387e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 1998 -8.4170423448085785e-02 -1 -2 1999\n            6.7419027909636497e-03</internalNodes>\n          <leafValues>\n            5.0073480606079102e-01 5.2980971336364746e-01\n            4.7161450982093811e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2000 1.0278150439262390e-02 -1 -2 2001\n            5.8800862170755863e-03</internalNodes>\n          <leafValues>\n            6.2693750858306885e-01 5.1548278331756592e-01\n            3.8130408525466919e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2002 -6.9679190346505493e-06 -1 -2 2003\n            8.2419527461752295e-04</internalNodes>\n          <leafValues>\n            4.4402399659156799e-01 4.6975341439247131e-01\n            5.4855042695999146e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2004 -5.5268318392336369e-03 -1 -2 2005\n            9.6128671430051327e-04</internalNodes>\n          <leafValues>\n            5.5136048793792725e-01 3.6186391115188599e-01\n            5.8384567499160767e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2006 2.4810510221868753e-03 -1 -2 2007\n            -1.0480589699000120e-03</internalNodes>\n          <leafValues>\n            2.5232228636741638e-01 4.1172578930854797e-01\n            5.3929960727691650e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2008 -6.1287907883524895e-03 -1 -2 2009\n            1.1682329932227731e-04</internalNodes>\n          <leafValues>\n            6.7263299226760864e-01 5.0411927700042725e-01\n            3.6077290773391724e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2010 -3.9909478276968002e-02 -1 -2 2011\n            1.5859459526836872e-03</internalNodes>\n          <leafValues>\n            1.5637390315532684e-01 4.8919808864593506e-01\n            5.7798451185226440e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2012 -2.2690229117870331e-02 -1 -2 2013\n            2.0916070789098740e-03</internalNodes>\n          <leafValues>\n            2.1868790686130524e-01 4.7715771198272705e-01\n            6.0992312431335449e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2014 -2.4715419858694077e-02 -1 -2 2015\n            -1.3419450260698795e-02</internalNodes>\n          <leafValues>\n            3.4639969468116760e-01 3.6306929588317871e-01\n            5.2521961927413940e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2016 -6.0629472136497498e-03 -1 -2 2017\n            -2.0921030081808567e-03</internalNodes>\n          <leafValues>\n            6.6663217544555664e-01 3.3995470404624939e-01\n            5.0356978178024292e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2018 2.5961859151721001e-02 -1 -2 2019\n            1.7908669542521238e-04</internalNodes>\n          <leafValues>\n            5.0368028879165649e-01 5.4185307025909424e-01\n            4.3189769983291626e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2020 -3.1546850223094225e-03 -1 -2 2021\n            -1.1397759662941098e-03</internalNodes>\n          <leafValues>\n            7.2210252285003662e-01 3.3209729194641113e-01\n            5.0244337320327759e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2022 -4.7840211540460587e-02 -1 -2 2023\n            4.1577088995836675e-04</internalNodes>\n          <leafValues>\n            1.9387650489807129e-01 4.8021888732910156e-01\n            5.7307147979736328e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2024 -4.4247039477340877e-04 -1 -2 2025\n            1.4479350065812469e-03</internalNodes>\n          <leafValues>\n            4.2625150084495544e-01 5.7191711664199829e-01\n            4.0641531348228455e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2026 1.5701510012149811e-02 -1 -2 2027\n            2.7805729769170284e-04</internalNodes>\n          <leafValues>\n            4.9957260489463806e-01 5.2892869710922241e-01\n            4.5817288756370544e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2028 -2.9010509606450796e-03 -1 -2 2029\n            2.0830519497394562e-04</internalNodes>\n          <leafValues>\n            6.0121482610702515e-01 5.0579768419265747e-01\n            3.5994321107864380e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2030 -5.1530029624700546e-02 -1 -2 2031\n            1.7163449956569821e-04</internalNodes>\n          <leafValues>\n            4.9917969107627869e-01 4.6754699945449829e-01\n            5.3747731447219849e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2032 2.3614279925823212e-02 -1 -2 2033\n            -5.6427798699587584e-04</internalNodes>\n          <leafValues>\n            6.5864789485931396e-01 3.8532960414886475e-01\n            5.1960402727127075e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2034 6.6903959959745407e-03 -1 -2 2035\n            -4.8789530992507935e-03</internalNodes>\n          <leafValues>\n            6.0042357444763184e-01 3.2932278513908386e-01\n            5.2452367544174194e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2036 -6.8537332117557526e-03 -1 -2 2037\n            9.9893810693174601e-04</internalNodes>\n          <leafValues>\n            2.5659140944480896e-01 4.6154940128326416e-01\n            5.9424322843551636e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2038 -1.3354700058698654e-04 -1 -2 2039\n            1.0165109997615218e-03</internalNodes>\n          <leafValues>\n            5.4873758554458618e-01 4.5783591270446777e-01\n            5.4269278049468994e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2040 9.1216771397739649e-04 -1 -2 2041\n            1.0080259526148438e-03</internalNodes>\n          <leafValues>\n            3.9394611120223999e-01 4.0497899055480957e-01\n            5.5207037925720215e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2042 -1.3102490629535168e-04 -1 -2 2043\n            5.5228749988600612e-04</internalNodes>\n          <leafValues>\n            4.8790889978408813e-01 4.8449438810348511e-01\n            5.5128258466720581e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2044 -1.2130969844292849e-04 -1 -2 2045\n            -1.5112989785848185e-05</internalNodes>\n          <leafValues>\n            4.3679711222648621e-01 6.4259552955627441e-01\n            4.8818269371986389e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2046 -4.0125829400494695e-04 -1 -2 2047\n            -6.5766851184889674e-04</internalNodes>\n          <leafValues>\n            5.3720992803573608e-01 5.8345532417297363e-01\n            4.8690780997276306e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2048 6.2220421386882663e-04 -1 -2 2049\n            1.4663359615951777e-03</internalNodes>\n          <leafValues>\n            3.8246369361877441e-01 4.8134881258010864e-01\n            6.9667392969131470e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2050 -4.9547709524631500e-02 -1 -2 2051\n            1.3017569435760379e-03</internalNodes>\n          <leafValues>\n            5.3927659988403320e-02 5.3374558687210083e-01\n            4.1607481241226196e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2052 -4.4914530590176582e-03 -1 -2 2053\n            1.6592369647696614e-03</internalNodes>\n          <leafValues>\n            5.9974372386932373e-01 3.7271851301193237e-01\n            5.1156342029571533e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2054 6.4695458859205246e-03 -1 -2 2055\n            4.9810269847512245e-03</internalNodes>\n          <leafValues>\n            5.2520352602005005e-01 5.2567178010940552e-01\n            3.9344060420989990e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2056 -3.8536980748176575e-02 -1 -2 2057\n            -2.8275650739669800e-01</internalNodes>\n          <leafValues>\n            2.0619249343872070e-01 6.1883211135864258e-02\n            4.9250578880310059e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2058 -9.0301828458905220e-03 -1 -2 2059\n            -4.3866269290447235e-02</internalNodes>\n          <leafValues>\n            3.1575900316238403e-01 2.0336820185184479e-01\n            5.1647698879241943e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2060 -4.5701069757342339e-03 -1 -2 2061\n            -2.3362410720437765e-03</internalNodes>\n          <leafValues>\n            6.6111832857131958e-01 2.8077891469001770e-01\n            4.9628761410713196e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2062 5.3960331715643406e-03 -1 -2 2063\n            -2.6297608856111765e-03</internalNodes>\n          <leafValues>\n            5.1463878154754639e-01 6.2844878435134888e-01\n            4.9555888772010803e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2064 -3.8577478844672441e-03 -1 -2 2065\n            1.3963800156489015e-03</internalNodes>\n          <leafValues>\n            1.4867480099201202e-01 4.7013381123542786e-01\n            6.3209718465805054e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2066 -8.8699469342827797e-03 -1 -2 2067\n            -7.0626288652420044e-04</internalNodes>\n          <leafValues>\n            5.2868181467056274e-01 4.6483701467514038e-01\n            5.3332102298736572e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2068 4.2645810171961784e-03 -1 -2 2069\n            6.1572100967168808e-02</internalNodes>\n          <leafValues>\n            5.0848782062530518e-01 3.6296251416206360e-01\n            8.7571567296981812e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2070 -4.5381980016827583e-03 -1 -2 2071\n            -4.0877899155020714e-03</internalNodes>\n          <leafValues>\n            4.8566961288452148e-01 4.5841160416603088e-01\n            5.4202407598495483e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2072 6.4308601431548595e-03 -1 -2 2073\n            7.0455260574817657e-03</internalNodes>\n          <leafValues>\n            2.7073028683662415e-01 5.0574868917465210e-01\n            7.0265239477157593e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2074 -2.3246440105140209e-03 -1 -2 2075\n            6.0276601288933307e-05</internalNodes>\n          <leafValues>\n            4.8272788524627686e-01 4.2472490668296814e-01\n            5.5087631940841675e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2076 1.8084559589624405e-02 -1 -2 2077\n            8.4693520329892635e-04</internalNodes>\n          <leafValues>\n            8.1048011779785156e-01 5.1546192169189453e-01\n            3.5143798589706421e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2078 -2.6931039988994598e-02 -1 -2 2079\n            -4.2346641421318054e-03</internalNodes>\n          <leafValues>\n            4.8868888616561890e-01 4.6223780512809753e-01\n            5.3824782371520996e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2080 2.6947110891342163e-02 -1 -2 2081\n            4.6446882188320160e-03</internalNodes>\n          <leafValues>\n            6.3665962219238281e-01 5.3685069084167480e-01\n            3.7654298543930054e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2082 -6.9577661342918873e-03 -1 -2 2083\n            8.7609712500125170e-04</internalNodes>\n          <leafValues>\n            4.2346870899200439e-01 4.6724060177803040e-01\n            5.3506839275360107e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2084 1.6103329835459590e-03 -1 -2 2085\n            -1.2848590267822146e-03</internalNodes>\n          <leafValues>\n            5.7327628135681152e-01 5.4817992448806763e-01\n            3.7845930457115173e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2086 1.0243539698421955e-02 -1 -2 2087\n            2.6889349101111293e-04</internalNodes>\n          <leafValues>\n            5.1559072732925415e-01 5.3531897068023682e-01\n            4.3871539831161499e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2088 3.7903659977018833e-03 -1 -2 2089\n            -2.9369680210947990e-02</internalNodes>\n          <leafValues>\n            5.0320029258728027e-01 5.8735388517379761e-01\n            2.2154450416564941e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            1 0 2090 6.0743088833987713e-03 -1 -2 2091\n            -1.2710720300674438e-02</internalNodes>\n          <leafValues>\n            5.4170298576354980e-01 6.0565119981765747e-01\n            4.9851819872856140e-01</leafValues></_>\n        <_>\n          <internalNodes>\n            0 1 2092 -5.9445449151098728e-03 -1 -2 2093\n            -2.8927479870617390e-03</internalNodes>\n          <leafValues>\n            3.3520698547363281e-01 6.9292408227920532e-01\n            4.7782200574874878e-01</leafValues></_></weakClassifiers></_></stages>\n  <features>\n    <_>\n      <rects>\n        <_>\n          2 7 16 4 -1.</_>\n        <_>\n          2 9 16 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 3 14 -1.</_>\n        <_>\n          8 11 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 6 1 6 -1.</_>\n        <_>\n          13 9 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 2 12 8 -1.</_>\n        <_>\n          8 2 4 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 1 9 -1.</_>\n        <_>\n          6 6 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 7 14 9 -1.</_>\n        <_>\n          3 10 14 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 4 4 -1.</_>\n        <_>\n          4 9 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 4 2 16 -1.</_>\n        <_>\n          9 12 2 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 18 5 -1.</_>\n        <_>\n          7 1 6 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 13 8 -1.</_>\n        <_>\n          4 9 13 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 7 16 9 -1.</_>\n        <_>\n          1 10 16 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 0 15 4 -1.</_>\n        <_>\n          2 2 15 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 6 4 -1.</_>\n        <_>\n          9 5 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 8 9 -1.</_>\n        <_>\n          6 6 8 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 3 8 -1.</_>\n        <_>\n          8 16 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 16 2 2 -1.</_>\n        <_>\n          3 17 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 1 6 12 -1.</_>\n        <_>\n          14 1 3 12 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 4 12 6 -1.</_>\n        <_>\n          8 4 4 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 6 15 -1.</_>\n        <_>\n          3 2 3 15 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 9 6 -1.</_>\n        <_>\n          5 6 9 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 11 6 3 -1.</_>\n        <_>\n          13 12 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 12 6 4 -1.</_>\n        <_>\n          12 14 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 11 6 3 -1.</_>\n        <_>\n          1 12 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 5 5 8 -1.</_>\n        <_>\n          2 9 5 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 10 4 -1.</_>\n        <_>\n          5 6 10 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 4 16 12 -1.</_>\n        <_>\n          2 8 16 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 12 6 -1.</_>\n        <_>\n          8 5 4 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 7 2 9 -1.</_>\n        <_>\n          13 10 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 2 9 -1.</_>\n        <_>\n          5 10 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 6 8 -1.</_>\n        <_>\n          9 1 2 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 0 4 12 -1.</_>\n        <_>\n          14 0 2 6 2.</_>\n        <_>\n          12 6 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 10 2 -1.</_>\n        <_>\n          5 9 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 6 4 -1.</_>\n        <_>\n          7 1 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 9 12 -1.</_>\n        <_>\n          3 3 3 12 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 12 -1.</_>\n        <_>\n          9 12 3 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 20 15 -1.</_>\n        <_>\n          0 10 20 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 2 6 8 -1.</_>\n        <_>\n          2 2 3 4 2.</_>\n        <_>\n          5 6 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 6 2 -1.</_>\n        <_>\n          2 2 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 15 6 4 -1.</_>\n        <_>\n          13 15 3 2 2.</_>\n        <_>\n          10 17 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 14 2 6 -1.</_>\n        <_>\n          12 16 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 15 4 4 -1.</_>\n        <_>\n          5 15 2 2 2.</_>\n        <_>\n          7 17 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 18 1 2 -1.</_>\n        <_>\n          7 19 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 12 10 -1.</_>\n        <_>\n          10 5 6 5 2.</_>\n        <_>\n          4 10 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 8 12 -1.</_>\n        <_>\n          11 4 4 6 2.</_>\n        <_>\n          7 10 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 2 3 -1.</_>\n        <_>\n          9 12 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 3 12 12 -1.</_>\n        <_>\n          3 3 6 6 2.</_>\n        <_>\n          9 9 6 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 11 5 3 -1.</_>\n        <_>\n          15 12 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 18 3 2 -1.</_>\n        <_>\n          11 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 11 5 3 -1.</_>\n        <_>\n          0 12 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 18 3 2 -1.</_>\n        <_>\n          8 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 8 16 2 -1.</_>\n        <_>\n          2 9 16 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 5 12 -1.</_>\n        <_>\n          9 12 5 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 8 6 -1.</_>\n        <_>\n          6 6 8 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 12 2 -1.</_>\n        <_>\n          8 7 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 9 6 8 -1.</_>\n        <_>\n          10 13 6 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 5 3 10 -1.</_>\n        <_>\n          12 10 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 3 9 -1.</_>\n        <_>\n          4 9 3 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 6 4 -1.</_>\n        <_>\n          9 4 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 3 8 3 -1.</_>\n        <_>\n          12 3 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 0 3 6 -1.</_>\n        <_>\n          15 3 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 12 10 8 -1.</_>\n        <_>\n          2 12 5 4 2.</_>\n        <_>\n          7 16 5 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 6 8 -1.</_>\n        <_>\n          5 9 6 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 3 8 3 -1.</_>\n        <_>\n          12 3 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 0 3 6 -1.</_>\n        <_>\n          15 3 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 8 3 -1.</_>\n        <_>\n          4 3 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 4 4 -1.</_>\n        <_>\n          2 3 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 2 3 2 -1.</_>\n        <_>\n          11 2 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 3 3 1 -1.</_>\n        <_>\n          11 3 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 3 4 -1.</_>\n        <_>\n          7 17 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 13 3 6 -1.</_>\n        <_>\n          4 15 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 5 1 14 -1.</_>\n        <_>\n          10 12 1 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 10 6 -1.</_>\n        <_>\n          5 6 10 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 0 6 3 -1.</_>\n        <_>\n          7 0 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 3 5 -1.</_>\n        <_>\n          7 0 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 6 5 -1.</_>\n        <_>\n          9 15 2 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 2 6 -1.</_>\n        <_>\n          9 12 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 17 3 2 -1.</_>\n        <_>\n          9 17 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 12 7 6 -1.</_>\n        <_>\n          1 14 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 3 7 -1.</_>\n        <_>\n          10 6 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 3 4 9 -1.</_>\n        <_>\n          16 6 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 3 7 -1.</_>\n        <_>\n          9 6 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 18 8 -1.</_>\n        <_>\n          0 5 9 4 2.</_>\n        <_>\n          9 9 9 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 5 2 10 -1.</_>\n        <_>\n          13 10 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 10 2 6 -1.</_>\n        <_>\n          12 13 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 0 3 5 -1.</_>\n        <_>\n          8 0 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 5 8 6 -1.</_>\n        <_>\n          6 7 8 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 3 6 14 -1.</_>\n        <_>\n          13 3 3 7 2.</_>\n        <_>\n          10 10 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 5 1 8 -1.</_>\n        <_>\n          13 9 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 6 14 -1.</_>\n        <_>\n          4 3 3 7 2.</_>\n        <_>\n          7 10 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 5 1 8 -1.</_>\n        <_>\n          6 9 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 1 6 -1.</_>\n        <_>\n          8 3 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 0 15 2 -1.</_>\n        <_>\n          2 1 15 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 7 20 6 -1.</_>\n        <_>\n          0 9 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 10 6 8 -1.</_>\n        <_>\n          10 14 6 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 3 2 -1.</_>\n        <_>\n          8 1 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 2 2 -1.</_>\n        <_>\n          9 1 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 12 9 -1.</_>\n        <_>\n          4 6 12 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 5 9 5 -1.</_>\n        <_>\n          9 5 3 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 9 5 -1.</_>\n        <_>\n          8 5 3 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 6 12 -1.</_>\n        <_>\n          4 10 6 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 0 6 18 -1.</_>\n        <_>\n          13 0 3 18 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 8 1 12 -1.</_>\n        <_>\n          10 12 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 2 6 10 -1.</_>\n        <_>\n          3 2 3 5 2.</_>\n        <_>\n          6 7 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 4 6 -1.</_>\n        <_>\n          3 2 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 18 3 2 -1.</_>\n        <_>\n          10 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 18 3 2 -1.</_>\n        <_>\n          11 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 8 2 6 -1.</_>\n        <_>\n          2 10 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 6 6 -1.</_>\n        <_>\n          7 7 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 19 6 1 -1.</_>\n        <_>\n          9 19 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 18 3 2 -1.</_>\n        <_>\n          11 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 3 1 -1.</_>\n        <_>\n          9 3 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 2 16 2 -1.</_>\n        <_>\n          2 2 8 1 2.</_>\n        <_>\n          10 3 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 5 3 -1.</_>\n        <_>\n          8 12 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 3 -1.</_>\n        <_>\n          7 14 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 6 15 -1.</_>\n        <_>\n          2 1 2 15 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 12 2 3 -1.</_>\n        <_>\n          2 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 13 1 3 -1.</_>\n        <_>\n          16 14 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 7 6 4 -1.</_>\n        <_>\n          16 7 3 2 2.</_>\n        <_>\n          13 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 3 6 -1.</_>\n        <_>\n          7 16 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 1 14 -1.</_>\n        <_>\n          7 12 1 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 12 2 3 -1.</_>\n        <_>\n          15 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 5 3 14 -1.</_>\n        <_>\n          10 12 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 2 6 -1.</_>\n        <_>\n          6 13 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 5 1 8 -1.</_>\n        <_>\n          6 9 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 11 2 1 -1.</_>\n        <_>\n          13 11 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 1 6 10 -1.</_>\n        <_>\n          15 1 3 5 2.</_>\n        <_>\n          12 6 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 2 3 -1.</_>\n        <_>\n          3 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 18 2 1 -1.</_>\n        <_>\n          10 18 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 17 9 -1.</_>\n        <_>\n          1 3 17 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 8 8 -1.</_>\n        <_>\n          1 2 4 4 2.</_>\n        <_>\n          5 6 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 6 4 -1.</_>\n        <_>\n          9 5 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 9 7 10 -1.</_>\n        <_>\n          10 14 7 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 6 4 -1.</_>\n        <_>\n          8 5 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 7 20 6 -1.</_>\n        <_>\n          0 9 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 5 9 10 -1.</_>\n        <_>\n          6 10 9 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 4 12 -1.</_>\n        <_>\n          8 10 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 8 3 -1.</_>\n        <_>\n          6 7 8 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 13 10 6 -1.</_>\n        <_>\n          3 13 5 3 2.</_>\n        <_>\n          8 16 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 1 4 11 -1.</_>\n        <_>\n          15 1 2 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 10 10 -1.</_>\n        <_>\n          10 7 5 5 2.</_>\n        <_>\n          5 12 5 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 4 11 -1.</_>\n        <_>\n          3 1 2 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 5 8 12 -1.</_>\n        <_>\n          1 11 8 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 7 6 4 -1.</_>\n        <_>\n          16 7 3 2 2.</_>\n        <_>\n          13 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 10 7 4 -1.</_>\n        <_>\n          11 12 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 20 12 -1.</_>\n        <_>\n          0 4 10 6 2.</_>\n        <_>\n          10 10 10 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 5 6 15 -1.</_>\n        <_>\n          1 10 6 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 10 3 8 -1.</_>\n        <_>\n          11 14 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 12 7 6 -1.</_>\n        <_>\n          11 14 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 2 3 -1.</_>\n        <_>\n          9 12 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 3 -1.</_>\n        <_>\n          8 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 14 14 4 -1.</_>\n        <_>\n          10 14 7 2 2.</_>\n        <_>\n          3 16 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 7 2 4 -1.</_>\n        <_>\n          18 9 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 6 6 -1.</_>\n        <_>\n          3 14 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 3 6 -1.</_>\n        <_>\n          0 6 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 3 3 -1.</_>\n        <_>\n          9 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 7 10 4 -1.</_>\n        <_>\n          15 7 5 2 2.</_>\n        <_>\n          10 9 5 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 2 6 8 -1.</_>\n        <_>\n          7 6 6 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 6 2 -1.</_>\n        <_>\n          8 3 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 6 3 5 -1.</_>\n        <_>\n          11 6 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 6 19 -1.</_>\n        <_>\n          11 0 2 19 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 1 2 -1.</_>\n        <_>\n          3 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 5 3 -1.</_>\n        <_>\n          7 15 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 18 4 -1.</_>\n        <_>\n          11 1 9 2 2.</_>\n        <_>\n          2 3 9 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 5 3 8 -1.</_>\n        <_>\n          11 5 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 18 4 -1.</_>\n        <_>\n          0 1 9 2 2.</_>\n        <_>\n          9 3 9 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 3 8 -1.</_>\n        <_>\n          8 5 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 6 -1.</_>\n        <_>\n          9 7 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 8 5 2 -1.</_>\n        <_>\n          10 9 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 10 15 1 -1.</_>\n        <_>\n          7 10 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 7 2 6 -1.</_>\n        <_>\n          2 9 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 3 3 -1.</_>\n        <_>\n          9 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 4 10 -1.</_>\n        <_>\n          9 12 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 8 8 2 -1.</_>\n        <_>\n          0 8 4 1 2.</_>\n        <_>\n          4 9 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 9 10 8 -1.</_>\n        <_>\n          5 9 5 4 2.</_>\n        <_>\n          10 13 5 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 4 -1.</_>\n        <_>\n          9 7 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 3 4 -1.</_>\n        <_>\n          10 6 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 2 1 -1.</_>\n        <_>\n          9 3 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 3 4 -1.</_>\n        <_>\n          9 6 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 0 4 14 -1.</_>\n        <_>\n          14 0 2 7 2.</_>\n        <_>\n          12 7 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 5 6 9 -1.</_>\n        <_>\n          12 5 3 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 6 16 -1.</_>\n        <_>\n          3 2 3 16 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 12 4 2 -1.</_>\n        <_>\n          1 13 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 6 1 -1.</_>\n        <_>\n          9 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 4 9 -1.</_>\n        <_>\n          8 6 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 10 4 6 -1.</_>\n        <_>\n          12 13 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 8 16 -1.</_>\n        <_>\n          12 1 4 8 2.</_>\n        <_>\n          8 9 4 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 3 6 -1.</_>\n        <_>\n          4 9 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 3 6 2 -1.</_>\n        <_>\n          4 3 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 12 -1.</_>\n        <_>\n          9 12 3 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 9 7 10 -1.</_>\n        <_>\n          10 14 7 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 9 7 10 -1.</_>\n        <_>\n          3 14 7 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 1 14 -1.</_>\n        <_>\n          7 12 1 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 14 1 6 -1.</_>\n        <_>\n          13 16 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 3 6 -1.</_>\n        <_>\n          14 14 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 14 1 6 -1.</_>\n        <_>\n          6 16 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 3 6 -1.</_>\n        <_>\n          3 14 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 5 3 -1.</_>\n        <_>\n          8 14 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 2 3 -1.</_>\n        <_>\n          9 15 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 10 8 -1.</_>\n        <_>\n          5 1 5 4 2.</_>\n        <_>\n          10 5 5 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 4 5 4 -1.</_>\n        <_>\n          6 6 5 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 10 18 1 -1.</_>\n        <_>\n          7 10 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 10 4 3 -1.</_>\n        <_>\n          11 10 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 6 1 -1.</_>\n        <_>\n          7 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 13 2 3 -1.</_>\n        <_>\n          3 14 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 12 3 4 -1.</_>\n        <_>\n          12 14 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 10 5 6 -1.</_>\n        <_>\n          11 12 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 8 16 2 -1.</_>\n        <_>\n          0 9 16 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 3 4 -1.</_>\n        <_>\n          2 3 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 3 -1.</_>\n        <_>\n          10 7 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 12 6 -1.</_>\n        <_>\n          9 6 4 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 3 -1.</_>\n        <_>\n          9 7 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 6 12 6 -1.</_>\n        <_>\n          7 6 4 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 5 6 5 -1.</_>\n        <_>\n          12 5 2 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 10 2 -1.</_>\n        <_>\n          5 7 5 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 6 5 -1.</_>\n        <_>\n          6 5 2 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 3 2 10 -1.</_>\n        <_>\n          9 8 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 16 2 -1.</_>\n        <_>\n          11 1 8 1 2.</_>\n        <_>\n          3 2 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 3 2 -1.</_>\n        <_>\n          9 10 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 16 2 -1.</_>\n        <_>\n          1 1 8 1 2.</_>\n        <_>\n          9 2 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 1 3 -1.</_>\n        <_>\n          8 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 12 10 -1.</_>\n        <_>\n          10 5 6 5 2.</_>\n        <_>\n          4 10 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 6 -1.</_>\n        <_>\n          10 13 3 3 2.</_>\n        <_>\n          7 16 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 9 3 2 -1.</_>\n        <_>\n          8 10 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 2 6 4 -1.</_>\n        <_>\n          9 2 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 9 3 -1.</_>\n        <_>\n          6 7 9 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 7 6 1 -1.</_>\n        <_>\n          12 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 18 6 -1.</_>\n        <_>\n          6 0 6 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 2 6 -1.</_>\n        <_>\n          6 13 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 12 3 6 -1.</_>\n        <_>\n          11 15 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 4 12 12 -1.</_>\n        <_>\n          10 4 6 6 2.</_>\n        <_>\n          4 10 6 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 3 6 -1.</_>\n        <_>\n          2 2 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 5 3 7 -1.</_>\n        <_>\n          2 5 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 13 12 4 -1.</_>\n        <_>\n          10 13 6 2 2.</_>\n        <_>\n          4 15 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 3 17 12 -1.</_>\n        <_>\n          3 9 17 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 3 14 12 -1.</_>\n        <_>\n          3 3 7 6 2.</_>\n        <_>\n          10 9 7 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 11 16 9 -1.</_>\n        <_>\n          2 14 16 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 3 6 -1.</_>\n        <_>\n          9 17 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 4 6 -1.</_>\n        <_>\n          10 14 2 3 2.</_>\n        <_>\n          8 17 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 2 6 1 -1.</_>\n        <_>\n          8 2 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 5 -1.</_>\n        <_>\n          10 5 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 5 -1.</_>\n        <_>\n          10 8 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 6 1 -1.</_>\n        <_>\n          9 12 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 3 5 -1.</_>\n        <_>\n          9 8 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 4 3 -1.</_>\n        <_>\n          8 10 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 20 6 -1.</_>\n        <_>\n          0 6 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 3 8 6 -1.</_>\n        <_>\n          1 3 4 3 2.</_>\n        <_>\n          5 6 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 6 4 -1.</_>\n        <_>\n          7 17 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 10 14 10 -1.</_>\n        <_>\n          3 15 14 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 4 4 4 -1.</_>\n        <_>\n          8 4 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 20 10 -1.</_>\n        <_>\n          0 9 20 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 4 2 14 -1.</_>\n        <_>\n          9 11 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 0 16 4 -1.</_>\n        <_>\n          2 2 16 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 12 6 8 -1.</_>\n        <_>\n          4 12 3 4 2.</_>\n        <_>\n          7 16 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 6 7 -1.</_>\n        <_>\n          3 5 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 7 10 4 -1.</_>\n        <_>\n          15 7 5 2 2.</_>\n        <_>\n          10 9 5 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 12 1 -1.</_>\n        <_>\n          9 8 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 2 2 -1.</_>\n        <_>\n          9 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 4 2 4 -1.</_>\n        <_>\n          9 6 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 3 6 -1.</_>\n        <_>\n          10 6 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 6 4 -1.</_>\n        <_>\n          15 7 3 2 2.</_>\n        <_>\n          12 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 3 6 -1.</_>\n        <_>\n          9 6 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 6 18 6 -1.</_>\n        <_>\n          1 6 9 3 2.</_>\n        <_>\n          10 9 9 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 1 3 3 -1.</_>\n        <_>\n          10 1 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 8 5 2 -1.</_>\n        <_>\n          10 9 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 3 3 -1.</_>\n        <_>\n          9 1 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 5 2 -1.</_>\n        <_>\n          5 9 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 8 8 -1.</_>\n        <_>\n          12 6 4 4 2.</_>\n        <_>\n          8 10 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 10 2 -1.</_>\n        <_>\n          5 7 5 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 12 10 -1.</_>\n        <_>\n          4 5 6 5 2.</_>\n        <_>\n          10 10 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 2 3 -1.</_>\n        <_>\n          5 6 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 6 3 -1.</_>\n        <_>\n          7 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 3 3 -1.</_>\n        <_>\n          9 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 3 3 -1.</_>\n        <_>\n          8 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 10 8 9 -1.</_>\n        <_>\n          1 13 8 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 3 -1.</_>\n        <_>\n          9 8 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 3 3 3 -1.</_>\n        <_>\n          13 3 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 3 3 -1.</_>\n        <_>\n          6 3 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 2 12 -1.</_>\n        <_>\n          5 10 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 11 18 4 -1.</_>\n        <_>\n          10 11 9 2 2.</_>\n        <_>\n          1 13 9 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 12 6 2 -1.</_>\n        <_>\n          7 13 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 3 6 -1.</_>\n        <_>\n          7 0 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 11 18 4 -1.</_>\n        <_>\n          0 11 9 2 2.</_>\n        <_>\n          9 13 9 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 12 6 2 -1.</_>\n        <_>\n          7 13 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 3 3 -1.</_>\n        <_>\n          9 13 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 2 3 -1.</_>\n        <_>\n          9 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 4 3 -1.</_>\n        <_>\n          8 12 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 3 4 2 -1.</_>\n        <_>\n          13 4 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 0 12 2 -1.</_>\n        <_>\n          4 1 12 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 9 8 8 -1.</_>\n        <_>\n          6 9 4 4 2.</_>\n        <_>\n          10 13 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 11 6 2 -1.</_>\n        <_>\n          1 12 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 5 18 8 -1.</_>\n        <_>\n          11 5 9 4 2.</_>\n        <_>\n          2 9 9 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 6 10 -1.</_>\n        <_>\n          7 6 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 3 6 -1.</_>\n        <_>\n          0 5 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 4 3 -1.</_>\n        <_>\n          4 6 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          19 3 1 6 -1.</_>\n        <_>\n          19 5 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 15 8 2 -1.</_>\n        <_>\n          6 16 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 1 6 -1.</_>\n        <_>\n          0 5 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 3 3 -1.</_>\n        <_>\n          5 6 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 4 3 -1.</_>\n        <_>\n          8 9 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 6 6 3 -1.</_>\n        <_>\n          12 6 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 2 6 -1.</_>\n        <_>\n          8 16 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 2 8 -1.</_>\n        <_>\n          9 15 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 6 6 3 -1.</_>\n        <_>\n          12 6 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 15 15 5 -1.</_>\n        <_>\n          10 15 5 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 14 2 2 -1.</_>\n        <_>\n          2 15 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 6 2 -1.</_>\n        <_>\n          6 7 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 6 1 -1.</_>\n        <_>\n          10 3 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 18 12 -1.</_>\n        <_>\n          7 0 6 12 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 14 8 6 -1.</_>\n        <_>\n          4 14 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 15 15 5 -1.</_>\n        <_>\n          5 15 5 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 6 1 -1.</_>\n        <_>\n          10 3 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 11 3 6 -1.</_>\n        <_>\n          11 14 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 6 1 -1.</_>\n        <_>\n          8 3 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 11 3 6 -1.</_>\n        <_>\n          6 14 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 3 4 -1.</_>\n        <_>\n          10 6 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 10 4 7 -1.</_>\n        <_>\n          12 10 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 3 4 -1.</_>\n        <_>\n          9 6 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 4 7 -1.</_>\n        <_>\n          6 6 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 3 4 12 -1.</_>\n        <_>\n          10 3 2 12 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 8 3 4 -1.</_>\n        <_>\n          11 8 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 18 14 -1.</_>\n        <_>\n          7 0 6 14 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 8 6 11 -1.</_>\n        <_>\n          5 8 3 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 4 15 4 -1.</_>\n        <_>\n          1 6 15 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 10 8 -1.</_>\n        <_>\n          5 9 10 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 2 6 8 -1.</_>\n        <_>\n          14 2 3 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 6 6 14 -1.</_>\n        <_>\n          14 6 3 7 2.</_>\n        <_>\n          11 13 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 12 -1.</_>\n        <_>\n          9 11 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 7 4 6 -1.</_>\n        <_>\n          3 9 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 3 6 6 -1.</_>\n        <_>\n          14 3 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 2 4 4 -1.</_>\n        <_>\n          15 4 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 6 7 -1.</_>\n        <_>\n          3 2 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 6 6 14 -1.</_>\n        <_>\n          3 6 3 7 2.</_>\n        <_>\n          6 13 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 16 8 -1.</_>\n        <_>\n          4 10 16 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 12 2 8 -1.</_>\n        <_>\n          10 16 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 0 6 20 -1.</_>\n        <_>\n          9 0 2 20 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 7 16 12 -1.</_>\n        <_>\n          1 7 8 6 2.</_>\n        <_>\n          9 13 8 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 3 3 -1.</_>\n        <_>\n          9 12 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 9 4 5 -1.</_>\n        <_>\n          11 9 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 3 1 2 -1.</_>\n        <_>\n          3 4 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 17 5 3 -1.</_>\n        <_>\n          7 18 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 4 8 -1.</_>\n        <_>\n          10 12 2 4 2.</_>\n        <_>\n          8 16 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 10 12 -1.</_>\n        <_>\n          12 4 5 6 2.</_>\n        <_>\n          7 10 5 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 4 3 -1.</_>\n        <_>\n          8 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 9 4 5 -1.</_>\n        <_>\n          7 9 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 8 2 -1.</_>\n        <_>\n          9 9 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 15 5 2 -1.</_>\n        <_>\n          14 16 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 2 3 -1.</_>\n        <_>\n          9 15 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 7 8 4 -1.</_>\n        <_>\n          1 7 4 2 2.</_>\n        <_>\n          5 9 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          19 3 1 2 -1.</_>\n        <_>\n          19 4 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 2 3 -1.</_>\n        <_>\n          9 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 14 14 4 -1.</_>\n        <_>\n          3 14 7 2 2.</_>\n        <_>\n          10 16 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 0 10 2 -1.</_>\n        <_>\n          5 1 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 14 4 6 -1.</_>\n        <_>\n          11 16 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 6 3 -1.</_>\n        <_>\n          7 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 6 -1.</_>\n        <_>\n          7 13 3 3 2.</_>\n        <_>\n          10 16 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 1 6 -1.</_>\n        <_>\n          0 4 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 8 2 -1.</_>\n        <_>\n          6 8 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 6 1 -1.</_>\n        <_>\n          9 7 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 6 10 -1.</_>\n        <_>\n          7 6 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 6 2 -1.</_>\n        <_>\n          0 3 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 4 2 4 -1.</_>\n        <_>\n          11 4 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 10 3 6 -1.</_>\n        <_>\n          11 13 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 9 8 2 -1.</_>\n        <_>\n          7 9 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 4 6 -1.</_>\n        <_>\n          2 0 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 0 6 2 -1.</_>\n        <_>\n          9 0 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 15 2 3 -1.</_>\n        <_>\n          9 16 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 1 2 -1.</_>\n        <_>\n          3 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 11 3 -1.</_>\n        <_>\n          4 6 11 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 4 2 4 -1.</_>\n        <_>\n          11 4 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 6 3 -1.</_>\n        <_>\n          10 3 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 2 4 -1.</_>\n        <_>\n          8 4 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 6 3 -1.</_>\n        <_>\n          8 3 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 4 4 3 -1.</_>\n        <_>\n          11 5 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 8 2 8 -1.</_>\n        <_>\n          11 12 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 5 -1.</_>\n        <_>\n          9 7 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 5 -1.</_>\n        <_>\n          10 7 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 11 1 6 -1.</_>\n        <_>\n          14 13 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 4 3 -1.</_>\n        <_>\n          8 9 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 2 2 -1.</_>\n        <_>\n          0 4 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 14 5 6 -1.</_>\n        <_>\n          4 16 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 4 4 3 -1.</_>\n        <_>\n          11 5 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 3 3 -1.</_>\n        <_>\n          12 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 4 3 -1.</_>\n        <_>\n          5 5 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 15 4 2 -1.</_>\n        <_>\n          7 15 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 1 5 9 -1.</_>\n        <_>\n          15 4 5 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 3 3 -1.</_>\n        <_>\n          9 11 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 6 2 6 -1.</_>\n        <_>\n          1 8 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 4 8 15 -1.</_>\n        <_>\n          2 9 8 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 3 2 -1.</_>\n        <_>\n          9 13 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 3 3 -1.</_>\n        <_>\n          9 13 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 6 3 5 -1.</_>\n        <_>\n          8 6 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 6 2 -1.</_>\n        <_>\n          7 3 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 1 8 10 -1.</_>\n        <_>\n          10 1 4 5 2.</_>\n        <_>\n          6 6 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 20 10 -1.</_>\n        <_>\n          10 0 10 5 2.</_>\n        <_>\n          0 5 10 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 3 1 -1.</_>\n        <_>\n          7 3 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 6 8 -1.</_>\n        <_>\n          2 2 2 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 10 3 4 -1.</_>\n        <_>\n          11 12 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 6 3 8 -1.</_>\n        <_>\n          12 10 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 3 4 -1.</_>\n        <_>\n          6 12 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 3 8 -1.</_>\n        <_>\n          5 10 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 6 18 6 -1.</_>\n        <_>\n          11 6 9 3 2.</_>\n        <_>\n          2 9 9 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 7 3 -1.</_>\n        <_>\n          7 15 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 2 12 -1.</_>\n        <_>\n          1 0 1 12 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 18 16 -1.</_>\n        <_>\n          1 10 18 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 5 3 -1.</_>\n        <_>\n          9 14 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 3 -1.</_>\n        <_>\n          8 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 18 6 -1.</_>\n        <_>\n          0 6 9 3 2.</_>\n        <_>\n          9 9 9 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 3 -1.</_>\n        <_>\n          7 14 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 4 1 3 -1.</_>\n        <_>\n          17 5 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 1 9 -1.</_>\n        <_>\n          12 14 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 4 1 3 -1.</_>\n        <_>\n          2 5 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 2 3 -1.</_>\n        <_>\n          5 5 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 18 3 -1.</_>\n        <_>\n          7 2 6 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 20 6 -1.</_>\n        <_>\n          0 3 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 6 3 -1.</_>\n        <_>\n          9 5 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 7 6 4 -1.</_>\n        <_>\n          16 7 3 2 2.</_>\n        <_>\n          13 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 4 10 -1.</_>\n        <_>\n          3 1 2 5 2.</_>\n        <_>\n          5 6 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 19 10 -1.</_>\n        <_>\n          0 9 19 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 12 -1.</_>\n        <_>\n          9 12 3 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 18 5 2 -1.</_>\n        <_>\n          11 19 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 16 6 4 -1.</_>\n        <_>\n          5 16 3 2 2.</_>\n        <_>\n          8 18 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 18 3 2 -1.</_>\n        <_>\n          5 19 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 11 3 2 -1.</_>\n        <_>\n          13 12 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 5 8 4 -1.</_>\n        <_>\n          8 5 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 18 6 -1.</_>\n        <_>\n          1 2 9 3 2.</_>\n        <_>\n          10 5 9 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 5 14 6 -1.</_>\n        <_>\n          3 7 14 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 1 2 6 -1.</_>\n        <_>\n          18 3 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 6 1 -1.</_>\n        <_>\n          11 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 6 11 -1.</_>\n        <_>\n          3 2 3 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 12 2 3 -1.</_>\n        <_>\n          4 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 12 9 2 -1.</_>\n        <_>\n          9 12 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 4 6 15 -1.</_>\n        <_>\n          9 4 3 15 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 6 1 -1.</_>\n        <_>\n          7 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 6 15 -1.</_>\n        <_>\n          8 4 3 15 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 6 7 -1.</_>\n        <_>\n          14 12 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 3 2 9 -1.</_>\n        <_>\n          18 6 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 3 1 -1.</_>\n        <_>\n          9 1 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 12 6 7 -1.</_>\n        <_>\n          3 12 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 7 6 4 -1.</_>\n        <_>\n          16 7 3 2 2.</_>\n        <_>\n          13 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 0 10 2 -1.</_>\n        <_>\n          8 1 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 7 6 4 -1.</_>\n        <_>\n          1 7 3 2 2.</_>\n        <_>\n          4 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 3 3 -1.</_>\n        <_>\n          1 3 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 4 3 -1.</_>\n        <_>\n          9 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 13 7 2 -1.</_>\n        <_>\n          12 14 7 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 12 9 2 -1.</_>\n        <_>\n          8 12 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 4 8 -1.</_>\n        <_>\n          6 14 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 18 4 -1.</_>\n        <_>\n          7 0 6 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 0 5 2 -1.</_>\n        <_>\n          12 1 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 1 12 -1.</_>\n        <_>\n          7 13 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 2 3 4 -1.</_>\n        <_>\n          7 2 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 13 20 6 -1.</_>\n        <_>\n          0 15 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 5 12 2 -1.</_>\n        <_>\n          14 5 6 1 2.</_>\n        <_>\n          8 6 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 2 3 -1.</_>\n        <_>\n          8 15 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 4 3 -1.</_>\n        <_>\n          8 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 13 7 6 -1.</_>\n        <_>\n          12 15 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 8 12 -1.</_>\n        <_>\n          10 0 4 6 2.</_>\n        <_>\n          6 6 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 15 9 4 -1.</_>\n        <_>\n          0 17 9 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 2 5 -1.</_>\n        <_>\n          10 0 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 6 -1.</_>\n        <_>\n          9 5 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 2 3 6 -1.</_>\n        <_>\n          17 4 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 11 2 3 -1.</_>\n        <_>\n          3 12 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 3 3 -1.</_>\n        <_>\n          7 14 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 5 3 -1.</_>\n        <_>\n          14 13 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 8 14 3 -1.</_>\n        <_>\n          4 9 14 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 12 5 3 -1.</_>\n        <_>\n          1 13 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 15 12 2 -1.</_>\n        <_>\n          1 15 6 1 2.</_>\n        <_>\n          7 16 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 4 2 -1.</_>\n        <_>\n          12 12 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 5 -1.</_>\n        <_>\n          10 8 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 6 -1.</_>\n        <_>\n          10 5 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 3 6 -1.</_>\n        <_>\n          0 4 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 4 2 -1.</_>\n        <_>\n          12 12 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 5 -1.</_>\n        <_>\n          10 7 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 11 4 2 -1.</_>\n        <_>\n          4 12 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 3 5 -1.</_>\n        <_>\n          9 8 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 3 3 1 -1.</_>\n        <_>\n          10 3 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 5 3 8 -1.</_>\n        <_>\n          17 5 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 3 1 -1.</_>\n        <_>\n          9 3 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 5 3 8 -1.</_>\n        <_>\n          2 5 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 1 3 3 -1.</_>\n        <_>\n          11 1 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 5 2 4 -1.</_>\n        <_>\n          17 5 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 8 14 3 -1.</_>\n        <_>\n          2 9 14 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 1 3 -1.</_>\n        <_>\n          9 8 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 1 8 10 -1.</_>\n        <_>\n          6 6 8 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 0 6 8 -1.</_>\n        <_>\n          16 0 3 4 2.</_>\n        <_>\n          13 4 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 5 2 4 -1.</_>\n        <_>\n          2 5 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 2 12 2 -1.</_>\n        <_>\n          4 3 12 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 4 4 -1.</_>\n        <_>\n          8 10 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 12 4 -1.</_>\n        <_>\n          9 6 4 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 8 1 -1.</_>\n        <_>\n          5 2 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 6 10 -1.</_>\n        <_>\n          3 1 2 10 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 8 2 -1.</_>\n        <_>\n          8 6 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 7 6 6 -1.</_>\n        <_>\n          12 7 2 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 8 2 -1.</_>\n        <_>\n          8 6 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 6 6 -1.</_>\n        <_>\n          6 7 2 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 14 16 4 -1.</_>\n        <_>\n          3 16 16 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 4 2 -1.</_>\n        <_>\n          8 13 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 3 3 -1.</_>\n        <_>\n          8 13 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 12 6 1 -1.</_>\n        <_>\n          8 12 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 10 2 3 -1.</_>\n        <_>\n          18 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 8 4 6 -1.</_>\n        <_>\n          16 10 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 2 1 -1.</_>\n        <_>\n          9 3 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 3 9 -1.</_>\n        <_>\n          8 1 1 9 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 11 6 -1.</_>\n        <_>\n          5 14 11 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 2 3 14 -1.</_>\n        <_>\n          12 9 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 3 -1.</_>\n        <_>\n          9 7 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 5 12 5 -1.</_>\n        <_>\n          7 5 4 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 6 3 -1.</_>\n        <_>\n          4 2 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 6 10 -1.</_>\n        <_>\n          5 5 3 5 2.</_>\n        <_>\n          8 10 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 18 2 2 -1.</_>\n        <_>\n          16 18 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 18 2 2 -1.</_>\n        <_>\n          16 18 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 2 5 -1.</_>\n        <_>\n          9 4 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 1 4 -1.</_>\n        <_>\n          8 6 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 12 4 -1.</_>\n        <_>\n          13 15 6 2 2.</_>\n        <_>\n          7 17 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 18 6 2 -1.</_>\n        <_>\n          11 19 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 4 10 -1.</_>\n        <_>\n          7 12 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 10 8 -1.</_>\n        <_>\n          5 10 10 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 1 6 12 -1.</_>\n        <_>\n          14 1 3 6 2.</_>\n        <_>\n          11 7 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 12 1 -1.</_>\n        <_>\n          9 8 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 3 6 -1.</_>\n        <_>\n          4 9 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 11 3 4 -1.</_>\n        <_>\n          4 13 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 16 2 2 -1.</_>\n        <_>\n          14 17 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 15 2 2 -1.</_>\n        <_>\n          15 16 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 12 6 2 -1.</_>\n        <_>\n          7 13 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 2 -1.</_>\n        <_>\n          8 14 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 1 6 12 -1.</_>\n        <_>\n          14 1 3 6 2.</_>\n        <_>\n          11 7 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 2 4 2 -1.</_>\n        <_>\n          12 3 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 10 12 6 -1.</_>\n        <_>\n          3 10 6 3 2.</_>\n        <_>\n          9 13 6 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 6 12 -1.</_>\n        <_>\n          3 1 3 6 2.</_>\n        <_>\n          6 7 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 6 4 14 -1.</_>\n        <_>\n          18 6 2 7 2.</_>\n        <_>\n          16 13 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 10 8 -1.</_>\n        <_>\n          10 1 5 4 2.</_>\n        <_>\n          5 5 5 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 4 14 -1.</_>\n        <_>\n          0 6 2 7 2.</_>\n        <_>\n          2 13 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 15 12 4 -1.</_>\n        <_>\n          1 15 6 2 2.</_>\n        <_>\n          7 17 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 17 3 3 -1.</_>\n        <_>\n          11 17 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 2 2 6 -1.</_>\n        <_>\n          12 2 1 3 2.</_>\n        <_>\n          11 5 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 17 3 3 -1.</_>\n        <_>\n          8 17 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 4 3 -1.</_>\n        <_>\n          8 16 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 15 4 2 -1.</_>\n        <_>\n          12 15 2 1 2.</_>\n        <_>\n          10 16 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 13 4 3 -1.</_>\n        <_>\n          13 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 13 4 3 -1.</_>\n        <_>\n          3 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 2 2 6 -1.</_>\n        <_>\n          7 2 1 3 2.</_>\n        <_>\n          8 5 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 16 3 -1.</_>\n        <_>\n          2 2 16 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 15 4 2 -1.</_>\n        <_>\n          12 15 2 1 2.</_>\n        <_>\n          10 16 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 15 4 2 -1.</_>\n        <_>\n          6 15 2 1 2.</_>\n        <_>\n          8 16 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 0 13 3 -1.</_>\n        <_>\n          3 1 13 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 9 20 3 -1.</_>\n        <_>\n          0 10 20 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 9 2 -1.</_>\n        <_>\n          6 8 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 3 6 -1.</_>\n        <_>\n          9 14 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 2 2 -1.</_>\n        <_>\n          9 11 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 5 -1.</_>\n        <_>\n          9 7 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 10 3 -1.</_>\n        <_>\n          5 6 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 5 -1.</_>\n        <_>\n          10 7 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 10 3 -1.</_>\n        <_>\n          10 6 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 9 2 2 -1.</_>\n        <_>\n          13 9 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 12 11 -1.</_>\n        <_>\n          8 3 4 11 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 2 7 -1.</_>\n        <_>\n          8 1 1 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 3 8 -1.</_>\n        <_>\n          8 4 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 9 2 2 -1.</_>\n        <_>\n          13 9 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 6 2 2 -1.</_>\n        <_>\n          12 6 1 1 2.</_>\n        <_>\n          11 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 2 3 -1.</_>\n        <_>\n          5 5 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 5 1 3 -1.</_>\n        <_>\n          6 6 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 9 2 2 -1.</_>\n        <_>\n          13 9 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 14 3 3 -1.</_>\n        <_>\n          16 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 9 2 2 -1.</_>\n        <_>\n          6 9 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 14 3 3 -1.</_>\n        <_>\n          1 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 1 1 6 -1.</_>\n        <_>\n          13 3 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 3 7 2 -1.</_>\n        <_>\n          13 4 7 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 20 14 -1.</_>\n        <_>\n          0 13 20 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 3 6 -1.</_>\n        <_>\n          0 6 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 1 9 6 -1.</_>\n        <_>\n          10 3 9 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 0 12 5 -1.</_>\n        <_>\n          8 0 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 18 5 -1.</_>\n        <_>\n          6 0 6 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 9 6 -1.</_>\n        <_>\n          1 3 9 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 15 2 2 -1.</_>\n        <_>\n          15 16 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 16 3 4 -1.</_>\n        <_>\n          13 18 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 15 2 2 -1.</_>\n        <_>\n          3 16 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 16 3 4 -1.</_>\n        <_>\n          4 18 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 14 1 3 -1.</_>\n        <_>\n          11 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 5 3 -1.</_>\n        <_>\n          9 14 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 3 6 -1.</_>\n        <_>\n          0 2 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 1 6 3 -1.</_>\n        <_>\n          6 1 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 4 3 -1.</_>\n        <_>\n          9 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 5 3 -1.</_>\n        <_>\n          8 16 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 3 2 -1.</_>\n        <_>\n          9 3 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 8 18 2 -1.</_>\n        <_>\n          1 9 18 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 14 1 3 -1.</_>\n        <_>\n          11 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 6 3 -1.</_>\n        <_>\n          8 14 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 1 3 -1.</_>\n        <_>\n          8 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 13 12 4 -1.</_>\n        <_>\n          4 13 6 2 2.</_>\n        <_>\n          10 15 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 7 2 2 -1.</_>\n        <_>\n          10 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 4 2 8 -1.</_>\n        <_>\n          14 4 1 4 2.</_>\n        <_>\n          13 8 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 4 6 -1.</_>\n        <_>\n          0 7 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 2 2 -1.</_>\n        <_>\n          9 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 0 3 7 -1.</_>\n        <_>\n          14 0 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 2 2 14 -1.</_>\n        <_>\n          11 2 1 14 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 0 3 7 -1.</_>\n        <_>\n          5 0 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 8 12 -1.</_>\n        <_>\n          5 5 4 6 2.</_>\n        <_>\n          9 11 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 4 6 3 -1.</_>\n        <_>\n          11 5 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 3 4 3 -1.</_>\n        <_>\n          12 4 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 10 12 -1.</_>\n        <_>\n          5 5 5 6 2.</_>\n        <_>\n          10 11 5 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 6 12 3 -1.</_>\n        <_>\n          9 6 6 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 2 7 -1.</_>\n        <_>\n          9 6 1 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 4 -1.</_>\n        <_>\n          9 5 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 3 -1.</_>\n        <_>\n          9 7 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 6 4 -1.</_>\n        <_>\n          7 1 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 16 7 3 -1.</_>\n        <_>\n          13 17 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 3 3 -1.</_>\n        <_>\n          12 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 16 7 3 -1.</_>\n        <_>\n          0 17 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 3 3 -1.</_>\n        <_>\n          5 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 9 8 10 -1.</_>\n        <_>\n          12 9 4 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 10 12 5 -1.</_>\n        <_>\n          12 10 4 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 9 8 10 -1.</_>\n        <_>\n          4 9 4 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 10 12 5 -1.</_>\n        <_>\n          4 10 4 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 3 6 2 -1.</_>\n        <_>\n          5 3 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 17 9 -1.</_>\n        <_>\n          0 3 17 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 12 2 -1.</_>\n        <_>\n          8 7 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 4 6 4 -1.</_>\n        <_>\n          12 4 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 10 20 4 -1.</_>\n        <_>\n          0 12 20 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 6 5 -1.</_>\n        <_>\n          6 3 2 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 18 4 -1.</_>\n        <_>\n          7 1 6 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 9 2 3 -1.</_>\n        <_>\n          13 9 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 15 7 4 -1.</_>\n        <_>\n          6 17 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 17 4 2 -1.</_>\n        <_>\n          3 18 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 4 8 10 -1.</_>\n        <_>\n          9 9 8 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 17 3 2 -1.</_>\n        <_>\n          10 17 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 2 4 8 -1.</_>\n        <_>\n          8 6 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 4 14 12 -1.</_>\n        <_>\n          3 4 7 6 2.</_>\n        <_>\n          10 10 7 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 6 4 -1.</_>\n        <_>\n          9 7 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 9 4 -1.</_>\n        <_>\n          6 9 9 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 10 3 3 -1.</_>\n        <_>\n          2 11 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 2 9 -1.</_>\n        <_>\n          4 9 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 3 3 -1.</_>\n        <_>\n          9 12 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 15 2 -1.</_>\n        <_>\n          3 2 15 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 2 3 -1.</_>\n        <_>\n          9 9 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 2 5 -1.</_>\n        <_>\n          10 6 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 3 -1.</_>\n        <_>\n          9 8 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 10 12 10 -1.</_>\n        <_>\n          4 15 12 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 10 4 2 -1.</_>\n        <_>\n          0 11 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 15 9 2 -1.</_>\n        <_>\n          5 16 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 6 3 -1.</_>\n        <_>\n          8 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 16 4 3 -1.</_>\n        <_>\n          8 17 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 9 4 2 -1.</_>\n        <_>\n          8 10 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 3 14 2 -1.</_>\n        <_>\n          3 4 14 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 12 1 2 -1.</_>\n        <_>\n          11 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 12 12 1 -1.</_>\n        <_>\n          8 12 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 1 2 -1.</_>\n        <_>\n          0 3 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 4 6 -1.</_>\n        <_>\n          9 4 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 20 14 -1.</_>\n        <_>\n          10 2 10 7 2.</_>\n        <_>\n          0 9 10 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 6 1 3 -1.</_>\n        <_>\n          14 7 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 20 12 -1.</_>\n        <_>\n          0 4 10 6 2.</_>\n        <_>\n          10 10 10 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 1 2 -1.</_>\n        <_>\n          8 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 18 3 2 -1.</_>\n        <_>\n          10 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 17 6 2 -1.</_>\n        <_>\n          11 17 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 2 3 -1.</_>\n        <_>\n          5 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 3 3 -1.</_>\n        <_>\n          5 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 15 3 2 -1.</_>\n        <_>\n          14 16 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 3 3 4 -1.</_>\n        <_>\n          12 3 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 15 3 2 -1.</_>\n        <_>\n          3 16 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 2 3 -1.</_>\n        <_>\n          9 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 3 7 -1.</_>\n        <_>\n          10 13 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 12 5 3 -1.</_>\n        <_>\n          12 13 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 18 3 2 -1.</_>\n        <_>\n          9 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 12 4 -1.</_>\n        <_>\n          4 7 6 2 2.</_>\n        <_>\n          10 9 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 19 14 1 -1.</_>\n        <_>\n          6 19 7 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 14 3 2 -1.</_>\n        <_>\n          16 15 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 6 10 -1.</_>\n        <_>\n          1 0 3 5 2.</_>\n        <_>\n          4 5 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 4 10 -1.</_>\n        <_>\n          1 0 2 5 2.</_>\n        <_>\n          3 5 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 3 5 6 -1.</_>\n        <_>\n          15 5 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 15 -1.</_>\n        <_>\n          9 10 2 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 5 6 -1.</_>\n        <_>\n          0 5 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 3 2 -1.</_>\n        <_>\n          7 0 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 8 8 2 -1.</_>\n        <_>\n          16 8 4 1 2.</_>\n        <_>\n          12 9 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 12 1 -1.</_>\n        <_>\n          9 8 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 13 3 3 -1.</_>\n        <_>\n          3 14 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 13 3 2 -1.</_>\n        <_>\n          5 14 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 15 3 3 -1.</_>\n        <_>\n          9 16 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 7 3 -1.</_>\n        <_>\n          7 16 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 14 11 6 -1.</_>\n        <_>\n          3 16 11 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 19 14 1 -1.</_>\n        <_>\n          7 19 7 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 17 6 2 -1.</_>\n        <_>\n          11 17 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 6 2 -1.</_>\n        <_>\n          14 11 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 17 6 2 -1.</_>\n        <_>\n          7 17 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 9 10 -1.</_>\n        <_>\n          3 1 3 10 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 1 3 3 -1.</_>\n        <_>\n          11 1 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 6 4 -1.</_>\n        <_>\n          9 5 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 3 3 -1.</_>\n        <_>\n          8 1 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 4 11 -1.</_>\n        <_>\n          2 4 2 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 6 4 -1.</_>\n        <_>\n          9 5 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 8 10 -1.</_>\n        <_>\n          10 0 4 5 2.</_>\n        <_>\n          6 5 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 5 14 -1.</_>\n        <_>\n          6 13 5 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 5 4 14 -1.</_>\n        <_>\n          8 12 4 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 6 5 -1.</_>\n        <_>\n          9 7 2 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 3 3 9 -1.</_>\n        <_>\n          9 6 3 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 3 3 -1.</_>\n        <_>\n          9 1 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 2 4 -1.</_>\n        <_>\n          10 6 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 8 6 9 -1.</_>\n        <_>\n          10 8 3 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 4 3 8 -1.</_>\n        <_>\n          17 4 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 9 10 6 -1.</_>\n        <_>\n          5 9 5 3 2.</_>\n        <_>\n          10 12 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 6 4 -1.</_>\n        <_>\n          8 5 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 4 2 -1.</_>\n        <_>\n          9 9 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 7 2 2 -1.</_>\n        <_>\n          11 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 4 8 -1.</_>\n        <_>\n          8 12 2 4 2.</_>\n        <_>\n          10 16 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 4 9 -1.</_>\n        <_>\n          0 4 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 3 3 -1.</_>\n        <_>\n          9 11 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 4 2 -1.</_>\n        <_>\n          8 12 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 8 4 2 -1.</_>\n        <_>\n          7 9 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 8 6 1 -1.</_>\n        <_>\n          9 8 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 0 4 9 -1.</_>\n        <_>\n          16 0 2 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 0 3 6 -1.</_>\n        <_>\n          16 3 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 4 9 -1.</_>\n        <_>\n          2 0 2 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 3 6 -1.</_>\n        <_>\n          1 3 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 6 9 -1.</_>\n        <_>\n          11 7 2 9 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 6 3 6 -1.</_>\n        <_>\n          11 6 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 18 2 -1.</_>\n        <_>\n          1 2 9 1 2.</_>\n        <_>\n          10 3 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 6 8 -1.</_>\n        <_>\n          7 8 2 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 6 16 -1.</_>\n        <_>\n          11 0 2 16 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 1 6 18 -1.</_>\n        <_>\n          17 1 3 9 2.</_>\n        <_>\n          14 10 3 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 9 2 3 -1.</_>\n        <_>\n          2 10 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 6 18 -1.</_>\n        <_>\n          0 1 3 9 2.</_>\n        <_>\n          3 10 3 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 8 4 12 -1.</_>\n        <_>\n          11 8 2 12 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 18 18 -1.</_>\n        <_>\n          2 10 18 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 3 1 -1.</_>\n        <_>\n          7 3 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 12 2 2 -1.</_>\n        <_>\n          4 13 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 5 3 -1.</_>\n        <_>\n          8 14 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 4 3 -1.</_>\n        <_>\n          8 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 5 3 -1.</_>\n        <_>\n          3 13 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 3 4 -1.</_>\n        <_>\n          7 3 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 10 2 2 -1.</_>\n        <_>\n          12 10 1 1 2.</_>\n        <_>\n          11 11 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 12 1 -1.</_>\n        <_>\n          9 8 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 4 8 -1.</_>\n        <_>\n          10 4 2 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 8 5 -1.</_>\n        <_>\n          10 6 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 4 6 4 -1.</_>\n        <_>\n          12 4 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 2 2 -1.</_>\n        <_>\n          13 7 1 1 2.</_>\n        <_>\n          12 8 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 5 10 8 -1.</_>\n        <_>\n          3 9 10 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 2 12 -1.</_>\n        <_>\n          7 7 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 2 2 -1.</_>\n        <_>\n          13 7 1 1 2.</_>\n        <_>\n          12 8 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 13 1 6 -1.</_>\n        <_>\n          11 16 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 6 15 -1.</_>\n        <_>\n          7 1 2 15 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 2 2 -1.</_>\n        <_>\n          6 7 1 1 2.</_>\n        <_>\n          7 8 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 5 2 2 -1.</_>\n        <_>\n          17 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 3 4 10 -1.</_>\n        <_>\n          12 3 2 5 2.</_>\n        <_>\n          10 8 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 5 2 2 -1.</_>\n        <_>\n          1 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 10 2 2 -1.</_>\n        <_>\n          7 10 1 1 2.</_>\n        <_>\n          8 11 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 14 4 -1.</_>\n        <_>\n          10 12 7 2 2.</_>\n        <_>\n          3 14 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 15 3 2 -1.</_>\n        <_>\n          9 16 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 13 3 3 -1.</_>\n        <_>\n          1 14 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 1 2 -1.</_>\n        <_>\n          0 4 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 6 1 -1.</_>\n        <_>\n          9 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 16 6 -1.</_>\n        <_>\n          0 6 16 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 3 2 14 -1.</_>\n        <_>\n          9 10 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 0 4 3 -1.</_>\n        <_>\n          12 0 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 18 12 2 -1.</_>\n        <_>\n          8 18 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 10 12 4 -1.</_>\n        <_>\n          8 10 4 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 2 2 -1.</_>\n        <_>\n          9 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 1 2 8 -1.</_>\n        <_>\n          15 1 1 4 2.</_>\n        <_>\n          14 5 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 4 9 1 -1.</_>\n        <_>\n          6 4 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 3 4 2 -1.</_>\n        <_>\n          3 4 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 15 2 4 -1.</_>\n        <_>\n          11 17 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 13 2 6 -1.</_>\n        <_>\n          14 15 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 1 6 -1.</_>\n        <_>\n          6 9 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 8 8 -1.</_>\n        <_>\n          6 14 8 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 3 -1.</_>\n        <_>\n          8 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 11 4 8 -1.</_>\n        <_>\n          10 15 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 6 1 -1.</_>\n        <_>\n          7 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 6 10 -1.</_>\n        <_>\n          8 4 3 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 2 6 3 -1.</_>\n        <_>\n          14 3 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 3 2 -1.</_>\n        <_>\n          9 13 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 4 6 -1.</_>\n        <_>\n          8 3 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 5 13 8 -1.</_>\n        <_>\n          3 9 13 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 5 5 3 -1.</_>\n        <_>\n          12 6 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 14 15 6 -1.</_>\n        <_>\n          5 16 15 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 5 5 3 -1.</_>\n        <_>\n          3 6 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 2 6 -1.</_>\n        <_>\n          9 14 1 3 2.</_>\n        <_>\n          10 17 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 3 2 -1.</_>\n        <_>\n          9 13 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 3 2 -1.</_>\n        <_>\n          9 14 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 6 3 -1.</_>\n        <_>\n          0 3 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 9 11 -1.</_>\n        <_>\n          3 1 3 11 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 6 -1.</_>\n        <_>\n          10 13 2 3 2.</_>\n        <_>\n          8 16 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 3 -1.</_>\n        <_>\n          7 14 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 14 4 -1.</_>\n        <_>\n          3 12 7 2 2.</_>\n        <_>\n          10 14 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 1 4 -1.</_>\n        <_>\n          7 16 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 6 -1.</_>\n        <_>\n          10 13 2 3 2.</_>\n        <_>\n          8 16 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 14 1 3 -1.</_>\n        <_>\n          10 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 6 -1.</_>\n        <_>\n          8 13 2 3 2.</_>\n        <_>\n          10 16 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 1 3 -1.</_>\n        <_>\n          9 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 15 2 3 -1.</_>\n        <_>\n          10 16 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 16 1 2 -1.</_>\n        <_>\n          11 17 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 2 2 -1.</_>\n        <_>\n          9 1 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 5 8 -1.</_>\n        <_>\n          0 5 5 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 14 2 3 -1.</_>\n        <_>\n          10 15 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 13 2 3 -1.</_>\n        <_>\n          10 14 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 16 6 -1.</_>\n        <_>\n          0 6 16 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 1 2 2 -1.</_>\n        <_>\n          5 1 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 3 -1.</_>\n        <_>\n          9 8 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 8 2 12 -1.</_>\n        <_>\n          10 12 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 2 -1.</_>\n        <_>\n          10 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 0 6 8 -1.</_>\n        <_>\n          7 0 2 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 6 -1.</_>\n        <_>\n          10 7 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 10 8 -1.</_>\n        <_>\n          8 16 10 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 6 -1.</_>\n        <_>\n          9 7 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 12 2 -1.</_>\n        <_>\n          10 7 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 8 3 -1.</_>\n        <_>\n          8 6 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 15 3 3 -1.</_>\n        <_>\n          16 16 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 12 3 -1.</_>\n        <_>\n          10 6 6 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 8 3 5 -1.</_>\n        <_>\n          8 8 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 10 20 2 -1.</_>\n        <_>\n          10 10 10 1 2.</_>\n        <_>\n          0 11 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 16 9 4 -1.</_>\n        <_>\n          14 16 3 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 3 4 -1.</_>\n        <_>\n          1 5 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 4 2 -1.</_>\n        <_>\n          8 15 2 1 2.</_>\n        <_>\n          10 16 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 8 19 3 -1.</_>\n        <_>\n          1 9 19 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 16 3 3 -1.</_>\n        <_>\n          15 17 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 20 10 -1.</_>\n        <_>\n          0 4 10 5 2.</_>\n        <_>\n          10 9 10 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 14 7 6 -1.</_>\n        <_>\n          2 16 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 6 6 -1.</_>\n        <_>\n          10 6 2 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 4 4 6 -1.</_>\n        <_>\n          16 6 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 3 -1.</_>\n        <_>\n          7 14 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 4 3 -1.</_>\n        <_>\n          7 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 13 6 2 -1.</_>\n        <_>\n          13 14 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 2 3 -1.</_>\n        <_>\n          14 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 13 6 2 -1.</_>\n        <_>\n          1 14 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 12 2 3 -1.</_>\n        <_>\n          4 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 4 3 5 -1.</_>\n        <_>\n          18 4 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 14 8 -1.</_>\n        <_>\n          12 5 7 4 2.</_>\n        <_>\n          5 9 7 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 8 6 5 -1.</_>\n        <_>\n          8 8 2 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 4 6 -1.</_>\n        <_>\n          0 6 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 1 3 6 -1.</_>\n        <_>\n          10 1 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 4 6 3 -1.</_>\n        <_>\n          10 5 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 3 6 -1.</_>\n        <_>\n          9 1 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 4 6 3 -1.</_>\n        <_>\n          4 5 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 3 3 -1.</_>\n        <_>\n          12 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 4 2 -1.</_>\n        <_>\n          12 12 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 20 6 -1.</_>\n        <_>\n          0 2 10 3 2.</_>\n        <_>\n          10 5 10 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 3 3 -1.</_>\n        <_>\n          5 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 10 16 4 -1.</_>\n        <_>\n          10 10 8 2 2.</_>\n        <_>\n          2 12 8 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 10 16 6 -1.</_>\n        <_>\n          11 10 8 3 2.</_>\n        <_>\n          3 13 8 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 10 16 6 -1.</_>\n        <_>\n          1 10 8 3 2.</_>\n        <_>\n          9 13 8 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 2 4 -1.</_>\n        <_>\n          5 7 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 16 9 4 -1.</_>\n        <_>\n          14 16 3 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 16 14 4 -1.</_>\n        <_>\n          10 16 7 2 2.</_>\n        <_>\n          3 18 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 16 9 4 -1.</_>\n        <_>\n          3 16 3 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 14 6 6 -1.</_>\n        <_>\n          1 14 3 3 2.</_>\n        <_>\n          4 17 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 2 1 -1.</_>\n        <_>\n          9 0 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 8 10 -1.</_>\n        <_>\n          10 7 4 5 2.</_>\n        <_>\n          6 12 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 15 1 2 -1.</_>\n        <_>\n          2 16 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 14 7 6 -1.</_>\n        <_>\n          0 16 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 8 6 2 -1.</_>\n        <_>\n          7 9 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 2 2 15 -1.</_>\n        <_>\n          9 7 2 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 2 2 -1.</_>\n        <_>\n          5 7 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 8 3 -1.</_>\n        <_>\n          6 7 8 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 13 5 6 -1.</_>\n        <_>\n          12 15 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 20 18 -1.</_>\n        <_>\n          0 9 20 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 6 6 -1.</_>\n        <_>\n          7 1 2 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 4 9 -1.</_>\n        <_>\n          7 1 2 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 19 18 1 -1.</_>\n        <_>\n          7 19 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 16 5 2 -1.</_>\n        <_>\n          14 17 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 15 10 -1.</_>\n        <_>\n          0 10 15 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 4 2 -1.</_>\n        <_>\n          7 15 2 1 2.</_>\n        <_>\n          9 16 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 11 2 2 -1.</_>\n        <_>\n          14 12 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 3 -1.</_>\n        <_>\n          9 9 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 11 2 2 -1.</_>\n        <_>\n          4 12 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 3 3 -1.</_>\n        <_>\n          8 9 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 2 3 -1.</_>\n        <_>\n          9 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 4 3 -1.</_>\n        <_>\n          8 9 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 9 4 10 -1.</_>\n        <_>\n          1 9 2 5 2.</_>\n        <_>\n          3 14 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 12 6 8 -1.</_>\n        <_>\n          2 12 2 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 1 4 2 -1.</_>\n        <_>\n          11 1 2 1 2.</_>\n        <_>\n          9 2 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 13 7 6 -1.</_>\n        <_>\n          12 15 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 0 2 3 -1.</_>\n        <_>\n          7 1 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 6 3 -1.</_>\n        <_>\n          9 14 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 6 4 -1.</_>\n        <_>\n          11 6 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 10 8 3 -1.</_>\n        <_>\n          8 10 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 4 3 -1.</_>\n        <_>\n          8 10 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 8 3 5 -1.</_>\n        <_>\n          7 8 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 8 1 -1.</_>\n        <_>\n          4 4 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 2 2 6 -1.</_>\n        <_>\n          8 2 1 3 2.</_>\n        <_>\n          9 5 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 7 20 6 -1.</_>\n        <_>\n          0 9 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 10 3 6 -1.</_>\n        <_>\n          12 13 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 1 4 -1.</_>\n        <_>\n          8 17 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 16 2 4 -1.</_>\n        <_>\n          5 18 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 2 8 12 -1.</_>\n        <_>\n          6 6 8 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 12 2 -1.</_>\n        <_>\n          8 7 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 0 6 1 -1.</_>\n        <_>\n          9 0 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 3 3 -1.</_>\n        <_>\n          8 12 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 3 6 -1.</_>\n        <_>\n          12 14 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 2 6 10 -1.</_>\n        <_>\n          14 2 3 5 2.</_>\n        <_>\n          11 7 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 10 12 -1.</_>\n        <_>\n          5 7 5 6 2.</_>\n        <_>\n          10 13 5 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 4 2 10 -1.</_>\n        <_>\n          4 9 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 3 -1.</_>\n        <_>\n          9 7 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 9 6 2 -1.</_>\n        <_>\n          11 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 2 2 -1.</_>\n        <_>\n          5 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 4 6 -1.</_>\n        <_>\n          0 4 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 7 3 4 -1.</_>\n        <_>\n          11 7 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 5 -1.</_>\n        <_>\n          10 7 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 1 1 3 -1.</_>\n        <_>\n          9 2 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 16 6 -1.</_>\n        <_>\n          0 6 8 3 2.</_>\n        <_>\n          8 9 8 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 15 3 3 -1.</_>\n        <_>\n          10 16 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 4 3 -1.</_>\n        <_>\n          9 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 2 6 10 -1.</_>\n        <_>\n          3 2 3 5 2.</_>\n        <_>\n          6 7 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 0 14 2 -1.</_>\n        <_>\n          3 1 14 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 3 3 -1.</_>\n        <_>\n          9 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 15 3 3 -1.</_>\n        <_>\n          10 16 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 2 6 -1.</_>\n        <_>\n          9 16 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 3 -1.</_>\n        <_>\n          7 14 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 3 6 -1.</_>\n        <_>\n          12 14 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 5 2 -1.</_>\n        <_>\n          8 13 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 3 6 -1.</_>\n        <_>\n          5 14 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 3 2 -1.</_>\n        <_>\n          8 13 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 13 7 6 -1.</_>\n        <_>\n          11 15 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 6 3 -1.</_>\n        <_>\n          7 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 13 14 4 -1.</_>\n        <_>\n          3 13 7 2 2.</_>\n        <_>\n          10 15 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 4 6 -1.</_>\n        <_>\n          8 14 2 3 2.</_>\n        <_>\n          10 17 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 4 3 -1.</_>\n        <_>\n          8 16 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 16 6 2 -1.</_>\n        <_>\n          9 16 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 6 2 -1.</_>\n        <_>\n          7 8 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 9 13 3 -1.</_>\n        <_>\n          3 10 13 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 4 -1.</_>\n        <_>\n          9 10 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 10 4 3 -1.</_>\n        <_>\n          8 11 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 3 4 -1.</_>\n        <_>\n          8 7 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 5 -1.</_>\n        <_>\n          9 7 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 3 3 4 -1.</_>\n        <_>\n          13 3 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 3 -1.</_>\n        <_>\n          9 7 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 3 4 -1.</_>\n        <_>\n          6 3 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 7 12 1 -1.</_>\n        <_>\n          7 7 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 5 3 3 -1.</_>\n        <_>\n          12 6 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 2 6 2 -1.</_>\n        <_>\n          11 3 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 2 14 2 -1.</_>\n        <_>\n          3 2 7 1 2.</_>\n        <_>\n          10 3 7 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 1 7 14 -1.</_>\n        <_>\n          6 8 7 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 0 12 5 -1.</_>\n        <_>\n          8 0 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 9 18 1 -1.</_>\n        <_>\n          7 9 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 10 5 -1.</_>\n        <_>\n          5 0 5 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 5 8 15 -1.</_>\n        <_>\n          2 10 8 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 5 3 3 -1.</_>\n        <_>\n          12 6 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 4 2 3 -1.</_>\n        <_>\n          13 5 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 15 4 3 -1.</_>\n        <_>\n          2 16 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 10 3 -1.</_>\n        <_>\n          10 6 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 6 2 2 -1.</_>\n        <_>\n          12 6 1 1 2.</_>\n        <_>\n          11 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 4 3 -1.</_>\n        <_>\n          12 5 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 6 2 2 -1.</_>\n        <_>\n          7 6 1 1 2.</_>\n        <_>\n          8 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 4 4 3 -1.</_>\n        <_>\n          4 5 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 4 3 3 -1.</_>\n        <_>\n          12 4 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 3 2 1 -1.</_>\n        <_>\n          9 3 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 5 3 -1.</_>\n        <_>\n          4 6 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 4 3 -1.</_>\n        <_>\n          4 7 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 4 3 3 -1.</_>\n        <_>\n          12 4 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 4 3 -1.</_>\n        <_>\n          8 9 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 4 3 3 -1.</_>\n        <_>\n          7 4 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 14 1 3 -1.</_>\n        <_>\n          4 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 3 -1.</_>\n        <_>\n          9 7 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 0 3 2 -1.</_>\n        <_>\n          17 1 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 10 2 9 -1.</_>\n        <_>\n          8 13 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 8 18 2 -1.</_>\n        <_>\n          0 9 18 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 15 2 3 -1.</_>\n        <_>\n          9 16 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 4 3 -1.</_>\n        <_>\n          8 8 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 14 6 6 -1.</_>\n        <_>\n          1 14 3 3 2.</_>\n        <_>\n          4 17 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 18 6 2 -1.</_>\n        <_>\n          0 19 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 9 4 3 -1.</_>\n        <_>\n          12 9 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 8 -1.</_>\n        <_>\n          10 8 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 9 4 3 -1.</_>\n        <_>\n          6 9 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 18 6 1 -1.</_>\n        <_>\n          6 18 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 2 -1.</_>\n        <_>\n          10 7 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 8 12 -1.</_>\n        <_>\n          10 7 4 6 2.</_>\n        <_>\n          6 13 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 2 -1.</_>\n        <_>\n          9 7 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 6 -1.</_>\n        <_>\n          9 7 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 16 14 4 -1.</_>\n        <_>\n          10 16 7 2 2.</_>\n        <_>\n          3 18 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 14 18 4 -1.</_>\n        <_>\n          10 14 9 2 2.</_>\n        <_>\n          1 16 9 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 3 -1.</_>\n        <_>\n          8 8 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 20 12 -1.</_>\n        <_>\n          0 4 10 6 2.</_>\n        <_>\n          10 10 10 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 10 12 -1.</_>\n        <_>\n          10 5 5 6 2.</_>\n        <_>\n          5 11 5 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 2 4 7 -1.</_>\n        <_>\n          10 2 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 4 3 -1.</_>\n        <_>\n          8 12 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 3 3 -1.</_>\n        <_>\n          8 13 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 13 5 6 -1.</_>\n        <_>\n          13 15 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 0 6 6 -1.</_>\n        <_>\n          9 0 2 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 13 5 6 -1.</_>\n        <_>\n          2 15 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 2 12 -1.</_>\n        <_>\n          0 4 1 6 2.</_>\n        <_>\n          1 10 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 19 3 1 -1.</_>\n        <_>\n          10 19 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 0 2 6 -1.</_>\n        <_>\n          18 2 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 1 6 -1.</_>\n        <_>\n          0 5 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 3 6 -1.</_>\n        <_>\n          0 2 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 2 3 7 -1.</_>\n        <_>\n          18 2 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 3 4 7 -1.</_>\n        <_>\n          10 3 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 3 7 -1.</_>\n        <_>\n          1 2 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 2 4 8 -1.</_>\n        <_>\n          8 2 2 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 0 1 4 -1.</_>\n        <_>\n          13 2 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 12 5 -1.</_>\n        <_>\n          9 1 4 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 1 4 -1.</_>\n        <_>\n          6 2 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 12 5 -1.</_>\n        <_>\n          7 1 4 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 3 8 -1.</_>\n        <_>\n          10 12 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 1 -1.</_>\n        <_>\n          9 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 6 3 -1.</_>\n        <_>\n          7 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 16 7 3 -1.</_>\n        <_>\n          5 17 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 12 20 6 -1.</_>\n        <_>\n          0 14 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 18 14 2 -1.</_>\n        <_>\n          4 19 14 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 3 8 -1.</_>\n        <_>\n          9 12 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 3 3 -1.</_>\n        <_>\n          7 14 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 12 10 -1.</_>\n        <_>\n          11 5 6 5 2.</_>\n        <_>\n          5 10 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 5 10 -1.</_>\n        <_>\n          8 6 5 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 9 12 -1.</_>\n        <_>\n          5 10 9 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 6 -1.</_>\n        <_>\n          7 15 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 5 16 -1.</_>\n        <_>\n          8 12 5 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 4 6 -1.</_>\n        <_>\n          8 15 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 2 2 -1.</_>\n        <_>\n          7 13 1 1 2.</_>\n        <_>\n          8 14 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 12 2 2 -1.</_>\n        <_>\n          7 12 1 1 2.</_>\n        <_>\n          8 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 0 2 14 -1.</_>\n        <_>\n          18 0 1 14 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 7 2 -1.</_>\n        <_>\n          12 12 7 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 18 1 2 -1.</_>\n        <_>\n          1 19 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 18 1 2 -1.</_>\n        <_>\n          2 19 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 1 -1.</_>\n        <_>\n          9 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 2 3 -1.</_>\n        <_>\n          9 6 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 2 2 -1.</_>\n        <_>\n          4 1 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 0 3 2 -1.</_>\n        <_>\n          3 1 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 10 3 4 -1.</_>\n        <_>\n          12 12 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 8 2 -1.</_>\n        <_>\n          7 8 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 3 4 -1.</_>\n        <_>\n          8 10 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 12 6 3 -1.</_>\n        <_>\n          7 13 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 10 3 -1.</_>\n        <_>\n          5 2 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 20 6 -1.</_>\n        <_>\n          0 3 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 6 6 3 -1.</_>\n        <_>\n          9 6 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 7 14 4 -1.</_>\n        <_>\n          3 9 14 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 3 6 -1.</_>\n        <_>\n          5 9 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 3 12 -1.</_>\n        <_>\n          8 12 3 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 17 6 2 -1.</_>\n        <_>\n          12 17 3 1 2.</_>\n        <_>\n          9 18 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 17 4 3 -1.</_>\n        <_>\n          10 18 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 2 4 2 -1.</_>\n        <_>\n          4 3 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 3 6 14 -1.</_>\n        <_>\n          9 3 2 14 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 13 1 6 -1.</_>\n        <_>\n          15 16 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 14 2 6 -1.</_>\n        <_>\n          13 16 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 11 5 6 -1.</_>\n        <_>\n          4 14 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 17 4 2 -1.</_>\n        <_>\n          6 17 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 20 2 -1.</_>\n        <_>\n          0 6 10 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 5 10 12 -1.</_>\n        <_>\n          11 5 5 6 2.</_>\n        <_>\n          6 11 5 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 0 2 12 -1.</_>\n        <_>\n          4 0 1 6 2.</_>\n        <_>\n          5 6 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 1 6 2 -1.</_>\n        <_>\n          6 1 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 7 2 1 -1.</_>\n        <_>\n          13 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 15 6 -1.</_>\n        <_>\n          5 7 15 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 10 18 2 -1.</_>\n        <_>\n          1 10 9 1 2.</_>\n        <_>\n          10 11 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 6 15 7 -1.</_>\n        <_>\n          6 6 5 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 4 3 -1.</_>\n        <_>\n          8 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 3 3 -1.</_>\n        <_>\n          9 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 4 3 -1.</_>\n        <_>\n          8 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 3 2 -1.</_>\n        <_>\n          8 14 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 14 5 3 -1.</_>\n        <_>\n          15 15 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 14 20 1 -1.</_>\n        <_>\n          0 14 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 14 6 3 -1.</_>\n        <_>\n          0 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 4 2 -1.</_>\n        <_>\n          5 4 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 20 1 -1.</_>\n        <_>\n          0 6 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 10 14 -1.</_>\n        <_>\n          11 3 5 7 2.</_>\n        <_>\n          6 10 5 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 4 2 -1.</_>\n        <_>\n          8 13 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 8 6 -1.</_>\n        <_>\n          6 3 4 3 2.</_>\n        <_>\n          10 6 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 7 2 1 -1.</_>\n        <_>\n          13 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 10 14 -1.</_>\n        <_>\n          11 3 5 7 2.</_>\n        <_>\n          6 10 5 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 2 1 -1.</_>\n        <_>\n          6 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 10 14 -1.</_>\n        <_>\n          4 3 5 7 2.</_>\n        <_>\n          9 10 5 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 2 -1.</_>\n        <_>\n          9 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 20 1 -1.</_>\n        <_>\n          0 3 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 10 3 -1.</_>\n        <_>\n          2 2 10 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 2 -1.</_>\n        <_>\n          10 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 17 3 2 -1.</_>\n        <_>\n          10 17 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 6 -1.</_>\n        <_>\n          10 7 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 17 3 2 -1.</_>\n        <_>\n          9 17 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 6 -1.</_>\n        <_>\n          9 7 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 3 4 6 -1.</_>\n        <_>\n          16 5 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 6 2 12 -1.</_>\n        <_>\n          16 6 1 6 2.</_>\n        <_>\n          15 12 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 4 18 10 -1.</_>\n        <_>\n          1 4 9 5 2.</_>\n        <_>\n          10 9 9 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 4 2 4 -1.</_>\n        <_>\n          9 6 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 5 3 2 -1.</_>\n        <_>\n          12 6 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 12 10 4 -1.</_>\n        <_>\n          5 14 10 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 3 2 -1.</_>\n        <_>\n          5 6 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 12 6 -1.</_>\n        <_>\n          8 6 4 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 4 6 6 -1.</_>\n        <_>\n          14 6 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 0 4 6 -1.</_>\n        <_>\n          18 0 2 3 2.</_>\n        <_>\n          16 3 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 6 6 -1.</_>\n        <_>\n          0 6 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 4 6 -1.</_>\n        <_>\n          0 0 2 3 2.</_>\n        <_>\n          2 3 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 0 8 5 -1.</_>\n        <_>\n          12 0 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 0 4 17 -1.</_>\n        <_>\n          16 0 2 17 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 18 20 -1.</_>\n        <_>\n          7 0 6 20 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 2 5 -1.</_>\n        <_>\n          7 0 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 20 1 -1.</_>\n        <_>\n          0 6 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 6 4 -1.</_>\n        <_>\n          10 7 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 16 4 -1.</_>\n        <_>\n          1 1 8 2 2.</_>\n        <_>\n          9 3 8 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 2 4 2 -1.</_>\n        <_>\n          7 2 2 1 2.</_>\n        <_>\n          9 3 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 9 3 -1.</_>\n        <_>\n          7 5 9 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 4 5 12 -1.</_>\n        <_>\n          10 10 5 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 2 3 -1.</_>\n        <_>\n          3 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 3 5 -1.</_>\n        <_>\n          9 8 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 9 2 3 -1.</_>\n        <_>\n          13 9 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 11 2 2 -1.</_>\n        <_>\n          15 12 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 2 3 -1.</_>\n        <_>\n          5 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 11 6 2 -1.</_>\n        <_>\n          2 12 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 11 4 3 -1.</_>\n        <_>\n          15 12 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 0 4 17 -1.</_>\n        <_>\n          16 0 2 17 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 11 4 3 -1.</_>\n        <_>\n          1 12 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 1 3 -1.</_>\n        <_>\n          9 12 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 9 6 7 -1.</_>\n        <_>\n          10 9 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 4 2 -1.</_>\n        <_>\n          8 16 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 9 6 7 -1.</_>\n        <_>\n          7 9 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 2 3 -1.</_>\n        <_>\n          9 15 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 20 2 -1.</_>\n        <_>\n          10 2 10 1 2.</_>\n        <_>\n          0 3 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 8 2 -1.</_>\n        <_>\n          6 8 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 20 2 -1.</_>\n        <_>\n          0 2 10 1 2.</_>\n        <_>\n          10 3 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 2 10 -1.</_>\n        <_>\n          3 1 1 5 2.</_>\n        <_>\n          4 6 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 4 1 10 -1.</_>\n        <_>\n          13 9 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 4 3 -1.</_>\n        <_>\n          9 9 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 11 16 4 -1.</_>\n        <_>\n          2 11 8 2 2.</_>\n        <_>\n          10 13 8 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 3 5 -1.</_>\n        <_>\n          6 1 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 2 3 -1.</_>\n        <_>\n          9 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 2 2 -1.</_>\n        <_>\n          9 12 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 10 20 2 -1.</_>\n        <_>\n          0 11 20 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 7 6 4 -1.</_>\n        <_>\n          1 7 3 2 2.</_>\n        <_>\n          4 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 0 8 8 -1.</_>\n        <_>\n          16 0 4 4 2.</_>\n        <_>\n          12 4 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 1 6 4 -1.</_>\n        <_>\n          16 1 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 2 14 -1.</_>\n        <_>\n          6 10 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 1 7 12 -1.</_>\n        <_>\n          6 7 7 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 0 15 5 -1.</_>\n        <_>\n          10 0 5 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 0 4 10 -1.</_>\n        <_>\n          15 0 2 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 18 3 -1.</_>\n        <_>\n          7 0 6 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 17 2 -1.</_>\n        <_>\n          0 1 17 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 0 3 3 -1.</_>\n        <_>\n          11 0 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 0 3 12 -1.</_>\n        <_>\n          11 0 1 12 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 3 4 16 -1.</_>\n        <_>\n          1 3 2 8 2.</_>\n        <_>\n          3 11 2 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 0 3 3 -1.</_>\n        <_>\n          8 0 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 2 6 -1.</_>\n        <_>\n          9 16 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 6 13 -1.</_>\n        <_>\n          11 0 2 13 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 3 2 -1.</_>\n        <_>\n          8 7 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 2 1 12 -1.</_>\n        <_>\n          8 6 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 10 12 6 -1.</_>\n        <_>\n          10 10 6 3 2.</_>\n        <_>\n          4 13 6 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 5 2 3 -1.</_>\n        <_>\n          13 6 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 10 12 6 -1.</_>\n        <_>\n          4 10 6 3 2.</_>\n        <_>\n          10 13 6 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 2 3 -1.</_>\n        <_>\n          5 6 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 6 7 -1.</_>\n        <_>\n          10 6 2 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 2 4 -1.</_>\n        <_>\n          9 6 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 6 7 -1.</_>\n        <_>\n          8 6 2 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 2 4 -1.</_>\n        <_>\n          10 6 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 9 2 3 -1.</_>\n        <_>\n          12 9 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 20 1 -1.</_>\n        <_>\n          0 6 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 10 2 -1.</_>\n        <_>\n          10 7 5 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 16 4 3 -1.</_>\n        <_>\n          1 17 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 3 3 -1.</_>\n        <_>\n          12 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 3 5 3 -1.</_>\n        <_>\n          10 4 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 9 14 8 -1.</_>\n        <_>\n          3 9 7 4 2.</_>\n        <_>\n          10 13 7 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 8 8 10 -1.</_>\n        <_>\n          6 8 4 5 2.</_>\n        <_>\n          10 13 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 3 3 -1.</_>\n        <_>\n          12 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 3 5 3 -1.</_>\n        <_>\n          10 4 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 3 3 -1.</_>\n        <_>\n          5 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 5 3 -1.</_>\n        <_>\n          5 4 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 16 2 3 -1.</_>\n        <_>\n          13 17 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 20 6 -1.</_>\n        <_>\n          0 7 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 14 3 3 -1.</_>\n        <_>\n          3 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 5 3 -1.</_>\n        <_>\n          7 16 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 9 2 3 -1.</_>\n        <_>\n          12 9 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 13 2 6 -1.</_>\n        <_>\n          15 13 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 9 2 3 -1.</_>\n        <_>\n          7 9 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 13 2 6 -1.</_>\n        <_>\n          4 13 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 4 2 4 -1.</_>\n        <_>\n          11 4 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 4 2 5 -1.</_>\n        <_>\n          13 4 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 2 4 -1.</_>\n        <_>\n          8 4 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 2 5 -1.</_>\n        <_>\n          6 4 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          19 6 1 2 -1.</_>\n        <_>\n          19 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 8 13 -1.</_>\n        <_>\n          12 7 4 13 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 1 2 -1.</_>\n        <_>\n          0 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 15 4 3 -1.</_>\n        <_>\n          6 16 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 8 2 2 -1.</_>\n        <_>\n          11 9 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 7 2 4 -1.</_>\n        <_>\n          11 7 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 13 2 3 -1.</_>\n        <_>\n          4 14 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 17 18 3 -1.</_>\n        <_>\n          6 17 6 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 18 5 -1.</_>\n        <_>\n          7 0 6 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 3 4 -1.</_>\n        <_>\n          5 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 6 2 2 -1.</_>\n        <_>\n          10 6 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 4 14 4 -1.</_>\n        <_>\n          13 4 7 2 2.</_>\n        <_>\n          6 6 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 16 6 4 -1.</_>\n        <_>\n          5 16 3 2 2.</_>\n        <_>\n          8 18 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 2 4 -1.</_>\n        <_>\n          7 17 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 5 5 14 -1.</_>\n        <_>\n          8 12 5 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 2 2 -1.</_>\n        <_>\n          9 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 3 7 -1.</_>\n        <_>\n          8 5 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 3 9 -1.</_>\n        <_>\n          0 3 3 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 8 8 -1.</_>\n        <_>\n          12 6 4 4 2.</_>\n        <_>\n          8 10 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 8 13 2 -1.</_>\n        <_>\n          4 9 13 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 6 1 -1.</_>\n        <_>\n          6 3 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 1 2 6 -1.</_>\n        <_>\n          9 3 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 5 6 4 -1.</_>\n        <_>\n          12 5 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 12 -1.</_>\n        <_>\n          9 9 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 4 3 -1.</_>\n        <_>\n          8 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 4 3 -1.</_>\n        <_>\n          8 13 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 3 6 7 -1.</_>\n        <_>\n          12 3 2 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 10 16 6 -1.</_>\n        <_>\n          3 12 16 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 3 10 -1.</_>\n        <_>\n          5 10 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 3 6 -1.</_>\n        <_>\n          6 13 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 2 2 12 -1.</_>\n        <_>\n          17 2 1 12 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 6 2 14 -1.</_>\n        <_>\n          16 13 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 11 12 9 -1.</_>\n        <_>\n          3 14 12 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 4 12 -1.</_>\n        <_>\n          2 2 2 12 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 0 2 18 -1.</_>\n        <_>\n          18 0 1 18 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 12 3 2 -1.</_>\n        <_>\n          16 13 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 2 15 -1.</_>\n        <_>\n          1 2 1 15 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 10 2 4 -1.</_>\n        <_>\n          1 12 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 1 2 18 -1.</_>\n        <_>\n          11 1 1 18 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 2 14 2 -1.</_>\n        <_>\n          10 2 7 1 2.</_>\n        <_>\n          3 3 7 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 2 18 -1.</_>\n        <_>\n          8 1 1 18 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 1 8 12 -1.</_>\n        <_>\n          6 7 8 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 4 3 -1.</_>\n        <_>\n          8 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 6 3 -1.</_>\n        <_>\n          7 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 13 5 2 -1.</_>\n        <_>\n          0 14 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 2 6 -1.</_>\n        <_>\n          9 0 1 3 2.</_>\n        <_>\n          10 3 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 2 6 -1.</_>\n        <_>\n          10 0 1 3 2.</_>\n        <_>\n          9 3 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 6 -1.</_>\n        <_>\n          10 7 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 2 6 -1.</_>\n        <_>\n          9 0 1 3 2.</_>\n        <_>\n          10 3 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 6 -1.</_>\n        <_>\n          9 7 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 2 6 -1.</_>\n        <_>\n          9 6 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 4 4 3 -1.</_>\n        <_>\n          9 4 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 4 3 -1.</_>\n        <_>\n          0 5 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 4 2 -1.</_>\n        <_>\n          8 8 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 6 6 3 -1.</_>\n        <_>\n          12 6 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 3 12 -1.</_>\n        <_>\n          9 10 3 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 2 3 -1.</_>\n        <_>\n          5 5 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 1 3 -1.</_>\n        <_>\n          5 7 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 17 3 2 -1.</_>\n        <_>\n          10 17 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 7 20 2 -1.</_>\n        <_>\n          0 8 20 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 6 7 -1.</_>\n        <_>\n          6 3 2 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 10 6 10 -1.</_>\n        <_>\n          5 10 3 5 2.</_>\n        <_>\n          8 15 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 17 3 2 -1.</_>\n        <_>\n          10 17 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 2 2 -1.</_>\n        <_>\n          9 11 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 17 3 2 -1.</_>\n        <_>\n          9 17 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 1 3 -1.</_>\n        <_>\n          5 7 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 20 2 -1.</_>\n        <_>\n          10 1 10 1 2.</_>\n        <_>\n          0 2 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 2 6 9 -1.</_>\n        <_>\n          14 5 6 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 3 2 -1.</_>\n        <_>\n          5 4 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 4 2 -1.</_>\n        <_>\n          7 4 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 2 6 9 -1.</_>\n        <_>\n          14 5 6 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 12 20 6 -1.</_>\n        <_>\n          0 14 20 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 2 16 4 -1.</_>\n        <_>\n          2 2 8 2 2.</_>\n        <_>\n          10 4 8 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 12 5 3 -1.</_>\n        <_>\n          7 13 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 9 6 10 -1.</_>\n        <_>\n          14 9 3 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 6 3 2 -1.</_>\n        <_>\n          16 7 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 9 6 10 -1.</_>\n        <_>\n          3 9 3 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 16 5 2 -1.</_>\n        <_>\n          0 17 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 2 3 -1.</_>\n        <_>\n          9 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 12 -1.</_>\n        <_>\n          9 11 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 2 6 2 -1.</_>\n        <_>\n          5 2 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 1 1 2 -1.</_>\n        <_>\n          4 2 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 15 1 2 -1.</_>\n        <_>\n          11 16 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 16 2 -1.</_>\n        <_>\n          11 1 8 1 2.</_>\n        <_>\n          3 2 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 6 2 2 -1.</_>\n        <_>\n          3 6 1 1 2.</_>\n        <_>\n          4 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 10 6 -1.</_>\n        <_>\n          5 11 5 3 2.</_>\n        <_>\n          10 14 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 11 4 6 -1.</_>\n        <_>\n          10 14 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 9 6 11 -1.</_>\n        <_>\n          16 9 2 11 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 9 6 11 -1.</_>\n        <_>\n          2 9 2 11 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 11 16 6 -1.</_>\n        <_>\n          2 11 8 3 2.</_>\n        <_>\n          10 14 8 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 0 8 10 -1.</_>\n        <_>\n          16 0 4 5 2.</_>\n        <_>\n          12 5 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 2 6 4 -1.</_>\n        <_>\n          16 2 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 8 10 -1.</_>\n        <_>\n          0 0 4 5 2.</_>\n        <_>\n          4 5 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 6 4 -1.</_>\n        <_>\n          2 2 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 9 15 2 -1.</_>\n        <_>\n          9 9 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 3 4 8 -1.</_>\n        <_>\n          14 3 2 4 2.</_>\n        <_>\n          12 7 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 2 2 9 -1.</_>\n        <_>\n          10 2 1 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 20 1 -1.</_>\n        <_>\n          10 2 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 1 4 5 -1.</_>\n        <_>\n          16 1 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 0 4 6 -1.</_>\n        <_>\n          16 3 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 6 4 -1.</_>\n        <_>\n          6 3 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 18 5 -1.</_>\n        <_>\n          6 0 6 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 2 12 14 -1.</_>\n        <_>\n          12 2 6 7 2.</_>\n        <_>\n          6 9 6 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 8 3 5 -1.</_>\n        <_>\n          12 8 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 12 2 2 -1.</_>\n        <_>\n          5 13 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 10 4 3 -1.</_>\n        <_>\n          7 10 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 9 15 2 -1.</_>\n        <_>\n          9 9 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 7 6 2 -1.</_>\n        <_>\n          12 7 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 9 15 2 -1.</_>\n        <_>\n          6 9 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 0 2 10 -1.</_>\n        <_>\n          5 0 1 5 2.</_>\n        <_>\n          6 5 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 20 14 -1.</_>\n        <_>\n          0 7 20 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 8 4 -1.</_>\n        <_>\n          12 7 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 7 8 4 -1.</_>\n        <_>\n          4 7 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 3 3 -1.</_>\n        <_>\n          9 1 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 4 -1.</_>\n        <_>\n          10 7 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 3 1 -1.</_>\n        <_>\n          10 9 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 9 3 2 -1.</_>\n        <_>\n          8 10 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 2 8 -1.</_>\n        <_>\n          8 4 1 4 2.</_>\n        <_>\n          9 8 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 12 3 -1.</_>\n        <_>\n          5 9 12 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 14 1 3 -1.</_>\n        <_>\n          11 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 3 6 -1.</_>\n        <_>\n          6 12 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 17 8 3 -1.</_>\n        <_>\n          4 18 8 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 6 2 3 -1.</_>\n        <_>\n          17 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 2 2 -1.</_>\n        <_>\n          10 12 1 1 2.</_>\n        <_>\n          9 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 2 4 -1.</_>\n        <_>\n          9 13 1 2 2.</_>\n        <_>\n          10 15 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 2 3 -1.</_>\n        <_>\n          9 12 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 12 10 -1.</_>\n        <_>\n          11 5 6 5 2.</_>\n        <_>\n          5 10 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 12 12 -1.</_>\n        <_>\n          12 3 6 6 2.</_>\n        <_>\n          6 9 6 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 2 2 -1.</_>\n        <_>\n          5 7 1 1 2.</_>\n        <_>\n          6 8 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 3 2 -1.</_>\n        <_>\n          5 3 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 2 12 14 -1.</_>\n        <_>\n          12 2 6 7 2.</_>\n        <_>\n          6 9 6 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 2 12 3 -1.</_>\n        <_>\n          9 2 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 18 17 -1.</_>\n        <_>\n          7 1 6 17 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 9 10 1 -1.</_>\n        <_>\n          5 9 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 8 4 3 -1.</_>\n        <_>\n          16 9 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 6 -1.</_>\n        <_>\n          7 16 6 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 14 1 6 -1.</_>\n        <_>\n          6 16 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 17 4 2 -1.</_>\n        <_>\n          6 18 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 18 6 2 -1.</_>\n        <_>\n          13 18 3 1 2.</_>\n        <_>\n          10 19 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 8 1 3 -1.</_>\n        <_>\n          16 9 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 3 -1.</_>\n        <_>\n          8 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 15 1 2 -1.</_>\n        <_>\n          9 16 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 0 3 12 -1.</_>\n        <_>\n          14 0 1 12 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 11 1 3 -1.</_>\n        <_>\n          15 12 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 3 3 -1.</_>\n        <_>\n          8 16 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 0 3 12 -1.</_>\n        <_>\n          5 0 1 12 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 3 -1.</_>\n        <_>\n          10 7 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 3 1 -1.</_>\n        <_>\n          10 9 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 2 12 14 -1.</_>\n        <_>\n          2 2 6 7 2.</_>\n        <_>\n          8 9 6 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 2 12 3 -1.</_>\n        <_>\n          8 2 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 18 2 2 -1.</_>\n        <_>\n          18 18 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 2 3 8 -1.</_>\n        <_>\n          18 2 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 18 2 2 -1.</_>\n        <_>\n          1 18 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 11 2 6 -1.</_>\n        <_>\n          6 14 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 10 5 6 -1.</_>\n        <_>\n          13 12 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 15 3 -1.</_>\n        <_>\n          5 9 15 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 10 5 6 -1.</_>\n        <_>\n          2 12 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 8 15 3 -1.</_>\n        <_>\n          0 9 15 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 2 3 1 -1.</_>\n        <_>\n          17 2 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 4 3 2 -1.</_>\n        <_>\n          18 4 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 8 8 12 -1.</_>\n        <_>\n          0 8 4 6 2.</_>\n        <_>\n          4 14 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 7 8 6 -1.</_>\n        <_>\n          1 7 4 3 2.</_>\n        <_>\n          5 10 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 1 6 2 -1.</_>\n        <_>\n          16 1 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 0 4 4 -1.</_>\n        <_>\n          17 0 2 2 2.</_>\n        <_>\n          15 2 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 4 11 -1.</_>\n        <_>\n          3 1 2 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 1 8 -1.</_>\n        <_>\n          5 9 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 6 1 -1.</_>\n        <_>\n          9 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 12 2 -1.</_>\n        <_>\n          8 7 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 4 4 -1.</_>\n        <_>\n          8 6 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 4 9 1 -1.</_>\n        <_>\n          5 4 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 2 8 -1.</_>\n        <_>\n          9 16 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 8 14 12 -1.</_>\n        <_>\n          3 14 14 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 13 7 3 -1.</_>\n        <_>\n          6 14 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 9 6 3 -1.</_>\n        <_>\n          7 9 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 1 6 3 -1.</_>\n        <_>\n          12 2 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 6 2 -1.</_>\n        <_>\n          8 13 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 18 2 -1.</_>\n        <_>\n          0 2 9 1 2.</_>\n        <_>\n          9 3 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 3 6 -1.</_>\n        <_>\n          6 13 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 0 6 6 -1.</_>\n        <_>\n          14 0 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 0 5 8 -1.</_>\n        <_>\n          15 4 5 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 16 6 4 -1.</_>\n        <_>\n          9 16 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 11 14 4 -1.</_>\n        <_>\n          2 11 7 2 2.</_>\n        <_>\n          9 13 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 10 6 10 -1.</_>\n        <_>\n          14 10 3 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 10 12 -1.</_>\n        <_>\n          14 8 5 6 2.</_>\n        <_>\n          9 14 5 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 10 6 10 -1.</_>\n        <_>\n          3 10 3 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 8 10 12 -1.</_>\n        <_>\n          1 8 5 6 2.</_>\n        <_>\n          6 14 5 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 3 6 1 -1.</_>\n        <_>\n          11 3 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 6 3 -1.</_>\n        <_>\n          9 4 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 6 1 -1.</_>\n        <_>\n          7 3 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 6 3 -1.</_>\n        <_>\n          6 5 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 16 3 3 -1.</_>\n        <_>\n          9 17 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 6 3 -1.</_>\n        <_>\n          8 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 8 12 -1.</_>\n        <_>\n          6 0 4 6 2.</_>\n        <_>\n          10 6 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 12 2 3 -1.</_>\n        <_>\n          4 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 16 6 3 -1.</_>\n        <_>\n          12 17 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 12 7 2 -1.</_>\n        <_>\n          7 13 7 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 16 6 3 -1.</_>\n        <_>\n          2 17 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 7 16 6 -1.</_>\n        <_>\n          0 10 16 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 3 -1.</_>\n        <_>\n          10 7 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 5 -1.</_>\n        <_>\n          10 7 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 20 10 -1.</_>\n        <_>\n          0 5 10 5 2.</_>\n        <_>\n          10 10 10 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 4 2 -1.</_>\n        <_>\n          5 1 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 6 8 10 -1.</_>\n        <_>\n          11 6 4 5 2.</_>\n        <_>\n          7 11 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 6 3 2 -1.</_>\n        <_>\n          17 7 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 8 10 -1.</_>\n        <_>\n          5 6 4 5 2.</_>\n        <_>\n          9 11 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 12 10 6 -1.</_>\n        <_>\n          5 14 10 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 3 -1.</_>\n        <_>\n          10 7 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 3 2 6 -1.</_>\n        <_>\n          11 3 1 3 2.</_>\n        <_>\n          10 6 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 3 3 -1.</_>\n        <_>\n          0 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 16 8 4 -1.</_>\n        <_>\n          3 16 4 2 2.</_>\n        <_>\n          7 18 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 5 2 -1.</_>\n        <_>\n          8 14 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 4 12 -1.</_>\n        <_>\n          8 11 4 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 9 2 2 -1.</_>\n        <_>\n          6 9 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 15 2 3 -1.</_>\n        <_>\n          9 16 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 9 2 3 -1.</_>\n        <_>\n          13 9 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 0 6 17 -1.</_>\n        <_>\n          16 0 2 17 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 10 2 2 -1.</_>\n        <_>\n          6 10 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 9 9 1 -1.</_>\n        <_>\n          5 9 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 2 3 -1.</_>\n        <_>\n          9 12 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 11 6 3 -1.</_>\n        <_>\n          7 12 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 3 2 -1.</_>\n        <_>\n          0 7 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 0 6 1 -1.</_>\n        <_>\n          9 0 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 16 3 3 -1.</_>\n        <_>\n          9 17 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 13 17 6 -1.</_>\n        <_>\n          2 16 17 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 3 3 7 -1.</_>\n        <_>\n          2 3 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 6 4 -1.</_>\n        <_>\n          3 1 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 1 6 5 -1.</_>\n        <_>\n          14 1 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 2 3 2 -1.</_>\n        <_>\n          13 3 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 6 5 -1.</_>\n        <_>\n          3 1 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 3 2 6 -1.</_>\n        <_>\n          2 5 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 3 2 -1.</_>\n        <_>\n          9 11 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 3 -1.</_>\n        <_>\n          8 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 3 1 -1.</_>\n        <_>\n          7 3 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 2 3 12 -1.</_>\n        <_>\n          8 6 3 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 12 1 2 -1.</_>\n        <_>\n          11 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 12 2 2 -1.</_>\n        <_>\n          12 12 1 1 2.</_>\n        <_>\n          11 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 2 2 -1.</_>\n        <_>\n          5 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 1 3 -1.</_>\n        <_>\n          5 5 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 11 16 4 -1.</_>\n        <_>\n          11 11 8 2 2.</_>\n        <_>\n          3 13 8 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 10 20 3 -1.</_>\n        <_>\n          0 11 20 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 11 16 4 -1.</_>\n        <_>\n          1 11 8 2 2.</_>\n        <_>\n          9 13 8 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 2 4 2 -1.</_>\n        <_>\n          4 3 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 6 2 2 -1.</_>\n        <_>\n          13 6 1 1 2.</_>\n        <_>\n          12 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 6 6 -1.</_>\n        <_>\n          12 13 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 2 2 -1.</_>\n        <_>\n          6 6 1 1 2.</_>\n        <_>\n          7 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 4 4 16 -1.</_>\n        <_>\n          8 4 2 16 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 18 3 2 -1.</_>\n        <_>\n          11 19 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 17 6 2 -1.</_>\n        <_>\n          12 17 3 1 2.</_>\n        <_>\n          9 18 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 13 5 2 -1.</_>\n        <_>\n          2 14 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 15 2 2 -1.</_>\n        <_>\n          3 16 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 3 -1.</_>\n        <_>\n          10 7 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 2 6 -1.</_>\n        <_>\n          9 6 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 14 7 6 -1.</_>\n        <_>\n          1 16 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 2 11 -1.</_>\n        <_>\n          9 1 1 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 4 -1.</_>\n        <_>\n          9 7 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 10 2 1 -1.</_>\n        <_>\n          11 10 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 3 9 -1.</_>\n        <_>\n          1 3 1 9 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 3 6 -1.</_>\n        <_>\n          0 5 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 15 2 2 -1.</_>\n        <_>\n          12 15 1 1 2.</_>\n        <_>\n          11 16 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 14 2 2 -1.</_>\n        <_>\n          12 14 1 1 2.</_>\n        <_>\n          11 15 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 2 2 -1.</_>\n        <_>\n          7 15 1 1 2.</_>\n        <_>\n          8 16 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 2 2 -1.</_>\n        <_>\n          7 14 1 1 2.</_>\n        <_>\n          8 15 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 6 -1.</_>\n        <_>\n          10 13 2 3 2.</_>\n        <_>\n          8 16 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 14 16 4 -1.</_>\n        <_>\n          10 14 8 2 2.</_>\n        <_>\n          2 16 8 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 2 2 -1.</_>\n        <_>\n          9 9 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 5 3 -1.</_>\n        <_>\n          7 8 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 6 2 -1.</_>\n        <_>\n          9 5 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 1 6 18 -1.</_>\n        <_>\n          11 1 2 18 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 3 4 -1.</_>\n        <_>\n          9 6 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 5 2 4 -1.</_>\n        <_>\n          8 5 1 2 2.</_>\n        <_>\n          9 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 2 6 -1.</_>\n        <_>\n          10 13 1 3 2.</_>\n        <_>\n          9 16 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 0 3 18 -1.</_>\n        <_>\n          12 0 1 18 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 3 18 -1.</_>\n        <_>\n          7 0 1 18 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 15 4 2 -1.</_>\n        <_>\n          7 15 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 9 18 1 -1.</_>\n        <_>\n          7 9 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 20 3 -1.</_>\n        <_>\n          0 1 20 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 2 4 -1.</_>\n        <_>\n          10 6 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 6 2 -1.</_>\n        <_>\n          8 10 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 7 20 1 -1.</_>\n        <_>\n          0 7 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 3 5 4 -1.</_>\n        <_>\n          11 5 5 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 10 1 -1.</_>\n        <_>\n          10 7 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 10 3 3 -1.</_>\n        <_>\n          8 11 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 0 16 8 -1.</_>\n        <_>\n          10 0 8 4 2.</_>\n        <_>\n          2 4 8 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 0 9 10 -1.</_>\n        <_>\n          11 5 9 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 8 18 -1.</_>\n        <_>\n          4 2 4 18 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 2 6 -1.</_>\n        <_>\n          0 2 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 9 2 -1.</_>\n        <_>\n          6 1 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 1 12 2 -1.</_>\n        <_>\n          4 2 12 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 16 14 -1.</_>\n        <_>\n          2 8 16 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 8 12 -1.</_>\n        <_>\n          5 7 8 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 2 2 -1.</_>\n        <_>\n          9 12 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 5 6 -1.</_>\n        <_>\n          9 12 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 0 13 8 -1.</_>\n        <_>\n          3 4 13 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 5 8 -1.</_>\n        <_>\n          6 11 5 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 3 -1.</_>\n        <_>\n          9 6 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 8 8 3 -1.</_>\n        <_>\n          6 9 8 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 2 7 6 -1.</_>\n        <_>\n          2 5 7 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 14 4 -1.</_>\n        <_>\n          2 1 7 2 2.</_>\n        <_>\n          9 3 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 14 1 3 -1.</_>\n        <_>\n          11 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 15 8 2 -1.</_>\n        <_>\n          6 16 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 1 3 -1.</_>\n        <_>\n          8 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 2 8 -1.</_>\n        <_>\n          8 15 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 15 8 2 -1.</_>\n        <_>\n          6 16 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 16 8 3 -1.</_>\n        <_>\n          7 17 8 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 16 2 2 -1.</_>\n        <_>\n          0 17 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 16 8 4 -1.</_>\n        <_>\n          1 16 4 2 2.</_>\n        <_>\n          5 18 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 9 16 3 -1.</_>\n        <_>\n          2 10 16 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 11 2 4 -1.</_>\n        <_>\n          13 11 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 13 16 6 -1.</_>\n        <_>\n          0 15 16 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 2 4 -1.</_>\n        <_>\n          6 11 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 2 2 18 -1.</_>\n        <_>\n          19 2 1 9 2.</_>\n        <_>\n          18 11 1 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          19 7 1 9 -1.</_>\n        <_>\n          19 10 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 2 18 -1.</_>\n        <_>\n          0 2 1 9 2.</_>\n        <_>\n          1 11 1 9 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 7 1 9 -1.</_>\n        <_>\n          0 10 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 2 2 -1.</_>\n        <_>\n          14 13 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 14 2 3 -1.</_>\n        <_>\n          11 15 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 8 6 2 -1.</_>\n        <_>\n          7 9 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 12 4 6 -1.</_>\n        <_>\n          7 12 2 3 2.</_>\n        <_>\n          9 15 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 5 3 -1.</_>\n        <_>\n          8 14 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 14 2 2 -1.</_>\n        <_>\n          13 14 1 1 2.</_>\n        <_>\n          12 15 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 3 -1.</_>\n        <_>\n          7 14 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 5 2 -1.</_>\n        <_>\n          7 14 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 10 16 4 -1.</_>\n        <_>\n          10 10 8 2 2.</_>\n        <_>\n          2 12 8 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 0 6 6 -1.</_>\n        <_>\n          9 0 2 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 6 3 -1.</_>\n        <_>\n          7 2 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 12 6 2 -1.</_>\n        <_>\n          0 13 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 11 2 -1.</_>\n        <_>\n          6 4 11 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 0 8 6 -1.</_>\n        <_>\n          16 0 4 3 2.</_>\n        <_>\n          12 3 4 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 1 2 -1.</_>\n        <_>\n          8 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 1 12 -1.</_>\n        <_>\n          8 12 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 11 2 2 -1.</_>\n        <_>\n          12 11 1 1 2.</_>\n        <_>\n          11 12 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 3 13 -1.</_>\n        <_>\n          13 7 1 13 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 11 2 2 -1.</_>\n        <_>\n          7 11 1 1 2.</_>\n        <_>\n          8 12 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 13 1 3 -1.</_>\n        <_>\n          3 14 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 18 3 2 -1.</_>\n        <_>\n          11 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 11 2 1 -1.</_>\n        <_>\n          11 11 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 10 5 9 -1.</_>\n        <_>\n          1 13 5 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 8 6 4 -1.</_>\n        <_>\n          6 8 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 12 1 4 -1.</_>\n        <_>\n          13 14 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 3 4 14 -1.</_>\n        <_>\n          13 3 2 7 2.</_>\n        <_>\n          11 10 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 12 1 4 -1.</_>\n        <_>\n          6 14 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 4 14 -1.</_>\n        <_>\n          5 3 2 7 2.</_>\n        <_>\n          7 10 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 18 3 2 -1.</_>\n        <_>\n          11 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 3 3 -1.</_>\n        <_>\n          9 13 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 2 12 6 -1.</_>\n        <_>\n          2 2 6 3 2.</_>\n        <_>\n          8 5 6 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 6 2 -1.</_>\n        <_>\n          9 6 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 18 12 -1.</_>\n        <_>\n          7 0 6 12 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 6 4 -1.</_>\n        <_>\n          5 7 3 2 2.</_>\n        <_>\n          8 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 10 4 -1.</_>\n        <_>\n          5 9 10 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 6 4 -1.</_>\n        <_>\n          9 7 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 2 -1.</_>\n        <_>\n          9 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 2 2 -1.</_>\n        <_>\n          9 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 17 8 3 -1.</_>\n        <_>\n          6 18 8 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 17 6 2 -1.</_>\n        <_>\n          12 17 3 1 2.</_>\n        <_>\n          9 18 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 12 2 2 -1.</_>\n        <_>\n          4 13 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 9 2 -1.</_>\n        <_>\n          3 13 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 6 1 -1.</_>\n        <_>\n          10 3 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 3 4 6 -1.</_>\n        <_>\n          11 3 2 3 2.</_>\n        <_>\n          9 6 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 6 5 -1.</_>\n        <_>\n          3 3 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 0 2 18 -1.</_>\n        <_>\n          2 6 2 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 2 4 9 -1.</_>\n        <_>\n          14 5 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 18 3 2 -1.</_>\n        <_>\n          11 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 2 4 9 -1.</_>\n        <_>\n          2 5 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 18 3 2 -1.</_>\n        <_>\n          8 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 14 3 3 -1.</_>\n        <_>\n          10 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 12 2 6 -1.</_>\n        <_>\n          10 15 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 3 6 -1.</_>\n        <_>\n          7 7 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 3 6 2 -1.</_>\n        <_>\n          3 4 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 7 3 -1.</_>\n        <_>\n          8 5 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 6 2 3 -1.</_>\n        <_>\n          13 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 2 12 -1.</_>\n        <_>\n          8 12 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 8 14 -1.</_>\n        <_>\n          5 4 4 7 2.</_>\n        <_>\n          9 11 4 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 20 8 -1.</_>\n        <_>\n          10 1 10 4 2.</_>\n        <_>\n          0 5 10 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 0 12 2 -1.</_>\n        <_>\n          4 1 12 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 1 20 8 -1.</_>\n        <_>\n          0 1 10 4 2.</_>\n        <_>\n          10 5 10 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 0 12 2 -1.</_>\n        <_>\n          4 1 12 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 6 3 -1.</_>\n        <_>\n          9 5 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 10 6 -1.</_>\n        <_>\n          8 15 10 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 6 3 -1.</_>\n        <_>\n          8 5 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 3 6 1 -1.</_>\n        <_>\n          8 3 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 18 9 2 -1.</_>\n        <_>\n          14 18 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 11 6 7 -1.</_>\n        <_>\n          13 11 3 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 12 10 -1.</_>\n        <_>\n          4 6 6 5 2.</_>\n        <_>\n          10 11 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 17 3 3 -1.</_>\n        <_>\n          9 17 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 18 9 2 -1.</_>\n        <_>\n          14 18 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 11 6 8 -1.</_>\n        <_>\n          13 11 3 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 16 2 2 -1.</_>\n        <_>\n          4 17 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 4 4 -1.</_>\n        <_>\n          7 17 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 3 3 -1.</_>\n        <_>\n          12 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 6 2 3 -1.</_>\n        <_>\n          13 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 6 1 -1.</_>\n        <_>\n          7 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 10 3 1 -1.</_>\n        <_>\n          8 10 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 12 20 4 -1.</_>\n        <_>\n          0 14 20 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 2 3 2 -1.</_>\n        <_>\n          10 3 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 3 3 -1.</_>\n        <_>\n          5 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 4 3 -1.</_>\n        <_>\n          5 6 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 4 3 -1.</_>\n        <_>\n          8 9 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 4 2 12 -1.</_>\n        <_>\n          10 8 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 4 3 -1.</_>\n        <_>\n          0 4 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 3 2 3 -1.</_>\n        <_>\n          1 4 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 1 4 11 -1.</_>\n        <_>\n          16 1 2 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 2 2 16 -1.</_>\n        <_>\n          19 2 1 8 2.</_>\n        <_>\n          18 10 1 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 8 6 12 -1.</_>\n        <_>\n          3 8 2 12 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 2 6 2 -1.</_>\n        <_>\n          7 2 3 1 2.</_>\n        <_>\n          10 3 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 8 2 -1.</_>\n        <_>\n          16 4 4 1 2.</_>\n        <_>\n          12 5 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 6 6 2 -1.</_>\n        <_>\n          12 6 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 8 2 -1.</_>\n        <_>\n          0 4 4 1 2.</_>\n        <_>\n          4 5 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 3 3 5 -1.</_>\n        <_>\n          2 3 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 3 4 6 -1.</_>\n        <_>\n          16 5 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 4 3 -1.</_>\n        <_>\n          8 7 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 1 3 -1.</_>\n        <_>\n          8 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 11 1 2 -1.</_>\n        <_>\n          4 12 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 6 3 -1.</_>\n        <_>\n          8 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 7 3 -1.</_>\n        <_>\n          7 16 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 2 8 -1.</_>\n        <_>\n          9 16 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 6 2 -1.</_>\n        <_>\n          6 6 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 4 2 -1.</_>\n        <_>\n          12 8 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 13 10 -1.</_>\n        <_>\n          5 8 13 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 4 2 -1.</_>\n        <_>\n          4 8 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 8 16 2 -1.</_>\n        <_>\n          0 8 8 1 2.</_>\n        <_>\n          8 9 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 8 2 5 -1.</_>\n        <_>\n          11 8 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 0 6 13 -1.</_>\n        <_>\n          10 0 3 13 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 6 4 2 -1.</_>\n        <_>\n          1 7 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 2 1 -1.</_>\n        <_>\n          5 3 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 8 2 5 -1.</_>\n        <_>\n          11 8 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 10 4 8 -1.</_>\n        <_>\n          12 10 2 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 8 2 5 -1.</_>\n        <_>\n          8 8 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 10 4 8 -1.</_>\n        <_>\n          6 10 2 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 9 12 -1.</_>\n        <_>\n          9 7 3 12 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 13 2 3 -1.</_>\n        <_>\n          11 13 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 10 6 10 -1.</_>\n        <_>\n          10 10 3 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 4 8 -1.</_>\n        <_>\n          8 11 2 4 2.</_>\n        <_>\n          10 15 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 1 4 11 -1.</_>\n        <_>\n          16 1 2 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 2 2 4 -1.</_>\n        <_>\n          18 2 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 6 2 -1.</_>\n        <_>\n          5 6 3 1 2.</_>\n        <_>\n          8 7 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 1 3 -1.</_>\n        <_>\n          5 5 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 1 4 14 -1.</_>\n        <_>\n          11 1 2 14 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 2 12 3 -1.</_>\n        <_>\n          8 2 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 4 14 -1.</_>\n        <_>\n          7 1 2 14 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 3 6 2 -1.</_>\n        <_>\n          9 3 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 0 18 4 -1.</_>\n        <_>\n          8 0 6 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 10 -1.</_>\n        <_>\n          9 10 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 3 4 -1.</_>\n        <_>\n          9 6 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 9 11 -1.</_>\n        <_>\n          8 5 3 11 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 6 3 5 -1.</_>\n        <_>\n          11 6 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 9 6 5 -1.</_>\n        <_>\n          8 9 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 6 3 5 -1.</_>\n        <_>\n          8 6 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 6 3 -1.</_>\n        <_>\n          9 10 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 0 3 7 -1.</_>\n        <_>\n          11 0 1 7 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 20 12 -1.</_>\n        <_>\n          0 9 20 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 2 -1.</_>\n        <_>\n          10 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 9 4 1 -1.</_>\n        <_>\n          7 9 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 13 3 2 -1.</_>\n        <_>\n          13 14 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 9 4 6 -1.</_>\n        <_>\n          16 9 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 6 3 -1.</_>\n        <_>\n          7 16 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 16 7 3 -1.</_>\n        <_>\n          6 17 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 14 9 6 -1.</_>\n        <_>\n          11 16 9 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          19 14 1 3 -1.</_>\n        <_>\n          19 15 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 9 6 6 -1.</_>\n        <_>\n          3 9 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 19 9 1 -1.</_>\n        <_>\n          3 19 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 14 9 6 -1.</_>\n        <_>\n          11 16 9 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 12 6 6 -1.</_>\n        <_>\n          12 14 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 14 8 6 -1.</_>\n        <_>\n          1 16 8 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 1 3 2 -1.</_>\n        <_>\n          9 1 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 2 2 4 -1.</_>\n        <_>\n          18 2 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 0 6 3 -1.</_>\n        <_>\n          16 0 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 2 4 -1.</_>\n        <_>\n          1 2 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 6 3 -1.</_>\n        <_>\n          2 0 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 3 2 -1.</_>\n        <_>\n          10 0 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 1 2 2 -1.</_>\n        <_>\n          12 1 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 0 3 2 -1.</_>\n        <_>\n          9 0 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 1 2 2 -1.</_>\n        <_>\n          7 1 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 8 2 3 -1.</_>\n        <_>\n          10 9 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 15 6 2 -1.</_>\n        <_>\n          13 16 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 2 2 -1.</_>\n        <_>\n          8 12 1 1 2.</_>\n        <_>\n          9 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 3 5 -1.</_>\n        <_>\n          9 15 1 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 4 12 -1.</_>\n        <_>\n          8 12 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 6 7 8 -1.</_>\n        <_>\n          7 10 7 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 11 8 2 -1.</_>\n        <_>\n          0 12 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 2 2 -1.</_>\n        <_>\n          8 11 1 1 2.</_>\n        <_>\n          9 12 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 12 1 -1.</_>\n        <_>\n          11 7 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 8 3 2 -1.</_>\n        <_>\n          11 8 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 7 12 1 -1.</_>\n        <_>\n          5 7 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 5 8 2 -1.</_>\n        <_>\n          6 5 4 1 2.</_>\n        <_>\n          10 6 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 3 10 -1.</_>\n        <_>\n          10 10 1 10 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 0 2 4 -1.</_>\n        <_>\n          16 0 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 10 3 10 -1.</_>\n        <_>\n          9 10 1 10 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 2 3 -1.</_>\n        <_>\n          9 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 9 4 2 -1.</_>\n        <_>\n          10 9 2 1 2.</_>\n        <_>\n          8 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 14 7 6 -1.</_>\n        <_>\n          12 16 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 1 3 1 -1.</_>\n        <_>\n          7 1 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 0 2 4 -1.</_>\n        <_>\n          3 0 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 11 2 2 -1.</_>\n        <_>\n          12 11 1 1 2.</_>\n        <_>\n          11 12 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 12 6 6 -1.</_>\n        <_>\n          12 14 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 6 10 -1.</_>\n        <_>\n          1 0 3 5 2.</_>\n        <_>\n          4 5 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 0 2 9 -1.</_>\n        <_>\n          3 3 2 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 13 3 2 -1.</_>\n        <_>\n          14 14 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 2 3 2 -1.</_>\n        <_>\n          15 3 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 13 5 2 -1.</_>\n        <_>\n          2 14 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 4 12 10 -1.</_>\n        <_>\n          3 4 6 5 2.</_>\n        <_>\n          9 9 6 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 14 6 -1.</_>\n        <_>\n          5 3 14 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 3 3 2 -1.</_>\n        <_>\n          15 4 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 11 2 2 -1.</_>\n        <_>\n          7 11 1 1 2.</_>\n        <_>\n          8 12 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 14 6 6 -1.</_>\n        <_>\n          2 16 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 13 8 3 -1.</_>\n        <_>\n          6 14 8 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 19 18 1 -1.</_>\n        <_>\n          7 19 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 1 6 -1.</_>\n        <_>\n          8 15 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 14 15 -1.</_>\n        <_>\n          0 5 14 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 0 16 8 -1.</_>\n        <_>\n          3 4 16 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 1 8 12 -1.</_>\n        <_>\n          6 7 8 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 3 3 -1.</_>\n        <_>\n          6 3 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 3 4 -1.</_>\n        <_>\n          6 1 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 14 4 6 -1.</_>\n        <_>\n          17 14 2 3 2.</_>\n        <_>\n          15 17 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 6 8 -1.</_>\n        <_>\n          15 11 3 4 2.</_>\n        <_>\n          12 15 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 2 4 -1.</_>\n        <_>\n          9 7 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 11 3 1 -1.</_>\n        <_>\n          7 11 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 3 2 14 -1.</_>\n        <_>\n          12 3 1 14 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 6 2 -1.</_>\n        <_>\n          15 11 3 1 2.</_>\n        <_>\n          12 12 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 5 2 -1.</_>\n        <_>\n          0 3 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 15 1 -1.</_>\n        <_>\n          5 0 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 6 2 -1.</_>\n        <_>\n          15 11 3 1 2.</_>\n        <_>\n          12 12 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 5 2 2 -1.</_>\n        <_>\n          10 5 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 2 -1.</_>\n        <_>\n          10 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 2 10 -1.</_>\n        <_>\n          9 0 1 5 2.</_>\n        <_>\n          10 5 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 14 2 2 -1.</_>\n        <_>\n          18 15 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 11 4 9 -1.</_>\n        <_>\n          13 14 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 2 2 -1.</_>\n        <_>\n          8 13 1 1 2.</_>\n        <_>\n          9 14 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 8 4 3 -1.</_>\n        <_>\n          7 9 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 9 4 2 -1.</_>\n        <_>\n          8 10 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 12 4 2 -1.</_>\n        <_>\n          13 13 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 14 2 2 -1.</_>\n        <_>\n          6 14 1 1 2.</_>\n        <_>\n          7 15 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 14 2 2 -1.</_>\n        <_>\n          0 15 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 3 -1.</_>\n        <_>\n          7 14 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 9 10 6 -1.</_>\n        <_>\n          7 11 10 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 9 12 4 -1.</_>\n        <_>\n          6 9 4 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 9 6 11 -1.</_>\n        <_>\n          10 9 3 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 3 -1.</_>\n        <_>\n          9 8 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 4 3 -1.</_>\n        <_>\n          9 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 3 3 17 -1.</_>\n        <_>\n          3 3 1 17 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 11 6 3 -1.</_>\n        <_>\n          0 12 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 3 11 9 -1.</_>\n        <_>\n          4 6 11 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 6 11 -1.</_>\n        <_>\n          3 2 3 11 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 0 4 5 -1.</_>\n        <_>\n          13 0 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 6 4 -1.</_>\n        <_>\n          12 7 3 2 2.</_>\n        <_>\n          9 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 8 2 -1.</_>\n        <_>\n          9 7 4 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 8 15 1 -1.</_>\n        <_>\n          6 8 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 12 12 2 -1.</_>\n        <_>\n          8 12 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 0 4 10 -1.</_>\n        <_>\n          15 0 2 5 2.</_>\n        <_>\n          13 5 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 2 2 -1.</_>\n        <_>\n          9 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 9 6 2 -1.</_>\n        <_>\n          6 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 17 4 3 -1.</_>\n        <_>\n          8 18 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 3 9 2 -1.</_>\n        <_>\n          11 3 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 3 9 2 -1.</_>\n        <_>\n          6 3 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 0 9 14 -1.</_>\n        <_>\n          8 0 3 14 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 3 7 10 -1.</_>\n        <_>\n          7 8 7 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 8 13 3 -1.</_>\n        <_>\n          4 9 13 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 12 14 4 -1.</_>\n        <_>\n          3 12 7 2 2.</_>\n        <_>\n          10 14 7 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 4 2 -1.</_>\n        <_>\n          8 13 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 10 9 8 -1.</_>\n        <_>\n          6 14 9 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 12 2 8 -1.</_>\n        <_>\n          9 16 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 3 3 -1.</_>\n        <_>\n          8 13 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 4 10 -1.</_>\n        <_>\n          7 5 2 10 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 15 3 3 -1.</_>\n        <_>\n          14 16 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 13 3 -1.</_>\n        <_>\n          4 7 13 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 15 3 3 -1.</_>\n        <_>\n          3 16 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 9 4 2 -1.</_>\n        <_>\n          3 9 2 1 2.</_>\n        <_>\n          5 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 11 20 4 -1.</_>\n        <_>\n          10 11 10 2 2.</_>\n        <_>\n          0 13 10 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 4 3 -1.</_>\n        <_>\n          8 16 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 11 20 4 -1.</_>\n        <_>\n          0 11 10 2 2.</_>\n        <_>\n          10 13 10 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 15 4 3 -1.</_>\n        <_>\n          8 16 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 13 1 6 -1.</_>\n        <_>\n          10 16 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 18 2 -1.</_>\n        <_>\n          11 1 9 1 2.</_>\n        <_>\n          2 2 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 14 3 3 -1.</_>\n        <_>\n          8 15 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 1 6 1 -1.</_>\n        <_>\n          6 1 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 13 1 3 -1.</_>\n        <_>\n          11 14 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 5 2 12 -1.</_>\n        <_>\n          13 11 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 14 18 6 -1.</_>\n        <_>\n          1 16 18 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 1 3 -1.</_>\n        <_>\n          8 14 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 13 6 3 -1.</_>\n        <_>\n          7 14 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 3 2 -1.</_>\n        <_>\n          9 11 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 3 3 -1.</_>\n        <_>\n          6 1 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 6 5 -1.</_>\n        <_>\n          8 5 3 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 6 14 -1.</_>\n        <_>\n          7 12 6 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 16 6 2 -1.</_>\n        <_>\n          9 16 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 2 12 -1.</_>\n        <_>\n          1 2 1 12 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 5 3 -1.</_>\n        <_>\n          1 1 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 3 3 -1.</_>\n        <_>\n          12 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 6 3 3 -1.</_>\n        <_>\n          12 7 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 3 3 -1.</_>\n        <_>\n          5 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 3 3 -1.</_>\n        <_>\n          5 7 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 4 8 -1.</_>\n        <_>\n          10 12 2 4 2.</_>\n        <_>\n          8 16 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 17 18 2 -1.</_>\n        <_>\n          11 17 9 1 2.</_>\n        <_>\n          2 18 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 3 2 2 -1.</_>\n        <_>\n          9 4 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 5 4 6 -1.</_>\n        <_>\n          8 7 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 8 6 -1.</_>\n        <_>\n          9 2 8 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 18 4 -1.</_>\n        <_>\n          7 0 6 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 4 8 -1.</_>\n        <_>\n          2 0 2 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 6 9 -1.</_>\n        <_>\n          2 4 2 9 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 4 18 2 -1.</_>\n        <_>\n          7 4 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 16 12 4 -1.</_>\n        <_>\n          14 16 6 2 2.</_>\n        <_>\n          8 18 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 18 2 -1.</_>\n        <_>\n          0 0 9 1 2.</_>\n        <_>\n          9 1 9 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 0 3 18 -1.</_>\n        <_>\n          4 0 1 18 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 9 4 7 -1.</_>\n        <_>\n          14 9 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 14 2 2 -1.</_>\n        <_>\n          15 15 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 9 4 7 -1.</_>\n        <_>\n          4 9 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 14 2 2 -1.</_>\n        <_>\n          3 15 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 0 6 6 -1.</_>\n        <_>\n          11 2 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 0 2 6 -1.</_>\n        <_>\n          15 0 1 3 2.</_>\n        <_>\n          14 3 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 11 2 2 -1.</_>\n        <_>\n          7 11 1 1 2.</_>\n        <_>\n          8 12 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 10 2 2 -1.</_>\n        <_>\n          8 10 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 2 6 -1.</_>\n        <_>\n          9 17 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 18 4 2 -1.</_>\n        <_>\n          12 19 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 17 4 3 -1.</_>\n        <_>\n          8 18 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 18 8 2 -1.</_>\n        <_>\n          2 19 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 9 16 3 -1.</_>\n        <_>\n          2 10 16 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 2 2 -1.</_>\n        <_>\n          9 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 14 2 4 -1.</_>\n        <_>\n          5 14 1 2 2.</_>\n        <_>\n          6 16 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 9 4 2 -1.</_>\n        <_>\n          8 9 2 1 2.</_>\n        <_>\n          10 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 5 -1.</_>\n        <_>\n          9 5 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 9 3 2 -1.</_>\n        <_>\n          10 9 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 9 3 2 -1.</_>\n        <_>\n          9 9 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 3 6 -1.</_>\n        <_>\n          9 8 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 4 8 -1.</_>\n        <_>\n          10 12 2 4 2.</_>\n        <_>\n          8 16 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 17 16 2 -1.</_>\n        <_>\n          10 17 8 1 2.</_>\n        <_>\n          2 18 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 3 8 -1.</_>\n        <_>\n          9 12 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 10 1 3 -1.</_>\n        <_>\n          3 11 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 10 6 -1.</_>\n        <_>\n          14 14 5 3 2.</_>\n        <_>\n          9 17 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 13 3 6 -1.</_>\n        <_>\n          14 15 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 19 18 1 -1.</_>\n        <_>\n          7 19 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 10 15 2 -1.</_>\n        <_>\n          7 10 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 17 16 3 -1.</_>\n        <_>\n          4 18 16 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 6 4 9 -1.</_>\n        <_>\n          8 9 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 16 2 4 -1.</_>\n        <_>\n          9 16 1 2 2.</_>\n        <_>\n          10 18 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 10 8 -1.</_>\n        <_>\n          5 9 10 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 1 4 2 -1.</_>\n        <_>\n          13 1 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 0 3 6 -1.</_>\n        <_>\n          14 2 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 2 2 -1.</_>\n        <_>\n          6 7 1 1 2.</_>\n        <_>\n          7 8 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 1 6 1 -1.</_>\n        <_>\n          9 1 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 11 3 3 -1.</_>\n        <_>\n          9 12 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 9 3 3 -1.</_>\n        <_>\n          13 9 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 3 3 -1.</_>\n        <_>\n          8 12 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 9 3 3 -1.</_>\n        <_>\n          6 9 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 11 1 3 -1.</_>\n        <_>\n          10 12 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 9 6 4 -1.</_>\n        <_>\n          10 9 3 2 2.</_>\n        <_>\n          7 11 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 2 2 -1.</_>\n        <_>\n          4 7 1 1 2.</_>\n        <_>\n          5 8 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 3 1 -1.</_>\n        <_>\n          6 7 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 3 2 3 -1.</_>\n        <_>\n          18 4 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 1 4 2 -1.</_>\n        <_>\n          13 1 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 1 4 2 -1.</_>\n        <_>\n          5 1 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 0 5 2 -1.</_>\n        <_>\n          3 1 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 7 6 4 -1.</_>\n        <_>\n          17 7 3 2 2.</_>\n        <_>\n          14 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 8 16 2 -1.</_>\n        <_>\n          4 9 16 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 11 5 6 -1.</_>\n        <_>\n          2 13 5 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 16 2 4 -1.</_>\n        <_>\n          5 16 1 2 2.</_>\n        <_>\n          6 18 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 6 2 12 -1.</_>\n        <_>\n          16 6 1 6 2.</_>\n        <_>\n          15 12 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 3 6 16 -1.</_>\n        <_>\n          15 3 2 16 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 12 12 -1.</_>\n        <_>\n          4 5 6 6 2.</_>\n        <_>\n          10 11 6 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 1 10 13 -1.</_>\n        <_>\n          10 1 5 13 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 5 2 2 -1.</_>\n        <_>\n          12 5 1 1 2.</_>\n        <_>\n          11 6 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 5 1 3 -1.</_>\n        <_>\n          13 6 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 4 2 4 -1.</_>\n        <_>\n          7 4 1 2 2.</_>\n        <_>\n          8 6 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 6 4 -1.</_>\n        <_>\n          10 5 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 4 6 -1.</_>\n        <_>\n          14 4 2 3 2.</_>\n        <_>\n          12 7 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 11 7 6 -1.</_>\n        <_>\n          12 13 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 6 6 -1.</_>\n        <_>\n          7 6 2 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 2 2 -1.</_>\n        <_>\n          9 9 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 6 2 2 -1.</_>\n        <_>\n          16 6 1 1 2.</_>\n        <_>\n          15 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 7 4 4 -1.</_>\n        <_>\n          16 7 2 2 2.</_>\n        <_>\n          14 9 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 6 2 -1.</_>\n        <_>\n          7 5 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 19 18 1 -1.</_>\n        <_>\n          7 19 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 3 3 3 -1.</_>\n        <_>\n          12 4 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 0 2 3 -1.</_>\n        <_>\n          16 1 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 3 3 -1.</_>\n        <_>\n          5 4 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 0 2 3 -1.</_>\n        <_>\n          2 1 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 6 2 2 -1.</_>\n        <_>\n          16 6 1 1 2.</_>\n        <_>\n          15 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 13 1 6 -1.</_>\n        <_>\n          10 16 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 7 10 2 -1.</_>\n        <_>\n          0 7 5 1 2.</_>\n        <_>\n          5 8 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 10 6 2 -1.</_>\n        <_>\n          3 11 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 18 4 2 -1.</_>\n        <_>\n          12 19 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 18 2 2 -1.</_>\n        <_>\n          13 18 1 1 2.</_>\n        <_>\n          12 19 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 19 2 1 -1.</_>\n        <_>\n          7 19 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 2 16 -1.</_>\n        <_>\n          0 4 1 8 2.</_>\n        <_>\n          1 12 1 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 1 4 9 -1.</_>\n        <_>\n          16 4 4 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 2 1 2 -1.</_>\n        <_>\n          10 3 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 14 4 6 -1.</_>\n        <_>\n          4 14 2 3 2.</_>\n        <_>\n          6 17 2 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 15 1 4 -1.</_>\n        <_>\n          4 17 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 20 4 -1.</_>\n        <_>\n          10 2 10 2 2.</_>\n        <_>\n          0 4 10 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 5 2 8 -1.</_>\n        <_>\n          14 9 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 12 4 5 -1.</_>\n        <_>\n          7 12 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 13 9 6 -1.</_>\n        <_>\n          0 15 9 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 11 3 -1.</_>\n        <_>\n          9 15 11 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 7 3 -1.</_>\n        <_>\n          7 15 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 6 2 2 -1.</_>\n        <_>\n          3 6 1 1 2.</_>\n        <_>\n          4 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 2 7 -1.</_>\n        <_>\n          7 7 1 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 5 1 3 -1.</_>\n        <_>\n          14 6 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 4 4 3 -1.</_>\n        <_>\n          13 5 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 7 4 4 -1.</_>\n        <_>\n          2 7 2 2 2.</_>\n        <_>\n          4 9 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 9 13 6 -1.</_>\n        <_>\n          2 12 13 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 1 3 4 -1.</_>\n        <_>\n          11 1 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 5 2 -1.</_>\n        <_>\n          9 9 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 14 11 3 -1.</_>\n        <_>\n          0 15 11 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 11 2 8 -1.</_>\n        <_>\n          8 15 2 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 10 6 -1.</_>\n        <_>\n          5 14 10 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 13 15 5 -1.</_>\n        <_>\n          10 13 5 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 10 1 10 -1.</_>\n        <_>\n          8 15 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 14 6 2 -1.</_>\n        <_>\n          6 14 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 7 3 -1.</_>\n        <_>\n          7 15 7 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 16 9 3 -1.</_>\n        <_>\n          7 17 9 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 3 -1.</_>\n        <_>\n          8 8 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 5 1 6 -1.</_>\n        <_>\n          3 8 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 5 11 2 -1.</_>\n        <_>\n          6 6 11 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 3 2 -1.</_>\n        <_>\n          10 0 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 1 3 -1.</_>\n        <_>\n          5 6 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 2 -1.</_>\n        <_>\n          9 7 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 2 10 6 -1.</_>\n        <_>\n          10 2 5 3 2.</_>\n        <_>\n          5 5 5 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 6 4 -1.</_>\n        <_>\n          8 4 3 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 16 3 4 -1.</_>\n        <_>\n          9 16 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 13 2 6 -1.</_>\n        <_>\n          9 13 1 3 2.</_>\n        <_>\n          10 16 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 1 -1.</_>\n        <_>\n          10 8 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 5 18 15 -1.</_>\n        <_>\n          2 10 18 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 3 6 2 -1.</_>\n        <_>\n          4 3 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 6 6 2 -1.</_>\n        <_>\n          9 6 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 17 4 3 -1.</_>\n        <_>\n          8 18 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 13 2 3 -1.</_>\n        <_>\n          10 14 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 10 20 4 -1.</_>\n        <_>\n          0 12 20 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 6 4 -1.</_>\n        <_>\n          5 7 3 2 2.</_>\n        <_>\n          8 9 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 12 1 2 -1.</_>\n        <_>\n          11 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 10 2 3 -1.</_>\n        <_>\n          10 11 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 5 2 2 -1.</_>\n        <_>\n          9 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 4 1 10 -1.</_>\n        <_>\n          4 9 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 18 4 2 -1.</_>\n        <_>\n          11 18 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 18 3 2 -1.</_>\n        <_>\n          12 19 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 6 16 6 -1.</_>\n        <_>\n          0 6 8 3 2.</_>\n        <_>\n          8 9 8 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 6 4 12 -1.</_>\n        <_>\n          7 12 4 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 18 4 2 -1.</_>\n        <_>\n          11 18 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 18 3 2 -1.</_>\n        <_>\n          12 19 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 12 1 2 -1.</_>\n        <_>\n          8 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 1 3 -1.</_>\n        <_>\n          8 14 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 18 4 2 -1.</_>\n        <_>\n          11 18 2 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 4 6 -1.</_>\n        <_>\n          14 12 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 0 3 4 -1.</_>\n        <_>\n          7 0 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 0 2 8 -1.</_>\n        <_>\n          4 0 1 4 2.</_>\n        <_>\n          5 4 1 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 17 9 3 -1.</_>\n        <_>\n          14 17 3 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 2 4 5 -1.</_>\n        <_>\n          16 2 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 5 9 -1.</_>\n        <_>\n          0 5 5 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 2 3 2 -1.</_>\n        <_>\n          8 2 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 17 9 3 -1.</_>\n        <_>\n          14 17 3 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 2 4 5 -1.</_>\n        <_>\n          16 2 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 17 9 3 -1.</_>\n        <_>\n          3 17 3 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 4 5 -1.</_>\n        <_>\n          2 2 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 11 10 9 -1.</_>\n        <_>\n          5 14 10 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 6 3 3 -1.</_>\n        <_>\n          9 7 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 17 5 3 -1.</_>\n        <_>\n          3 18 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 5 4 7 -1.</_>\n        <_>\n          9 5 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 2 5 -1.</_>\n        <_>\n          9 8 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 2 18 2 -1.</_>\n        <_>\n          2 3 18 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 8 15 6 -1.</_>\n        <_>\n          7 8 5 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 2 5 -1.</_>\n        <_>\n          10 8 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 10 4 6 -1.</_>\n        <_>\n          12 12 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 3 6 2 -1.</_>\n        <_>\n          14 4 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 2 3 -1.</_>\n        <_>\n          5 6 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 6 3 3 -1.</_>\n        <_>\n          4 7 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 3 3 -1.</_>\n        <_>\n          14 13 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 12 11 3 -1.</_>\n        <_>\n          6 13 11 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 3 6 -1.</_>\n        <_>\n          1 4 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 4 7 -1.</_>\n        <_>\n          3 0 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 8 3 4 -1.</_>\n        <_>\n          10 8 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 9 2 2 -1.</_>\n        <_>\n          10 10 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 8 3 4 -1.</_>\n        <_>\n          9 8 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 4 10 10 -1.</_>\n        <_>\n          4 9 10 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 3 2 -1.</_>\n        <_>\n          10 10 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 10 3 2 -1.</_>\n        <_>\n          9 11 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 10 3 2 -1.</_>\n        <_>\n          9 10 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 4 14 12 -1.</_>\n        <_>\n          2 4 7 6 2.</_>\n        <_>\n          9 10 7 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 12 1 6 -1.</_>\n        <_>\n          10 15 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 3 8 16 -1.</_>\n        <_>\n          11 3 4 8 2.</_>\n        <_>\n          7 11 4 8 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 6 8 10 -1.</_>\n        <_>\n          5 6 4 5 2.</_>\n        <_>\n          9 11 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 2 8 8 -1.</_>\n        <_>\n          6 2 4 4 2.</_>\n        <_>\n          10 6 4 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 5 4 2 -1.</_>\n        <_>\n          12 5 2 1 2.</_>\n        <_>\n          10 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 3 3 -1.</_>\n        <_>\n          12 5 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 19 12 1 -1.</_>\n        <_>\n          8 19 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 2 3 1 -1.</_>\n        <_>\n          9 2 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 17 4 3 -1.</_>\n        <_>\n          13 18 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 6 3 -1.</_>\n        <_>\n          7 15 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 2 3 -1.</_>\n        <_>\n          9 15 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 6 3 -1.</_>\n        <_>\n          7 16 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 18 3 2 -1.</_>\n        <_>\n          11 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 2 3 -1.</_>\n        <_>\n          14 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 10 4 6 -1.</_>\n        <_>\n          4 12 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 13 3 2 -1.</_>\n        <_>\n          4 14 3 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 16 2 3 -1.</_>\n        <_>\n          9 17 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 18 3 2 -1.</_>\n        <_>\n          11 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 18 3 2 -1.</_>\n        <_>\n          8 18 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 10 4 2 -1.</_>\n        <_>\n          1 11 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 4 6 3 -1.</_>\n        <_>\n          12 5 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 4 1 3 -1.</_>\n        <_>\n          14 5 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 4 6 3 -1.</_>\n        <_>\n          2 5 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 4 1 3 -1.</_>\n        <_>\n          5 5 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 3 3 -1.</_>\n        <_>\n          14 13 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 12 2 3 -1.</_>\n        <_>\n          15 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 16 4 3 -1.</_>\n        <_>\n          3 17 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 0 4 2 -1.</_>\n        <_>\n          8 1 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 20 1 -1.</_>\n        <_>\n          0 0 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 3 4 -1.</_>\n        <_>\n          10 7 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 0 20 1 -1.</_>\n        <_>\n          10 0 10 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 3 4 -1.</_>\n        <_>\n          9 7 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 6 19 3 -1.</_>\n        <_>\n          1 7 19 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 4 2 -1.</_>\n        <_>\n          12 8 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 8 3 3 -1.</_>\n        <_>\n          7 9 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 3 3 -1.</_>\n        <_>\n          8 7 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 9 16 3 -1.</_>\n        <_>\n          2 10 16 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 4 2 12 -1.</_>\n        <_>\n          9 8 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 3 2 5 -1.</_>\n        <_>\n          8 3 1 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 3 -1.</_>\n        <_>\n          9 8 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 4 3 -1.</_>\n        <_>\n          9 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 8 6 4 -1.</_>\n        <_>\n          10 8 3 2 2.</_>\n        <_>\n          7 10 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 2 2 -1.</_>\n        <_>\n          10 7 1 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 5 6 6 -1.</_>\n        <_>\n          7 5 2 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 1 3 6 -1.</_>\n        <_>\n          10 1 1 6 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 5 12 2 -1.</_>\n        <_>\n          8 5 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 2 6 4 -1.</_>\n        <_>\n          6 2 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 8 2 -1.</_>\n        <_>\n          4 8 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 6 14 6 -1.</_>\n        <_>\n          10 6 7 3 2.</_>\n        <_>\n          3 9 7 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 6 14 3 -1.</_>\n        <_>\n          3 6 7 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 2 2 -1.</_>\n        <_>\n          0 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 13 4 3 -1.</_>\n        <_>\n          8 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 0 3 20 -1.</_>\n        <_>\n          14 0 1 20 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 8 10 3 -1.</_>\n        <_>\n          10 9 10 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 0 3 20 -1.</_>\n        <_>\n          5 0 1 20 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 8 10 3 -1.</_>\n        <_>\n          0 9 10 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 5 3 4 -1.</_>\n        <_>\n          13 5 1 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 12 4 -1.</_>\n        <_>\n          10 7 4 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 14 6 6 -1.</_>\n        <_>\n          1 14 3 3 2.</_>\n        <_>\n          4 17 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 17 6 2 -1.</_>\n        <_>\n          1 18 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 8 6 12 -1.</_>\n        <_>\n          17 8 3 6 2.</_>\n        <_>\n          14 14 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 5 2 2 -1.</_>\n        <_>\n          18 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 16 4 2 -1.</_>\n        <_>\n          3 16 2 1 2.</_>\n        <_>\n          5 17 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 16 6 2 -1.</_>\n        <_>\n          4 16 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 8 6 12 -1.</_>\n        <_>\n          17 8 3 6 2.</_>\n        <_>\n          14 14 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 5 2 2 -1.</_>\n        <_>\n          18 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 16 9 2 -1.</_>\n        <_>\n          8 16 3 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 14 6 6 -1.</_>\n        <_>\n          3 14 3 3 2.</_>\n        <_>\n          6 17 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 8 6 12 -1.</_>\n        <_>\n          17 8 3 6 2.</_>\n        <_>\n          14 14 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 7 2 12 -1.</_>\n        <_>\n          11 11 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 8 6 12 -1.</_>\n        <_>\n          0 8 3 6 2.</_>\n        <_>\n          3 14 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 2 12 -1.</_>\n        <_>\n          7 11 2 4 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 1 2 -1.</_>\n        <_>\n          14 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 13 8 1 -1.</_>\n        <_>\n          12 13 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 16 6 -1.</_>\n        <_>\n          0 6 16 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 4 8 2 -1.</_>\n        <_>\n          1 4 4 1 2.</_>\n        <_>\n          5 5 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 12 1 2 -1.</_>\n        <_>\n          14 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          15 12 2 3 -1.</_>\n        <_>\n          15 13 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 16 3 3 -1.</_>\n        <_>\n          8 17 3 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 12 1 2 -1.</_>\n        <_>\n          5 13 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 4 3 15 -1.</_>\n        <_>\n          14 4 1 15 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          17 3 2 6 -1.</_>\n        <_>\n          18 3 1 3 2.</_>\n        <_>\n          17 6 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 4 3 15 -1.</_>\n        <_>\n          5 4 1 15 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 3 2 6 -1.</_>\n        <_>\n          1 3 1 3 2.</_>\n        <_>\n          2 6 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 15 12 4 -1.</_>\n        <_>\n          7 17 12 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 0 19 3 -1.</_>\n        <_>\n          1 1 19 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 17 10 2 -1.</_>\n        <_>\n          3 17 5 1 2.</_>\n        <_>\n          8 18 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 5 10 15 -1.</_>\n        <_>\n          2 10 10 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 8 3 4 -1.</_>\n        <_>\n          13 10 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          19 13 1 2 -1.</_>\n        <_>\n          19 14 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 8 3 4 -1.</_>\n        <_>\n          4 10 3 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 13 1 2 -1.</_>\n        <_>\n          0 14 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 2 12 -1.</_>\n        <_>\n          12 13 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 7 2 2 -1.</_>\n        <_>\n          15 7 1 1 2.</_>\n        <_>\n          14 8 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 3 8 2 -1.</_>\n        <_>\n          5 4 8 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 2 6 -1.</_>\n        <_>\n          0 4 2 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 2 2 12 -1.</_>\n        <_>\n          19 2 1 6 2.</_>\n        <_>\n          18 8 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 1 1 2 -1.</_>\n        <_>\n          18 2 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 2 2 12 -1.</_>\n        <_>\n          0 2 1 6 2.</_>\n        <_>\n          1 8 1 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 1 1 2 -1.</_>\n        <_>\n          1 2 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          16 4 4 14 -1.</_>\n        <_>\n          18 4 2 7 2.</_>\n        <_>\n          16 11 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 14 1 6 -1.</_>\n        <_>\n          10 17 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 4 4 14 -1.</_>\n        <_>\n          0 4 2 7 2.</_>\n        <_>\n          2 11 2 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 1 6 -1.</_>\n        <_>\n          9 17 1 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 4 3 -1.</_>\n        <_>\n          9 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 12 2 -1.</_>\n        <_>\n          8 7 4 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 8 4 3 -1.</_>\n        <_>\n          0 9 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 2 2 -1.</_>\n        <_>\n          4 7 1 1 2.</_>\n        <_>\n          5 8 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 7 2 1 -1.</_>\n        <_>\n          13 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 4 4 5 -1.</_>\n        <_>\n          11 4 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 8 3 3 -1.</_>\n        <_>\n          5 8 1 3 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 3 8 1 -1.</_>\n        <_>\n          4 3 4 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          13 7 2 1 -1.</_>\n        <_>\n          13 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 7 3 2 -1.</_>\n        <_>\n          15 7 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 7 2 1 -1.</_>\n        <_>\n          6 7 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 7 3 2 -1.</_>\n        <_>\n          4 7 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          18 5 2 2 -1.</_>\n        <_>\n          18 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 14 2 2 -1.</_>\n        <_>\n          13 14 1 1 2.</_>\n        <_>\n          12 15 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          0 5 2 2 -1.</_>\n        <_>\n          0 6 2 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 14 2 2 -1.</_>\n        <_>\n          6 14 1 1 2.</_>\n        <_>\n          7 15 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 12 6 5 -1.</_>\n        <_>\n          9 12 2 5 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 17 5 2 -1.</_>\n        <_>\n          12 18 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 11 6 3 -1.</_>\n        <_>\n          4 11 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 9 6 3 -1.</_>\n        <_>\n          4 9 3 3 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 7 2 12 -1.</_>\n        <_>\n          12 13 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 7 5 3 -1.</_>\n        <_>\n          8 8 5 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 2 12 -1.</_>\n        <_>\n          6 13 2 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 2 9 18 -1.</_>\n        <_>\n          4 2 3 18 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 17 5 2 -1.</_>\n        <_>\n          12 18 5 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 12 2 -1.</_>\n        <_>\n          4 7 6 2 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 7 6 1 -1.</_>\n        <_>\n          8 7 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 3 3 2 -1.</_>\n        <_>\n          8 3 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 4 3 1 -1.</_>\n        <_>\n          10 4 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          11 11 3 1 -1.</_>\n        <_>\n          12 11 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          8 4 3 1 -1.</_>\n        <_>\n          9 4 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 11 3 1 -1.</_>\n        <_>\n          7 11 1 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 13 6 6 -1.</_>\n        <_>\n          12 15 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          14 13 1 6 -1.</_>\n        <_>\n          14 15 1 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 13 6 6 -1.</_>\n        <_>\n          2 15 6 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 5 18 1 -1.</_>\n        <_>\n          7 5 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 12 2 -1.</_>\n        <_>\n          10 7 6 1 2.</_>\n        <_>\n          4 8 6 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 1 8 10 -1.</_>\n        <_>\n          10 1 4 5 2.</_>\n        <_>\n          6 6 4 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          3 13 4 3 -1.</_>\n        <_>\n          3 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 13 4 3 -1.</_>\n        <_>\n          6 14 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 14 4 3 -1.</_>\n        <_>\n          9 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 9 2 3 -1.</_>\n        <_>\n          12 10 2 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 14 4 3 -1.</_>\n        <_>\n          7 15 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 0 2 1 -1.</_>\n        <_>\n          10 0 1 1 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 0 10 5 -1.</_>\n        <_>\n          5 0 5 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 8 7 -1.</_>\n        <_>\n          6 6 4 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 0 10 5 -1.</_>\n        <_>\n          10 0 5 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          6 6 8 7 -1.</_>\n        <_>\n          10 6 4 7 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 9 10 8 -1.</_>\n        <_>\n          10 9 5 4 2.</_>\n        <_>\n          5 13 5 4 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 0 4 10 -1.</_>\n        <_>\n          12 0 2 5 2.</_>\n        <_>\n          10 5 2 5 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          1 4 8 3 -1.</_>\n        <_>\n          1 5 8 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 4 8 3 -1.</_>\n        <_>\n          4 5 8 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          9 7 4 3 -1.</_>\n        <_>\n          9 8 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          12 8 3 12 -1.</_>\n        <_>\n          12 14 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          7 7 4 3 -1.</_>\n        <_>\n          7 8 4 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 8 3 12 -1.</_>\n        <_>\n          5 14 3 6 2.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          10 0 7 6 -1.</_>\n        <_>\n          10 2 7 2 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          2 1 18 1 -1.</_>\n        <_>\n          8 1 6 1 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          5 0 3 8 -1.</_>\n        <_>\n          6 0 1 8 3.</_></rects></_>\n    <_>\n      <rects>\n        <_>\n          4 7 4 2 -1.</_>\n        <_>\n          4 8 4 1 2.</_></rects></_></features></cascade>\n</opencv_storage>\n"
  },
  {
    "path": "samples/pom.xml",
    "content": "<project>\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>org.bytedeco.javacv</groupId>\n    <artifactId>demo</artifactId>\n    <version>1.5.13</version>\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>1.8</maven.compiler.target>\n    </properties>\n    <dependencies>\n        <dependency>\n            <groupId>org.bytedeco</groupId>\n            <artifactId>javacv-platform</artifactId>\n            <version>1.5.13</version>\n        </dependency>\n\n        <!-- Additional dependencies required to use CUDA and cuDNN -->\n        <dependency>\n            <groupId>org.bytedeco</groupId>\n            <artifactId>opencv-platform-gpu</artifactId>\n            <version>4.13.0-1.5.13</version>\n        </dependency>\n\n        <!-- Optional GPL builds with (almost) everything enabled -->\n        <dependency>\n            <groupId>org.bytedeco</groupId>\n            <artifactId>ffmpeg-platform-gpl</artifactId>\n            <version>8.0.1-1.5.13</version>\n        </dependency>\n    </dependencies>\n    <build>\n        <sourceDirectory>.</sourceDirectory>\n        <plugins>\n          <plugin>\n            <artifactId>maven-compiler-plugin</artifactId>\n            <configuration>\n              <excludes>\n                <exclude>FaceApplet.java</exclude>\n                <exclude>FacePreview.java</exclude>\n                <exclude>RecordActivity.java</exclude>\n              </excludes>\n            </configuration>\n          </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "src/main/java/cl/eye/CLCamera.java",
    "content": "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r\n//\r\n// This file is part of CL-EyeMulticam SDK\r\n//\r\n// Java JNI CLEyeMulticam wrapper\r\n//\r\n// It allows the use of multiple CL-Eye cameras in your own Java applications\r\n//\r\n// For updates and file downloads go to: http://codelaboratories.com/research/view/cl-eye-muticamera-sdk\r\n//\r\n// Copyright 2008-2012 (c) Code Laboratories, Inc. All rights reserved.\r\n//\r\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r\npackage cl.eye;\r\n//import processing.core.*;\r\n\r\npublic class CLCamera\r\n{\r\n    // camera color mode\r\n    public static int CLEYE_MONO_PROCESSED\t= 0;\r\n    public static int CLEYE_COLOR_PROCESSED\t= 1;\r\n    public static int CLEYE_MONO_RAW            = 2;\r\n    public static int CLEYE_COLOR_RAW           = 3;\r\n    public static int CLEYE_BAYER_RAW           = 4;\r\n\r\n    // camera resolution\r\n    public static int CLEYE_QVGA\t\t= 0;\r\n    public static int CLEYE_VGA\t\t\t= 1;\r\n\r\n    // camera sensor parameters\r\n    public static int CLEYE_AUTO_GAIN \t\t= 0;  \t// [0, 1]\r\n    public static int CLEYE_GAIN\t\t= 1;\t// [0, 79]\r\n    public static int CLEYE_AUTO_EXPOSURE\t= 2;    // [0, 1]\r\n    public static int CLEYE_EXPOSURE\t\t= 3;    // [0, 511]\r\n    public static int CLEYE_AUTO_WHITEBALANCE\t= 4;\t// [0, 1]\r\n    public static int CLEYE_WHITEBALANCE_RED\t= 5;\t// [0, 255]\r\n    public static int CLEYE_WHITEBALANCE_GREEN\t= 6;   \t// [0, 255]\r\n    public static int CLEYE_WHITEBALANCE_BLUE\t= 7;    // [0, 255]\r\n    // camera linear transform parameters\r\n    public static int CLEYE_HFLIP\t\t= 8;    // [0, 1]\r\n    public static int CLEYE_VFLIP\t\t= 9;    // [0, 1]\r\n    public static int CLEYE_HKEYSTONE\t\t= 10;   // [-500, 500]\r\n    public static int CLEYE_VKEYSTONE\t\t= 11;   // [-500, 500]\r\n    public static int CLEYE_XOFFSET\t\t= 12;   // [-500, 500]\r\n    public static int CLEYE_YOFFSET\t\t= 13;   // [-500, 500]\r\n    public static int CLEYE_ROTATION\t\t= 14;   // [-500, 500]\r\n    public static int CLEYE_ZOOM\t\t= 15;   // [-500, 500]\r\n    // camera non-linear transform parameters\r\n    public static int CLEYE_LENSCORRECTION1\t= 16;\t// [-500, 500]\r\n    public static int CLEYE_LENSCORRECTION2\t= 17;\t// [-500, 500]\r\n    public static int CLEYE_LENSCORRECTION3\t= 18;\t// [-500, 500]\r\n    public static int CLEYE_LENSBRIGHTNESS\t= 19;\t// [-500, 500]\r\n\r\n    native static int CLEyeGetCameraCount();\r\n    native static String CLEyeGetCameraUUID(int index);\r\n    native static int CLEyeCreateCamera(int cameraIndex, int mode, int resolution, int framerate);\r\n    native static boolean CLEyeDestroyCamera(int cameraIndex);\r\n    native static boolean CLEyeCameraStart(int cameraInstance);\r\n    native static boolean CLEyeCameraStop(int cameraInstance);\r\n    native static boolean CLEyeSetCameraParameter(int cameraInstance, int param, int val);\r\n    native static int CLEyeGetCameraParameter(int cameraInstance, int param);\r\n    native static boolean CLEyeCameraGetFrame(int cameraInstance, int[] imgData, int waitTimeout);\r\n\r\n    private int cameraInstance = 0;\r\n//    private PApplet parent;\r\n    private static boolean libraryLoaded = false;\r\n    private static String dllpathx32 = \"C://Program Files//Code Laboratories//CL-Eye Platform SDK//Bin//CLEyeMulticam.dll\";\r\n    private static String dllpathx64 = \"C://Program Files (x86)//Code Laboratories//CL-Eye Platform SDK//Bin//CLEyeMulticam.dll\";\r\n\r\n    // static methods\r\n    static\r\n    {\r\n        try\r\n        {\r\n            System.load(dllpathx32);\r\n            libraryLoaded = true;\r\n            System.out.println(\"CLEyeMulticam.dll loaded\");\r\n        }\r\n        catch(UnsatisfiedLinkError e1)\r\n        {\r\n            System.out.println(\"(1) Could not find the CLEyeMulticam.dll\");\r\n            try\r\n            {\r\n                System.load(dllpathx64);\r\n                libraryLoaded = true;\r\n                System.out.println(\"CLEyeMulticam.dll loaded\");\r\n            }\r\n            catch(UnsatisfiedLinkError e2)\r\n            {\r\n                System.out.println(\"(2) Could not find the CLEyeMulticam.dll\");\r\n            }\r\n        }\r\n    }\r\n    public static boolean IsLibraryLoaded()\r\n    {\r\n        return libraryLoaded;\r\n    }\r\n    public static void loadLibrary(String libraryPath)\r\n    {\r\n        if(libraryLoaded)   return;\r\n        try\r\n        {\r\n            System.load(libraryPath);\r\n            System.out.println(\"CLEyeMulticam.dll loaded\");\r\n        }\r\n        catch(UnsatisfiedLinkError e1)\r\n        {\r\n            System.out.println(\"(3) Could not find the CLEyeMulticam.dll (Custom Path)\");\r\n        }\r\n    }\r\n    public static int cameraCount()\r\n    {\r\n        return CLEyeGetCameraCount();\r\n    }\r\n    public static String cameraUUID(int index)\r\n    {\r\n        return CLEyeGetCameraUUID(index);\r\n    }\r\n    // public methods\r\n//    public CLCamera(PApplet parent)\r\n//    {\r\n//        this.parent = parent;\r\n//        parent.registerDispose(this);\r\n//    }\r\n    public void dispose()\r\n    {\r\n        stopCamera();\r\n        destroyCamera();\r\n    }\r\n    public boolean createCamera(int cameraIndex, int mode, int resolution, int framerate)\r\n    {\r\n        cameraInstance = CLEyeCreateCamera(cameraIndex, mode, resolution, framerate);\r\n        return cameraInstance != 0;\r\n    }\r\n    public boolean destroyCamera()\r\n    {\r\n        return CLEyeDestroyCamera(cameraInstance);\r\n    }\r\n    public boolean startCamera()\r\n    {\r\n        return CLEyeCameraStart(cameraInstance);\r\n    }\r\n    public boolean stopCamera()\r\n    {\r\n        return CLEyeCameraStop(cameraInstance);\r\n    }\r\n    public boolean getCameraFrame(int[] imgData, int waitTimeout)\r\n    {\r\n        return CLEyeCameraGetFrame(cameraInstance, imgData, waitTimeout);\r\n    }\r\n    public boolean setCameraParam(int param, int val)\r\n    {\r\n        return CLEyeSetCameraParameter(cameraInstance, param, val);\r\n    }\r\n    public int getCameraParam(int param)\r\n    {\r\n        return CLEyeGetCameraParameter(cameraInstance, param);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/AndroidFrameConverter.java",
    "content": "/*\n * Copyright (C) 2015-2016 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport android.graphics.Bitmap;\nimport android.hardware.Camera;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\n\n/**\n * A utility class to copy data between {@link Frame} and {@link Bitmap}.\n * Since {@link Bitmap} does not expose its internal buffer, we cannot share\n * allocated memory with {@link Frame}.\n * <p>\n * This class is not optimized for speed. For best performance, convert first\n * your data to and from RGBA with optimized functions from FFmpeg or OpenCV.\n * Further, pixel formats other than grayscale, BGR, and RGBA are not well\n * supported. Their conversions might fail in undefined ways.\n *\n * @author Samuel Audet\n */\npublic class AndroidFrameConverter extends FrameConverter<Bitmap> {\n    Bitmap bitmap;\n    ByteBuffer buffer;\n    byte[] row;\n\n    /**\n     * Convert YUV 4:2:0 SP (NV21) data to BGR, as received, for example,\n     * via {@link Camera.PreviewCallback#onPreviewFrame(byte[],Camera)}.\n     */\n    public Frame convert(byte[] data, int width, int height) {\n        if (frame == null || frame.imageWidth != width\n                || frame.imageHeight != height || frame.imageChannels != 3) {\n            if (frame != null) {\n                frame.close();\n            }\n            frame = new Frame(width, height, Frame.DEPTH_UBYTE, 3);\n        }\n        ByteBuffer out = (ByteBuffer)frame.image[0];\n        int stride = frame.imageStride;\n\n        // ported from https://android.googlesource.com/platform/development/+/master/tools/yuv420sp2rgb/yuv420sp2rgb.c\n        int offset = height * width;\n        for (int i = 0; i < height; i++) {\n            for (int j = 0; j < width; j++) {\n                int Y = data[i * width + j] & 0xFF;\n                int V = data[offset + (i/2) * width + 2 * (j/2)    ] & 0xFF;\n                int U = data[offset + (i/2) * width + 2 * (j/2) + 1] & 0xFF;\n\n                // Yuv Convert\n                Y -= 16;\n                U -= 128;\n                V -= 128;\n\n                if (Y < 0)\n                    Y = 0;\n\n                // R = (int)(1.164 * Y + 2.018 * U);\n                // G = (int)(1.164 * Y - 0.813 * V - 0.391 * U);\n                // B = (int)(1.164 * Y + 1.596 * V);\n\n                int B = (int)(1192 * Y + 2066 * U);\n                int G = (int)(1192 * Y - 833 * V - 400 * U);\n                int R = (int)(1192 * Y + 1634 * V);\n\n                R = Math.min(262143, Math.max(0, R));\n                G = Math.min(262143, Math.max(0, G));\n                B = Math.min(262143, Math.max(0, B));\n\n                R >>= 10; R &= 0xff;\n                G >>= 10; G &= 0xff;\n                B >>= 10; B &= 0xff;\n\n                out.put(i * stride + 3 * j,     (byte)B);\n                out.put(i * stride + 3 * j + 1, (byte)G);\n                out.put(i * stride + 3 * j + 2, (byte)R);\n            }\n        }\n        return frame;\n    }\n\n    @Override public Frame convert(Bitmap bitmap) {\n        if (bitmap == null) {\n            return null;\n        }\n\n        int channels = 0;\n        switch (bitmap.getConfig()) {\n            case ALPHA_8:   channels = 1; break;\n            case RGB_565:\n            case ARGB_4444: channels = 2; break;\n            case ARGB_8888: channels = 4; break;\n            default: assert false;\n        }\n\n        if (frame == null || frame.imageWidth != bitmap.getWidth() || frame.imageStride != bitmap.getRowBytes()\n                || frame.imageHeight != bitmap.getHeight() || frame.imageChannels != channels) {\n            if (frame != null) {\n                frame.close();\n            }\n            frame = new Frame(bitmap.getWidth(), bitmap.getHeight(), Frame.DEPTH_UBYTE, channels, bitmap.getRowBytes());\n        }\n\n        bitmap.copyPixelsToBuffer(frame.image[0].position(0));\n\n        return frame;\n    }\n\n    ByteBuffer gray2rgba(ByteBuffer in, int width, int height, int stride, int rowBytes) {\n        if (buffer == null || buffer.capacity() < height * rowBytes) {\n            buffer = ByteBuffer.allocate(height * rowBytes);\n        }\n        if (row == null || row.length != stride)\n            row = new byte[stride];\n        for (int y = 0; y < height; y++) {\n            in.position(y * stride);\n            in.get(row);\n            for (int x = 0; x < width; x++) {\n                // GRAY -> RGBA\n                byte B = row[x];\n                int rgba = (B & 0xff) << 24 |\n                           (B & 0xff) << 16 |\n                           (B & 0xff) <<  8 | 0xff;\n                buffer.putInt(y * rowBytes + 4 * x, rgba);\n            }\n        }\n        return buffer;\n    }\n\n    ByteBuffer bgr2rgba(ByteBuffer in, int width, int height, int stride, int rowBytes) {\n        if (!in.order().equals(ByteOrder.LITTLE_ENDIAN)) {\n            in = in.order(ByteOrder.LITTLE_ENDIAN);\n        }\n        if (buffer == null || buffer.capacity() < height * rowBytes) {\n            buffer = ByteBuffer.allocate(height * rowBytes);\n        }\n        for (int y = 0; y < height; y++) {\n            for (int x = 0; x < width; x++) {\n                // BGR -> RGBA\n                int rgb;\n                if (x < width - 1 || y < height - 1) {\n                    rgb = in.getInt(y * stride + 3 * x);\n                } else {\n                    int b = in.get(y * stride + 3 * x    ) & 0xff;\n                    int g = in.get(y * stride + 3 * x + 1) & 0xff;\n                    int r = in.get(y * stride + 3 * x + 2) & 0xff;\n                    rgb = (r << 16) | (g << 8) | b;\n                }\n                buffer.putInt(y * rowBytes + 4 * x, (rgb << 8) | 0xff);\n            }\n        }\n        return buffer;\n    }\n\n    @Override public Bitmap convert(Frame frame) {\n        if (frame == null || frame.image == null) {\n            return null;\n        }\n\n        Bitmap.Config config = null;\n        switch (frame.imageChannels) {\n            case 2: config = Bitmap.Config.RGB_565; break;\n            case 1:\n            case 3:\n            case 4: config = Bitmap.Config.ARGB_8888; break;\n            default: assert false;\n        }\n\n        if (bitmap == null || bitmap.getWidth() != frame.imageWidth\n                || bitmap.getHeight() != frame.imageHeight || bitmap.getConfig() != config) {\n            bitmap = Bitmap.createBitmap(frame.imageWidth, frame.imageHeight, config);\n        }\n\n        // assume frame.imageDepth == Frame.DEPTH_UBYTE\n        ByteBuffer in = (ByteBuffer)frame.image[0];\n        int width = frame.imageWidth;\n        int height = frame.imageHeight;\n        int stride = frame.imageStride;\n        int rowBytes = bitmap.getRowBytes();\n        if (frame.imageChannels == 1) {\n            gray2rgba(in, width, height, stride, rowBytes);\n            bitmap.copyPixelsFromBuffer(buffer.position(0));\n        } else if (frame.imageChannels == 3) {\n            bgr2rgba(in, width, height, stride, rowBytes);\n            bitmap.copyPixelsFromBuffer(buffer.position(0));\n        } else {\n            // assume matching strides\n            bitmap.copyPixelsFromBuffer(in.position(0));\n        }\n        return bitmap;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/BaseChildSettings.java",
    "content": "/*\n * Copyright (C) 2009-2011 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.beans.PropertyChangeEvent;\nimport java.beans.PropertyChangeListener;\nimport java.beans.PropertyVetoException;\nimport java.beans.beancontext.BeanContextChildSupport;\nimport java.util.ListResourceBundle;\nimport java.util.concurrent.Callable;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class BaseChildSettings extends BeanContextChildSupport implements Comparable<BaseChildSettings> {\n\n    public void addPropertyChangeListener(PropertyChangeListener listener) {\n        pcSupport.addPropertyChangeListener(listener);\n    }\n    public void removePropertyChangeListener(PropertyChangeListener listener) {\n        pcSupport.removePropertyChangeListener(listener);\n    }\n\n    public int compareTo(BaseChildSettings o) {\n        return getName().compareTo(o.getName());\n    }\n\n    protected String getName() {\n        return \"\";\n    }\n\n    public static class PropertyVetoExceptionThatNetBeansLikes extends PropertyVetoException implements Callable {\n        public PropertyVetoExceptionThatNetBeansLikes(String mess, PropertyChangeEvent evt)  {\n            super(mess, evt);\n        }\n        public Object call() throws Exception {\n            LogRecord lg = new LogRecord(Level.ALL, getMessage());\n            lg.setResourceBundle(new ListResourceBundle() {\n                protected Object[][] getContents() {\n                    return new Object[][] { {getMessage(), getMessage()} };\n                }\n            });\n            return new LogRecord[] { lg };\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/BaseSettings.java",
    "content": "/*\n * Copyright (C) 2009-2011 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.beans.PropertyChangeListener;\nimport java.beans.beancontext.BeanContextSupport;\nimport java.util.Arrays;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class BaseSettings extends BeanContextSupport implements Comparable<BaseSettings> {\n\n    public void addPropertyChangeListener(PropertyChangeListener listener) {\n        pcSupport.addPropertyChangeListener(listener);\n        for (Object s : toArray()) {\n            if (s instanceof BaseChildSettings) {\n                ((BaseChildSettings)s).addPropertyChangeListener(listener);\n            } else if (s instanceof BaseSettings) {\n                ((BaseSettings)s).addPropertyChangeListener(listener);\n            }\n        }\n    }\n    public void removePropertyChangeListener(PropertyChangeListener listener) {\n        pcSupport.removePropertyChangeListener(listener);\n        for (Object s : toArray()) {\n            if (s instanceof BaseChildSettings) {\n                ((BaseChildSettings)s).removePropertyChangeListener(listener);\n            } else if (s instanceof BaseSettings) {\n                ((BaseSettings)s).addPropertyChangeListener(listener);\n            }\n        }\n    }\n\n    public int compareTo(BaseSettings o) {\n        return getName().compareTo(o.getName());\n    }\n\n    protected String getName() {\n        return \"\";\n    }\n\n    @Override public Object[] toArray() {\n        Object[] a = super.toArray();\n        Arrays.sort(a);\n        return a;\n    }\n    @Override public Object[] toArray(Object[] a) {\n        a = super.toArray(a);\n        Arrays.sort(a);\n        return a;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/Blobs.java",
    "content": "package org.bytedeco.javacv;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n//***************************************************************//\n//* Blob analysis package  Version3.0 3 Oct 2012                *//\n//* - Version 1.0:  8 Aug 2003                                  *//\n//* - Version 1.2:  3 Jan 2008                                  *//\n//* - Version 1.3:  5 Jan 2008 Add BLOBCOLOR                    *//\n//* - Version 1.4: 13 January 2008 Add ROI function             *//\n//* - Version 1.5: 13 April 2008 Fix perimeter on Region 0      *//\n//* - Version 1.6:  1 May 2008 Reduce size of working storage   *//\n//* - Version 1.7:  2 May 2008 Speed up run code initialization *//\n//* - Version 1.8:  4 May 2008 Fix bugs in perimeter & Reg 0    *//\n//* - Version 2.0:  3 Jan 2009 Add labeling functionality       *//\n//* - Version 3.0:  3 Oct 2012 Convert to Java                  *//\n//* -   Eliminate labeling functionality (but it's still there) *//\n//* -   Simplify (at slight expense of performance)             *//\n//* -   Reduce to 4 connectivity                                *//\n//*                                                             *//\n//* Input: IplImage binary image                                *//\n//* Output: attributes of each connected region                 *//\n//* Internal data: labeled array (could easily be externalized) *//\n//* Author: Dave Grossman                                       *//\n//* Email: dgrossman2@gmail.com                                 *//\n//* Acknowledgement: my code is based on an algorithm that was  *//\n//* to the best of my knowledge originally developed by Gerry   *//\n//* Agin of SRI around the year 1973. I have not been able to   *//\n//* find any published references to his earlier work. I posted *//\n//* early versions of my program to OpenCV, where they morphed  *//\n//* eventually into cvBlobsLib.                                 *//\n//*                                                             *//\n//* As the author of this code, I place all of this code into   *//\n//* the public domain. Users can use it for any legal purpose.  *//\n//*                                                             *//\n//*             - Dave Grossman                                 *//\n//*                                                             *//\n//* Typical calling sequence:                                   *//\n//*     Blobs Blob = new Blobs();                               *//\n//*     Blob.BlobAnalysis(                                      *//\n//*         image3, // image                                    *//\n//*         -1, -1, // ROI start col, row (-1 means full image) *//\n//*         -1, -1, // ROI cols, rows                           *//\n//*         0,      // border (0 = black; 1 = white)            *//\n//*         20);    // minarea                                  *//\n//*     Blob.PrintRegionData();                                 *//\n//*     int BlobLabel = Blob.NextRegion(                        *//\n//*         -1,     // parentcolor (-1 = ignore)                *//\n//*         0,      // color (0 = black; 1 = white; -1 = ignore *//\n//*         100,    // minarea                                  *//\n//*         500,    // maxarea                                  *//\n//*         15);    // starting label (default 0)               *//\n//*                                                             *//\n//* Ellipse properties can be derived from moments:             *//\n//*     h = (XX + YY) / 2                                       *//\n//*     Major axis = h + sqrt ( h^2 - XX * YY + XY^2)           *//\n//*     Minor axis = h - sqrt ( h^2 - XX * YY2 + XY^2)          *//\n//*     Eccentricity = (sqrt(abs(XX - YY)) + 4 * XY)/AREA       *//\n//***************************************************************//\n\npublic class Blobs\n{\n    // The following parameters should be configured by the user:\n    // On ScanSnap Manager, \"Best\" setting = 300dpi gray level\n    // jpg compression is set to minimum so that quality is highest\n    // Each page jpg image is then a little under 1 MB \n    static int BLOBROWCOUNT = 3500; // 11 inches * 8.5 inches standard page\n    static int BLOBCOLCOUNT = 2700; // with some added cushion to be safe\n    \n    // Allow for vast number of blobs so there is no memory overrun\n    static int BLOBTOTALCOUNT = (BLOBROWCOUNT + BLOBCOLCOUNT) * 5;\n\n    //--------------------------------------------------------------\n    // Do not change anything below this line\n    public static int BLOBLABEL = 0;\n    public static int BLOBPARENT = 1;\n    public static int BLOBCOLOR = 2;\n    public static int BLOBAREA = 3;\n    public static int BLOBPERIMETER = 4;\n    public static int BLOBSUMX = 5;\n    public static int BLOBSUMY = 6;\n    public static int BLOBSUMXX = 7;\n    public static int BLOBSUMYY = 8;\n    public static int BLOBSUMXY = 9;\n    public static int BLOBMINX = 10;\n    public static int BLOBMAXX = 11;\n    public static int BLOBMINY = 12;\n    public static int BLOBMAXY = 13;\n    public static int BLOBDATACOUNT = 14; \n\n    public static int [][] LabelMat = new int [BLOBROWCOUNT][BLOBCOLCOUNT];\n    public static double [][] RegionData = new double [BLOBTOTALCOUNT][BLOBDATACOUNT];\n    public static int MaxLabel; \n    \n    public int LabelA, LabelB, LabelC, LabelD;\n    public int ColorA, ColorB, ColorC, ColorD;\n    public int jrow, jcol;  // index within ROI\n    public static int [] SubsumedLabel = new int [BLOBTOTALCOUNT];\n    public static int [] CondensationMap = new int [BLOBTOTALCOUNT];\n    \n    // Print out all the data for all the regions (blobs)\n    public void PrintRegionData() { PrintRegionData(0, MaxLabel); }\n    public void PrintRegionData(int Label0, int Label1)\n    {\n        if(Label0 < 0) Label0 = 0;\n        if(Label1 > MaxLabel) Label1 = MaxLabel;\n        if(Label1 < Label0) return;\n        for(int Label = Label0; Label <= Label1; Label++)\n        {\n            double [] Property = RegionData[Label];\n            \n            int ThisLabel = (int)Property[BLOBLABEL];\n            int ThisParent = (int)Property[BLOBPARENT];\n            int ThisColor = (int)Property[BLOBCOLOR];\n            double ThisArea = Property[BLOBAREA];\n            double ThisPerimeter = Property[BLOBPERIMETER];\n            double ThisSumX = Property[BLOBSUMX];\n            double ThisSumY = Property[BLOBSUMY];\n            double ThisSumXX = Property[BLOBSUMXX];\n            double ThisSumYY = Property[BLOBSUMYY];\n            double ThisSumXY = Property[BLOBSUMXY];\n            int ThisMinX = (int)Property[BLOBMINX];\n            int ThisMaxX = (int)Property[BLOBMAXX];\n            int ThisMinY = (int)Property[BLOBMINY];\n            int ThisMaxY = (int)Property[BLOBMAXY];\n            \n            String Str1 = \" \" + Label + \": L[\" + ThisLabel + \"] P[\" + ThisParent + \"] C[\" + ThisColor + \"]\";\n            String Str2 = \" AP[\" + ThisArea + \", \" + ThisPerimeter + \"]\";\n            String Str3 = \" M1[\" + ThisSumX + \", \" + ThisSumY + \"] M2[\" + ThisSumXX + \", \" + ThisSumYY + \", \" + ThisSumXY + \"]\";\n            String Str4 = \" MINMAX[\" + ThisMinX + \", \" + ThisMaxX + \", \" + ThisMinY + \", \" + ThisMaxY + \"]\";\n            \n            String Str = Str1 + Str2 + Str3 + Str4;\n            System.out.println(Str);\n        }\n        System.out.println();\n    }\n\n    // Determine the next (higher number) region that meets the desired conditions\n    public static int NextRegion(int Parent, int Color, double MinArea, double MaxArea, int Label)\n    {\n        double DParent = (double) Parent; \n        double DColor = (double) Color; if(DColor > 0) DColor = 1;\n        \n        int i;\n        for(i = Label; i <= MaxLabel; i++)\n        {\n            double [] Region = RegionData[i];\n            double ThisParent = Region[BLOBPARENT];\n            double ThisColor = Region[BLOBCOLOR];\n            if(DParent >= 0 && DParent != ThisParent) continue;\n            if(DColor >= 0 && DColor != ThisColor) continue;\n            if(Region[BLOBAREA] < MinArea || Region[BLOBAREA] > MaxArea) continue;  \n            break;      // We have a match!\n        }\n        if(i > MaxLabel) i = -1;    // Use -1 to flag that there was no match\n        return i;\n    }\n\n    // Determine the prior (lower number) region that meets the desired conditions\n    public static int PriorRegion(int Parent, int Color, double MinArea, double MaxArea, int Label)\n    {\n        double DParent = (double) Parent; \n        double DColor = (double) Color; if(DColor > 0) DColor = 1;\n        \n        int i;\n        for(i = Label; i >= 0; i--)\n        {\n            double [] Region = RegionData[i];\n            double ThisParent = Region[BLOBPARENT];\n            double ThisColor = Region[BLOBCOLOR];\n            if(DParent >= 0 && DParent != ThisParent) continue;\n            if(DColor >= 0 && DColor != ThisColor) continue;\n            if(Region[BLOBAREA] < MinArea || Region[BLOBAREA] > MaxArea) continue;  \n            break;      // We have a match!\n        }\n        if(i < 0) i = -1;   // Use -1 to flag that there was no match\n        return i;\n    }\n    \n    public void ResetRegion(int Label)\n    {\n        double [] RegionD = RegionData[Label];\n        RegionD[BLOBLABEL] = \n        RegionD[BLOBPARENT] = \n        RegionD[BLOBCOLOR] =\n        RegionD[BLOBAREA] =\n        RegionD[BLOBPERIMETER] =\n        RegionD[BLOBSUMX] =\n        RegionD[BLOBSUMY] = \n        RegionD[BLOBSUMXX] = \n        RegionD[BLOBSUMYY] = \n        RegionD[BLOBSUMXY] = \n        RegionD[BLOBMINX] = \n        RegionD[BLOBMAXX] = \n        RegionD[BLOBMINY] = \n        RegionD[BLOBMAXY] = 0.0;\n        System.arraycopy(RegionD,0,RegionData[Label],0,BLOBDATACOUNT);  // RegionData[Label] <- RegionD;\n    }\n    \n    public void OldRegion(\n            int NewLabelD,  // 3rd update this (may be the same as Label1 or Label2)\n            int Label1,     // 1st increment this by 1 \n            int Label2)     // 2nd increment this by 1\n    {\n        int DeltaPerimeter = 0;\n        \n        if(Label1 >= 0 && Label1 != NewLabelD)\n        {\n            DeltaPerimeter++;\n            double [] Region1 = RegionData[Label1];\n            Region1[BLOBPERIMETER]++;\n            System.arraycopy(Region1,0,RegionData[Label1],0,BLOBDATACOUNT); // RegionData[Label1] <- Region1;\n        }\n        \n        if(Label2 >= 0 && Label2 != NewLabelD)\n        {\n            DeltaPerimeter++;\n            double [] Region2 = RegionData[Label2];\n            Region2[BLOBPERIMETER]++;\n            System.arraycopy(Region2,0,RegionData[Label2],0,BLOBDATACOUNT); // RegionData[Label2] <- Region2;\n        }\n        \n        LabelD = NewLabelD;\n        double [] RegionD = RegionData[LabelD];\n        RegionD[BLOBLABEL] = LabelD;\n        RegionD[BLOBPARENT] += 0.0;     // no change\n        RegionD[BLOBCOLOR] += 0.0;      // no change\n        RegionD[BLOBAREA] += 1.0;\n        RegionD[BLOBPERIMETER] += DeltaPerimeter;\n        RegionD[BLOBSUMX] += jcol;\n        RegionD[BLOBSUMY] += jrow;\n        RegionD[BLOBSUMXX] += jcol*jcol;\n        RegionD[BLOBSUMYY] += jrow*jrow;\n        RegionD[BLOBSUMXY] += jcol*jrow;\n        RegionD[BLOBMINX] = Math.min(RegionD[BLOBMINX], jcol);\n        RegionD[BLOBMAXX] = Math.max(RegionD[BLOBMAXX], jcol);\n        RegionD[BLOBMINY] = Math.min(RegionD[BLOBMINY], jrow);\n        RegionD[BLOBMAXY] = Math.max(RegionD[BLOBMAXY], jrow);\n        System.arraycopy(RegionD,0,RegionData[LabelD],0,BLOBDATACOUNT); // RegionData[LabelD] <- RegionD;\n   }\n    \n    public void NewRegion(int ParentLabel)\n    {\n        LabelD = ++MaxLabel;\n        double [] RegionD = RegionData[LabelD];\n        RegionD[BLOBLABEL] = LabelD;\n        RegionD[BLOBPARENT] = (double) ParentLabel;\n        RegionD[BLOBCOLOR] = ColorD;\n        RegionD[BLOBAREA] = 1.0;\n        RegionD[BLOBPERIMETER] = 2.0;\n        RegionD[BLOBSUMX] = jcol;\n        RegionD[BLOBSUMY] = jrow;\n        RegionD[BLOBSUMXX] = jcol*jcol;\n        RegionD[BLOBSUMYY] = jrow*jrow;\n        RegionD[BLOBSUMXY] = jcol*jrow;\n        RegionD[BLOBMINX] = jcol;\n        RegionD[BLOBMAXX] = jcol;\n        RegionD[BLOBMINY] = jrow;\n        RegionD[BLOBMAXY] = jrow;\n\n        System.arraycopy(RegionD,0,RegionData[LabelD],0,BLOBDATACOUNT); // RegionData[LabelD] <- RegionD;\n        SubsumedLabel[LabelD] = -1;     // Flag label as not subsumed\n\n        double [] RegionB = RegionData[LabelB];\n        RegionB[BLOBPERIMETER]++;\n        System.arraycopy(RegionB,0,RegionData[LabelB],0,BLOBDATACOUNT); // RegionData[LabelB] <- RegionB;\n        \n        double [] RegionC = RegionData[LabelC];\n        RegionC[BLOBPERIMETER]++;\n\n        System.arraycopy(RegionC,0,RegionData[LabelC],0,BLOBDATACOUNT); // RegionData[LabelC] <- RegionC;\n    }\n    \n    public void Subsume(int GoodLabel, int BadLabel, int PSign) // Combine data with parent\n    {\n        LabelD = GoodLabel;\n        double [] GoodRegion = RegionData[GoodLabel];   \n        double [] BadRegion = RegionData[BadLabel];\n    \n        GoodRegion[BLOBLABEL] = GoodRegion[BLOBLABEL];      // no change\n        GoodRegion[BLOBPARENT] = GoodRegion[BLOBPARENT];    // no change\n        GoodRegion[BLOBCOLOR] = GoodRegion[BLOBCOLOR];      // no change\n        GoodRegion[BLOBAREA] += BadRegion[BLOBAREA];\n        GoodRegion[BLOBPERIMETER] += BadRegion[BLOBPERIMETER] * PSign;  // + external or - internal perimeter\n        GoodRegion[BLOBSUMX] += BadRegion[BLOBSUMX];\n        GoodRegion[BLOBSUMY] += BadRegion[BLOBSUMY];\n        GoodRegion[BLOBSUMXX] += BadRegion[BLOBSUMXX];\n        GoodRegion[BLOBSUMYY] += BadRegion[BLOBSUMYY];\n        GoodRegion[BLOBSUMXY] += BadRegion[BLOBSUMXY];\n        GoodRegion[BLOBMINX] = Math.min(GoodRegion[BLOBMINX], BadRegion[BLOBMINX]);\n        GoodRegion[BLOBMAXX] = Math.max(GoodRegion[BLOBMAXX], BadRegion[BLOBMAXX]);\n        GoodRegion[BLOBMINY] = Math.min(GoodRegion[BLOBMINY], BadRegion[BLOBMINY]);\n        GoodRegion[BLOBMAXY] = Math.max(GoodRegion[BLOBMAXY], BadRegion[BLOBMAXY]);\n        \n        System.arraycopy(GoodRegion,0,RegionData[GoodLabel],0,BLOBDATACOUNT);   // RegionData[GoodLabel] <- GoodRegion;\n    }\n\n    public static int SubsumptionChain(int x) { return SubsumptionChain(x, 0); }\n    public static int SubsumptionChain(int x, int Print)\n    {\n        String Str = \"\";\n        if(Print > 0) Str = \"Subsumption chain for \" + x + \": \";\n        int Lastx = x;\n        while(x > -1)\n        {\n            Lastx = x;\n            if(Print > 0) Str += \" \" + x;\n            if(x == 0) break;\n            x = SubsumedLabel[x];\n        }\n        if(Print > 0) System.out.println(Str);\n        return Lastx;\n    }\n\n    //---------------------------------------------------------------------------------------\n    // Main blob analysis routine\n    //---------------------------------------------------------------------------------------\n    // RegionData[0] is the border. It has Property[BLOBPARENT] = 0. \n\n    public int BlobAnalysis(IplImage Src,           // input image\n                int Col0, int Row0,                 // start of ROI\n                int Cols, int Rows,                 // size of ROI\n                int Border,                         // border color (0 = black; 1 = white)\n                int MinArea)                        // minimum region area\n    {\n        CvMat SrcMat = Src.asCvMat();\n        int SrcCols = SrcMat.cols();\n        int SrcRows = SrcMat.rows();\n        \n        if(Col0 < 0) Col0 = 0;\n        if(Row0 < 0) Row0 = 0;\n        if(Cols < 0) Cols = SrcCols;\n        if(Rows < 0) Rows = SrcRows;\n        if(Col0 + Cols > SrcCols) Cols = SrcCols - Col0;\n        if(Row0 + Rows > SrcRows) Rows = SrcRows - Row0;\n\n        if(Cols > BLOBCOLCOUNT || Rows > BLOBROWCOUNT )\n        {\n            System.out.println(\"Error in Class Blobs: Image too large: Edit Blobs.java\");\n            System.exit(666);\n            return 0;\n        }\n        \n        // Initialization\n        int FillLabel = 0;\n        int FillColor = 0; if(Border > 0) { FillColor = 1; }\n        LabelA = LabelB = LabelC = LabelD = 0;\n        ColorA = ColorB = ColorC = ColorD = FillColor;\n        for(int k = 0; k < BLOBTOTALCOUNT; k++) SubsumedLabel[k] = -1;\n        \n        // Initialize border region\n        MaxLabel = 0;\n        double [] BorderRegion = RegionData[0];\n        BorderRegion[BLOBLABEL] = 0.0;\n        BorderRegion[BLOBPARENT] = -1.0;\n        BorderRegion[BLOBAREA] = Rows + Cols + 4;   // Top, left, and 4 corners\n        BorderRegion[BLOBCOLOR] = FillColor;\n        BorderRegion[BLOBSUMX] = 0.5 * ( (2.0 + Cols) * (Cols - 1.0) ) - Rows - 1 ;\n        BorderRegion[BLOBSUMY] = 0.5 * ( (2.0 + Rows) * (Rows - 1.0) ) - Cols - 1 ;\n        BorderRegion[BLOBMINX] = -1;\n        BorderRegion[BLOBMINY] = -1;\n        BorderRegion[BLOBMAXX] = Cols + 1.0;\n        BorderRegion[BLOBMAXY] = Rows + 1.0;\n        System.arraycopy(BorderRegion,0,RegionData[0],0,BLOBDATACOUNT); // RegionData[0] <- BorderRegion;\n        \n        //  The cells are identified this way\n        //          Last |AB|\n        //          This |CD|\n        //\n        // With 4 connectivity, there are 8 possibilities for the cells:\n        //                      No color transition     Color transition\n        //          Case              1  2  3  4          5  6  7  8 \n        //          Last Row        |pp|pp|pq|pq|       |pp|pp|pq|pq|   \n        //          This Row        |pP|qQ|pP|qQ|       |pQ|qP|pQ|qP|\n        //\n        // Region numbers are p, q, r, x; where p<>q\n        // Upper case letter is the current element at column=x row=y\n        // Color is 0 or 1      (1 stands for 255 in the actual image)\n        // Note that Case 4 is complicated because it joins two regions\n        //--------------------------\n        // Case 1: Colors A=B; C=D; A=C     \n        // Case 2: Colors A=B; C=D; A<>C    \n        // Case 3: Colors A<>B;C=D; A=C     \n        // Case 4: Colors A<>B;C=D; A<>C    \n        // Case 5: Colors A=B; C<>D; A=C    \n        // Case 6: Colors A=B; C<>D; A<>C   \n        // Case 7: Colors A<>B;C<>D; A=C    \n        // Case 8: Colors A<>B;C<>D; A<>C   \n        //--------------------------\n                    \n        // Loop over rows of ROI. irow = Row0 is 1st row of image; irow = Row0+Row is last row of image.\n        for(int irow = Row0; irow < Row0+Rows; irow++)  // index within Src\n        {\n            jrow = irow - Row0; // index within ROI. 0 is first row. Rows is last row.\n            \n            // Loop over columns of ROI.\n            for(int icol = Col0; icol < Col0+Cols; icol++)  // index within Src\n            {\n                jcol = icol - Col0; // index within ROI\n \n                // initialize\n                ColorA = ColorB = ColorC = FillColor;\n                LabelA = LabelB = LabelC = LabelD = 0;\n                ColorD = (int) SrcMat.get(jrow,jcol);       // fetch color of cell\n            \n                if(jrow == 0 || jcol == 0)  // first column or row\n                {\n                    if(jcol > 0)\n                    {\n                        ColorC = (int) SrcMat.get(jrow,jcol-1);\n                        LabelC = LabelMat[jrow][jcol-1];\n                    }\n                    if(jrow > 0)\n                    {\n                        ColorB = (int) SrcMat.get(jrow-1,jcol);\n                        LabelB = LabelMat[jrow-1][jcol];\n                    }\n                }\n                else\n                {\n                    ColorA = (int) SrcMat.get(jrow-1,jcol-1); if(ColorA > 0) ColorA = 1;\n                    ColorB = (int) SrcMat.get(jrow-1,jcol); if(ColorB > 0) ColorB = 1;\n                    ColorC = (int) SrcMat.get(jrow,jcol-1); if(ColorC > 0) ColorC = 1;\n                    LabelA = LabelMat[jrow-1][jcol-1];\n                    LabelB = LabelMat[jrow-1][jcol];\n                    LabelC = LabelMat[jrow][jcol-1];\n                }   \n                if(ColorA > 0) ColorA = 1;\n                if(ColorB > 0) ColorB = 1;\n                if(ColorC > 0) ColorC = 1;\n                if(ColorD > 0) ColorD = 1;\n                    \n                // Determine Case\n                int Case = 0;\n                if(ColorA == ColorB)\n                {\n                    if(ColorC == ColorD) { if(ColorA == ColorC) Case = 1; else Case = 2; }\n                    else { if(ColorA == ColorC) Case = 5; else Case = 6; }\n                }\n                else\n                {\n                    if(ColorC == ColorD) { if(ColorA == ColorC) Case = 3; else Case = 4; }\n                    else { if(ColorA == ColorC) Case = 7; else Case = 8; }\n                }\n\n                // Take appropriate action\n                if(Case == 1) { OldRegion(LabelC, -1, -1); }\n                else if(Case == 2 || Case == 3) { OldRegion(LabelC, LabelB, LabelC); }\n                else if(Case == 5 || Case == 8) // Isolated\n                {\n                    if((jrow == Rows || jcol == Cols) && ColorD == FillColor) { OldRegion(0, -1, -1); } // attached to border region 0\n                    else NewRegion(LabelB);\n                }\n                else if(Case == 6 || Case == 7) { OldRegion(LabelB, LabelB, LabelC); }\n                else            // Case 4 - The complicated situation\n                {\n                    int LabelBRoot = SubsumptionChain(LabelB); \n                    int LabelCRoot = SubsumptionChain(LabelC);\n                    int LabelRoot = Math.min(LabelBRoot, LabelCRoot);\n                    int LabelX;\n                    if(LabelBRoot < LabelCRoot) { OldRegion(LabelB, -1, -1); LabelX = LabelC; }\n                    else { OldRegion(LabelC, -1, -1); LabelX = LabelB; }\n                    int NextLabelX = LabelX;\n                    while(LabelRoot < LabelX)\n                    {\n                        NextLabelX = SubsumedLabel[LabelX];\n                        SubsumedLabel[LabelX] = LabelRoot;\n                        LabelX = NextLabelX;\n                    }\n                }\n                    \n                // Last column or row. Final corner was handled earlier in Cases 5 and 8.\n                if((jrow == Rows || jcol == Cols) && ColorD == FillColor)\n                {\n                    if(jcol < Cols)         // bottom row   \n                    {\n                        if(ColorC != FillColor)     // Subsume B chain to border region 0\n                        {\n                            int LabelRoot = SubsumptionChain(LabelB);\n                            SubsumedLabel[LabelRoot] = 0;\n                        }\n                    }\n                    else if(jrow < Rows)    // right column\n                    {\n                        if(ColorB != FillColor)     // Subsume C chain to border region 0\n                        {\n                            int LabelRoot = SubsumptionChain(LabelC);\n                            SubsumedLabel[LabelRoot] = 0;\n                        }\n                    }\n                    OldRegion(0, -1, -1);   // attached to border region 0\n                }\n\n                LabelMat[jrow][jcol] = LabelD;\n                    \n            }\n        }\n\n        // Compute Condensation map\n        int Offset = 0;\n        for(int Label = 1; Label <= MaxLabel; Label++)\n        {\n            if(SubsumedLabel[Label] > -1) Offset++;\n            CondensationMap[Label] = Label - Offset;\n        }\n\n        // Subsume regions that were flagged as connected; Perimeters add\n        for(int Label = 1; Label <= MaxLabel; Label++)\n        {\n            int BetterLabel = SubsumptionChain(Label);\n            if(BetterLabel != Label) Subsume(BetterLabel, Label, 1);\n        }   \n\n        // Condense subsumed regions\n        int NewMaxLabel = 0;\n        for(int OldLabel = 1; OldLabel <= MaxLabel; OldLabel++)\n        {\n            if(SubsumedLabel[OldLabel] < 0) // Renumber valid regions only\n            {\n                double [] OldRegion = RegionData[OldLabel];\n                int OldParent = (int) OldRegion[BLOBPARENT];\n                int NewLabel = CondensationMap[OldLabel];\n                int NewParent = SubsumptionChain(OldParent);\n                NewParent = CondensationMap[NewParent];\n                OldRegion[BLOBLABEL] = (double) NewLabel;\n                OldRegion[BLOBPARENT] = (double) NewParent;\n                System.arraycopy(OldRegion,0,RegionData[NewLabel],0,BLOBDATACOUNT); //RegionData[NewLabel] <- ThisRegion;\n                NewMaxLabel = NewLabel;\n            }\n        }\n    \n        // Zero out unneeded high labels\n        for(int Label = NewMaxLabel+1; Label <= MaxLabel; Label++) ResetRegion(Label);\n        MaxLabel = NewMaxLabel;\n        \n        // Flag for subsumption regions that have too small area\n        for(int Label = MaxLabel; Label > 0; Label--)\n        {\n            double [] ThisRegion = RegionData[Label];\n            int ThisArea = (int) ThisRegion[BLOBAREA];\n            if(ThisArea < MinArea)\n            {\n                int ThisParent = (int) ThisRegion[BLOBPARENT];\n                SubsumedLabel[Label] =  ThisParent;             // Flag this label as having been subsumed\n            }\n            else SubsumedLabel[Label] =  -1;\n        }\n        \n        // Compute Condensation map\n        Offset = 0;\n        for(int Label = 1; Label <= MaxLabel; Label++)\n        {\n            if(SubsumedLabel[Label] > -1) Offset++;\n            CondensationMap[Label] = Label - Offset;      \n        }\n\n        // Subsume regions that were flagged as enclosed; Perimeters subtract\n        for(int Label = 1; Label <= MaxLabel; Label++)\n        {\n            int BetterLabel = SubsumptionChain(Label);\n            if(BetterLabel != Label) Subsume(BetterLabel, Label, -1);\n        }   \n    \n        // Condense subsumed regions\n        for(int OldLabel = 1; OldLabel <= MaxLabel; OldLabel++)\n        {\n            if(SubsumedLabel[OldLabel] < 0) // Renumber valid regions only\n            {\n                double [] OldRegion = RegionData[OldLabel];\n                int OldParent = (int) OldRegion[BLOBPARENT];\n                int NewLabel = CondensationMap[OldLabel];\n                int NewParent = SubsumptionChain(OldParent);\n                NewParent = CondensationMap[NewParent];\n                OldRegion[BLOBLABEL] = (double) NewLabel;\n                OldRegion[BLOBPARENT] = (double) NewParent;\n                System.arraycopy(OldRegion,0,RegionData[NewLabel],0,BLOBDATACOUNT); //RegionData[NewLabel] <- ThisRegion;\n                NewMaxLabel = NewLabel;\n            }\n        }\n        \n        // Zero out unneeded high labels\n        for(int Label = NewMaxLabel+1; Label <= MaxLabel; Label++) ResetRegion(Label);\n        MaxLabel = NewMaxLabel;\n\n        // Normalize summation fields into moments \n        for(int Label = 0; Label <= MaxLabel; Label++)\n        {\n            double [] ThisRegion = RegionData[Label];\n            \n            // Extract fields\n            double Area = ThisRegion[BLOBAREA];\n            double SumX = ThisRegion[BLOBSUMX];\n            double SumY = ThisRegion[BLOBSUMY];\n            double SumXX = ThisRegion[BLOBSUMXX];\n            double SumYY = ThisRegion[BLOBSUMYY];\n            double SumXY = ThisRegion[BLOBSUMXY];\n            \n            // Get averages\n            SumX /= Area;\n            SumY /= Area;\n            SumXX /= Area;\n            SumYY /= Area;\n            SumXY /= Area;\n            \n            // Create moments\n            SumXX -= SumX * SumX;\n            SumYY -= SumY * SumY;\n            SumXY -= SumX * SumY;\n            if(SumXY > -1.0E-14 && SumXY < 1.0E-14) SumXY = (float) 0.0; // Eliminate roundoff error\n\n            ThisRegion[BLOBSUMX] = SumX;\n            ThisRegion[BLOBSUMY] = SumY;\n            ThisRegion[BLOBSUMXX] = SumXX;\n            ThisRegion[BLOBSUMYY] = SumYY;\n            ThisRegion[BLOBSUMXY] = SumXY;\n\n            System.arraycopy(ThisRegion,0,RegionData[Label],0,BLOBDATACOUNT);   // RegionData[Label] <- ThisRegion;\n        }\n    \n        // Adjust border region\n        BorderRegion = RegionData[0];\n        BorderRegion[BLOBSUMXX] = BorderRegion[BLOBSUMYY] = BorderRegion[BLOBSUMXY] = 0;    // Mark invalid fields\n        System.arraycopy(BorderRegion,0,RegionData[0],0,BLOBDATACOUNT); // RegionData[0] <- BorderRegion;\n        \n        return MaxLabel;\n    }\n    \n    // Sort RegionData array on any column. (I couldn't figure out how to use the built-in java sort.)\n    static double iField, jField;\n    static double [] iProperty, jProperty;\n    public static void SortRegions(int Col)\n    {\n        for(int i = 0; i < MaxLabel; i++)\n        {\n            for(int j = i+1; j <= Blobs.MaxLabel; j++)\n            {\n                iProperty = RegionData[i];\n                jProperty = RegionData[j];\n                iField = iProperty[Col];\n                jField = jProperty[Col];\n                if(iField > jField)\n                {\n                    RegionData[i] = jProperty;\n                    RegionData[j] = iProperty;\n                }\n            }\n        }\n    }\n}\n\n\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/BufferRing.java",
    "content": "/*\n * Copyright (C) 2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class BufferRing<B extends BufferRing.ReleasableBuffer> {\n    public BufferRing(BufferFactory<B> factory, int size) {\n        buffers = new Object[size];\n        for (int i = 0; i < size; i++) {\n            buffers[i] = factory.create();\n        }\n        position = 0;\n    }\n\n    public interface BufferFactory<B extends ReleasableBuffer> {\n        B create();\n    }\n\n    public interface ReleasableBuffer {\n        void release();\n    }\n\n    private Object[] buffers;\n    private int position;\n\n    public int capacity() {\n        return buffers.length;\n    }\n\n    public int position() {\n        return position;\n    }\n    public BufferRing position(int position) {\n        this.position = ((position % buffers.length) + buffers.length) % buffers.length;\n        return this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public B get() {\n        return (B)buffers[position];\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public B get(int offset) {\n        return (B)buffers[((position + offset) % buffers.length + buffers.length) % buffers.length];\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void release() {\n        for (int i = 0; i < buffers.length; i++) {\n            ((B)buffers[i]).release();\n        }\n        buffers = null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/CameraDevice.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.beans.PropertyChangeEvent;\nimport java.beans.PropertyChangeListener;\nimport java.beans.PropertyVetoException;\nimport java.io.File;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport org.bytedeco.javacpp.Pointer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class CameraDevice extends ProjectiveDevice {\n    public CameraDevice(String name) {\n        super(name);\n    }\n    public CameraDevice(String name, String filename) throws Exception {\n        super(name, filename);\n        settings.setImageWidth(imageWidth);\n        settings.setImageHeight(imageHeight);\n    }\n    public CameraDevice(String name, FileStorage fs) throws Exception {\n        super(name, fs);\n        settings.setImageWidth(imageWidth);\n        settings.setImageHeight(imageHeight);\n    }\n    public CameraDevice(Settings settings) throws Exception {\n        super((ProjectiveDevice.Settings)settings);\n    }\n\n    public interface Settings {\n        String getName();\n        void setName(String name);\n        double getResponseGamma();\n        void setResponseGamma(double gamma);\n\n        Integer getDeviceNumber();\n        void setDeviceNumber(Integer deviceNumber) throws PropertyVetoException;\n        File getDeviceFile();\n        void setDeviceFile(File deviceFile) throws PropertyVetoException;\n        String getDeviceFilename();\n        void setDeviceFilename(String deviceFilename) throws PropertyVetoException;\n        String getDevicePath();\n        void setDevicePath(String devicePath) throws PropertyVetoException;\n        Class<? extends FrameGrabber> getFrameGrabber();\n        void setFrameGrabber(Class<? extends FrameGrabber> frameGrabber);\n        String getDescription();\n\n        String getFormat();\n        void setFormat(String format);\n        int getImageWidth();\n        void setImageWidth(int imageWidth);\n        int getImageHeight();\n        void setImageHeight(int imageHeight);\n        double getFrameRate();\n        void setFrameRate(double frameRate);\n        boolean isTriggerMode();\n        void setTriggerMode(boolean triggerMode);\n        int getBitsPerPixel();\n        void setBitsPerPixel(int bitsPerPixel);\n        FrameGrabber.ImageMode getImageMode();\n        void setImageMode(FrameGrabber.ImageMode imageMode);\n        int getTimeout();\n        void setTimeout(int timeout);\n        int getNumBuffers();\n        void setNumBuffers(int numBuffers);\n        boolean isDeinterlace();\n        void setDeinterlace(boolean deinterlace);\n\n        void addPropertyChangeListener(PropertyChangeListener listener);\n        void removePropertyChangeListener(PropertyChangeListener listener);\n    }\n\n    public static class SettingsImplementation extends ProjectiveDevice.Settings implements Settings {\n        public SettingsImplementation() { name = \"Camera  0\"; }\n        public SettingsImplementation(ProjectiveDevice.Settings settings) {\n            super(settings);\n            if (settings instanceof SettingsImplementation) {\n                SettingsImplementation s = (SettingsImplementation)settings;\n                this.deviceNumber = s.deviceNumber;\n                this.deviceFile   = s.deviceFile;\n                this.devicePath   = s.devicePath;\n                this.frameGrabber = s.frameGrabber;\n                this.format       = s.format;\n                this.imageWidth   = s.imageWidth;\n                this.imageHeight  = s.imageHeight;\n                this.frameRate    = s.frameRate;\n                this.triggerMode  = s.triggerMode;\n                this.bpp          = s.bpp;\n                this.imageMode    = s.imageMode;\n                this.timeout      = s.timeout;\n                this.numBuffers   = s.numBuffers;\n                this.deinterlace  = s.deinterlace;\n            }\n        }\n\n        Integer deviceNumber = null;\n        File deviceFile = null;\n        String devicePath = null;\n        Class<? extends FrameGrabber> frameGrabber = null;\n\n        public Integer getDeviceNumber() {\n            return deviceNumber;\n        }\n        public void setDeviceNumber(Integer deviceNumber) throws PropertyVetoException {\n            if (deviceNumber != null) {\n                try {\n                    if (frameGrabber != null) {\n                        try {\n                            frameGrabber.getConstructor(int.class);\n                        } catch (NoSuchMethodException e) {\n                            frameGrabber.getConstructor(Integer.class);\n                        }\n                    }\n                    setDevicePath(null);\n                    setDeviceFile(null);\n                } catch (NoSuchMethodException e) {\n                    throw new PropertyVetoExceptionThatNetBeansLikes(frameGrabber.getSimpleName() + \" does not accept a deviceNumber.\",\n                            new PropertyChangeEvent(this, \"deviceNumber\", this.deviceNumber, this.deviceNumber = null));\n                }\n            }\n            String oldDescription = getDescription();\n            firePropertyChange(\"deviceNumber\", this.deviceNumber, this.deviceNumber = deviceNumber);\n            firePropertyChange(\"description\", oldDescription, getDescription());\n        }\n\n        public File getDeviceFile() {\n            return deviceFile;\n        }\n        public void setDeviceFile(File deviceFile) throws PropertyVetoException {\n            if (deviceFile != null) {\n                try {\n                    if (frameGrabber != null) {\n                        frameGrabber.getConstructor(File.class);\n                    }\n                    setDeviceNumber(null);\n                    setDevicePath(null);\n                } catch (NoSuchMethodException e) {\n                    deviceFile = null;\n                    throw new PropertyVetoExceptionThatNetBeansLikes(frameGrabber.getSimpleName() + \" does not accept a deviceFile.\",\n                            new PropertyChangeEvent(this, \"deviceFile\", this.deviceFile, this.deviceFile = null));\n                }\n            }\n            String oldDescription = getDescription();\n            firePropertyChange(\"deviceFile\", this.deviceFile, this.deviceFile = deviceFile);\n            firePropertyChange(\"description\", oldDescription, getDescription());\n        }\n        public String getDeviceFilename() {\n            return getDeviceFile() == null ? \"\" : getDeviceFile().getPath();\n        }\n        public void setDeviceFilename(String deviceFilename) throws PropertyVetoException {\n            setDeviceFile(deviceFilename == null || deviceFilename.length() == 0 ?\n                null : new File(deviceFilename));\n        }\n\n        public String getDevicePath() {\n            return devicePath;\n        }\n        public void setDevicePath(String devicePath) throws PropertyVetoException {\n            if (devicePath != null) {\n                try {\n                    if (frameGrabber != null) {\n                        frameGrabber.getConstructor(String.class);\n                    }\n                    setDeviceNumber(null);\n                    setDeviceFile(null);\n                } catch (NoSuchMethodException e) {\n                    devicePath = \"\";\n                    throw new PropertyVetoExceptionThatNetBeansLikes(frameGrabber.getSimpleName() + \" does not accept a devicePath.\",\n                            new PropertyChangeEvent(this, \"devicePath\", this.devicePath, this.devicePath = null));\n                }\n            }\n            String oldDescription = getDescription();\n            firePropertyChange(\"devicePath\", this.devicePath, this.devicePath = devicePath);\n            firePropertyChange(\"description\", oldDescription, getDescription());\n        }\n\n        public Class<? extends FrameGrabber> getFrameGrabber() {\n            return frameGrabber;\n        }\n        public void setFrameGrabber(Class<? extends FrameGrabber> frameGrabber) {\n            String oldDescription = getDescription();\n            firePropertyChange(\"frameGrabber\", this.frameGrabber, this.frameGrabber = frameGrabber);\n            firePropertyChange(\"description\", oldDescription, getDescription());\n\n            if (frameGrabber == null) {\n                firePropertyChange(\"deviceNumber\", this.deviceNumber, this.deviceNumber = null);\n                firePropertyChange(\"deviceFile\", this.deviceFile, this.deviceFile = null);\n                firePropertyChange(\"devicePath\", this.devicePath, this.devicePath = null);\n                return;\n            }\n\n            boolean hasDeviceNumber = false;\n            try {\n                frameGrabber.getConstructor(int.class);\n                hasDeviceNumber = true;\n            } catch (NoSuchMethodException e) {\n                try {\n                    frameGrabber.getConstructor(Integer.class);\n                    hasDeviceNumber = true;\n                } catch (NoSuchMethodException e2) {\n                    firePropertyChange(\"deviceNumber\", this.deviceNumber, this.deviceNumber = null);\n                }\n            }\n            try {\n                frameGrabber.getConstructor(File.class);\n            } catch (NoSuchMethodException e) {\n                firePropertyChange(\"deviceFile\", this.deviceFile, this.deviceFile = null);\n            }\n            try {\n                frameGrabber.getConstructor(String.class);\n            } catch (NoSuchMethodException e) {\n                firePropertyChange(\"devicePath\", this.devicePath, this.devicePath = null);\n            }\n\n            if (hasDeviceNumber && deviceNumber == null && deviceFile == null && devicePath == null) {\n                try {\n                    setDeviceNumber(0);\n                } catch (PropertyVetoException e) { }\n            }\n        }\n\n        public String getDescription() {\n            String[] descriptions = null;\n            try {\n                Method m = frameGrabber.getMethod(\"getDeviceDescriptions\");\n                descriptions = (String[])m.invoke(null);\n            } catch (java.lang.Exception ex) { }\n\n            if (descriptions != null && deviceNumber != null && deviceNumber < descriptions.length) {\n                return descriptions[deviceNumber];\n            } else {\n                return \"\";\n            }\n        }\n\n        String format = \"\";\n        int imageWidth = 0, imageHeight = 0;\n        double frameRate = 0;\n        boolean triggerMode = false;\n        int bpp = 0;\n        FrameGrabber.ImageMode imageMode = FrameGrabber.ImageMode.COLOR;\n        int timeout = 10000;\n        int numBuffers = 4;\n        boolean deinterlace = false;\n\n        public String getFormat() {\n            return format;\n        }\n        public void setFormat(String format) {\n            this.format = format;\n        }\n\n        public int getImageWidth() {\n            return imageWidth;\n        }\n        public void setImageWidth(int imageWidth) {\n            this.imageWidth = imageWidth;\n        }\n\n        public int getImageHeight() {\n            return imageHeight;\n        }\n        public void setImageHeight(int imageHeight) {\n            this.imageHeight = imageHeight;\n        }\n\n        public double getFrameRate() {\n            return frameRate;\n        }\n        public void setFrameRate(double frameRate) {\n            this.frameRate = frameRate;\n        }\n\n        public boolean isTriggerMode() {\n            return triggerMode;\n        }\n        public void setTriggerMode(boolean triggerMode) {\n            this.triggerMode = triggerMode;\n        }\n\n        public int getBitsPerPixel() {\n            return bpp;\n        }\n        public void setBitsPerPixel(int bitsPerPixel) {\n            this.bpp = bitsPerPixel;\n        }\n\n        public FrameGrabber.ImageMode getImageMode() {\n            return imageMode;\n        }\n        public void setImageMode(FrameGrabber.ImageMode imageMode) {\n            this.imageMode = imageMode;\n        }\n\n        public int getTimeout() {\n            return timeout;\n        }\n        public void setTimeout(int timeout) {\n            this.timeout = timeout;\n        }\n\n        public int getNumBuffers() {\n            return numBuffers;\n        }\n        public void setNumBuffers(int numBuffers) {\n            this.numBuffers = numBuffers;\n        }\n\n        public boolean isDeinterlace() {\n            return deinterlace;\n        }\n        public void setDeinterlace(boolean deinterlace) {\n            this.deinterlace = deinterlace;\n        }\n    }\n\n    // pouah.. hurray for Scala!\n    public static class CalibrationSettings extends ProjectiveDevice.CalibrationSettings implements Settings {\n        public CalibrationSettings() { }\n        public CalibrationSettings(ProjectiveDevice.CalibrationSettings settings) {\n            super(settings);\n            if (settings instanceof CalibrationSettings) {\n                si = new SettingsImplementation(((CalibrationSettings)settings).si);\n            }\n        }\n        SettingsImplementation si = new SettingsImplementation() {\n            @Override public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {\n                CalibrationSettings.this.firePropertyChange(propertyName, oldValue, newValue);\n            }\n        };\n\n        @Override public String getName() { return si.getName(); }\n        @Override public void setName(String name) { si.setName(name); }\n        @Override public double getResponseGamma() { return si.getResponseGamma(); }\n        @Override public void setResponseGamma(double responseGamma) { si.setResponseGamma(responseGamma); }\n//        @Override public double getNominalDistance() { return si.getNominalDistance(); }\n//        @Override public void setNominalDistance(double nominalDistance) { si.setNominalDistance(nominalDistance); }\n\n        public Integer getDeviceNumber() { return si.getDeviceNumber(); }\n        public void setDeviceNumber(Integer deviceNumber) throws PropertyVetoException { si.setDeviceNumber(deviceNumber); }\n        public File getDeviceFile() { return si.getDeviceFile(); }\n        public void setDeviceFile(File deviceFile) throws PropertyVetoException { si.setDeviceFile(deviceFile); }\n        public String getDeviceFilename() { return si.getDeviceFilename(); }\n        public void setDeviceFilename(String deviceFilename) throws PropertyVetoException { si.setDeviceFilename(deviceFilename); }\n        public String getDevicePath() { return si.getDevicePath(); }\n        public void setDevicePath(String devicePath) throws PropertyVetoException { si.setDevicePath(devicePath); }\n        public Class<? extends FrameGrabber> getFrameGrabber() { return si.getFrameGrabber(); }\n        public void setFrameGrabber(Class<? extends FrameGrabber> frameGrabber) { si.setFrameGrabber(frameGrabber); }\n        public String getDescription() { return si.getDescription(); }\n\n        public String getFormat() { return si.getFormat(); }\n        public void setFormat(String format) { si.setFormat(format); }\n        public int getImageWidth() { return si.getImageWidth(); }\n        public void setImageWidth(int imageWidth) { si.setImageWidth(imageWidth); }\n        public int getImageHeight() { return si.getImageHeight(); }\n        public void setImageHeight(int imageHeight) { si.setImageHeight(imageHeight); }\n        public double getFrameRate() { return si.getFrameRate(); }\n        public void setFrameRate(double frameRate) { si.setFrameRate(frameRate); }\n        public boolean isTriggerMode() { return si.isTriggerMode(); }\n        public void setTriggerMode(boolean triggerMode) { si.setTriggerMode(triggerMode); }\n        public int getBitsPerPixel() { return si.getBitsPerPixel(); }\n        public void setBitsPerPixel(int bitsPerPixel) { si.setBitsPerPixel(bitsPerPixel); }\n        public FrameGrabber.ImageMode getImageMode() { return si.getImageMode(); }\n        public void setImageMode(FrameGrabber.ImageMode imageMode) { si.setImageMode(imageMode); }\n        public int getTimeout() { return si.getTimeout(); }\n        public void setTimeout(int timeout) { si.setTimeout(timeout); }\n        public int getNumBuffers() { return si.getNumBuffers(); }\n        public void setNumBuffers(int numBuffers) { si.setNumBuffers(numBuffers); }\n        public boolean isDeinterlace() { return si.isDeinterlace(); }\n        public void setDeinterlace(boolean deinterlace) { si.setDeinterlace(deinterlace); }\n    }\n\n    public static class CalibratedSettings extends ProjectiveDevice.CalibratedSettings implements Settings {\n        public CalibratedSettings() { }\n        public CalibratedSettings(ProjectiveDevice.CalibratedSettings settings) {\n            super(settings);\n            if (settings instanceof CalibratedSettings) {\n                si = new SettingsImplementation(((CalibratedSettings)settings).si);\n            }\n        }\n        SettingsImplementation si = new SettingsImplementation() {\n            @Override public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {\n                CalibratedSettings.this.firePropertyChange(propertyName, oldValue, newValue);\n            }\n        };\n\n        @Override public String getName() { return si.getName(); }\n        @Override public void setName(String name) { si.setName(name); }\n        @Override public double getResponseGamma() { return si.getResponseGamma(); }\n        @Override public void setResponseGamma(double responseGamma) { si.setResponseGamma(responseGamma); }\n//        @Override public double getNominalDistance() { return si.getNominalDistance(); }\n//        @Override public void setNominalDistance(double nominalDistance) { si.setNominalDistance(nominalDistance); }\n\n        public Integer getDeviceNumber() { return si.getDeviceNumber(); }\n        public void setDeviceNumber(Integer deviceNumber) throws PropertyVetoException { si.setDeviceNumber(deviceNumber); }\n        public File getDeviceFile() { return si.getDeviceFile(); }\n        public void setDeviceFile(File deviceFile) throws PropertyVetoException { si.setDeviceFile(deviceFile); }\n        public String getDeviceFilename() { return si.getDeviceFilename(); }\n        public void setDeviceFilename(String deviceFilename) throws PropertyVetoException { si.setDeviceFilename(deviceFilename); }\n        public String getDevicePath() { return si.getDevicePath(); }\n        public void setDevicePath(String devicePath) throws PropertyVetoException { si.setDevicePath(devicePath); }\n        public Class<? extends FrameGrabber> getFrameGrabber() { return si.getFrameGrabber(); }\n        public void setFrameGrabber(Class<? extends FrameGrabber> frameGrabber) { si.setFrameGrabber(frameGrabber); }\n        public String getDescription() { return si.getDescription(); }\n\n        public String getFormat() { return si.getFormat(); }\n        public void setFormat(String format) { si.setFormat(format); }\n        public int getImageWidth() { return si.getImageWidth(); }\n        public void setImageWidth(int imageWidth) { si.setImageWidth(imageWidth); }\n        public int getImageHeight() { return si.getImageHeight(); }\n        public void setImageHeight(int imageHeight) { si.setImageHeight(imageHeight); }\n        public double getFrameRate() { return si.getFrameRate(); }\n        public void setFrameRate(double frameRate) { si.setFrameRate(frameRate); }\n        public boolean isTriggerMode() { return si.isTriggerMode(); }\n        public void setTriggerMode(boolean triggerMode) { si.setTriggerMode(triggerMode); }\n        public int getBitsPerPixel() { return si.getBitsPerPixel(); }\n        public void setBitsPerPixel(int bitsPerPixel) { si.setBitsPerPixel(bitsPerPixel); }\n        public FrameGrabber.ImageMode getImageMode() { return si.getImageMode(); }\n        public void setImageMode(FrameGrabber.ImageMode imageMode) { si.setImageMode(imageMode); }\n        public int getTimeout() { return si.getTimeout(); }\n        public void setTimeout(int timeout) { si.setTimeout(timeout); }\n        public int getNumBuffers() { return si.getNumBuffers(); }\n        public void setNumBuffers(int numBuffers) { si.setNumBuffers(numBuffers); }\n        public boolean isDeinterlace() { return si.isDeinterlace(); }\n        public void setDeinterlace(boolean deinterlace) { si.setDeinterlace(deinterlace); }\n    }\n\n    private Settings settings;\n    @Override public ProjectiveDevice.Settings getSettings() {\n        return (ProjectiveDevice.Settings)settings;\n    }\n    public void setSettings(Settings settings) {\n        setSettings((ProjectiveDevice.Settings)settings);\n    }\n    @Override public void setSettings(ProjectiveDevice.Settings settings) {\n        super.setSettings(settings);\n        if (settings instanceof ProjectiveDevice.CalibrationSettings) {\n            this.settings = new CalibrationSettings((ProjectiveDevice.CalibrationSettings)settings);\n        } else if (settings instanceof ProjectiveDevice.CalibratedSettings) {\n            this.settings = new CalibratedSettings((ProjectiveDevice.CalibratedSettings)settings);\n        } else {\n            this.settings = new SettingsImplementation((ProjectiveDevice.Settings)settings);\n        }\n        if (this.settings.getName() == null || this.settings.getName().length() == 0) {\n            this.settings.setName(\"Camera \" + String.format(\"%2d\", this.settings.getDeviceNumber()));\n        }\n    }\n\n    public FrameGrabber createFrameGrabber() throws FrameGrabber.Exception {\n        try {\n            settings.getFrameGrabber().getMethod(\"tryLoad\").invoke(null);\n            FrameGrabber f;\n            if (settings.getDeviceFile() != null) {\n                f = settings.getFrameGrabber().getConstructor(File.class).newInstance(settings.getDeviceFile());\n            } else if (settings.getDevicePath() != null && settings.getDevicePath().length() > 0) {\n                f = settings.getFrameGrabber().getConstructor(String.class).newInstance(settings.getDevicePath());\n            } else {\n                int number = settings.getDeviceNumber() == null ? 0 : settings.getDeviceNumber();\n                try {\n                    f = settings.getFrameGrabber().getConstructor(int.class).newInstance(number);\n                } catch (NoSuchMethodException e) {\n                    f = settings.getFrameGrabber().getConstructor(Integer.class).newInstance(number);\n                }\n            }\n            f.setFormat(settings.getFormat());\n            f.setImageWidth(settings.getImageWidth());\n            f.setImageHeight(settings.getImageHeight());\n            f.setFrameRate(settings.getFrameRate());\n            f.setTriggerMode(settings.isTriggerMode());\n            f.setBitsPerPixel(settings.getBitsPerPixel());\n            f.setImageMode(settings.getImageMode());\n            f.setTimeout(settings.getTimeout());\n            f.setNumBuffers(settings.getNumBuffers());\n            f.setGamma(settings.getResponseGamma());\n            f.setDeinterlace(settings.isDeinterlace());\n            return f;\n        } catch (Throwable t) {\n            if (t instanceof InvocationTargetException) {\n                t = ((InvocationTargetException)t).getCause();\n            }\n            if (t instanceof FrameGrabber.Exception) {\n                throw (FrameGrabber.Exception)t;\n            } else {\n                throw new FrameGrabber.Exception(\"Failed to create \" + settings.getFrameGrabber(), t);\n            }\n        }\n    }\n\n    public static CameraDevice[] read(String filename) throws Exception {\n        FileStorage fs = new FileStorage(filename, FileStorage.READ);\n        CameraDevice[] devices = read(fs);\n        fs.release();\n        return devices;\n    }\n    public static CameraDevice[] read(FileStorage fs) throws Exception {\n        FileNode node = fs.get(\"Cameras\");\n        FileNodeIterator seq = node.begin();\n        int count = (int)seq.remaining();\n\n        CameraDevice[] devices = new CameraDevice[count];\n        for (int i = 0; i < count; i++, seq.increment()) {\n            FileNode n = seq.multiply();\n            if (n.empty()) continue;\n            String name = n.asBytePointer().getString();\n            devices[i] = new CameraDevice(name, fs);\n        }\n        return devices;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/CameraSettings.java",
    "content": "/*\n * Copyright (C) 2009-2011 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.beans.PropertyVetoException;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class CameraSettings extends BaseSettings {\n\n    public CameraSettings() {\n        this(false);\n    }\n    public CameraSettings(boolean calibrated) {\n        this.calibrated = calibrated;\n    }\n\n    boolean calibrated = false;\n    double monitorWindowsScale = 1.0;\n    Class<? extends FrameGrabber> frameGrabber = null;\n\n    public int getQuantity() {\n        return size();\n    }\n    public void setQuantity(int quantity) throws PropertyVetoException {\n        quantity = Math.max(1, quantity);\n        Object[] a = toArray();\n        int i = a.length;\n        while (i > quantity) {\n            remove(a[i-1]);\n            i--;\n        }\n        while (i < quantity) {\n            CameraDevice.Settings c = calibrated ? new CameraDevice.CalibratedSettings() :\n                                                   new CameraDevice.CalibrationSettings();\n            c.setName(\"Camera \" + String.format(\"%2d\", i));\n            c.setDeviceNumber(i);\n            c.setFrameGrabber(frameGrabber);\n            add(c);\n            i++;\n        }\n        pcSupport.firePropertyChange(\"quantity\", a.length, quantity);\n    }\n\n    public double getMonitorWindowsScale() {\n        return monitorWindowsScale;\n    }\n    public void setMonitorWindowsScale(double monitorWindowsScale) {\n        this.monitorWindowsScale = monitorWindowsScale;\n    }\n\n    public Class<? extends FrameGrabber> getFrameGrabber() {\n        return frameGrabber;\n    }\n    public void setFrameGrabber(Class<? extends FrameGrabber> frameGrabber) {\n        pcSupport.firePropertyChange(\"frameGrabber\", this.frameGrabber, this.frameGrabber = frameGrabber);\n    }\n\n    @Override public CameraDevice.Settings[] toArray() {\n        return (CameraDevice.Settings[])toArray(new CameraDevice.Settings[size()]);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/CanvasFrame.java",
    "content": "/*\n * Copyright (C) 2009-2015 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.awt.Canvas;\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.DisplayMode;\nimport java.awt.EventQueue;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.GraphicsConfiguration;\nimport java.awt.GraphicsDevice;\nimport java.awt.GraphicsEnvironment;\nimport java.awt.Image;\nimport java.awt.KeyEventDispatcher;\nimport java.awt.KeyboardFocusManager;\nimport java.awt.Transparency;\nimport java.awt.color.ColorSpace;\nimport java.awt.color.ICC_ColorSpace;\nimport java.awt.color.ICC_ProfileRGB;\nimport java.awt.event.ComponentAdapter;\nimport java.awt.event.ComponentEvent;\nimport java.awt.event.KeyEvent;\nimport java.awt.image.BufferStrategy;\nimport java.awt.image.BufferedImage;\nimport javax.swing.JFrame;\nimport javax.swing.JRootPane;\n\n/**\n *\n * @author Samuel Audet\n * \n * Make sure OpenGL or XRender is enabled to get low latency, something like\n *      export _JAVA_OPTIONS=-Dsun.java2d.opengl=True\n *      export _JAVA_OPTIONS=-Dsun.java2d.xrender=True\n */\npublic class CanvasFrame extends JFrame {\n\n    public static class Exception extends java.lang.Exception {\n        public Exception(String message) { super(message); }\n        public Exception(String message, Throwable cause) { super(message, cause); }\n    }\n\n    public static String[] getScreenDescriptions() {\n        GraphicsDevice[] screens = getScreenDevices();\n        String[] descriptions = new String[screens.length];\n        for (int i = 0; i < screens.length; i++) {\n            descriptions[i] = screens[i].getIDstring();\n        }\n        return descriptions;\n    }\n    public static DisplayMode getDisplayMode(int screenNumber) {\n        GraphicsDevice[] screens = getScreenDevices();\n        if (screenNumber >= 0 && screenNumber < screens.length) {\n            return screens[screenNumber].getDisplayMode();\n        } else {\n            return null;\n        }\n    }\n    public static double getGamma(int screenNumber) {\n        GraphicsDevice[] screens = getScreenDevices();\n        if (screenNumber >= 0 && screenNumber < screens.length) {\n            return getGamma(screens[screenNumber]);\n        } else {\n            return 0.0;\n        }\n    }\n    public static double getDefaultGamma() {\n        return getGamma(getDefaultScreenDevice());\n    }\n\n    public static double getGamma(GraphicsDevice screen) {\n        ColorSpace cs = screen.getDefaultConfiguration().getColorModel().getColorSpace();\n        if (cs.isCS_sRGB()) {\n            return 2.2;\n        } else {\n            try {\n                return ((ICC_ProfileRGB)((ICC_ColorSpace)cs).getProfile()).getGamma(0);\n            } catch (RuntimeException e) { }\n        }\n        return 0.0;\n    }\n    public static GraphicsDevice getScreenDevice(int screenNumber) throws Exception {\n        GraphicsDevice[] screens = getScreenDevices();\n        if (screenNumber >= screens.length) {\n            throw new Exception(\"CanvasFrame Error: Screen number \" + screenNumber + \" not found. \" +\n                                \"There are only \" + screens.length + \" screens.\");\n        }\n        return screens[screenNumber];//.getDefaultConfiguration();\n    }\n    public static GraphicsDevice[] getScreenDevices() {\n        return GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();\n    }\n    public static GraphicsDevice getDefaultScreenDevice() {\n        return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();\n    }\n\n    public CanvasFrame(String title) {\n        this(title, 0.0);\n    }\n    public CanvasFrame(String title, double gamma) {\n        super(title);\n        init(false, null, gamma);\n    }\n\n    public CanvasFrame(String title, GraphicsConfiguration gc) {\n        this(title, gc, 0.0);\n    }\n    public CanvasFrame(String title, GraphicsConfiguration gc, double gamma) {\n        super(title, gc);\n        init(false, null, gamma);\n    }\n\n    public CanvasFrame(String title, int screenNumber, DisplayMode displayMode) throws Exception {\n        this(title, screenNumber, displayMode, 0.0);\n    }\n    public CanvasFrame(String title, int screenNumber, DisplayMode displayMode, double gamma) throws Exception {\n        super(title, getScreenDevice(screenNumber).getDefaultConfiguration());\n        init(true, displayMode, gamma);\n    }\n\n    private void init(final boolean fullScreen, final DisplayMode displayMode, final double gamma) {\n        Runnable r = new Runnable() { public void run() {\n            KeyboardFocusManager.getCurrentKeyboardFocusManager().\n                    addKeyEventDispatcher(keyEventDispatch);\n\n            GraphicsDevice gd = getGraphicsConfiguration().getDevice();\n            DisplayMode d = gd.getDisplayMode(), d2 = null;\n            if (displayMode != null && d != null) {\n                int w = displayMode.getWidth();\n                int h = displayMode.getHeight();\n                int b = displayMode.getBitDepth();\n                int r = displayMode.getRefreshRate();\n                d2 = new DisplayMode(w > 0 ? w : d.getWidth(),    h > 0 ? h : d.getHeight(),\n                                     b > 0 ? b : d.getBitDepth(), r > 0 ? r : d.getRefreshRate());\n            }\n            if (fullScreen) {\n                setUndecorated(true);\n                getRootPane().setWindowDecorationStyle(JRootPane.NONE);\n                setResizable(false);\n                gd.setFullScreenWindow(CanvasFrame.this);\n            } else {\n                setLocationByPlatform(true);\n            }\n            if (d2 != null && !d2.equals(d)) {\n                gd.setDisplayMode(d2);\n            }\n            double g = gamma == 0.0 ? getGamma(gd) : gamma;\n            inverseGamma = g == 0.0 ? 1.0 : 1.0/g;\n\n            // Must be called after the fullscreen stuff, but before\n            // getting our BufferStrategy or even creating our Canvas\n            setVisible(true);\n\n            initCanvas(fullScreen, displayMode, gamma);\n        }};\n\n        if (EventQueue.isDispatchThread()) {\n            r.run();\n        } else {\n            try {\n                EventQueue.invokeAndWait(r);\n            } catch (java.lang.Exception ex) { }\n        }\n    }\n\n    protected void initCanvas(boolean fullScreen, DisplayMode displayMode, double gamma) {\n        canvas = new Canvas() {\n            @Override public void update(Graphics g) {\n                paint(g);\n            }\n            @Override public void paint(Graphics g) {\n                // Calling BufferStrategy.show() here sometimes throws\n                // NullPointerException or IllegalStateException,\n                // but otherwise seems to work fine.\n                try {\n                    if (canvas.getWidth() <= 0 || canvas.getHeight() <= 0) {\n                        return;\n                    }\n                    BufferStrategy strategy = canvas.getBufferStrategy();\n                    do {\n                        do {\n                            g = strategy.getDrawGraphics();\n                            if (color != null) {\n                                g.setColor(color);\n                                g.fillRect(0, 0, getWidth(), getHeight());\n                            }\n                            if (image != null) {\n                                g.drawImage(image, 0, 0, getWidth(), getHeight(), null);\n                            }\n                            if (buffer != null) {\n                                g.drawImage(buffer, 0, 0, getWidth(), getHeight(), null);\n                            }\n                            g.dispose();\n                        } while (strategy.contentsRestored());\n                        strategy.show();\n                    } while (strategy.contentsLost());\n                } catch (NullPointerException e) {\n                } catch (IllegalStateException e) { }\n            }\n        };\n        if (fullScreen) {\n            canvas.setSize(getSize());\n            needInitialResize = false;\n        } else {\n            canvas.setSize(10,10); // mac bug\n            needInitialResize = true;\n        }\n        getContentPane().add(canvas);\n        canvas.setVisible(true);\n        canvas.createBufferStrategy(2);\n        //canvas.setIgnoreRepaint(true);\n    }\n\n    // used for example as debugging console...\n    public static CanvasFrame global = null;\n\n    // Latency is about 60 ms on Metacity and Windows XP, and 90 ms on Compiz Fusion,\n    // but we set the default to twice as much to take into account the roundtrip\n    // camera latency as well, just to be sure\n    public static final long DEFAULT_LATENCY = 200;\n    private long latency = DEFAULT_LATENCY;\n\n    private KeyEvent keyEvent = null;\n    private KeyEventDispatcher keyEventDispatch = new KeyEventDispatcher() {\n        public boolean dispatchKeyEvent(KeyEvent e) {\n            if (e.getID() == KeyEvent.KEY_PRESSED) {\n                synchronized (CanvasFrame.this) {\n                    keyEvent = e;\n                    CanvasFrame.this.notify();\n                }\n            }\n            return false;\n        }\n    };\n\n    protected Canvas canvas = null;\n    protected boolean needInitialResize = false;\n    protected double initialScale = 1.0;\n    protected double inverseGamma = 1.0;\n    private Color color = null;\n    private Image image = null;\n    private BufferedImage buffer = null;\n    private Java2DFrameConverter converter = new Java2DFrameConverter();\n\n    public long getLatency() {\n        // if there exists some way to estimate the latency in real time,\n        // add it here\n        return latency;\n    }\n    public void setLatency(long latency) {\n        this.latency = latency;\n    }\n    public void waitLatency() throws InterruptedException {\n        Thread.sleep(getLatency());\n    }\n\n    public KeyEvent waitKey() throws InterruptedException {\n        return waitKey(0);\n    }\n    public synchronized KeyEvent waitKey(int delay) throws InterruptedException {\n        if (delay >= 0) {\n            keyEvent = null;\n            wait(delay);\n        }\n        KeyEvent e = keyEvent;\n        keyEvent = null;\n        return e;\n    }\n\n    public Canvas getCanvas() {\n        return canvas;\n    }\n\n    public Dimension getCanvasSize() {\n        return canvas.getSize();\n    }\n    public void setCanvasSize(final int width, final int height) {\n        Dimension d = getCanvasSize();\n        if (d.width == width && d.height == height) {\n            return;\n        }\n\n        Runnable r = new Runnable() { public void run() {\n            // There is apparently a bug in Java code for Linux, and what happens goes like this:\n            // 1. Canvas gets resized, checks the visible area (has not changed) and updates\n            // BufferStrategy with the same size. 2. pack() resizes the frame and changes\n            // the visible area 3. We call Canvas.setSize() with different dimensions, to make\n            // it check the visible area and reallocate the BufferStrategy almost correctly\n            // 4. Finally, we resize the Canvas to the desired size... phew!\n            setExtendedState(NORMAL); // force unmaximization\n            canvas.setSize(width, height);\n            pack();\n            canvas.setSize(width+1, height+1);\n            canvas.setSize(width, height);\n            needInitialResize = false;\n        }};\n\n        if (EventQueue.isDispatchThread()) {\n            r.run();\n        } else {\n            try {\n                EventQueue.invokeAndWait(r);\n            } catch (java.lang.Exception ex) { }\n        }\n    }\n\n    public double getCanvasScale() {\n        return initialScale;\n    }\n    public void setCanvasScale(double initialScale) {\n        this.initialScale = initialScale;\n        this.needInitialResize = true;\n    }\n\n    public Graphics2D createGraphics() {\n        if (buffer == null || buffer.getWidth() != canvas.getWidth() || buffer.getHeight() != canvas.getHeight()) {\n            BufferedImage newbuffer = canvas.getGraphicsConfiguration().createCompatibleImage(\n                    canvas.getWidth(), canvas.getHeight(), Transparency.TRANSLUCENT);\n            if (buffer != null) {\n                Graphics g = newbuffer.getGraphics();\n                g.drawImage(buffer, 0, 0, null);\n                g.dispose();\n            }\n            buffer = newbuffer;\n        }\n        return buffer.createGraphics();\n    }\n    public void releaseGraphics(Graphics2D g) {\n        g.dispose();\n        canvas.paint(null);\n    }\n\n    public void showColor(Color color) {\n        this.color = color;\n        this.image = null;\n        canvas.paint(null);\n    }\n\n    // Java2D will do gamma correction for TYPE_CUSTOM BufferedImage, but\n    // not for the standard types, so we need to do it manually.\n    public void showImage(Frame image) {\n        showImage(image, false);\n    }\n    public void showImage(Frame image, boolean flipChannels) {\n        showImage(converter.getBufferedImage(image, converter.getBufferedImageType(image) ==\n                BufferedImage.TYPE_CUSTOM ? 1.0 : inverseGamma, flipChannels, null));\n    }\n    public void showImage(Image image) {\n        if (image == null) {\n            return;\n        } else if (isResizable() && needInitialResize) {\n            int w = (int)Math.round(image.getWidth (null)*initialScale);\n            int h = (int)Math.round(image.getHeight(null)*initialScale);\n            setCanvasSize(w, h);\n        }\n        this.color = null;\n        this.image = image;\n        canvas.paint(null);\n    }\n\n    // This should not be called from the event dispatch thread (EDT),\n    // but if it is, it should not totally crash... In the worst case,\n    // it will simply timeout waiting for the moved events.\n    public static void tile(final CanvasFrame[] frames) {\n\n        class MovedListener extends ComponentAdapter {\n            boolean moved = false;\n            @Override public void componentMoved(ComponentEvent e) {\n                moved = true;\n                Component c = e.getComponent();\n                synchronized (c) {\n                    c.notify();\n                }\n            }\n        }\n        final MovedListener movedListener = new MovedListener();\n\n        // layout the canvas frames for the cameras in tiles\n        int canvasCols = (int)Math.round(Math.sqrt(frames.length));\n        if (canvasCols*canvasCols < frames.length) {\n            // if we don't get a square, favor horizontal layouts\n            // since screens are usually wider than cameras...\n            // and we also have title bars, tasks bar, menus, etc that\n            // takes up vertical space\n            canvasCols++;\n        }\n        int canvasX = 0, canvasY = 0;\n        int canvasMaxY = 0;\n        for (int i = 0; i < frames.length; i++) {\n            final int n = i;\n            final int x = canvasX;\n            final int y = canvasY;\n            try {\n                movedListener.moved = false;\n                EventQueue.invokeLater(new Runnable() {\n                    public void run() {\n                        frames[n].addComponentListener(movedListener);\n                        frames[n].setLocation(x, y);\n                    }\n                });\n                int count = 0;\n                while (!movedListener.moved && count < 5) {\n                    // wait until the window manager actually places our window...\n                    // wait a maximum of 500 ms since this does not work if\n                    // we are on the event dispatch thread. also some window\n                    // managers like Windows do not always send us the event...\n                    synchronized (frames[n]) {\n                        frames[n].wait(100);\n                    }\n                    count++;\n                }\n                EventQueue.invokeLater(new Runnable() {\n                    public void run() {\n                        frames[n].removeComponentListener(movedListener);\n                    }\n                });\n            } catch (java.lang.Exception ex) { }\n            canvasX = frames[i].getX()+frames[i].getWidth();\n            canvasMaxY = Math.max(canvasMaxY, frames[i].getY()+frames[i].getHeight());\n            if ((i+1)%canvasCols == 0) {\n                canvasX = 0;\n                canvasY = canvasMaxY;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ColorCalibrator.java",
    "content": "/*\n * Copyright (C) 2009-2011 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.awt.Color;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ColorCalibrator {\n    public ColorCalibrator(ProjectiveDevice device) {\n        this.device = device;\n    }\n\n    private ProjectiveDevice device;\n\n    public double calibrate(Color[] referenceColors, Color[] deviceColors) {\n        assert(referenceColors.length == deviceColors.length);\n\n        int[] order = device.getRGBColorOrder();\n\n        // solve for X and a in   c = X p + a\n        CvMat A = CvMat.create(referenceColors.length*3, 12);\n        CvMat b = CvMat.create(referenceColors.length*3, 1);\n        CvMat x = CvMat.create(12, 1);\n\n        double gamma = device.getSettings().getResponseGamma();\n\n        for (int i = 0; i < referenceColors.length; i++) {\n            float[] dc = deviceColors   [i].getRGBColorComponents(null);\n            float[] rc = referenceColors[i].getRGBColorComponents(null);\n\n            double dc1 = Math.pow(dc[order[0]], gamma);\n            double dc2 = Math.pow(dc[order[1]], gamma);\n            double dc3 = Math.pow(dc[order[2]], gamma);\n            for (int j = 0; j < 3; j++) {\n                int k = i*36 + j*16;\n                A.put(k  , dc1);\n                A.put(k+1, dc2);\n                A.put(k+2, dc3);\n                A.put(k+3, 1.0);\n                if (j < 2) {\n                    for (int m = 0; m < 12; m++) {\n                        A.put(k+4+m, 0.0);\n                    }\n                }\n            }\n\n            b.put(i*3  , rc[order[0]]);\n            b.put(i*3+1, rc[order[1]]);\n            b.put(i*3+2, rc[order[2]]);\n        }\n\n        //System.out.println(\"A =\\n\" + A);\n        //System.out.println(\"b =\\n\" + b);\n\n//                A.height = b.height = 18;\n        if (cvSolve(A, b, x, CV_SVD) != 1.0) {\n            System.out.println(\"Error solving.\");\n        }\n\n        // compute RMSE and R^2 coefficient ...\n        CvMat b2 = CvMat.create(b.rows(), 1);\n        cvMatMul(A, x, b2);\n        double MSE = cvNorm(b, b2)*cvNorm(b, b2)/b.rows();\n        double RMSE = Math.sqrt(MSE);\n        CvScalar mean = new CvScalar(), stddev = new CvScalar();\n        cvAvgSdv(b, mean, stddev, null);\n        double R2  = 1 - MSE/(stddev.val(0)*stddev.val(0));\n        //System.out.println(\"RMSE: \" + RMSE + \" R2: \" + R2);\n        //System.out.println(\"b2 =\\n\" + b2);\n\n        device.colorMixingMatrix = CvMat.create(3, 3);\n        device.additiveLight     = CvMat.create(3, 1);\n        for (int i = 0; i < 3; i++) {\n            double x0 = x.get(i*4  );\n            double x1 = x.get(i*4+1);\n            double x2 = x.get(i*4+2);\n            double x3 = x.get(i*4+3);\n            device.colorMixingMatrix.put(i*3  , x0);\n            device.colorMixingMatrix.put(i*3+1, x1);\n            device.colorMixingMatrix.put(i*3+2, x2);\n            device.additiveLight    .put(i,     x3);\n        }\n\n        //System.out.println(device.colorMixingMatrix);\n        //System.out.println(device.additiveLight);\n\n        device.colorR2 = R2;\n        return device.avgColorErr = RMSE;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/DC1394FrameGrabber.java",
    "content": "/*\n * Copyright (C) 2009-2016 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.ShortBuffer;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Loader;\n\nimport org.bytedeco.libdc1394.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.libdc1394.global.dc1394.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class DC1394FrameGrabber extends FrameGrabber {\n    public static String[] getDeviceDescriptions() throws Exception {\n        tryLoad();\n\n        dc1394_t d = dc1394_new();\n        if (d == null) {\n            throw new Exception(\"dc1394_new() Error: Failed to initialize libdc1394.\");\n        }\n        dc1394camera_list_t list = new dc1394camera_list_t(null);\n        int err = dc1394_camera_enumerate(d, list);\n        if (err != DC1394_SUCCESS) {\n            throw new Exception(\"dc1394_camera_enumerate() Error \" + err + \": Failed to enumerate cameras.\");\n        }\n        int num = list.num();\n        String[] descriptions = new String[num];\n\n        if (num > 0) {\n            dc1394camera_id_t ids = list.ids();\n            for (int i = 0; i < num; i ++) {\n                ids.position(i);\n                dc1394camera_t camera = dc1394_camera_new_unit(d, ids.guid(), ids.unit());\n                if (camera == null) {\n                    throw new Exception(\"dc1394_camera_new_unit() Error: Failed to initialize camera with GUID 0x\" +\n                            Long.toHexString(ids.guid())+ \" / \" + camera.unit() + \".\");\n                }\n                descriptions[i] = camera.vendor().getString() + \" \" + camera.model().getString() + \" 0x\" +\n                        Long.toHexString(camera.guid()) + \" / \" + camera.unit();\n                dc1394_camera_free(camera);\n            }\n        }\n\n        dc1394_camera_free_list(list);\n        dc1394_free(d);\n        return descriptions;\n    }\n\n    public static DC1394FrameGrabber createDefault(File deviceFile)   throws Exception { throw new Exception(DC1394FrameGrabber.class + \" does not support device files.\"); }\n    public static DC1394FrameGrabber createDefault(String devicePath) throws Exception { throw new Exception(DC1394FrameGrabber.class + \" does not support device paths.\"); }\n    public static DC1394FrameGrabber createDefault(int deviceNumber)  throws Exception { return new DC1394FrameGrabber(deviceNumber); }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.libdc1394.global.dc1394.class);\n            } catch (Throwable t) {\n                throw loadingException = new Exception(\"Failed to load \" + DC1394FrameGrabber.class, t);\n            }\n        }\n    }\n\n    public DC1394FrameGrabber(int deviceNumber) throws Exception {\n        d = dc1394_new();\n        dc1394camera_list_t list = new dc1394camera_list_t(null);\n        int err = dc1394_camera_enumerate (d, list);\n        if (err != DC1394_SUCCESS) {\n            throw new Exception(\"dc1394_camera_enumerate() Error \" + err + \": Failed to enumerate cameras.\");\n        }\n        int num = list.num();\n        if (num <= deviceNumber) {\n            throw new Exception(\"DC1394Grabber() Error: Camera number \" + deviceNumber +\n                    \" not found. There are only \" + num + \" devices.\");\n        }\n        dc1394camera_id_t ids = list.ids().position(deviceNumber);\n        camera = dc1394_camera_new_unit(d, ids.guid(), ids.unit());\n        if (camera == null) {\n            throw new Exception(\"dc1394_camera_new_unit() Error: Failed to initialize camera with GUID 0x\" +\n                    Long.toHexString(ids.guid())+ \" / \" + camera.unit() + \".\");\n        }\n        dc1394_camera_free_list(list);\n//System.out.println(\"Using camera with GUID 0x\" + Long.toHexString(camera.guid) + \" / \" + camera.unit);\n    }\n    public void release() throws Exception {\n        if (camera != null) {\n            stop();\n            dc1394_camera_free(camera);\n            camera = null;\n        }\n        if (d != null) {\n            dc1394_free(d);\n            d = null;\n        }\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    private static final boolean linux = Loader.getPlatform().startsWith(\"linux\");\n    private dc1394_t d = null;\n    private dc1394camera_t camera = null;\n    private pollfd fds = linux ? new pollfd() : null;\n    private boolean oneShotMode = false;\n    private boolean resetDone   = false;\n    private dc1394video_frame_t[] raw_image =\n        { new dc1394video_frame_t(null), new dc1394video_frame_t(null) };\n    private dc1394video_frame_t conv_image = new dc1394video_frame_t();\n    private dc1394video_frame_t frame = null;\n    private dc1394video_frame_t enqueue_image = null;\n    private IplImage temp_image, return_image = null;\n    private FrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n    private final int[] out = new int[1];\n    private final float[] outFloat = new float[1];\n    private final float[] gammaOut = new float[1];\n\n    @Override public double getGamma() {\n        return Float.isNaN(gammaOut[0]) || Float.isInfinite(gammaOut[0]) || gammaOut[0] == 0.0f ? 2.2 : gammaOut[0];\n    }\n\n    @Override public int getImageWidth() {\n        return return_image == null ? super.getImageWidth() : return_image.width();\n    }\n\n    @Override public int getImageHeight() {\n        return return_image == null ? super.getImageHeight() : return_image.height();\n    }\n\n    @Override public double getFrameRate() {\n        if (camera == null) {\n            return super.getFrameRate();\n        } else {\n            if (dc1394_feature_get_absolute_value(camera, \n                    DC1394_FEATURE_FRAME_RATE, outFloat) != DC1394_SUCCESS) {\n                dc1394_video_get_framerate(camera, out);\n                dc1394_framerate_as_float(out[0], outFloat);\n            }\n            return outFloat[0];\n        }\n    }\n\n    @Override public void setImageMode(ImageMode imageMode) {\n        if (imageMode != this.imageMode) {\n            temp_image = null;\n            return_image = null;\n        }\n        super.setImageMode(imageMode);\n    }\n\n    public void start() throws Exception {\n        start(true, true);\n    }\n    public void start(boolean tryReset, boolean try1394b) throws Exception {\n        int c = -1;\n        if (imageMode == ImageMode.COLOR || imageMode == ImageMode.RAW) {\n            if (imageWidth <= 0 || imageHeight <= 0) {\n                c = -1;\n            } else if (imageWidth <= 640 && imageHeight <= 480) {\n                c = DC1394_VIDEO_MODE_640x480_RGB8;\n            } else if (imageWidth <= 800 && imageHeight <= 600) {\n                c = DC1394_VIDEO_MODE_800x600_RGB8;\n            } else if (imageWidth <= 1024 && imageHeight <= 768) {\n                c = DC1394_VIDEO_MODE_1024x768_RGB8;\n            } else if (imageWidth <= 1280 && imageHeight <= 960) {\n                c = DC1394_VIDEO_MODE_1280x960_RGB8;\n            } else if (imageWidth <= 1600 && imageHeight <= 1200) {\n                c = DC1394_VIDEO_MODE_1600x1200_RGB8;\n            }\n        } else if (imageMode == ImageMode.GRAY) {\n            if (imageWidth <= 0 || imageHeight <= 0) {\n                c = -1;\n            } else if (imageWidth <= 640 && imageHeight <= 480) {\n                c = bpp > 8 ? DC1394_VIDEO_MODE_640x480_MONO16 : DC1394_VIDEO_MODE_640x480_MONO8;\n            } else if (imageWidth <= 800 && imageHeight <= 600) {\n                c = bpp > 8 ? DC1394_VIDEO_MODE_800x600_MONO16 : DC1394_VIDEO_MODE_800x600_MONO8;\n            } else if (imageWidth <= 1024 && imageHeight <= 768) {\n                c = bpp > 8 ? DC1394_VIDEO_MODE_1024x768_MONO16 : DC1394_VIDEO_MODE_1024x768_MONO8;\n            } else if (imageWidth <= 1280 && imageHeight <= 960) {\n                c = bpp > 8 ? DC1394_VIDEO_MODE_1280x960_MONO16 : DC1394_VIDEO_MODE_1280x960_MONO8;\n            } else if (imageWidth <= 1600 && imageHeight <= 1200) {\n                c = bpp > 8 ? DC1394_VIDEO_MODE_1600x1200_MONO16 : DC1394_VIDEO_MODE_1600x1200_MONO8;\n            }\n        }\n        \n        if (c == -1) {\n            // otherwise, still need to set current video mode to kick start the ISO channel...\n            dc1394_video_get_mode(camera, out);\n            c = out[0];\n        }\n\n        int f = -1;\n        if (frameRate <= 0) {\n            f = -1;\n        } else if (frameRate <= 1.876) {\n            f = DC1394_FRAMERATE_1_875;\n        } else if (frameRate <= 3.76) {\n            f = DC1394_FRAMERATE_3_75;\n        } else if (frameRate <= 7.51) {\n            f = DC1394_FRAMERATE_7_5;\n        } else if (frameRate <= 15.01) {\n            f = DC1394_FRAMERATE_15;\n        } else if (frameRate <= 30.01) {\n            f = DC1394_FRAMERATE_30;\n        } else if (frameRate <= 60.01) {\n            f = DC1394_FRAMERATE_60;\n        } else if (frameRate <= 120.01) {\n            f = DC1394_FRAMERATE_120;\n        } else if (frameRate <= 240.01) {\n            f = DC1394_FRAMERATE_240;\n        }\n\n        if (f == -1) {\n            // otherwise, still need to set current framerate to kick start the ISO channel...\n            dc1394_video_get_framerate(camera, out);\n            f = out[0];\n        }\n\n        try {\n            oneShotMode = false;\n            if (triggerMode) {\n                int err = dc1394_external_trigger_set_power(camera, DC1394_ON);\n                if (err != DC1394_SUCCESS) {\n                    // no trigger support, use one-shot mode instead\n                    oneShotMode = true;\n                } else {\n                    err = dc1394_external_trigger_set_mode(camera, DC1394_TRIGGER_MODE_14);\n                    if (err != DC1394_SUCCESS) {\n                        // try with trigger mode 0 instead\n                        err = dc1394_external_trigger_set_mode(camera, DC1394_TRIGGER_MODE_0);\n                    }\n                    err = dc1394_external_trigger_set_source(camera, DC1394_TRIGGER_SOURCE_SOFTWARE);\n                    if (err != DC1394_SUCCESS) {\n                        // no support for software trigger, use one-shot mode instead\n                        oneShotMode = true;\n                        dc1394_external_trigger_set_power(camera, DC1394_OFF);\n                    }\n                }\n            }\n\n            int err = dc1394_video_set_operation_mode(camera, DC1394_OPERATION_MODE_LEGACY);\n            if (try1394b) {\n                err = dc1394_video_set_operation_mode(camera, DC1394_OPERATION_MODE_1394B);\n                if (err == DC1394_SUCCESS) {\n                    err = dc1394_video_set_iso_speed(camera, DC1394_ISO_SPEED_800);\n                }\n            }\n            if (err != DC1394_SUCCESS || !try1394b) {\n                err = dc1394_video_set_iso_speed(camera, DC1394_ISO_SPEED_400);\n                if (err != DC1394_SUCCESS) {\n                    throw new Exception(\"dc1394_video_set_iso_speed() Error \" + err + \": Could not set maximum iso speed.\");\n                }\n            }\n\n            err = dc1394_video_set_mode(camera, c);\n            if (err != DC1394_SUCCESS) {\n                throw new Exception(\"dc1394_video_set_mode() Error \" + err + \": Could not set video mode.\");\n            }\n\n            if (dc1394_is_video_mode_scalable(c) == DC1394_TRUE) {\n                err = dc1394_format7_set_roi(camera, c,\n                        DC1394_QUERY_FROM_CAMERA, DC1394_QUERY_FROM_CAMERA,\n                        DC1394_QUERY_FROM_CAMERA, DC1394_QUERY_FROM_CAMERA,\n                        DC1394_QUERY_FROM_CAMERA, DC1394_QUERY_FROM_CAMERA);\n                if (err != DC1394_SUCCESS) {\n                    throw new Exception(\"dc1394_format7_set_roi() Error \" + err + \": Could not set format7 mode.\");\n                }\n            } else {\n                err = dc1394_video_set_framerate(camera, f);\n                if (err != DC1394_SUCCESS) {\n                    throw new Exception(\"dc1394_video_set_framerate() Error \" + err + \": Could not set framerate.\");\n                }\n            }\n\n            err = dc1394_capture_setup(camera, numBuffers, DC1394_CAPTURE_FLAGS_DEFAULT);\n            if (err != DC1394_SUCCESS) {\n                throw new Exception(\"dc1394_capture_setup() Error \" + err + \": Could not setup camera-\\n\" +\n                        \"make sure that the video mode and framerate are\\nsupported by your camera.\");\n            }\n\n            if (gamma != 0.0) {\n                err = dc1394_feature_set_absolute_value(camera, DC1394_FEATURE_GAMMA, (float)gamma);\n                if (err != DC1394_SUCCESS) {\n                    throw new Exception(\"dc1394_feature_set_absolute_value() Error \" + err + \": Could not set gamma.\");\n                }\n            }\n            err = dc1394_feature_get_absolute_value(camera, DC1394_FEATURE_GAMMA, gammaOut);\n            if (err != DC1394_SUCCESS) {\n                gammaOut[0] = 2.2f;\n            }\n\n            if (linux) {\n                fds.fd(dc1394_capture_get_fileno(camera));\n            }\n\n            if (!oneShotMode) {\n                err = dc1394_video_set_transmission(camera, DC1394_ON);\n                if (err != DC1394_SUCCESS) {\n                    throw new Exception(\"dc1394_video_set_transmission() Error \" + err + \": Could not start camera iso transmission.\");\n                }\n            }\n        } catch (Exception e) {\n            // if we couldn't start, try again with a bus reset\n            if (tryReset && !resetDone) {\n                try {\n                    dc1394_reset_bus(camera);\n                    Thread.sleep(100);\n                    resetDone = true;\n                    start(false, try1394b);\n                } catch (InterruptedException ex) {\n                    // reset interrupt to be nice\n                    Thread.currentThread().interrupt();\n                    throw new Exception(\"dc1394_reset_bus() Error: Could not reset bus and try to start again.\", ex);\n                }\n            } else {\n                throw e;\n            }\n        } finally {\n            resetDone = false;\n        }\n\n        if (linux && try1394b) {\n            if (triggerMode) {\n                trigger();\n            }\n            fds.events(POLLIN);\n            if (poll(fds, 1, timeout) == 0) {\n                // we are obviously not getting anything..\n                // try again without 1394b\n                stop();\n                start(tryReset, false);\n            } else if (triggerMode) {\n                grab();\n                enqueue();\n            }\n        }\n    }\n\n    public void stop() throws Exception {\n        enqueue_image = null;\n        temp_image    = null;\n        return_image  = null;\n        timestamp   = 0;\n        frameNumber = 0;\n\n        int err = dc1394_video_set_transmission(camera, DC1394_OFF);\n        if (err != DC1394_SUCCESS) {\n            throw new Exception(\"dc1394_video_set_transmission() Error \" + err + \": Could not stop the camera?\");\n        }\n        err = dc1394_capture_stop(camera);\n        if (err != DC1394_SUCCESS && err != DC1394_CAPTURE_IS_NOT_SET) {\n            throw new Exception(\"dc1394_capture_stop() Error \" + err + \": Could not stop the camera?\");\n        }\n        err = dc1394_external_trigger_get_mode(camera, out);\n        if (err == DC1394_SUCCESS && out[0] >= DC1394_TRIGGER_MODE_0) {\n            err = dc1394_external_trigger_set_power(camera, DC1394_OFF);\n            if (err != DC1394_SUCCESS) {\n                throw new Exception(\"dc1394_external_trigger_set_power() Error \" + err + \": Could not switch off external trigger.\");\n            }\n        }\n    }\n\n    private void enqueue() throws Exception {\n        enqueue(enqueue_image);\n        enqueue_image = null;\n    }\n    private void enqueue(dc1394video_frame_t image) throws Exception {\n        if (image != null) {\n            int err = dc1394_capture_enqueue(camera, image);\n            if (err != DC1394_SUCCESS) {\n                throw new Exception(\"dc1394_capture_enqueue() Error \" + err + \": Could not release a frame.\");\n            }\n        }\n    }\n\n    public void trigger() throws Exception {\n        enqueue();\n        if (oneShotMode) {\n            int err = dc1394_video_set_one_shot(camera, DC1394_ON);\n            if (err != DC1394_SUCCESS) {\n                throw new Exception(\"dc1394_video_set_one_shot() Error \" + err + \": Could not set camera into one-shot mode.\");\n            }\n        } else {\n            long time = System.currentTimeMillis();\n            do {\n                dc1394_software_trigger_get_power(camera, out);\n                if (System.currentTimeMillis() - time > timeout) {\n                    break;\n                    //throw new Exception(\"trigger() Error: Timeout occured.\");\n                }\n            } while (out[0] == DC1394_ON);\n            int err = dc1394_software_trigger_set_power(camera, DC1394_ON);\n            if (err != DC1394_SUCCESS) {\n                throw new Exception(\"dc1394_software_trigger_set_power() Error \" + err + \": Could not trigger camera.\");\n            }\n        }\n    }\n\n    public Frame grab() throws Exception {\n        enqueue();\n        if (linux) {\n            fds.events(POLLIN);\n            if (poll(fds, 1, timeout) == 0) {\n                throw new Exception(\"poll() Error: Timeout occured. (Has start() been called?)\");\n            }\n        }\n        int i = 0;\n        int err = dc1394_capture_dequeue(camera, DC1394_CAPTURE_POLICY_WAIT, raw_image[i]);\n        if (err != DC1394_SUCCESS) {\n            throw new Exception(\"dc1394_capture_dequeue(WAIT) Error \" + err + \": Could not capture a frame. (Has start() been called?)\");\n        }\n        // try to poll for more images, to get the most recent one...\n        int numDequeued = 0;\n        while (!raw_image[i].isNull()) {\n            enqueue();\n            enqueue_image = raw_image[i];\n            i = (i+1)%2;\n            numDequeued++;\n            err = dc1394_capture_dequeue(camera, DC1394_CAPTURE_POLICY_POLL, raw_image[i]);\n            if (err != DC1394_SUCCESS) {\n                throw new Exception(\"dc1394_capture_dequeue(POLL) Error \" + err + \": Could not capture a frame.\");\n            }\n        }\n        frame = raw_image[(i+1)%2];\n        int w = frame.size(0);\n        int h = frame.size(1);\n        int depth = frame.data_depth();\n        int iplDepth = 0;\n        switch (depth) {\n            case 8:  iplDepth = IPL_DEPTH_8U;  break;\n            case 16: iplDepth = IPL_DEPTH_16U; break;\n            default: assert false;\n        }\n        int stride = frame.stride();\n        int size = frame.image_bytes();\n        int numChannels = stride/w*8/depth;\n        ByteOrder frameEndian = frame.little_endian() != 0 ?\n                ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;\n        boolean alreadySwapped = false;\n        int color_coding = frame.color_coding();\n        boolean colorbayer = color_coding == DC1394_COLOR_CODING_RAW8 ||\n                             color_coding == DC1394_COLOR_CODING_RAW16;\n        boolean colorrgb   = color_coding == DC1394_COLOR_CODING_RGB8 ||\n                             color_coding == DC1394_COLOR_CODING_RGB16;\n        boolean coloryuv   = color_coding == DC1394_COLOR_CODING_YUV411 ||\n                             color_coding == DC1394_COLOR_CODING_YUV422 ||\n                             color_coding == DC1394_COLOR_CODING_YUV444;\n        BytePointer imageData = frame.image();\n\n        if ((depth <= 8 || frameEndian.equals(ByteOrder.nativeOrder())) && !coloryuv &&\n                (imageMode == ImageMode.RAW || (imageMode == ImageMode.COLOR && numChannels == 3) ||\n                (imageMode == ImageMode.GRAY && numChannels == 1 && !colorbayer))) {\n            if (return_image == null) {\n                return_image = IplImage.createHeader(w, h, iplDepth, numChannels);\n            }\n            return_image.widthStep(stride);\n            return_image.imageSize(size);\n            return_image.imageData(imageData);\n        } else {\n            // in the padding, there's sometimes timeframe information and stuff\n            // that libdc1394 will copy for us, so we need to allocate it\n            int padding_bytes = frame.padding_bytes();\n            int padding1 = (int)Math.ceil((double)padding_bytes/(w * depth/8));\n            int padding3 = (int)Math.ceil((double)padding_bytes/(w*3*depth/8));\n            if (return_image == null) {\n                int c       = imageMode == ImageMode.COLOR ? 3 : 1;\n                int padding = imageMode == ImageMode.COLOR ? padding3 : padding1;\n                return_image = IplImage.create(w, h+padding, iplDepth, c);\n                return_image.height(return_image.height() - padding);\n            }\n            if (temp_image == null) {\n                if (imageMode == ImageMode.COLOR &&\n                        (numChannels > 1 || depth > 8) && !coloryuv && !colorbayer) {\n                    temp_image = IplImage.create(w, h+padding1, iplDepth, numChannels);\n                    temp_image.height(temp_image.height() - padding1);\n                } else if (imageMode == ImageMode.GRAY &&\n                        (coloryuv || colorbayer || (colorrgb && depth > 8))) {\n                    temp_image = IplImage.create(w, h+padding3, iplDepth, 3);\n                    temp_image.height(temp_image.height() - padding3);\n                } else if (imageMode == ImageMode.GRAY && colorrgb) {\n                    temp_image = IplImage.createHeader(w, h, iplDepth, 3);\n                } else if (imageMode == ImageMode.COLOR && numChannels == 1 && !coloryuv && !colorbayer) {\n                    temp_image = IplImage.createHeader(w, h, iplDepth, 1);\n                } else {\n                    temp_image = return_image;\n                }\n            }\n            conv_image.size(0, temp_image.width());\n            conv_image.size(1, temp_image.height());\n            if (depth > 8) {\n                conv_image.color_coding(imageMode == ImageMode.RAW  ? DC1394_COLOR_CODING_RAW16  :\n                                        temp_image.nChannels() == 1 ? DC1394_COLOR_CODING_MONO16 :\n                                                                      DC1394_COLOR_CODING_RGB16);\n                conv_image.data_depth(16);\n            } else {\n                conv_image.color_coding(imageMode == ImageMode.RAW  ? DC1394_COLOR_CODING_RAW8   :\n                                        temp_image.nChannels() == 1 ? DC1394_COLOR_CODING_MONO8  :\n                                                                      DC1394_COLOR_CODING_RGB8);\n                conv_image.data_depth(8);\n            }\n            conv_image.stride(temp_image.widthStep());\n            int temp_size = temp_image.imageSize();\n            conv_image.allocated_image_bytes(temp_size).\n                    total_bytes(temp_size).image_bytes(temp_size);\n            conv_image.image(temp_image.imageData());\n\n            if (colorbayer) {\n                // from raw Bayer... invert R and B to get BGR images\n                // (like OpenCV wants them) instead of RGB\n                int c = frame.color_filter();\n                if (c               == DC1394_COLOR_FILTER_RGGB) {\n                    frame.color_filter(DC1394_COLOR_FILTER_BGGR);\n                } else if (c        == DC1394_COLOR_FILTER_GBRG) {\n                    frame.color_filter(DC1394_COLOR_FILTER_GRBG);\n                } else if (c        == DC1394_COLOR_FILTER_GRBG) {\n                    frame.color_filter(DC1394_COLOR_FILTER_GBRG);\n                } else if (c        == DC1394_COLOR_FILTER_BGGR) {\n                    frame.color_filter(DC1394_COLOR_FILTER_RGGB);\n                } else {\n                    assert false;\n                }\n                // other better methods than \"simple\" give garbage at 16 bits..\n                err = dc1394_debayer_frames(frame, conv_image, DC1394_BAYER_METHOD_SIMPLE);\n                frame.color_filter(c);\n                if (err != DC1394_SUCCESS) {\n                    throw new Exception(\"dc1394_debayer_frames() Error \" + err + \": Could not debayer frame.\");\n                }\n            } else if (depth > 8 &&\n                    frame.data_depth()   == conv_image.data_depth() &&\n                    frame.color_coding() == conv_image.color_coding() &&\n                    frame.stride()       == conv_image.stride()) {\n                // we just need a copy to swap bytes..\n                ShortBuffer in  = frame.getByteBuffer().order(frameEndian).asShortBuffer();\n                ShortBuffer out = temp_image.getByteBuffer().order(ByteOrder.nativeOrder()).asShortBuffer();\n                out.put(in);\n                alreadySwapped = true;\n            } else if ((imageMode == ImageMode.GRAY && colorrgb) ||\n                    (imageMode == ImageMode.COLOR && numChannels == 1 && !coloryuv && !colorbayer)) {\n                temp_image.widthStep(stride);\n                temp_image.imageSize(size);\n                temp_image.imageData(imageData);\n            } else if (!colorrgb && (colorbayer || coloryuv || numChannels > 1)) {\n                // from YUV, etc.\n                err = dc1394_convert_frames(frame, conv_image);\n                if (err != DC1394_SUCCESS) {\n                    throw new Exception(\"dc1394_convert_frames() Error \" + err + \": Could not convert frame.\");\n                }\n            }\n\n            if (!alreadySwapped && depth > 8 && !frameEndian.equals(ByteOrder.nativeOrder())) {\n                // ack, the camera's endianness doesn't correspond to our machine ...\n                // swap bytes of 16-bit images\n                ByteBuffer  bb  = temp_image.getByteBuffer();\n                ShortBuffer in  = bb.order(frameEndian).asShortBuffer();\n                ShortBuffer out = bb.order(ByteOrder.nativeOrder()).asShortBuffer();\n                out.put(in);\n            }\n\n            // should we copy the padding as well?\n            if (imageMode == ImageMode.COLOR && numChannels == 1 && !coloryuv && !colorbayer) {\n                cvCvtColor(temp_image, return_image, CV_GRAY2BGR);\n            } else if (imageMode == ImageMode.GRAY && (colorbayer || colorrgb || coloryuv)) {\n                cvCvtColor(temp_image, return_image, CV_BGR2GRAY);\n            }\n        }\n\n        switch (frame.color_filter()) {\n            case DC1394_COLOR_FILTER_RGGB: sensorPattern = SENSOR_PATTERN_RGGB; break;\n            case DC1394_COLOR_FILTER_GBRG: sensorPattern = SENSOR_PATTERN_GBRG; break;\n            case DC1394_COLOR_FILTER_GRBG: sensorPattern = SENSOR_PATTERN_GRBG; break;\n            case DC1394_COLOR_FILTER_BGGR: sensorPattern = SENSOR_PATTERN_BGGR; break;\n            default: sensorPattern = -1L;\n        }\n\n        enqueue_image = frame;\n        timestamp = frame.timestamp();\n        frameNumber += numDequeued;\n//        int[] cycle_timer = { 0 };\n//        long[] local_time = { 0 };\n//        dc1394_read_cycle_timer(camera, cycle_timer, local_time);\n//System.out.println(\"frame age = \" + (local_time[0] - timestamp));\n        return converter.convert(return_image);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FFmpegFrameFilter.java",
    "content": "/*\n * Copyright (C) 2015-2024 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n *\n * Based on the filtering_video.c file included in FFmpeg 2.7.1\n * which is covered by the following copyright notice:\n *\n * Copyright (c) 2010 Nicolas George\n * Copyright (c) 2011 Stefano Sabatini\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.DoubleBuffer;\nimport java.nio.FloatBuffer;\nimport java.nio.IntBuffer;\nimport java.nio.ShortBuffer;\nimport java.util.Locale;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.DoublePointer;\nimport org.bytedeco.javacpp.FloatPointer;\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.Pointer;\nimport org.bytedeco.javacpp.PointerScope;\nimport org.bytedeco.javacpp.PointerPointer;\nimport org.bytedeco.javacpp.ShortPointer;\n\nimport org.bytedeco.ffmpeg.avcodec.*;\nimport org.bytedeco.ffmpeg.avfilter.*;\nimport org.bytedeco.ffmpeg.avformat.*;\nimport org.bytedeco.ffmpeg.avutil.*;\nimport static org.bytedeco.ffmpeg.global.avcodec.*;\nimport static org.bytedeco.ffmpeg.global.avfilter.*;\nimport static org.bytedeco.ffmpeg.global.avformat.*;\nimport static org.bytedeco.ffmpeg.global.avutil.*;\n\n/**\n * A {@link FrameFilter} that uses FFmpeg to filter frames. We can refer to\n * <a href=\"https://ffmpeg.org/ffmpeg-filters.html\">FFmpeg Filters Documentation</a>\n * to get a list of filters and the options we can use. The input image width and\n * height must be specified on the constructor, while other optional values may be\n * set via corresponding properties.\n *\n * @author Samuel Audet\n */\npublic class FFmpegFrameFilter extends FrameFilter {\n\n    public static class Exception extends FrameFilter.Exception {\n        public Exception(String message) { super(message + \" (For more details, make sure FFmpegLogCallback.set() has been called.)\"); }\n        public Exception(String message, Throwable cause) { super(message, cause); }\n    }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.ffmpeg.global.avutil.class);\n                Loader.load(org.bytedeco.ffmpeg.global.avcodec.class);\n                Loader.load(org.bytedeco.ffmpeg.global.avformat.class);\n//                Loader.load(org.bytedeco.ffmpeg.global.postproc.class);\n                Loader.load(org.bytedeco.ffmpeg.global.swresample.class);\n                Loader.load(org.bytedeco.ffmpeg.global.swscale.class);\n                Loader.load(org.bytedeco.ffmpeg.global.avfilter.class);\n\n//                av_register_all();\n//                avfilter_register_all();\n            } catch (Throwable t) {\n                if (t instanceof Exception) {\n                    throw loadingException = (Exception)t;\n                } else {\n                    throw loadingException = new Exception(\"Failed to load \" + FFmpegFrameRecorder.class, t);\n                }\n            }\n        }\n    }\n\n    static {\n        try {\n            tryLoad();\n        } catch (Exception ex) { }\n    }\n\n    public FFmpegFrameFilter(String videoFilters, String audioFilters, int imageWidth, int imageHeight, int audioChannels) {\n        this.filters = videoFilters;\n        this.imageWidth = imageWidth;\n        this.imageHeight = imageHeight;\n        this.pixelFormat = AV_PIX_FMT_BGR24;\n        this.frameRate = 30;\n        this.aspectRatio = 0;\n        this.videoInputs = 1;\n\n        this.afilters = audioFilters;\n        this.audioChannels = audioChannels;\n        this.sampleFormat = AV_SAMPLE_FMT_S16;\n        this.sampleRate = 44100;\n        this.audioInputs = 1;\n    }\n\n    public FFmpegFrameFilter(String filters, int imageWidth, int imageHeight) {\n        this(filters, null, imageWidth, imageHeight, 0);\n    }\n\n    public FFmpegFrameFilter(String afilters, int audioChannels) {\n        this(null, afilters, 0, 0, audioChannels);\n    }\n\n    @Override public void release() throws Exception {\n        synchronized (org.bytedeco.ffmpeg.global.avfilter.class) {\n            releaseUnsafe();\n        }\n    }\n    public synchronized void releaseUnsafe() throws Exception {\n        started = false;\n\n        if (default_layout != null) {\n            default_layout.releaseReference();\n            default_layout = null;\n        }\n\n        if (image_ptr2 != null) {\n            for (int i = 0; i < image_ptr2.length; i++) {\n                av_free(image_ptr2[i]);\n            }\n            image_ptr2 = null;\n        }\n        if (filter_graph != null) {\n            avfilter_graph_free(filter_graph);\n            buffersink_ctx.releaseReference();\n            for (int i = 0; i < buffersrc_ctx.length; i++) {\n                buffersrc_ctx[i].releaseReference();\n                setpts_ctx[i].releaseReference();\n            }\n            time_base.releaseReference();\n            buffersink_ctx = null;\n            buffersrc_ctx = null;\n            setpts_ctx = null;\n            filter_graph = null;\n            time_base = null;\n        }\n        if (afilter_graph != null) {\n            avfilter_graph_free(afilter_graph);\n            abuffersink_ctx.releaseReference();\n            for (int i = 0; i < abuffersrc_ctx.length; i++) {\n                abuffersrc_ctx[i].releaseReference();\n                asetpts_ctx[i].releaseReference();\n            }\n            atime_base.releaseReference();\n            abuffersink_ctx = null;\n            abuffersrc_ctx = null;\n            asetpts_ctx = null;\n            afilter_graph = null;\n            atime_base = null;\n        }\n        if (image_frame != null) {\n            av_frame_free(image_frame);\n            image_frame = null;\n        }\n        if (samples_frame != null) {\n            av_frame_free(samples_frame);\n            samples_frame = null;\n        }\n        if (filt_frame != null) {\n            av_frame_free(filt_frame);\n            filt_frame = null;\n        }\n        frame = null;\n        Pointer.trimMemory();\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    AVFilterContext buffersink_ctx;\n    AVFilterContext[] buffersrc_ctx;\n    AVFilterContext[] setpts_ctx;\n    AVFilterGraph filter_graph;\n    AVRational time_base;\n\n    AVFilterContext abuffersink_ctx;\n    AVFilterContext[] abuffersrc_ctx;\n    AVFilterContext[] asetpts_ctx;\n    AVFilterGraph afilter_graph;\n    AVRational atime_base;\n\n    AVFrame image_frame;\n    AVFrame samples_frame;\n    AVFrame filt_frame;\n\n    BytePointer[] image_ptr, image_ptr2;\n    BytePointer[] samples_ptr;\n    Buffer[] image_buf, image_buf2;\n    Buffer[] samples_buf;\n    Frame frame, inframe;\n    AVChannelLayout default_layout;\n\n    private volatile boolean started = false;\n\n    @Override public int getImageWidth() {\n        return buffersink_ctx != null ? av_buffersink_get_w(buffersink_ctx) : super.getImageWidth();\n    }\n\n    @Override public int getImageHeight() {\n        return buffersink_ctx != null ? av_buffersink_get_h(buffersink_ctx) : super.getImageHeight();\n    }\n\n    @Override public int getPixelFormat() {\n        return buffersink_ctx != null ? av_buffersink_get_format(buffersink_ctx) : super.getPixelFormat();\n    }\n\n    @Override public double getFrameRate() {\n        if (buffersink_ctx != null) {\n            AVRational r = av_buffersink_get_frame_rate(buffersink_ctx);\n            if (r.num() == 0 && r.den() == 0) {\n                r = av_buffersink_get_time_base(buffersink_ctx);\n                return (double)r.den() / r.num();\n            }\n            return (double)r.num() / r.den();\n        } else {\n            return super.getFrameRate();\n        }\n    }\n\n    @Override public double getAspectRatio() {\n        if (buffersink_ctx != null) {\n            AVRational r = av_buffersink_get_sample_aspect_ratio(buffersink_ctx);\n            double a = (double)r.num() / r.den();\n            return a == 0.0 ? 1.0 : a;\n        } else {\n            return super.getAspectRatio();\n        }\n    }\n\n    @Override public int getAudioChannels() {\n        return abuffersink_ctx != null ? av_buffersink_get_channels(abuffersink_ctx) : super.getAudioChannels();\n    }\n\n    @Override public int getSampleFormat() {\n        return abuffersink_ctx != null ? av_buffersink_get_format(abuffersink_ctx) : super.getSampleFormat();\n    }\n\n    @Override public int getSampleRate() {\n        return abuffersink_ctx != null ? av_buffersink_get_sample_rate(abuffersink_ctx) : super.getSampleRate();\n    }\n\n    @Override public void start() throws Exception {\n        synchronized (org.bytedeco.ffmpeg.global.avfilter.class) {\n            startUnsafe();\n        }\n    }\n    public synchronized void startUnsafe() throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (frame != null) {\n            throw new Exception(\"start() has already been called: Call stop() before calling start() again.\");\n        }\n\n        image_frame = av_frame_alloc();\n        samples_frame = av_frame_alloc();\n        filt_frame = av_frame_alloc();\n        image_ptr = new BytePointer[] { null };\n        image_ptr2 = new BytePointer[] { null };\n        image_buf = new Buffer[] { null };\n        image_buf2 = new Buffer[] { null };\n        samples_ptr = new BytePointer[] { null };\n        samples_buf = new Buffer[] { null };\n        frame = new Frame();\n        default_layout = new AVChannelLayout().retainReference();\n\n        if (videoFilterArgs != null && videoInputs != videoFilterArgs.length) {\n            throw new Exception(\"The length of videoFilterArgs is different from videoInputs\");\n        }\n        if (audioFilterArgs != null && audioInputs != audioFilterArgs.length) {\n            throw new Exception(\"The length of audioFilterArgs is different from audioInputs\");\n        }\n        if (image_frame == null || samples_frame == null || filt_frame == null) {\n            throw new Exception(\"Could not allocate frames\");\n        }\n        if (filters != null && imageWidth > 0 && imageHeight > 0 && videoInputs > 0) {\n            startVideoUnsafe();\n        }\n        if (afilters != null && audioChannels > 0 && audioInputs > 0) {\n            startAudioUnsafe();\n        }\n\n        started = true;\n\n        }\n    }\n\n    private void startVideoUnsafe() throws Exception {\n        int ret;\n        AVFilter buffersrc  = avfilter_get_by_name(\"buffer\");\n        AVFilter buffersink = avfilter_get_by_name(\"buffersink\");\n        AVFilter setpts = avfilter_get_by_name(\"setpts\");\n        AVFilterInOut[] outputs = new AVFilterInOut[videoInputs];\n        AVFilterInOut inputs  = avfilter_inout_alloc();\n        AVRational frame_rate = av_d2q(frameRate, 1001000);\n        AVRational time_base = av_inv_q(frame_rate);\n        int pix_fmts[] = { pixelFormat, AV_PIX_FMT_NONE };\n\n        try {\n            filter_graph = avfilter_graph_alloc();\n            if (outputs == null || inputs == null || filter_graph == null) {\n                throw new Exception(\"Could not allocate video filter graph: Out of memory?\");\n            }\n\n            /* buffer video source: the decoded frames from the decoder will be inserted here. */\n            AVRational r = av_d2q(aspectRatio > 0 ? aspectRatio : 1, 255);\n            buffersrc_ctx = new AVFilterContext[videoInputs];\n            setpts_ctx = new AVFilterContext[videoInputs];\n            for (int i = 0; i < videoInputs; i++) {\n                String name = videoInputs > 1 ? i + \":v\" : \"in\";\n                outputs[i] = avfilter_inout_alloc();\n\n                String args = videoFilterArgs != null && videoFilterArgs[i] != null ? videoFilterArgs[i]\n                        : String.format(Locale.ROOT, \"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d:frame_rate=%d/%d\",\n                               imageWidth, imageHeight, pixelFormat, time_base.num(), time_base.den(), r.num(), r.den(), frame_rate.num(), frame_rate.den());\n                ret = avfilter_graph_create_filter(buffersrc_ctx[i] = new AVFilterContext().retainReference(), buffersrc, name,\n                                                   args, null, filter_graph);\n                if (ret < 0) {\n                    throw new Exception(\"avfilter_graph_create_filter() error \" + ret + \": Cannot create video buffer source.\");\n                }\n\n                ret = avfilter_graph_create_filter(setpts_ctx[i] = new AVFilterContext().retainReference(), setpts, videoInputs > 1 ? \"setpts\" + i : \"setpts\",\n                                                   \"N\", null, filter_graph);\n                if (ret < 0) {\n                    throw new Exception(\"avfilter_graph_create_filter() error \" + ret + \": Cannot create setpts filter.\");\n                }\n\n                ret = avfilter_link(buffersrc_ctx[i], 0, setpts_ctx[i], 0);\n                if (ret < 0) {\n                    throw new Exception(\"avfilter_graph_create_filter() error \" + ret + \": Cannot link setpts filter.\");\n                }\n\n                /*\n                 * Set the endpoints for the filter graph. The filter_graph will\n                 * be linked to the graph described by filters_descr.\n                 */\n\n                /*\n                 * The buffer source output must be connected to the input pad of\n                 * the first filter described by filters_descr; since the first\n                 * filter input label is not specified, it is set to \"in\" by\n                 * default.\n                 */\n                outputs[i].name(av_strdup(new BytePointer(name)));\n                outputs[i].filter_ctx(setpts_ctx[i]);\n                outputs[i].pad_idx(0);\n                outputs[i].next(null);\n                if (i > 0) {\n                    outputs[i - 1].next(outputs[i]);\n                }\n            }\n\n            String name = videoInputs > 1 ? \"v\" : \"out\";\n\n            /* buffer video sink: to terminate the filter chain. */\n            ret = avfilter_graph_create_filter(buffersink_ctx = new AVFilterContext().retainReference(), buffersink, name,\n                                               null, null, filter_graph);\n            if (ret < 0) {\n                throw new Exception(\"avfilter_graph_create_filter() error \" + ret + \": Cannot create video buffer sink.\");\n            }\n//            ret = av_opt_set_bin(buffersink_ctx, \"pix_fmts\", new BytePointer(new IntPointer(pix_fmts)), 4, AV_OPT_SEARCH_CHILDREN);\n//            if (ret < 0) {\n//                throw new Exception(\"av_opt_set_bin() error \" + ret + \": Cannot set output pixel format.\");\n//            }\n\n            /*\n             * The buffer sink input must be connected to the output pad of\n             * the last filter described by filters_descr; since the last\n             * filter output label is not specified, it is set to \"out\" by\n             * default.\n             */\n            inputs.name(av_strdup(new BytePointer(name)));\n            inputs.filter_ctx(buffersink_ctx);\n            inputs.pad_idx(0);\n            inputs.next(null);\n            if ((ret = avfilter_graph_parse_ptr(filter_graph, filters,\n                                                inputs, outputs[0], null)) < 0) {\n                throw new Exception(\"avfilter_graph_parse_ptr() error \" + ret);\n            }\n            if ((ret = avfilter_graph_config(filter_graph, null)) < 0) {\n                throw new Exception(\"avfilter_graph_config() error \" + ret);\n            }\n            this.time_base = av_buffersink_get_time_base(buffersink_ctx).retainReference();\n        } finally {\n            avfilter_inout_free(inputs);\n            avfilter_inout_free(outputs[0]);\n        }\n    }\n\n    private void startAudioUnsafe() throws Exception {\n        int ret;\n        AVFilter abuffersrc  = avfilter_get_by_name(\"abuffer\");\n        AVFilter abuffersink = avfilter_get_by_name(\"abuffersink\");\n        AVFilter asetpts = avfilter_get_by_name(\"asetpts\");\n        AVFilterInOut[] aoutputs = new AVFilterInOut[audioInputs];\n        AVFilterInOut ainputs  = avfilter_inout_alloc();\n        int sample_fmts[] = { sampleFormat, AV_PIX_FMT_NONE };\n\n        try {\n            afilter_graph = avfilter_graph_alloc();\n            if (aoutputs == null || ainputs == null || afilter_graph == null) {\n                throw new Exception(\"Could not allocate audio filter graph: Out of memory?\");\n            }\n\n            abuffersrc_ctx = new AVFilterContext[audioInputs];\n            asetpts_ctx = new AVFilterContext[audioInputs];\n            for (int i = 0; i < audioInputs; i++) {\n                String name = audioInputs > 1 ? i + \":a\" : \"in\";\n                aoutputs[i] = avfilter_inout_alloc();\n\n                /* buffer audio source: the decoded frames from the decoder will be inserted here. */\n                av_channel_layout_default(default_layout, audioChannels);\n                String aargs = audioFilterArgs != null && audioFilterArgs[i] != null ? audioFilterArgs[i]\n                        : String.format(Locale.ROOT, \"channels=%d:sample_fmt=%d:sample_rate=%d:channel_layout=%d\",\n                                audioChannels, sampleFormat, sampleRate, default_layout.u_mask());\n                ret = avfilter_graph_create_filter(abuffersrc_ctx[i] = new AVFilterContext().retainReference(), abuffersrc, name,\n                                                   aargs, null, afilter_graph);\n                if (ret < 0) {\n                    throw new Exception(\"avfilter_graph_create_filter() error \" + ret + \": Cannot create audio buffer source.\");\n                }\n\n                ret = avfilter_graph_create_filter(asetpts_ctx[i] = new AVFilterContext().retainReference(), asetpts, audioInputs > 1 ? \"asetpts\" + i : \"asetpts\",\n                                                   \"N\", null, afilter_graph);\n                if (ret < 0) {\n                    throw new Exception(\"avfilter_graph_create_filter() error \" + ret + \": Cannot create asetpts filter.\");\n                }\n\n                ret = avfilter_link(abuffersrc_ctx[i], 0, asetpts_ctx[i], 0);\n                if (ret < 0) {\n                    throw new Exception(\"avfilter_graph_create_filter() error \" + ret + \": Cannot link asetpts filter.\");\n                }\n\n                /*\n                 * Set the endpoints for the filter graph. The filter_graph will\n                 * be linked to the graph described by filters_descr.\n                 */\n\n                /*\n                 * The buffer source output must be connected to the input pad of\n                 * the first filter described by filters_descr; since the first\n                 * filter input label is not specified, it is set to \"in\" by\n                 * default.\n                 */\n                aoutputs[i].name(av_strdup(new BytePointer(name)));\n                aoutputs[i].filter_ctx(asetpts_ctx[i]);\n                aoutputs[i].pad_idx(0);\n                aoutputs[i].next(null);\n                if (i > 0) {\n                    aoutputs[i - 1].next(aoutputs[i]);\n                }\n            }\n\n            String name = audioInputs > 1 ? \"a\" : \"out\";\n\n            /* buffer audio sink: to terminate the filter chain. */\n            ret = avfilter_graph_create_filter(abuffersink_ctx = new AVFilterContext().retainReference(), abuffersink, name,\n                                               null, null, afilter_graph);\n            if (ret < 0) {\n                throw new Exception(\"avfilter_graph_create_filter() error \" + ret + \": Cannot create audio buffer sink.\");\n            }\n//            ret = av_opt_set_bin(abuffersink_ctx, \"sample_fmts\", new BytePointer(new IntPointer(sample_fmts)), 4, AV_OPT_SEARCH_CHILDREN);\n//            if (ret < 0) {\n//                throw new Exception(\"av_opt_set_bin() error \" + ret + \": Cannot set output sample format.\");\n//            }\n\n            /*\n             * The buffer sink input must be connected to the output pad of\n             * the last filter described by filters_descr; since the last\n             * filter output label is not specified, it is set to \"out\" by\n             * default.\n             */\n            ainputs.name(av_strdup(new BytePointer(name)));\n            ainputs.filter_ctx(abuffersink_ctx);\n            ainputs.pad_idx(0);\n            ainputs.next(null);\n            if ((ret = avfilter_graph_parse_ptr(afilter_graph, afilters,\n                                                ainputs, aoutputs[0], null)) < 0) {\n                throw new Exception(\"avfilter_graph_parse_ptr() error \" + ret);\n            }\n            if ((ret = avfilter_graph_config(afilter_graph, null)) < 0) {\n                throw new Exception(\"avfilter_graph_config() error \" + ret);\n            }\n            this.atime_base = av_buffersink_get_time_base(abuffersink_ctx).retainReference();\n        } finally {\n            avfilter_inout_free(ainputs);\n            avfilter_inout_free(aoutputs[0]);\n        }\n    }\n\n    @Override public void stop() throws Exception {\n        release();\n    }\n\n    @Override public void push(Frame frame) throws Exception {\n        push(frame, frame != null && frame.opaque instanceof AVFrame ? ((AVFrame)frame.opaque).format() : AV_PIX_FMT_NONE);\n    }\n    public void push(Frame frame, int pixelFormat) throws Exception {\n        push(0, frame, pixelFormat);\n    }\n    public void push(int n, Frame frame) throws Exception {\n        push(n, frame, frame != null && frame.opaque instanceof AVFrame ? ((AVFrame)frame.opaque).format() : AV_PIX_FMT_NONE);\n    }\n    public synchronized void push(int n, Frame frame, int pixelFormat) throws Exception {\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        inframe = frame;\n        if (frame != null && frame.image != null && buffersrc_ctx != null) {\n            image_frame.pts(frame.timestamp * time_base.den() / (1000000L * time_base.num()));\n            pushImage(n, frame.imageWidth, frame.imageHeight, frame.imageDepth,\n                    frame.imageChannels, frame.imageStride, pixelFormat, frame.image);\n        }\n        if (frame != null && frame.samples != null && abuffersrc_ctx != null) {\n            samples_frame.pts(frame.timestamp * atime_base.den() / (1000000L * atime_base.num()));\n            pushSamples(n, frame.audioChannels, sampleRate, sampleFormat, frame.samples);\n        }\n        if (frame == null || (frame.image == null && frame.samples == null)) {\n            // indicate EOF as required, for example, by the \"palettegen\" filter\n            if (buffersrc_ctx != null && n < buffersrc_ctx.length) {\n                av_buffersrc_add_frame_flags(buffersrc_ctx[n], null, AV_BUFFERSRC_FLAG_PUSH);\n            }\n            if (abuffersrc_ctx != null && n < abuffersrc_ctx.length) {\n                av_buffersrc_add_frame_flags(abuffersrc_ctx[n], null, AV_BUFFERSRC_FLAG_PUSH);\n            }\n        }\n    }\n\n    public synchronized void pushImage(int n, int width, int height, int depth, int channels, int stride, int pixelFormat, Buffer ... image) throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        int ret;\n        int step = stride * Math.abs(depth) / 8;\n        BytePointer data = image[0] instanceof ByteBuffer\n                ? new BytePointer((ByteBuffer)image[0]).position(0)\n                : new BytePointer(new Pointer(image[0]).position(0));\n\n        if (pixelFormat == AV_PIX_FMT_NONE) {\n            if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 3) {\n                pixelFormat = AV_PIX_FMT_BGR24;\n            } else if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 1) {\n                pixelFormat = AV_PIX_FMT_GRAY8;\n            } else if ((depth == Frame.DEPTH_USHORT || depth == Frame.DEPTH_SHORT) && channels == 1) {\n                pixelFormat = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN) ?\n                        AV_PIX_FMT_GRAY16BE : AV_PIX_FMT_GRAY16LE;\n            } else if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 4) {\n                pixelFormat = AV_PIX_FMT_RGBA;\n            } else if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 2) {\n                pixelFormat = AV_PIX_FMT_NV21; // Android's camera capture format\n            } else {\n                throw new Exception(\"Could not guess pixel format of image: depth=\" + depth + \", channels=\" + channels);\n            }\n        }\n\n        if (pixelFormat == AV_PIX_FMT_NV21) {\n            step = width;\n        }\n\n        av_image_fill_arrays(new PointerPointer(image_frame), image_frame.linesize(), data, pixelFormat, width, height, 1);\n        image_frame.linesize(0, step);\n        image_frame.format(pixelFormat);\n        image_frame.width(width);\n        image_frame.height(height);\n\n        /* push the decoded frame into the filtergraph */\n        if ((ret = av_buffersrc_add_frame_flags(buffersrc_ctx[n], image_frame, AV_BUFFERSRC_FLAG_KEEP_REF | AV_BUFFERSRC_FLAG_PUSH)) < 0) {\n            throw new Exception(\"av_buffersrc_add_frame_flags() error \" + ret + \": Error while feeding the filtergraph.\");\n        }\n\n        }\n    }\n\n    public synchronized void pushSamples(int n, int audioChannels, int sampleRate, int sampleFormat, Buffer ... samples) throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        int ret;\n        Pointer[] data = new Pointer[samples.length];\n        int sampleSize = samples != null ? ((samples[0].limit() - samples[0].position()) / (samples.length > 1 ? 1 : audioChannels)) : 0;\n        if (samples != null && samples[0] instanceof ByteBuffer) {\n            sampleFormat = data.length > 1 ? AV_SAMPLE_FMT_U8P : AV_SAMPLE_FMT_U8;\n            for (int i = 0; i < data.length; i++) {\n                data[i] = new BytePointer((ByteBuffer)samples[i]);\n            }\n        } else if (samples != null && samples[0] instanceof ShortBuffer) {\n            sampleFormat = data.length > 1 ? AV_SAMPLE_FMT_S16P : AV_SAMPLE_FMT_S16;\n            for (int i = 0; i < data.length; i++) {\n                data[i] = new ShortPointer((ShortBuffer)samples[i]);\n            }\n        } else if (samples != null && samples[0] instanceof IntBuffer) {\n            sampleFormat = data.length > 1 ? AV_SAMPLE_FMT_S32P : AV_SAMPLE_FMT_S32;\n            for (int i = 0; i < data.length; i++) {\n                data[i] = new IntPointer((IntBuffer)samples[i]);\n            }\n        } else if (samples != null && samples[0] instanceof FloatBuffer) {\n            sampleFormat = data.length > 1 ? AV_SAMPLE_FMT_FLTP : AV_SAMPLE_FMT_FLT;\n            for (int i = 0; i < data.length; i++) {\n                data[i] = new FloatPointer((FloatBuffer)samples[i]);\n            }\n        } else if (samples != null && samples[0] instanceof DoubleBuffer) {\n            sampleFormat = data.length > 1 ? AV_SAMPLE_FMT_DBLP : AV_SAMPLE_FMT_DBL;\n            for (int i = 0; i < data.length; i++) {\n                data[i] = new DoublePointer((DoubleBuffer)samples[i]);\n            }\n        } else if (samples != null) {\n            for (int i = 0; i < data.length; i++) {\n                data[i] = new Pointer(samples[i]);\n            }\n        }\n\n        av_samples_fill_arrays(new PointerPointer(samples_frame), samples_frame.linesize(), new BytePointer(data[0]), audioChannels, sampleSize, sampleFormat, 1);\n        for (int i = 0; i < samples.length; i++) {\n            samples_frame.data(i, new BytePointer(data[i]));\n        }\n        av_channel_layout_default(default_layout, audioChannels);\n        samples_frame.ch_layout(default_layout);\n        samples_frame.nb_samples(sampleSize);\n        samples_frame.format(sampleFormat);\n        samples_frame.sample_rate(sampleRate);\n\n        /* push the decoded frame into the filtergraph */\n        if ((ret = av_buffersrc_add_frame_flags(abuffersrc_ctx[n], samples_frame, AV_BUFFERSRC_FLAG_KEEP_REF | AV_BUFFERSRC_FLAG_PUSH)) < 0) {\n            throw new Exception(\"av_buffersrc_add_frame_flags() error \" + ret + \": Error while feeding the filtergraph.\");\n        }\n\n        }\n    }\n\n    @Override public synchronized Frame pull() throws Exception {\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        frame.keyFrame = false;\n        frame.imageWidth = 0;\n        frame.imageHeight = 0;\n        frame.imageDepth = 0;\n        frame.imageChannels = 0;\n        frame.imageStride = 0;\n        frame.image = null;\n        frame.sampleRate = 0;\n        frame.audioChannels = 0;\n        frame.samples = null;\n        frame.opaque = null;\n\n        Frame f = null;\n        if (f == null && buffersrc_ctx != null) {\n            f = pullImage();\n        }\n        if (f == null && abuffersrc_ctx != null) {\n            f = pullSamples();\n        }\n        if (f == null && inframe != null\n                && ((inframe.image != null && buffersrc_ctx == null)\n                || (inframe.samples != null && abuffersrc_ctx == null))) {\n            f = inframe;\n        }\n        inframe = null;\n        return f;\n    }\n\n    public synchronized Frame pullImage() throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        av_frame_unref(filt_frame);\n\n        /* pull a filtered frame from the filtergraph */\n        int ret = av_buffersink_get_frame(buffersink_ctx, filt_frame);\n        if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF()) {\n            return null;\n        } else if (ret < 0) {\n            throw new Exception(\"av_buffersink_get_frame(): Error occurred: \"\n                    + av_make_error_string(new BytePointer(256), 256, ret).getString());\n        }\n        frame.imageWidth  = filt_frame.width();\n        frame.imageHeight = filt_frame.height();\n        frame.imageDepth = Frame.DEPTH_UBYTE;\n        if (filt_frame.data(1) == null) {\n            frame.imageStride = filt_frame.linesize(0);\n            BytePointer ptr = filt_frame.data(0);\n            // Fix bug on vflip filter, frame.imageStride can be negative\n            // see https://github.com/bytedeco/javacv/issues/975\n            if (ptr != null && !ptr.equals(image_ptr[0])) {\n                image_ptr[0] = ptr.capacity(frame.imageHeight * Math.abs(frame.imageStride));\n                image_buf[0] = ptr.asBuffer();\n            }\n            frame.image = image_buf;\n            frame.image[0].position(0).limit(frame.imageHeight * Math.abs(frame.imageStride));\n            frame.imageChannels = Math.abs(frame.imageStride) / frame.imageWidth;\n            frame.opaque = filt_frame;\n        } else {\n            frame.imageStride = frame.imageWidth;\n            int size = av_image_get_buffer_size(filt_frame.format(), frame.imageWidth, frame.imageHeight, 1);\n            if (image_ptr2[0] == null || image_ptr2[0].capacity() < size) {\n                av_free(image_ptr2[0]);\n                image_ptr2[0] = new BytePointer(av_malloc(size)).capacity(size);\n                image_buf2[0] = image_ptr2[0].asBuffer();\n            }\n            frame.image = image_buf2;\n            frame.image[0].position(0).limit(size);\n            frame.imageChannels = (size + frame.imageWidth * frame.imageHeight - 1) / (frame.imageWidth * frame.imageHeight);\n            ret = av_image_copy_to_buffer(image_ptr2[0].position(0), (int)image_ptr2[0].capacity(),\n                    new PointerPointer(filt_frame), filt_frame.linesize(), filt_frame.format(), frame.imageWidth, frame.imageHeight, 1);\n            if (ret < 0) {\n                throw new Exception(\"av_image_copy_to_buffer() error \" + ret + \": Cannot pull image.\");\n            }\n            frame.opaque = image_ptr2[0];\n        }\n        frame.timestamp = 1000000L * filt_frame.pts() * time_base.num() / time_base.den();\n        return frame;\n\n        }\n    }\n\n    public synchronized Frame pullSamples() throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        av_frame_unref(filt_frame);\n\n        /* pull a filtered frame from the filtergraph */\n        int ret = av_buffersink_get_frame(abuffersink_ctx, filt_frame);\n        if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF()) {\n            return null;\n        } else if (ret < 0) {\n            throw new Exception(\"av_buffersink_get_frame(): Error occurred: \"\n                    + av_make_error_string(new BytePointer(256), 256, ret).getString());\n        }\n        int sample_format = filt_frame.format();\n        int planes = av_sample_fmt_is_planar(sample_format) != 0 ? (int)filt_frame.ch_layout().nb_channels() : 1;\n        int data_size = av_samples_get_buffer_size((IntPointer)null, filt_frame.ch_layout().nb_channels(),\n                filt_frame.nb_samples(), filt_frame.format(), 1) / planes;\n        if (samples_buf == null || samples_buf.length != planes) {\n            samples_ptr = new BytePointer[planes];\n            samples_buf = new Buffer[planes];\n        }\n        frame.audioChannels = filt_frame.ch_layout().nb_channels();\n        frame.sampleRate = filt_frame.sample_rate();\n        frame.samples = samples_buf;\n        frame.opaque = filt_frame;\n        int sample_size = data_size / av_get_bytes_per_sample(sample_format);\n        for (int i = 0; i < planes; i++) {\n            BytePointer p = filt_frame.data(i);\n            if (!p.equals(samples_ptr[i]) || samples_ptr[i].capacity() < data_size) {\n                samples_ptr[i] = p.capacity(data_size);\n                ByteBuffer b   = p.asBuffer();\n                switch (sample_format) {\n                    case AV_SAMPLE_FMT_U8:\n                    case AV_SAMPLE_FMT_U8P:  samples_buf[i] = b; break;\n                    case AV_SAMPLE_FMT_S16:\n                    case AV_SAMPLE_FMT_S16P: samples_buf[i] = b.asShortBuffer();  break;\n                    case AV_SAMPLE_FMT_S32:\n                    case AV_SAMPLE_FMT_S32P: samples_buf[i] = b.asIntBuffer();    break;\n                    case AV_SAMPLE_FMT_FLT:\n                    case AV_SAMPLE_FMT_FLTP: samples_buf[i] = b.asFloatBuffer();  break;\n                    case AV_SAMPLE_FMT_DBL:\n                    case AV_SAMPLE_FMT_DBLP: samples_buf[i] = b.asDoubleBuffer(); break;\n                    default: assert false;\n                }\n            }\n            samples_buf[i].position(0).limit(sample_size);\n        }\n        frame.timestamp = 1000000L * filt_frame.pts() * atime_base.num() / atime_base.den();\n        return frame;\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FFmpegFrameGrabber.java",
    "content": "/*\n * Copyright (C) 2009-2025 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n *\n * Based on the avcodec_sample.0.5.0.c file available at\n * http://web.me.com/dhoerl/Home/Tech_Blog/Entries/2009/1/22_Revised_avcodec_sample.c_files/avcodec_sample.0.5.0.c\n * by Martin Böhme, Stephen Dranger, and David Hoerl\n * as well as on the decoding_encoding.c file included in FFmpeg 0.11.1,\n * and on the decode_video.c file included in FFmpeg 4.4,\n * which is covered by the following copyright notice:\n *\n * Copyright (c) 2001 Fabrice Bellard\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.DoublePointer;\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.Pointer;\nimport org.bytedeco.javacpp.PointerScope;\nimport org.bytedeco.javacpp.PointerPointer;\n\nimport org.bytedeco.ffmpeg.avcodec.*;\nimport org.bytedeco.ffmpeg.avformat.*;\nimport org.bytedeco.ffmpeg.avutil.*;\nimport org.bytedeco.ffmpeg.swresample.*;\nimport org.bytedeco.ffmpeg.swscale.*;\nimport static org.bytedeco.ffmpeg.global.avcodec.*;\nimport static org.bytedeco.ffmpeg.global.avdevice.*;\nimport static org.bytedeco.ffmpeg.global.avformat.*;\nimport static org.bytedeco.ffmpeg.global.avutil.*;\nimport static org.bytedeco.ffmpeg.global.swresample.*;\nimport static org.bytedeco.ffmpeg.global.swscale.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class FFmpegFrameGrabber extends FrameGrabber {\n\n    public static class Exception extends FrameGrabber.Exception {\n        public Exception(String message) { super(message + \" (For more details, make sure FFmpegLogCallback.set() has been called.)\"); }\n        public Exception(String message, Throwable cause) { super(message, cause); }\n    }\n\n    public static String[] getDeviceDescriptions() throws Exception {\n        tryLoad();\n        throw new UnsupportedOperationException(\"Device enumeration not support by FFmpeg.\");\n    }\n\n    public static FFmpegFrameGrabber createDefault(File deviceFile)   throws Exception { return new FFmpegFrameGrabber(deviceFile); }\n    public static FFmpegFrameGrabber createDefault(String devicePath) throws Exception { return new FFmpegFrameGrabber(devicePath); }\n    public static FFmpegFrameGrabber createDefault(int deviceNumber)  throws Exception { throw new Exception(FFmpegFrameGrabber.class + \" does not support device numbers.\"); }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.ffmpeg.global.avutil.class);\n                Loader.load(org.bytedeco.ffmpeg.global.swresample.class);\n                Loader.load(org.bytedeco.ffmpeg.global.avcodec.class);\n                Loader.load(org.bytedeco.ffmpeg.global.avformat.class);\n                Loader.load(org.bytedeco.ffmpeg.global.swscale.class);\n\n                // Register all formats and codecs\n                av_jni_set_java_vm(Loader.getJavaVM(), null);\n//                avcodec_register_all();\n//                av_register_all();\n                avformat_network_init();\n\n                Loader.load(org.bytedeco.ffmpeg.global.avdevice.class);\n                avdevice_register_all();\n            } catch (Throwable t) {\n                if (t instanceof Exception) {\n                    throw loadingException = (Exception)t;\n                } else {\n                    throw loadingException = new Exception(\"Failed to load \" + FFmpegFrameGrabber.class, t);\n                }\n            }\n        }\n    }\n\n    static {\n        try {\n            tryLoad();\n//            FFmpegLockCallback.init();\n        } catch (Exception ex) { }\n    }\n\n    public FFmpegFrameGrabber(URL url) {\n        this(url.toString());\n    }\n    public FFmpegFrameGrabber(File file) {\n        this(file.getAbsolutePath());\n    }\n    public FFmpegFrameGrabber(String filename) {\n        this.filename = filename;\n        this.pixelFormat = AV_PIX_FMT_NONE;\n        this.sampleFormat = AV_SAMPLE_FMT_NONE;\n    }\n    /** Calls {@code FFmpegFrameGrabber(inputStream, Integer.MAX_VALUE - 8)}\n     *  so that the whole input stream is seekable. */\n    public FFmpegFrameGrabber(InputStream inputStream) {\n        this(inputStream, Integer.MAX_VALUE - 8);\n    }\n    /** Set maximumSize to 0 to disable seek and minimize startup time. */\n    public FFmpegFrameGrabber(InputStream inputStream, int maximumSize) {\n        this.inputStream = inputStream;\n        this.closeInputStream = true;\n        this.pixelFormat = AV_PIX_FMT_NONE;\n        this.sampleFormat = AV_SAMPLE_FMT_NONE;\n        this.maximumSize = maximumSize;\n    }\n    public void release() throws Exception {\n        synchronized (org.bytedeco.ffmpeg.global.avcodec.class) {\n            releaseUnsafe();\n        }\n    }\n    public synchronized void releaseUnsafe() throws Exception {\n        started = false;\n\n        if (plane_ptr != null && plane_ptr2 != null) {\n            plane_ptr.releaseReference();\n            plane_ptr2.releaseReference();\n            plane_ptr = plane_ptr2 = null;\n        }\n\n        if (pkt != null) {\n            if (pkt.stream_index() != -1) {\n                av_packet_unref(pkt);\n            }\n            pkt.releaseReference();\n            pkt = null;\n        }\n\n        if (default_layout != null) {\n            default_layout.releaseReference();\n            default_layout = null;\n        }\n\n        // Free the RGB image\n        if (image_ptr != null) {\n            for (int i = 0; i < image_ptr.length; i++) {\n                if (imageMode != ImageMode.RAW) {\n                    av_free(image_ptr[i]);\n                }\n            }\n            image_ptr = null;\n        }\n        if (picture_rgb != null) {\n            av_frame_free(picture_rgb);\n            picture_rgb = null;\n        }\n\n        // Free the native format picture frame\n        if (picture != null) {\n            av_frame_free(picture);\n            picture = null;\n        }\n\n        // Close the video codec\n        if (video_c != null) {\n            avcodec_free_context(video_c);\n            video_c = null;\n        }\n\n        // Free the audio samples frame\n        if (samples_frame != null) {\n            av_frame_free(samples_frame);\n            samples_frame = null;\n        }\n\n        // Close the audio codec\n        if (audio_c != null) {\n            avcodec_free_context(audio_c);\n            audio_c = null;\n        }\n\n        // Close the video file\n        if (inputStream == null && oc != null && !oc.isNull()) {\n            avformat_close_input(oc);\n            oc = null;\n        }\n\n        if (img_convert_ctx != null) {\n            sws_freeContext(img_convert_ctx);\n            img_convert_ctx = null;\n        }\n\n        if (samples_ptr_out != null) {\n            for (int i = 0; i < samples_ptr_out.length; i++) {\n                av_free(samples_ptr_out[i].position(0));\n            }\n            samples_ptr_out = null;\n            samples_buf_out = null;\n        }\n\n        if (samples_convert_ctx != null) {\n            swr_free(samples_convert_ctx);\n            samples_convert_ctx.releaseReference();\n            samples_convert_ctx = null;\n        }\n\n        frameGrabbed  = false;\n        frame         = null;\n        timestamp     = 0;\n        frameNumber   = 0;\n\n        if (inputStream != null) {\n            try {\n                if (oc == null) {\n                    // when called a second time\n                    if (closeInputStream) {\n                        inputStream.close();\n                    }\n                } else if (maximumSize > 0) {\n                    try {\n                        inputStream.reset();\n                    } catch (IOException ex) {\n                        // \"Resetting to invalid mark\", give up?\n                        System.err.println(\"Error on InputStream.reset(): \" + ex);\n                    }\n                }\n            } catch (IOException ex) {\n                throw new Exception(\"Error on InputStream.close(): \", ex);\n            } finally {\n                inputStreams.remove(oc);\n                if (avio != null) {\n                    if (avio.buffer() != null) {\n                        av_free(avio.buffer());\n                        avio.buffer(null);\n                    }\n                    av_free(avio);\n                    avio = null;\n                }\n                if (oc != null) {\n                    avformat_close_input(oc);\n                    oc = null;\n                }\n            }\n        }\n        Pointer.trimMemory();\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    static Map<Pointer,InputStream> inputStreams = Collections.synchronizedMap(new HashMap<Pointer,InputStream>());\n\n    static class ReadCallback extends Read_packet_Pointer_BytePointer_int {\n        @Override public int call(Pointer opaque, BytePointer buf, int buf_size) {\n            try {\n                byte[] b = new byte[buf_size];\n                InputStream is = inputStreams.get(opaque);\n                int size = is.read(b, 0, buf_size);\n                if (size < 0) {\n                    return AVERROR_EOF();\n                } else {\n                    buf.put(b, 0, size);\n                    return size;\n                }\n            }\n            catch (Throwable t) {\n                System.err.println(\"Error on InputStream.read(): \" + t);\n                return -1;\n            }\n        }\n    }\n\n    static class SeekCallback extends Seek_Pointer_long_int {\n        @Override public long call(Pointer opaque, long offset, int whence) {\n            try {\n                InputStream is = inputStreams.get(opaque);\n                long size = 0;\n                switch (whence) {\n                    case 0: is.reset(); break; // SEEK_SET\n                    case 1: break;             // SEEK_CUR\n                    case 2:                    // SEEK_END\n                        is.reset();\n                        while (true) {\n                            long n = is.skip(Long.MAX_VALUE);\n                            if (n == 0) break;\n                            size += n;\n                        }\n                        offset += size;\n                        is.reset();\n                        break;\n                    case AVSEEK_SIZE:\n                        long remaining = 0;\n                        while (true) {\n                            long n = is.skip(Long.MAX_VALUE);\n                            if (n == 0) break;\n                            remaining += n;\n                        }\n                        is.reset();\n                        while (true) {\n                            long n = is.skip(Long.MAX_VALUE);\n                            if (n == 0) break;\n                            size += n;\n                        }\n                        offset = size - remaining;\n                        is.reset();\n                        break;\n                    default: return -1;\n                }\n                long remaining = offset;\n                while (remaining > 0) {\n                    long skipped = is.skip(remaining);\n                    if (skipped == 0) break; // end of the stream\n                    remaining -= skipped;\n                }\n                return whence == AVSEEK_SIZE ? size : 0;\n            } catch (Throwable t) {\n                System.err.println(\"Error on InputStream.reset() or skip(): \" + t);\n                return -1;\n            }\n        }\n    }\n\n    static ReadCallback readCallback = new ReadCallback().retainReference();\n    static SeekCallback seekCallback = new SeekCallback().retainReference();\n\n    private InputStream     inputStream;\n    private boolean         closeInputStream;\n    private int             maximumSize;\n    private AVIOContext     avio;\n    private String          filename;\n    private AVFormatContext oc;\n    private AVStream        video_st, audio_st;\n    private AVCodecContext  video_c, audio_c;\n    private AVFrame         picture, picture_rgb;\n    private BytePointer[]   image_ptr;\n    private Buffer[]        image_buf;\n    private AVFrame         samples_frame;\n    private BytePointer[]   samples_ptr;\n    private Buffer[]        samples_buf;\n    private BytePointer[]   samples_ptr_out;\n    private Buffer[]        samples_buf_out;\n    private PointerPointer  plane_ptr, plane_ptr2;\n    private AVPacket        pkt;\n    private SwsContext      img_convert_ctx;\n    private SwrContext      samples_convert_ctx;\n    private int             samples_channels, samples_format, samples_rate;\n    private boolean         frameGrabbed;\n    private Frame           frame;\n    private int[]           streams;\n    private AVChannelLayout default_layout;\n\n    private volatile boolean started = false;\n\n    public boolean isCloseInputStream() {\n        return closeInputStream;\n    }\n    public void setCloseInputStream(boolean closeInputStream) {\n        this.closeInputStream = closeInputStream;\n    }\n\n    /**\n     * Is there a video stream?\n     * @return  {@code video_st!=null;}\n     */\n    public boolean hasVideo() {\n        return video_st!=null;\n    }\n\n    /**\n     * Is there an audio stream?\n     * @return  {@code audio_st!=null;}\n     */\n    public boolean hasAudio() {\n        return audio_st!=null;\n    }\n\n    @Override public double getGamma() {\n        // default to a gamma of 2.2 for cheap Webcams, DV cameras, etc.\n        if (gamma == 0.0) {\n            return 2.2;\n        } else {\n            return gamma;\n        }\n    }\n\n    @Override public String getFormat() {\n        if (oc == null) {\n            return super.getFormat();\n        } else {\n            return oc.iformat().name().getString();\n        }\n    }\n\n    @Override public int getImageWidth() {\n        return imageWidth > 0 || video_c == null ? super.getImageWidth() : video_c.width();\n    }\n\n    @Override public int getImageHeight() {\n        return imageHeight > 0 || video_c == null ? super.getImageHeight() : video_c.height();\n    }\n\n    @Override public int getAudioChannels() {\n        return audioChannels > 0 || audio_c == null ? super.getAudioChannels() : audio_c.ch_layout().nb_channels();\n    }\n\n    @Override public int getPixelFormat() {\n        if (imageMode == ImageMode.COLOR || imageMode == ImageMode.GRAY) {\n            if (pixelFormat == AV_PIX_FMT_NONE) {\n                return imageMode == ImageMode.COLOR ? AV_PIX_FMT_BGR24 : AV_PIX_FMT_GRAY8;\n            } else {\n                return pixelFormat;\n            }\n        } else if (video_c != null) { // RAW\n            return video_c.pix_fmt();\n        } else {\n            return super.getPixelFormat();\n        }\n    }\n\n    @Override public int getVideoCodec() {\n        return video_c == null ? super.getVideoCodec() : video_c.codec_id();\n    }\n\n    @Override\n    public String getVideoCodecName(){\n        return  video_c == null ? super.getVideoCodecName() : video_c.codec().name().getString();\n    }\n\n    @Override public int getVideoBitrate() {\n        return video_c == null ? super.getVideoBitrate() : (int)video_c.bit_rate();\n    }\n\n    @Override public double getAspectRatio() {\n        if (video_st == null) {\n            return super.getAspectRatio();\n        } else {\n            AVRational r = av_guess_sample_aspect_ratio(oc, video_st, picture);\n            double a = (double)r.num() / r.den();\n            return a == 0.0 ? 1.0 : a;\n        }\n    }\n\n    /** Returns {@link #getVideoFrameRate()} */\n    @Override public double getFrameRate() {\n        return getVideoFrameRate();\n    }\n\n    /**Estimation of audio frames per second.\n     *\n     * Care must be taken as this method may require unnecessary call of\n     * grabFrame(true, false, false, false, false) with frameGrabbed set to true.\n     *\n     * @return (double) getSampleRate()) / samples_frame.nb_samples()\n     * if samples_frame.nb_samples() is not zero, otherwise return 0\n     */\n    public double getAudioFrameRate() {\n        if (audio_st == null) {\n            return 0.0;\n        } else {\n            if (samples_frame == null || samples_frame.nb_samples() == 0) {\n                try {\n                    grabFrame(true, false, false, false, false);\n                    frameGrabbed = true;\n                } catch (Exception e) {\n                    return 0.0;\n                }\n            }\n            if (samples_frame != null && samples_frame.nb_samples() != 0)\n                return ((double) getSampleRate()) / samples_frame.nb_samples();\n            else return 0.0;\n\n        }\n    }\n\n    public double getVideoFrameRate() {\n        if (video_st == null) {\n            return super.getFrameRate();\n        } else {\n            AVRational r = video_st.avg_frame_rate();\n            if (r.num() == 0 && r.den() == 0) {\n                r = video_st.r_frame_rate();\n            }\n            return (double)r.num() / r.den();\n        }\n    }\n\n    @Override public int getAudioCodec() {\n        return audio_c == null ? super.getAudioCodec() : audio_c.codec_id();\n    }\n\n    @Override public String getAudioCodecName() {\n        return audio_c == null ? super.getAudioCodecName() : audio_c.codec().name().getString();\n    }\n\n    @Override public int getAudioBitrate() {\n        return audio_c == null ? super.getAudioBitrate() : (int)audio_c.bit_rate();\n    }\n\n    @Override public int getSampleFormat() {\n        if (sampleMode == SampleMode.SHORT || sampleMode == SampleMode.FLOAT) {\n            if (sampleFormat == AV_SAMPLE_FMT_NONE) {\n                return sampleMode == SampleMode.SHORT ? AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLT;\n            } else {\n                return sampleFormat;\n            }\n        } else if (audio_c != null) { // RAW\n            return audio_c.sample_fmt();\n        } else {\n            return super.getSampleFormat();\n        }\n    }\n\n    @Override public int getSampleRate() {\n        return sampleRate > 0 || audio_c == null ? super.getSampleRate() : audio_c.sample_rate();\n    }\n\n    @Override public Map<String, String> getMetadata() {\n        if (oc == null) {\n            return super.getMetadata();\n        }\n        AVDictionaryEntry entry = null;\n        Map<String, String> metadata = new HashMap<String, String>();\n        while ((entry = av_dict_get(oc.metadata(), \"\", entry, AV_DICT_IGNORE_SUFFIX)) != null) {\n            metadata.put(entry.key().getString(charset), entry.value().getString(charset));\n        }\n        return metadata;\n    }\n\n    @Override public Map<String, String> getVideoMetadata() {\n        if (video_st == null) {\n            return super.getVideoMetadata();\n        }\n        AVDictionaryEntry entry = null;\n        Map<String, String> metadata = new HashMap<String, String>();\n        while ((entry = av_dict_get(video_st.metadata(), \"\", entry, AV_DICT_IGNORE_SUFFIX)) != null) {\n            metadata.put(entry.key().getString(charset), entry.value().getString(charset));\n        }\n        return metadata;\n    }\n\n    @Override public Map<String, String> getAudioMetadata() {\n        if (audio_st == null) {\n            return super.getAudioMetadata();\n        }\n        AVDictionaryEntry entry = null;\n        Map<String, String> metadata = new HashMap<String, String>();\n        while ((entry = av_dict_get(audio_st.metadata(), \"\", entry, AV_DICT_IGNORE_SUFFIX)) != null) {\n            metadata.put(entry.key().getString(charset), entry.value().getString(charset));\n        }\n        return metadata;\n    }\n\n    @Override public String getMetadata(String key) {\n        if (oc == null) {\n            return super.getMetadata(key);\n        }\n        AVDictionaryEntry entry = av_dict_get(oc.metadata(), key, null, 0);\n        return entry == null || entry.value() == null ? null : entry.value().getString(charset);\n    }\n\n    @Override public String getVideoMetadata(String key) {\n        if (video_st == null) {\n            return super.getVideoMetadata(key);\n        }\n        AVDictionaryEntry entry = av_dict_get(video_st.metadata(), key, null, 0);\n        return entry == null || entry.value() == null ? null : entry.value().getString(charset);\n    }\n\n    @Override public String getAudioMetadata(String key) {\n        if (audio_st == null) {\n            return super.getAudioMetadata(key);\n        }\n        AVDictionaryEntry entry = av_dict_get(audio_st.metadata(), key, null, 0);\n        return entry == null || entry.value() == null ? null : entry.value().getString(charset);\n    }\n\n    @Override public Map<String, Buffer> getVideoSideData() {\n        if (video_st == null) {\n            return super.getVideoSideData();\n        }\n        videoSideData = new HashMap<String, Buffer>();\n        for (int i = 0; i < video_st.codecpar().nb_coded_side_data(); i++) {\n            AVPacketSideData sd = video_st.codecpar().coded_side_data().position(i);\n            String key = av_packet_side_data_name(sd.type()).getString();\n            Buffer value = sd.data().capacity(sd.size()).asBuffer();\n            videoSideData.put(key, value);\n        }\n        return videoSideData;\n    }\n\n    @Override public Buffer getVideoSideData(String key) {\n        return getVideoSideData().get(key);\n    }\n\n    /** Returns the rotation in degrees from the side data of the video stream, or 0 if unknown. */\n    public double getDisplayRotation() {\n        ByteBuffer b = (ByteBuffer)getVideoSideData(\"Display Matrix\");\n        return b != null ? av_display_rotation_get(new IntPointer(new BytePointer(b))) : 0;\n    }\n\n    @Override public Map<String, Buffer> getAudioSideData() {\n        if (audio_st == null) {\n            return super.getAudioSideData();\n        }\n        audioSideData = new HashMap<String, Buffer>();\n        for (int i = 0; i < audio_st.codecpar().nb_coded_side_data(); i++) {\n            AVPacketSideData sd = audio_st.codecpar().coded_side_data().position(i);\n            String key = av_packet_side_data_name(sd.type()).getString();\n            Buffer value = sd.data().capacity(sd.size()).asBuffer();\n            audioSideData.put(key, value);\n        }\n        return audioSideData;\n    }\n\n    @Override public Buffer getAudioSideData(String key) {\n        return getAudioSideData().get(key);\n    }\n\n    /** default override of super.setFrameNumber implies setting\n     *  of a frame close to a video frame having that number */\n    @Override public void setFrameNumber(int frameNumber) throws Exception {\n        if (hasVideo()) setTimestamp(Math.round((1000000L * frameNumber + 500000L)/ getFrameRate()));\n        else super.frameNumber = frameNumber;\n    }\n\n    /** if there is video stream tries to seek to video frame with corresponding timestamp\n     *  otherwise sets super.frameNumber only because frameRate==0 if there is no video stream */\n    public void setVideoFrameNumber(int frameNumber) throws Exception {\n        // best guess, AVSEEK_FLAG_FRAME has not been implemented in FFmpeg...\n        if (hasVideo()) setVideoTimestamp(Math.round((1000000L * frameNumber + 500000L)/ getFrameRate()));\n        else super.frameNumber = frameNumber;\n    }\n\n    /** if there is audio stream tries to seek to audio frame with corresponding timestamp\n     *  ignoring otherwise */\n    public void setAudioFrameNumber(int frameNumber) throws Exception {\n        // best guess, AVSEEK_FLAG_FRAME has not been implemented in FFmpeg...\n        if (hasAudio()) setAudioTimestamp(Math.round((1000000L * frameNumber + 500000L)/ getAudioFrameRate()));\n    }\n\n    /** setTimestamp without checking frame content (using old code used in JavaCV versions prior to 1.4.1) */\n    @Override public void setTimestamp(long timestamp) throws Exception {\n        setTimestamp(timestamp, false);\n    }\n\n    /** setTimestamp with possibility to select between old quick seek code or new code\n     * doing check of frame content. The frame check can be useful with corrupted files, when seeking may\n     * end up with an empty frame not containing video nor audio */\n    public void setTimestamp(long timestamp, boolean checkFrame) throws Exception {\n        setTimestamp(timestamp, checkFrame ? EnumSet.of(Frame.Type.VIDEO, Frame.Type.AUDIO) : null);\n    }\n\n    /** setTimestamp with resulting video frame type if there is a video stream.\n     * This should provide precise seek to a video frame containing the requested timestamp\n     * in most cases.\n     * */\n    public void setVideoTimestamp(long timestamp) throws Exception {\n        setTimestamp(timestamp, EnumSet.of(Frame.Type.VIDEO));\n    }\n\n    /** setTimestamp with resulting audio frame type if there is an audio stream.\n     * This should provide precise seek to an audio frame containing the requested timestamp\n     * in most cases.\n     * */\n    public void setAudioTimestamp(long timestamp) throws Exception {\n        setTimestamp(timestamp, EnumSet.of(Frame.Type.AUDIO));\n    }\n\n    /** setTimestamp with a priority the resulting frame should be:\n     *  video (frameTypesToSeek contains only Frame.Type.VIDEO),\n     *  audio (frameTypesToSeek contains only Frame.Type.AUDIO),\n     *  or any (frameTypesToSeek contains both)\n     */\n    private synchronized void setTimestamp(long timestamp, EnumSet<Frame.Type> frameTypesToSeek) throws Exception {\n        int ret;\n        if (oc == null) {\n            super.timestamp = timestamp;\n        } else {\n            timestamp = timestamp * AV_TIME_BASE / 1000000L;\n\n            /* the stream start time */\n            long ts0 = oc.start_time() != AV_NOPTS_VALUE ? oc.start_time() : 0;\n\n            if (frameTypesToSeek != null //new code providing check of frame content while seeking to the timestamp\n                    && (frameTypesToSeek.contains(Frame.Type.VIDEO) || frameTypesToSeek.contains(Frame.Type.AUDIO))\n                    && (hasVideo() || hasAudio())) {\n\n                /*     After the call of ffmpeg's avformat_seek_file(...) with the flag set to AVSEEK_FLAG_BACKWARD\n                 * the decoding position should be located before the requested timestamp in a closest position\n                 * from which all the active streams can be decoded successfully.\n                 * The following seeking consists of two stages:\n                 * 1. Grab frames till the frame corresponding to that \"closest\" position\n                 * (the first frame containing decoded data).\n                 *\n                 * 2. Grab frames till the desired timestamp is reached. The number of steps is restricted\n                 * by doubled estimation of frames between that \"closest\" position and the desired position.\n                 *\n                 * frameTypesToSeek parameter sets the preferred type of frames to seek.\n                 * It can be chosen from three possible types: VIDEO, AUDIO or any of them.\n                 * The setting means only a preference in the type. That is, if VIDEO or AUDIO is\n                 * specified but the file does not have video or audio stream - any type will be used instead.\n                 */\n\n                /* Check if file contains requested streams */\n                if ((frameTypesToSeek.contains(Frame.Type.VIDEO) && !hasVideo() ) ||\n                        (frameTypesToSeek.contains(Frame.Type.AUDIO) && !hasAudio() ))\n                    frameTypesToSeek = EnumSet.of(Frame.Type.VIDEO, Frame.Type.AUDIO);\n\n                /*  If frameTypesToSeek is set explicitly to VIDEO or AUDIO\n                 *  we need to use start time of the corresponding stream\n                 *  instead of the common start time\n                 */\n                if (frameTypesToSeek.size()==1) {\n                    if (frameTypesToSeek.contains(Frame.Type.VIDEO)) {\n                        if (video_st!=null && video_st.start_time() != AV_NOPTS_VALUE) {\n                            AVRational time_base = video_st.time_base();\n                            ts0 = 1000000L * video_st.start_time() * time_base.num() / time_base.den();\n                        }\n                    }\n                    else if (frameTypesToSeek.contains(Frame.Type.AUDIO)) {\n                        if (audio_st!=null && audio_st.start_time() != AV_NOPTS_VALUE) {\n                            AVRational time_base = audio_st.time_base();\n                            ts0 = 1000000L * audio_st.start_time() * time_base.num() / time_base.den();\n                        }\n                    }\n                }\n\n                /*  Sometimes the ffmpeg's avformat_seek_file(...) function brings us not to a position before\n                 *  the desired but few frames after. In case we need a frame-precision seek we may\n                 *  try to request an earlier timestamp.\n                 */\n                long early_ts = timestamp;\n\n                /* add the stream start time */\n                timestamp += ts0;\n                early_ts += ts0;\n\n                long initialSeekPosition = Long.MIN_VALUE;\n                long maxSeekSteps = 0;\n                long count = 0;\n                Frame seekFrame = null;\n                do {\n                    if ((ret = avformat_seek_file(oc, -1, 0L, early_ts, early_ts, AVSEEK_FLAG_BACKWARD)) < 0)\n                        throw new Exception(\"avformat_seek_file() error \" + ret + \": Could not seek file to timestamp \" + timestamp + \".\");\n                    if (video_c != null) {\n                        avcodec_flush_buffers(video_c);\n                    }\n                    if (audio_c != null) {\n                        avcodec_flush_buffers(audio_c);\n                    }\n                    if (pkt.stream_index() != -1) {\n                        av_packet_unref(pkt);\n                        pkt.stream_index(-1);\n                    }\n                    seekFrame = grabFrame(frameTypesToSeek.contains(Frame.Type.AUDIO), frameTypesToSeek.contains(Frame.Type.VIDEO), false, false, false);\n                    if (seekFrame == null) return;\n                    initialSeekPosition = seekFrame.timestamp;\n                    if(early_ts==0L) break;\n                    early_ts-=500000L;\n                    if(early_ts<0) early_ts=0L;\n                } while (initialSeekPosition>timestamp);\n                double frameDuration = 0.0;\n                if (seekFrame.image != null && this.getFrameRate() > 0)\n                    frameDuration =  AV_TIME_BASE / (double)getFrameRate();\n                else if (seekFrame.samples != null && samples_frame != null && getSampleRate() > 0) {\n                    frameDuration =  AV_TIME_BASE * samples_frame.nb_samples() / (double)getSampleRate();\n                }\n\n                if(frameDuration>0.0) {\n                    maxSeekSteps = 0; //no more grab if the distance to the requested timestamp is smaller than frameDuration\n                    if (timestamp - initialSeekPosition + 1 > frameDuration)  //allow for a rounding error\n                              maxSeekSteps = (long)(10*(timestamp - initialSeekPosition)/frameDuration);\n                }\n                else if (initialSeekPosition < timestamp) maxSeekSteps = 1000;\n\n                double delta = 0.0; //for the timestamp correction\n                count = 0;\n                while(count < maxSeekSteps) {\n                    seekFrame = grabFrame(frameTypesToSeek.contains(Frame.Type.AUDIO), frameTypesToSeek.contains(Frame.Type.VIDEO), false, false, false);\n                    if (seekFrame == null) return; //is it better to throw NullPointerException?\n\n                    count++;\n                    double ts=seekFrame.timestamp;\n                    frameDuration = 0.0;\n                    if (seekFrame.image != null && this.getFrameRate() > 0)\n                        frameDuration =  AV_TIME_BASE / (double)getFrameRate();\n                    else if (seekFrame.samples != null && samples_frame != null && getSampleRate() > 0)\n                        frameDuration =  AV_TIME_BASE * samples_frame.nb_samples() / (double)getSampleRate();\n\n                    delta = 0.0;\n                    if (frameDuration>0.0) {\n                        delta = (ts-ts0)/frameDuration - Math.round((ts-ts0)/frameDuration);\n                        if (Math.abs(delta)>0.2) delta=0.0;\n                    }\n                    ts-=delta*frameDuration; // corrected timestamp\n                    if (ts + frameDuration > timestamp) break;\n                }\n            } else { //old quick seeking code used in JavaCV versions prior to 1.4.1\n                /* add the stream start time */\n                timestamp += ts0;\n                if ((ret = avformat_seek_file(oc, -1, Long.MIN_VALUE, timestamp, Long.MAX_VALUE, AVSEEK_FLAG_BACKWARD)) < 0) {\n                    throw new Exception(\"avformat_seek_file() error \" + ret + \": Could not seek file to timestamp \" + timestamp + \".\");\n                }\n                if (video_c != null) {\n                    avcodec_flush_buffers(video_c);\n                }\n                if (audio_c != null) {\n                    avcodec_flush_buffers(audio_c);\n                }\n                if (pkt.stream_index() != -1) {\n                    av_packet_unref(pkt);\n                    pkt.stream_index(-1);\n                }\n                /* comparing to timestamp +/- 1 avoids rouding issues for framerates\n                which are no proper divisors of 1000000, e.g. where\n                av_frame_get_best_effort_timestamp in grabFrame sets this.timestamp\n                to ...666 and the given timestamp has been rounded to ...667\n                (or vice versa)\n                 */\n                int count = 0; // prevent infinite loops with corrupted files\n                while (this.timestamp > timestamp + 1 && grabFrame(true, true, false, false) != null && count++ < 1000) {\n                    // flush frames if seeking backwards\n                }\n                count = 0;\n                while (this.timestamp < timestamp - 1 && grabFrame(true, true, false, false) != null && count++ < 1000) {\n                    // decode up to the desired frame\n                }\n            }\n            frameGrabbed = true;\n        }\n    }\n\n    /** Returns {@link #getLengthInVideoFrames()} */\n    @Override public int getLengthInFrames() {\n        // best guess...\n        return getLengthInVideoFrames();\n    }\n\n    @Override public long getLengthInTime() {\n        return oc.duration() * 1000000L / AV_TIME_BASE;\n    }\n\n    /** Returns {@code (int) Math.round(getLengthInTime() * getFrameRate() / 1000000L)}, which is an approximation in general. */\n    public int getLengthInVideoFrames() {\n        // best guess...\n        return (int) Math.round(getLengthInTime() * getFrameRate() / 1000000L);\n    }\n\n    public int getLengthInAudioFrames() {\n        // best guess...\n        double afr = getAudioFrameRate();\n        if (afr > 0) return (int) (getLengthInTime() * afr / 1000000L);\n        else return 0;\n    }\n\n    public AVFormatContext getFormatContext() {\n        return oc;\n    }\n\n    /** Calls {@code start(true)}. */\n    @Override public void start() throws Exception {\n        start(true);\n    }\n    /** Set findStreamInfo to false to minimize startup time, at the expense of robustness. */\n    public void start(boolean findStreamInfo) throws Exception {\n        synchronized (org.bytedeco.ffmpeg.global.avcodec.class) {\n            startUnsafe(findStreamInfo);\n        }\n    }\n    public void startUnsafe() throws Exception {\n        startUnsafe(true);\n    }\n    public synchronized void startUnsafe(boolean findStreamInfo) throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (oc != null && !oc.isNull()) {\n            throw new Exception(\"start() has already been called: Call stop() before calling start() again.\");\n        }\n\n        int ret;\n        img_convert_ctx = null;\n        oc              = new AVFormatContext(null);\n        video_c         = null;\n        audio_c         = null;\n        plane_ptr       = new PointerPointer(AVFrame.AV_NUM_DATA_POINTERS).retainReference();\n        plane_ptr2      = new PointerPointer(AVFrame.AV_NUM_DATA_POINTERS).retainReference();\n        pkt             = new AVPacket().retainReference();\n        frameGrabbed    = false;\n        frame           = new Frame();\n        timestamp       = 0;\n        frameNumber     = 0;\n        default_layout  = new AVChannelLayout().retainReference();\n\n        pkt.stream_index(-1);\n\n        // Open video file\n        AVInputFormat f = null;\n        if (format != null && format.length() > 0) {\n            if ((f = av_find_input_format(format)) == null) {\n                throw new Exception(\"av_find_input_format() error: Could not find input format \\\"\" + format + \"\\\".\");\n            }\n        }\n        AVDictionary options = new AVDictionary(null);\n        if (frameRate > 0) {\n            AVRational r = av_d2q(frameRate, 1001000);\n            av_dict_set(options, \"framerate\", r.num() + \"/\" + r.den(), 0);\n        }\n        if (pixelFormat >= 0) {\n            av_dict_set(options, \"pixel_format\", av_get_pix_fmt_name(pixelFormat).getString(), 0);\n        } else if (imageMode != ImageMode.RAW) {\n            av_dict_set(options, \"pixel_format\", imageMode == ImageMode.COLOR ? \"bgr24\" : \"gray8\", 0);\n        }\n        if (imageWidth > 0 && imageHeight > 0) {\n            av_dict_set(options, \"video_size\", imageWidth + \"x\" + imageHeight, 0);\n        }\n        if (sampleRate > 0) {\n            av_dict_set(options, \"sample_rate\", \"\" + sampleRate, 0);\n        }\n        if (audioChannels > 0) {\n            av_dict_set(options, \"channels\", \"\" + audioChannels, 0);\n        }\n        for (Entry<String, String> e : this.options.entrySet()) {\n            av_dict_set(options, e.getKey(), e.getValue(), 0);\n        }\n        if (inputStream != null) {\n            if (!inputStream.markSupported()) {\n                inputStream = new BufferedInputStream(inputStream);\n            }\n            inputStream.mark(maximumSize);\n            oc = avformat_alloc_context();\n            avio = avio_alloc_context(new BytePointer(av_malloc(4096)), 4096, 0, oc, readCallback, null, maximumSize > 0 ? seekCallback : null);\n            oc.pb(avio);\n\n            filename = inputStream.toString();\n            inputStreams.put(oc, inputStream);\n        }\n        if ((ret = avformat_open_input(oc, filename, f, options)) < 0) {\n            av_dict_set(options, \"pixel_format\", null, 0);\n            if ((ret = avformat_open_input(oc, filename, f, options)) < 0) {\n                throw new Exception(\"avformat_open_input() error \" + ret + \": Could not open input \\\"\" + filename + \"\\\". (Has setFormat() been called?)\");\n            }\n        }\n        FFmpegLogCallback.logRejectedOptions(options, \"avformat_open_input\");\n        av_dict_free(options);\n\n        oc.max_delay(maxDelay);\n\n        // Retrieve stream information, if desired\n        if (findStreamInfo && (ret = avformat_find_stream_info(oc, (PointerPointer)null)) < 0) {\n            throw new Exception(\"avformat_find_stream_info() error \" + ret + \": Could not find stream information.\");\n        }\n\n        if (av_log_get_level() >= AV_LOG_INFO) {\n            // Dump information about file onto standard error\n            av_dump_format(oc, 0, filename, 0);\n        }\n\n        // Find the first stream with the user-specified disposition property\n        int nb_streams = oc.nb_streams();\n        for (int i = 0; i < nb_streams; i++) {\n            AVStream st = oc.streams(i);\n            AVCodecParameters par = st.codecpar();\n            if (videoStream < 0 && par.codec_type() == AVMEDIA_TYPE_VIDEO && st.disposition() == videoDisposition) {\n                videoStream = i;\n            } else if (audioStream < 0 && par.codec_type() == AVMEDIA_TYPE_AUDIO && st.disposition() == audioDisposition) {\n                audioStream = i;\n            }\n        }\n\n        // Find the first video and audio stream, unless the user specified otherwise\n        video_st = audio_st = null;\n        AVCodecParameters video_par = null, audio_par = null;\n        streams = new int[nb_streams];\n        for (int i = 0; i < nb_streams; i++) {\n            AVStream st = oc.streams(i);\n            // Get a pointer to the codec context for the video or audio stream\n            AVCodecParameters par = st.codecpar();\n            streams[i] = par.codec_type();\n            if (video_st == null && par.codec_type() == AVMEDIA_TYPE_VIDEO\n                    && par.codec_id() != AV_CODEC_ID_NONE && (videoStream < 0 || videoStream == i)) {\n                video_st = st;\n                video_par = par;\n                videoStream = i;\n            } else if (audio_st == null && par.codec_type() == AVMEDIA_TYPE_AUDIO\n                    && par.codec_id() != AV_CODEC_ID_NONE && (audioStream < 0 || audioStream == i)) {\n                audio_st = st;\n                audio_par = par;\n                audioStream = i;\n            }\n        }\n        if (video_st == null && audio_st == null) {\n            throw new Exception(\"Did not find a video or audio stream inside \\\"\" + filename\n                    + \"\\\" for videoStream == \" + videoStream + \" and audioStream == \" + audioStream + \".\");\n        }\n\n        if (video_st != null) {\n            // Find the decoder for the video stream\n            AVCodec codec = avcodec_find_decoder_by_name(videoCodecName);\n            if (codec == null) {\n                codec = avcodec_find_decoder(video_par.codec_id());\n            }\n            if (codec == null) {\n                throw new Exception(\"avcodec_find_decoder() error: Unsupported video format or codec not found: \" + video_par.codec_id() + \".\");\n            }\n\n            /* Allocate a codec context for the decoder */\n            if ((video_c = avcodec_alloc_context3(codec)) == null) {\n                throw new Exception(\"avcodec_alloc_context3() error: Could not allocate video decoding context.\");\n            }\n\n            /* copy the stream parameters from the muxer */\n            if ((ret = avcodec_parameters_to_context(video_c, video_st.codecpar())) < 0) {\n                releaseUnsafe();\n                throw new Exception(\"avcodec_parameters_to_context() error \" + ret + \": Could not copy the video stream parameters.\");\n            }\n\n            options = new AVDictionary(null);\n            for (Entry<String, String> e : videoOptions.entrySet()) {\n                av_dict_set(options, e.getKey(), e.getValue(), 0);\n            }\n\n            // Enable multithreading when available\n            video_c.thread_count(0);\n\n            // Open video codec\n            if ((ret = avcodec_open2(video_c, codec, options)) < 0) {\n                throw new Exception(\"avcodec_open2() error \" + ret + \": Could not open video codec.\");\n            }\n            FFmpegLogCallback.logRejectedOptions(options, \"avcodec_open2\");\n            av_dict_free(options);\n\n            // Hack to correct wrong frame rates that seem to be generated by some codecs\n            if (video_c.time_base().num() > 1000 && video_c.time_base().den() == 1) {\n                video_c.time_base().den(1000);\n            }\n\n            // Allocate video frame and an AVFrame structure for the RGB image\n            if ((picture = av_frame_alloc()) == null) {\n                throw new Exception(\"av_frame_alloc() error: Could not allocate raw picture frame.\");\n            }\n            if ((picture_rgb = av_frame_alloc()) == null) {\n                throw new Exception(\"av_frame_alloc() error: Could not allocate RGB picture frame.\");\n            }\n\n            initPictureRGB();\n        }\n\n        if (audio_st != null) {\n            // Find the decoder for the audio stream\n            AVCodec codec = avcodec_find_decoder_by_name(audioCodecName);\n            if (codec == null) {\n                codec = avcodec_find_decoder(audio_par.codec_id());\n            }\n            if (codec == null) {\n                throw new Exception(\"avcodec_find_decoder() error: Unsupported audio format or codec not found: \" + audio_par.codec_id() + \".\");\n            }\n\n            /* Allocate a codec context for the decoder */\n            if ((audio_c = avcodec_alloc_context3(codec)) == null) {\n                throw new Exception(\"avcodec_alloc_context3() error: Could not allocate audio decoding context.\");\n            }\n\n            /* copy the stream parameters from the muxer */\n            if ((ret = avcodec_parameters_to_context(audio_c, audio_st.codecpar())) < 0) {\n                releaseUnsafe();\n                throw new Exception(\"avcodec_parameters_to_context() error \" + ret + \": Could not copy the audio stream parameters.\");\n            }\n\n            options = new AVDictionary(null);\n            for (Entry<String, String> e : audioOptions.entrySet()) {\n                av_dict_set(options, e.getKey(), e.getValue(), 0);\n            }\n\n            // Enable multithreading when available\n            audio_c.thread_count(0);\n\n            // Open audio codec\n            if ((ret = avcodec_open2(audio_c, codec, options)) < 0) {\n                throw new Exception(\"avcodec_open2() error \" + ret + \": Could not open audio codec.\");\n            }\n            FFmpegLogCallback.logRejectedOptions(options, \"avcodec_open2\");\n            av_dict_free(options);\n\n            // Allocate audio samples frame\n            if ((samples_frame = av_frame_alloc()) == null) {\n                throw new Exception(\"av_frame_alloc() error: Could not allocate audio frame.\");\n            }\n\n            samples_ptr = new BytePointer[] { null };\n            samples_buf = new Buffer[] { null };\n        }\n        started = true;\n\n        }\n    }\n\n    private void initPictureRGB() {\n        int width  = imageWidth  > 0 ? imageWidth  : video_c.width();\n        int height = imageHeight > 0 ? imageHeight : video_c.height();\n\n        switch (imageMode) {\n            case COLOR:\n            case GRAY:\n                // If size changes I new allocation is needed -> free the old one.\n                if (image_ptr != null) {\n                    // First kill all references, then free it.\n                    image_buf = null;\n                    BytePointer[] temp = image_ptr;\n                    image_ptr = null;\n                    av_free(temp[0]);\n                }\n                int fmt = getPixelFormat();\n\n                // work around bug in swscale: https://trac.ffmpeg.org/ticket/1031\n                int align = 64;\n                int stride = width;\n                for (int i = 1; i <= align; i += i) {\n                     stride = (width + (i - 1)) & ~(i - 1);\n                     av_image_fill_linesizes(picture_rgb.linesize(), fmt, stride);\n                     if ((picture_rgb.linesize(0) & (align - 1)) == 0) {\n                        break;\n                    }\n                }\n\n                // Determine required buffer size and allocate buffer\n                int size = av_image_get_buffer_size(fmt, stride, height, 1);\n                image_ptr = new BytePointer[] { new BytePointer(av_malloc(size)).capacity(size) };\n                image_buf = new Buffer[] { image_ptr[0].asBuffer() };\n\n                // Assign appropriate parts of buffer to image planes in picture_rgb\n                // Note that picture_rgb is an AVFrame, but AVFrame is a superset of AVPicture\n                av_image_fill_arrays(new PointerPointer(picture_rgb), picture_rgb.linesize(), image_ptr[0], fmt, stride, height, 1);\n                picture_rgb.format(fmt);\n                picture_rgb.width(width);\n                picture_rgb.height(height);\n                break;\n\n            case RAW:\n                image_ptr = new BytePointer[] { null };\n                image_buf = new Buffer[] { null };\n                break;\n\n            default:\n                assert false;\n        }\n    }\n\n    @Override public void stop() throws Exception {\n        release();\n    }\n\n    @Override public synchronized void trigger() throws Exception {\n        if (oc == null || oc.isNull()) {\n            throw new Exception(\"Could not trigger: No AVFormatContext. (Has start() been called?)\");\n        }\n        if (pkt.stream_index() != -1) {\n            av_packet_unref(pkt);\n            pkt.stream_index(-1);\n        }\n        for (int i = 0; i < numBuffers+1; i++) {\n            if (av_read_frame(oc, pkt) < 0) {\n                return;\n            }\n            av_packet_unref(pkt);\n        }\n    }\n\n    private void processImage() throws Exception {\n        frame.imageWidth  = imageWidth  > 0 ? imageWidth  : video_c.width();\n        frame.imageHeight = imageHeight > 0 ? imageHeight : video_c.height();\n        frame.imageDepth = Frame.DEPTH_UBYTE;\n        switch (imageMode) {\n            case COLOR:\n            case GRAY:\n                // Deinterlace Picture\n                if (deinterlace) {\n                    throw new Exception(\"Cannot deinterlace: Functionality moved to FFmpegFrameFilter.\");\n                }\n\n                // Has the size changed?\n                if (frame.imageWidth != picture_rgb.width() || frame.imageHeight != picture_rgb.height()) {\n                    initPictureRGB();\n                }\n\n                // Copy \"metadata\" fields\n                av_frame_copy_props(picture_rgb, picture);\n\n                // Convert the image into BGR or GRAY format that OpenCV uses\n                img_convert_ctx = sws_getCachedContext(img_convert_ctx,\n                        video_c.width(), video_c.height(), video_c.pix_fmt(),\n                        frame.imageWidth, frame.imageHeight, getPixelFormat(),\n                        imageScalingFlags != 0 ? imageScalingFlags : SWS_BILINEAR,\n                        null, null, (DoublePointer)null);\n                if (img_convert_ctx == null) {\n                    throw new Exception(\"sws_getCachedContext() error: Cannot initialize the conversion context.\");\n                }\n\n                // Convert the image from its native format to RGB or GRAY\n                sws_scale(img_convert_ctx, new PointerPointer(picture), picture.linesize(), 0,\n                        video_c.height(), new PointerPointer(picture_rgb), picture_rgb.linesize());\n                frame.imageStride = picture_rgb.linesize(0);\n                frame.image = image_buf;\n                frame.opaque = picture_rgb;\n                break;\n\n            case RAW:\n                frame.imageStride = picture.linesize(0);\n                BytePointer ptr = picture.data(0);\n                if (ptr != null && !ptr.equals(image_ptr[0])) {\n                    image_ptr[0] = ptr.capacity(frame.imageHeight * frame.imageStride);\n                    image_buf[0] = ptr.asBuffer();\n                }\n                frame.image = image_buf;\n                frame.opaque = picture;\n                break;\n\n            default:\n                assert false;\n        }\n        frame.image[0].limit(frame.imageHeight * frame.imageStride);\n        frame.imageChannels = frame.imageStride / frame.imageWidth;\n    }\n\n    private void processSamples() throws Exception {\n        int ret;\n\n        int sample_format = samples_frame.format();\n        int planes = av_sample_fmt_is_planar(sample_format) != 0 ? (int)samples_frame.ch_layout().nb_channels() : 1;\n        int data_size = av_samples_get_buffer_size((IntPointer)null, audio_c.ch_layout().nb_channels(),\n                samples_frame.nb_samples(), audio_c.sample_fmt(), 1) / planes;\n        if (samples_buf == null || samples_buf.length != planes) {\n            samples_ptr = new BytePointer[planes];\n            samples_buf = new Buffer[planes];\n        }\n        frame.sampleRate = audio_c.sample_rate();\n        frame.audioChannels = audio_c.ch_layout().nb_channels();\n        frame.samples = samples_buf;\n        frame.opaque = samples_frame;\n        int sample_size = data_size / av_get_bytes_per_sample(sample_format);\n        for (int i = 0; i < planes; i++) {\n            BytePointer p = samples_frame.data(i);\n            if (!p.equals(samples_ptr[i]) || samples_ptr[i].capacity() < data_size) {\n                samples_ptr[i] = p.capacity(data_size);\n                ByteBuffer b   = p.asBuffer();\n                switch (sample_format) {\n                    case AV_SAMPLE_FMT_U8:\n                    case AV_SAMPLE_FMT_U8P:  samples_buf[i] = b; break;\n                    case AV_SAMPLE_FMT_S16:\n                    case AV_SAMPLE_FMT_S16P: samples_buf[i] = b.asShortBuffer();  break;\n                    case AV_SAMPLE_FMT_S32:\n                    case AV_SAMPLE_FMT_S32P: samples_buf[i] = b.asIntBuffer();    break;\n                    case AV_SAMPLE_FMT_FLT:\n                    case AV_SAMPLE_FMT_FLTP: samples_buf[i] = b.asFloatBuffer();  break;\n                    case AV_SAMPLE_FMT_DBL:\n                    case AV_SAMPLE_FMT_DBLP: samples_buf[i] = b.asDoubleBuffer(); break;\n                    default: assert false;\n                }\n            }\n            samples_buf[i].position(0).limit(sample_size);\n        }\n\n        if (audio_c.ch_layout().nb_channels() != getAudioChannels() || audio_c.sample_fmt() != getSampleFormat() || audio_c.sample_rate() != getSampleRate()) {\n            if (samples_convert_ctx == null || samples_channels != getAudioChannels() || samples_format != getSampleFormat() || samples_rate != getSampleRate()) {\n                if (samples_convert_ctx == null) {\n                    samples_convert_ctx = new SwrContext().retainReference();\n                }\n                av_channel_layout_default(default_layout, getAudioChannels());\n                if ((ret = swr_alloc_set_opts2(samples_convert_ctx, default_layout, getSampleFormat(), getSampleRate(),\n                        audio_c.ch_layout(), audio_c.sample_fmt(), audio_c.sample_rate(), 0, null)) < 0) {\n                    throw new Exception(\"swr_alloc_set_opts2() error \" + ret + \": Cannot allocate the conversion context.\");\n                } else if ((ret = swr_init(samples_convert_ctx)) < 0) {\n                    throw new Exception(\"swr_init() error \" + ret + \": Cannot initialize the conversion context.\");\n                }\n                samples_channels = getAudioChannels();\n                samples_format = getSampleFormat();\n                samples_rate = getSampleRate();\n            }\n\n            int sample_size_in = samples_frame.nb_samples();\n            int planes_out = av_sample_fmt_is_planar(samples_format) != 0 ? (int)samples_frame.ch_layout().nb_channels() : 1;\n            int sample_size_out = swr_get_out_samples(samples_convert_ctx, sample_size_in);\n            int sample_bytes_out = av_get_bytes_per_sample(samples_format);\n            int buffer_size_out = sample_size_out * sample_bytes_out * (planes_out > 1 ? 1 : samples_channels);\n            if (samples_buf_out == null || samples_buf.length != planes_out || samples_ptr_out[0].capacity() < buffer_size_out) {\n                for (int i = 0; samples_ptr_out != null && i < samples_ptr_out.length; i++) {\n                    av_free(samples_ptr_out[i].position(0));\n                }\n                samples_ptr_out = new BytePointer[planes_out];\n                samples_buf_out = new Buffer[planes_out];\n\n                for (int i = 0; i < planes_out; i++) {\n                    samples_ptr_out[i] = new BytePointer(av_malloc(buffer_size_out)).capacity(buffer_size_out);\n                    ByteBuffer b = samples_ptr_out[i].asBuffer();\n                    switch (samples_format) {\n                        case AV_SAMPLE_FMT_U8:\n                        case AV_SAMPLE_FMT_U8P:  samples_buf_out[i] = b; break;\n                        case AV_SAMPLE_FMT_S16:\n                        case AV_SAMPLE_FMT_S16P: samples_buf_out[i] = b.asShortBuffer();  break;\n                        case AV_SAMPLE_FMT_S32:\n                        case AV_SAMPLE_FMT_S32P: samples_buf_out[i] = b.asIntBuffer();    break;\n                        case AV_SAMPLE_FMT_FLT:\n                        case AV_SAMPLE_FMT_FLTP: samples_buf_out[i] = b.asFloatBuffer();  break;\n                        case AV_SAMPLE_FMT_DBL:\n                        case AV_SAMPLE_FMT_DBLP: samples_buf_out[i] = b.asDoubleBuffer(); break;\n                        default: assert false;\n                    }\n                }\n            }\n            frame.sampleRate = samples_rate;\n            frame.audioChannels = samples_channels;\n            frame.samples = samples_buf_out;\n\n            if ((ret = swr_convert(samples_convert_ctx, plane_ptr.put(samples_ptr_out), sample_size_out, plane_ptr2.put(samples_ptr), sample_size_in)) < 0) {\n                throw new Exception(\"swr_convert() error \" + ret + \": Cannot convert audio samples.\");\n            }\n            for (int i = 0; i < planes_out; i++) {\n                samples_ptr_out[i].position(0).limit(ret * (planes_out > 1 ? 1 : samples_channels));\n                samples_buf_out[i].position(0).limit(ret * (planes_out > 1 ? 1 : samples_channels));\n            }\n        }\n    }\n\n    public Frame grab() throws Exception {\n        return grabFrame(true, true, true, false, true);\n    }\n    public Frame grabImage() throws Exception {\n        return grabFrame(false, true, true, false, false);\n    }\n    public Frame grabSamples() throws Exception {\n        return grabFrame(true, false, true, false, false);\n    }\n    public Frame grabKeyFrame() throws Exception {\n        return grabFrame(false, true, true, true, false);\n    }\n    public Frame grabFrame(boolean doAudio, boolean doVideo, boolean doProcessing, boolean keyFrames) throws Exception {\n        return grabFrame(doAudio, doVideo, doProcessing, keyFrames, true);\n    }\n    public synchronized Frame grabFrame(boolean doAudio, boolean doVideo, boolean doProcessing, boolean keyFrames, boolean doData) throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (oc == null || oc.isNull()) {\n            throw new Exception(\"Could not grab: No AVFormatContext. (Has start() been called?)\");\n        } else if ((!doVideo || video_st == null) && (!doAudio || audio_st == null) && !doData) {\n            return null;\n        }\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        boolean videoFrameGrabbed = frameGrabbed && frame.image != null;\n        boolean audioFrameGrabbed = frameGrabbed && frame.samples != null;\n        boolean dataFrameGrabbed = frameGrabbed && frame.data != null;\n        frameGrabbed = false;\n        if (doVideo && videoFrameGrabbed) {\n            if (doProcessing) {\n                processImage();\n            }\n            frame.keyFrame = (picture.flags() & AVFrame.AV_FRAME_FLAG_KEY) != 0;\n            return frame;\n        } else if (doAudio && audioFrameGrabbed) {\n            if (doProcessing) {\n                processSamples();\n            }\n            frame.keyFrame = (samples_frame.flags() & AVFrame.AV_FRAME_FLAG_KEY) != 0;\n            return frame;\n        } else if (doData && dataFrameGrabbed) {\n            return frame;\n        }\n\n        frame.keyFrame = false;\n        frame.imageWidth = 0;\n        frame.imageHeight = 0;\n        frame.imageDepth = 0;\n        frame.imageChannels = 0;\n        frame.imageStride = 0;\n        frame.image = null;\n        frame.sampleRate = 0;\n        frame.audioChannels = 0;\n        frame.samples = null;\n        frame.data = null;\n        frame.opaque = null;\n        frame.type = null;\n\n        boolean done = false;\n        boolean readPacket = pkt.stream_index() == -1;\n        while (!done) {\n            int ret = 0;\n            if (readPacket) {\n                if (pkt.stream_index() != -1) {\n                    // Free the packet that was allocated by av_read_frame\n                    av_packet_unref(pkt);\n                    pkt.stream_index(-1);\n                }\n                if ((ret = av_read_frame(oc, pkt)) < 0) {\n                    if (ret == AVERROR_EAGAIN()) {\n                        try {\n                            Thread.sleep(10);\n                            continue;\n                        } catch (InterruptedException ex) {\n                            // reset interrupt to be nice\n                            Thread.currentThread().interrupt();\n                            return null;\n                        }\n                    }\n                    if ((doVideo && video_st != null) || (doAudio && audio_st != null)) {\n                        // The video or audio codec may have buffered some frames\n                        pkt.stream_index(doVideo && video_st != null ? video_st.index() : audio_st.index());\n                        pkt.flags(AV_PKT_FLAG_KEY);\n                        pkt.data(null);\n                        pkt.size(0);\n                    } else {\n                        pkt.stream_index(-1);\n                        return null;\n                    }\n                }\n            }\n\n            frame.streamIndex = pkt.stream_index();\n\n            // Is this a packet from the video stream?\n            if (doVideo && video_st != null && frame.streamIndex == video_st.index()\n                    && (!keyFrames || pkt.flags() == AV_PKT_FLAG_KEY)) {\n                // Decode video frame\n                if (readPacket) {\n                    ret = avcodec_send_packet(video_c, pkt);\n                    if (pkt.data() == null && pkt.size() == 0) {\n                        pkt.stream_index(-1);\n                    }\n                    if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF()) {\n                        // The video codec may have buffered some frames\n                    } else if (ret < 0) {\n                        // Ignore errors to emulate the behavior of the old API\n                        // throw new Exception(\"avcodec_send_packet() error \" + ret + \": Error sending a video packet for decoding.\");\n                    }\n                }\n\n                // Did we get a video frame?\n                while (!done) {\n                    ret = avcodec_receive_frame(video_c, picture);\n                    if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF()) {\n                        if (pkt.data() == null && pkt.size() == 0) {\n                            pkt.stream_index(-1);\n                            doVideo = false;\n                            if (doAudio) {\n                                readPacket = false;\n                                break;\n                            }\n                            return null;\n                        } else {\n                            readPacket = true;\n                            break;\n                        }\n                    } else if (ret < 0) {\n                        // Ignore errors to emulate the behavior of the old API\n                        // throw new Exception(\"avcodec_receive_frame() error \" + ret + \": Error during video decoding.\");\n                        readPacket = true;\n                        break;\n                    }\n\n                    if (!keyFrames || picture.pict_type() == AV_PICTURE_TYPE_I) {\n                        long pts = picture.best_effort_timestamp();\n                        AVRational time_base = video_st.time_base();\n                        timestamp = 1000000L * pts * time_base.num() / time_base.den();\n                        long ts0 = oc.start_time() != AV_NOPTS_VALUE ? oc.start_time() : 0;\n                        // best guess, AVCodecContext.frame_number = number of decoded frames...\n                        frameNumber = (int)Math.round((timestamp - ts0) * getFrameRate() / 1000000L);\n                        frame.image = image_buf;\n                        if (doProcessing) {\n                            processImage();\n                        }\n                        /* the picture is allocated by the decoder. no need to\n                           free it */\n                        done = true;\n                        frame.timestamp = timestamp;\n                        frame.keyFrame = (picture.flags() & AVFrame.AV_FRAME_FLAG_KEY) != 0;\n                        frame.pictType = (char)av_get_picture_type_char(picture.pict_type());\n                        frame.type = Frame.Type.VIDEO;\n                    }\n                }\n            } else if (doAudio && audio_st != null && frame.streamIndex == audio_st.index()) {\n                // Decode audio frame\n                if (readPacket) {\n                    ret = avcodec_send_packet(audio_c, pkt);\n                    if (ret < 0) {\n                        // Ignore errors to emulate the behavior of the old API\n                        // throw new Exception(\"avcodec_send_packet() error \" + ret + \": Error sending an audio packet for decoding.\");\n                    }\n                }\n\n                // Did we get an audio frame?\n                while (!done) {\n                    ret = avcodec_receive_frame(audio_c, samples_frame);\n                    if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF()) {\n                        if (pkt.data() == null && pkt.size() == 0) {\n                            pkt.stream_index(-1);\n                            doAudio = false;\n                            return null;\n                        } else {\n                            readPacket = true;\n                            break;\n                        }\n                    } else if (ret < 0) {\n                        // Ignore errors to emulate the behavior of the old API\n                        // throw new Exception(\"avcodec_receive_frame() error \" + ret + \": Error during audio decoding.\");\n                        readPacket = true;\n                        break;\n                    }\n\n                    long pts = samples_frame.best_effort_timestamp();\n                    AVRational time_base = audio_st.time_base();\n                    timestamp = 1000000L * pts * time_base.num() / time_base.den();\n                    frame.samples = samples_buf;\n                    /* if a frame has been decoded, output it */\n                    if (doProcessing) {\n                        processSamples();\n                    }\n                    done = true;\n                    frame.timestamp = timestamp;\n                    frame.keyFrame = (samples_frame.flags() & AVFrame.AV_FRAME_FLAG_KEY) != 0;\n                    frame.type = Frame.Type.AUDIO;\n                }\n            } else if (readPacket && doData\n                    && frame.streamIndex > -1 && frame.streamIndex < streams.length\n                    && streams[frame.streamIndex] != AVMEDIA_TYPE_VIDEO && streams[frame.streamIndex] != AVMEDIA_TYPE_AUDIO) {\n                // Export the stream byte data for non audio / video frames\n                frame.data = pkt.data().position(0).capacity(pkt.size()).asByteBuffer();\n                frame.opaque = pkt;\n                done = true;\n                switch (streams[frame.streamIndex]) {\n                    case AVMEDIA_TYPE_DATA: frame.type = Frame.Type.DATA; break;\n                    case AVMEDIA_TYPE_SUBTITLE: frame.type = Frame.Type.SUBTITLE; break;\n                    case AVMEDIA_TYPE_ATTACHMENT: frame.type = Frame.Type.ATTACHMENT; break;\n                    default: frame.type = null;\n                }\n            } else {\n                // Current packet is not needed (different stream index required)\n                readPacket = true;\n            }\n        }\n        return frame;\n\n        }\n    }\n\n    public synchronized AVPacket grabPacket() throws Exception {\n        if (oc == null || oc.isNull()) {\n            throw new Exception(\"Could not grab: No AVFormatContext. (Has start() been called?)\");\n        }\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        // Return the next frame of a stream.\n        if (av_read_frame(oc, pkt) < 0) {\n            return null;\n        }\n\n        return pkt;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FFmpegFrameRecorder.java",
    "content": "/*\n * Copyright (C) 2009-2025 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n *\n * Based on the output-example.c file included in FFmpeg 0.6.5\n * as well as on the decoding_encoding.c file included in FFmpeg 0.11.1,\n * and on the encode_video.c file included in FFmpeg 4.4,\n * which are covered by the following copyright notice:\n *\n * Libavformat API example: Output a media file in any supported\n * libavformat format. The default codecs are used.\n *\n * Copyright (c) 2001,2003 Fabrice Bellard\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.net.URL;\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.DoubleBuffer;\nimport java.nio.FloatBuffer;\nimport java.nio.IntBuffer;\nimport java.nio.ShortBuffer;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.DoublePointer;\nimport org.bytedeco.javacpp.FloatPointer;\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.Pointer;\nimport org.bytedeco.javacpp.PointerScope;\nimport org.bytedeco.javacpp.PointerPointer;\nimport org.bytedeco.javacpp.ShortPointer;\n\nimport org.bytedeco.ffmpeg.avcodec.*;\nimport org.bytedeco.ffmpeg.avformat.*;\nimport org.bytedeco.ffmpeg.avutil.*;\nimport org.bytedeco.ffmpeg.swresample.*;\nimport org.bytedeco.ffmpeg.swscale.*;\nimport static org.bytedeco.ffmpeg.global.avcodec.*;\nimport static org.bytedeco.ffmpeg.global.avdevice.*;\nimport static org.bytedeco.ffmpeg.global.avformat.*;\nimport static org.bytedeco.ffmpeg.global.avutil.*;\nimport static org.bytedeco.ffmpeg.global.swresample.*;\nimport static org.bytedeco.ffmpeg.global.swscale.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class FFmpegFrameRecorder extends FrameRecorder {\n\n    public static class Exception extends FrameRecorder.Exception {\n        public Exception(String message) { super(message + \" (For more details, make sure FFmpegLogCallback.set() has been called.)\"); }\n        public Exception(String message, Throwable cause) { super(message, cause); }\n    }\n\n    public static FFmpegFrameRecorder createDefault(File f, int w, int h)   throws Exception { return new FFmpegFrameRecorder(f, w, h); }\n    public static FFmpegFrameRecorder createDefault(String f, int w, int h) throws Exception { return new FFmpegFrameRecorder(f, w, h); }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.ffmpeg.global.avutil.class);\n                Loader.load(org.bytedeco.ffmpeg.global.swresample.class);\n                Loader.load(org.bytedeco.ffmpeg.global.avcodec.class);\n                Loader.load(org.bytedeco.ffmpeg.global.avformat.class);\n                Loader.load(org.bytedeco.ffmpeg.global.swscale.class);\n\n                /* initialize libavcodec, and register all codecs and formats */\n                av_jni_set_java_vm(Loader.getJavaVM(), null);\n//                avcodec_register_all();\n//                av_register_all();\n                avformat_network_init();\n\n                Loader.load(org.bytedeco.ffmpeg.global.avdevice.class);\n                avdevice_register_all();\n            } catch (Throwable t) {\n                if (t instanceof Exception) {\n                    throw loadingException = (Exception)t;\n                } else {\n                    throw loadingException = new Exception(\"Failed to load \" + FFmpegFrameRecorder.class, t);\n                }\n            }\n        }\n    }\n\n    static {\n        try {\n            tryLoad();\n//            FFmpegLockCallback.init();\n        } catch (Exception ex) { }\n    }\n\n    public FFmpegFrameRecorder(URL url, int audioChannels) {\n        this(url.toString(), 0, 0, audioChannels);\n    }\n    public FFmpegFrameRecorder(File file, int audioChannels) {\n        this(file, 0, 0, audioChannels);\n    }\n    public FFmpegFrameRecorder(String filename, int audioChannels) {\n        this(filename, 0, 0, audioChannels);\n    }\n    public FFmpegFrameRecorder(URL url, int imageWidth, int imageHeight) {\n        this(url.toString(), imageWidth, imageHeight, 0);\n    }\n    public FFmpegFrameRecorder(File file, int imageWidth, int imageHeight) {\n        this(file, imageWidth, imageHeight, 0);\n    }\n    public FFmpegFrameRecorder(String filename, int imageWidth, int imageHeight) {\n        this(filename, imageWidth, imageHeight, 0);\n    }\n    public FFmpegFrameRecorder(URL url, int imageWidth, int imageHeight, int audioChannels) {\n        this(url.toString(), imageWidth, imageHeight, audioChannels);\n    }\n    public FFmpegFrameRecorder(File file, int imageWidth, int imageHeight, int audioChannels) {\n        this(file.getAbsolutePath(), imageWidth, imageHeight, audioChannels);\n    }\n    public FFmpegFrameRecorder(String filename, int imageWidth, int imageHeight, int audioChannels) {\n        this.filename      = filename;\n        this.imageWidth    = imageWidth;\n        this.imageHeight   = imageHeight;\n        this.audioChannels = audioChannels;\n\n        this.pixelFormat   = AV_PIX_FMT_NONE;\n        this.videoCodec    = AV_CODEC_ID_NONE;\n        this.videoBitrate  = 400000;\n        this.frameRate     = 30;\n\n        this.sampleFormat  = AV_SAMPLE_FMT_NONE;\n        this.audioCodec    = AV_CODEC_ID_NONE;\n        this.audioBitrate  = 64000;\n        this.sampleRate    = 44100;\n\n        this.interleaved = true;\n    }\n\n    public FFmpegFrameRecorder(OutputStream outputStream, int audioChannels) {\n        this(outputStream.toString(), audioChannels);\n        this.outputStream = outputStream;\n        this.closeOutputStream = true;\n    }\n    public FFmpegFrameRecorder(OutputStream outputStream, int imageWidth, int imageHeight) {\n        this(outputStream.toString(), imageWidth, imageHeight);\n        this.outputStream = outputStream;\n        this.closeOutputStream = true;\n    }\n    public FFmpegFrameRecorder(OutputStream outputStream, int imageWidth, int imageHeight, int audioChannels) {\n        this(outputStream.toString(), imageWidth, imageHeight, audioChannels);\n        this.outputStream = outputStream;\n        this.closeOutputStream = true;\n    }\n    public void release() throws Exception {\n        synchronized (org.bytedeco.ffmpeg.global.avcodec.class) {\n            releaseUnsafe();\n        }\n    }\n    public synchronized void releaseUnsafe() throws Exception {\n        started = false;\n\n        if (display_matrix != null) {\n            display_matrix.releaseReference();\n        }\n\n        if (plane_ptr != null && plane_ptr2 != null) {\n            plane_ptr.releaseReference();\n            plane_ptr2.releaseReference();\n            plane_ptr = plane_ptr2 = null;\n        }\n\n        if (video_pkt != null && audio_pkt != null) {\n            video_pkt.releaseReference();\n            audio_pkt.releaseReference();\n            video_pkt = audio_pkt = null;\n        }\n\n        if (default_layout != null) {\n            default_layout.releaseReference();\n            default_layout = null;\n        }\n\n        /* close each codec */\n        if (video_c != null) {\n            avcodec_free_context(video_c);\n            video_c = null;\n        }\n        if (audio_c != null) {\n            avcodec_free_context(audio_c);\n            audio_c = null;\n        }\n        if (picture_buf != null) {\n            av_free(picture_buf);\n            picture_buf = null;\n        }\n        if (picture != null) {\n            av_frame_free(picture);\n            picture = null;\n        }\n        if (tmp_picture != null) {\n            av_frame_free(tmp_picture);\n            tmp_picture = null;\n        }\n        if (video_outbuf != null) {\n            av_free(video_outbuf);\n            video_outbuf = null;\n        }\n        if (frame != null) {\n            av_frame_free(frame);\n            frame = null;\n        }\n        if (samples_in != null) {\n            for (int i = 0; i < samples_in.length; i++) {\n                if (samples_in[i] != null) {\n                    samples_in[i].releaseReference();\n                }\n            }\n            samples_in = null;\n        }\n        if (samples_out != null) {\n            for (int i = 0; i < samples_out.length; i++) {\n                av_free(samples_out[i].position(0));\n            }\n            samples_out = null;\n        }\n        if (audio_outbuf != null) {\n            av_free(audio_outbuf);\n            audio_outbuf = null;\n        }\n        if (video_st != null && video_st.metadata() != null) {\n            av_dict_free(video_st.metadata());\n            video_st.metadata(null);\n        }\n        if (audio_st != null && audio_st.metadata() != null) {\n            av_dict_free(audio_st.metadata());\n            audio_st.metadata(null);\n        }\n        video_st = null;\n        audio_st = null;\n        filename = null;\n\n        AVFormatContext outputStreamKey = oc;\n        if (oc != null && !oc.isNull()) {\n            if (outputStream == null && (oformat.flags() & AVFMT_NOFILE) == 0) {\n                /* close the output file */\n                avio_close(oc.pb());\n            }\n\n            /* free the streams */\n            avformat_free_context(oc);\n            oc = null;\n        }\n\n        if (img_convert_ctx != null) {\n            sws_freeContext(img_convert_ctx);\n            img_convert_ctx = null;\n        }\n\n        if (samples_convert_ctx != null) {\n            swr_free(samples_convert_ctx);\n            samples_convert_ctx.releaseReference();\n            samples_convert_ctx = null;\n        }\n\n        if (outputStream != null) {\n            try {\n                if (closeOutputStream) {\n                    outputStream.close();\n                }\n            } catch (IOException ex) {\n                throw new Exception(\"Error on OutputStream.close(): \", ex);\n            } finally {\n                outputStream = null;\n                outputStreams.remove(outputStreamKey);\n                if (avio != null) {\n                    if (avio.buffer() != null) {\n                        av_free(avio.buffer());\n                        avio.buffer(null);\n                    }\n                    av_free(avio);\n                    avio = null;\n                }\n            }\n        }\n        Pointer.trimMemory();\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    static Map<Pointer,OutputStream> outputStreams = Collections.synchronizedMap(new HashMap<Pointer,OutputStream>());\n\n    static class WriteCallback extends Write_packet_Pointer_BytePointer_int {\n        @Override public int call(Pointer opaque, BytePointer buf, int buf_size) {\n            try {\n                byte[] b = new byte[buf_size];\n                OutputStream os = outputStreams.get(opaque);\n                buf.get(b, 0, buf_size);\n                os.write(b, 0, buf_size);\n                return buf_size;\n            }\n            catch (Throwable t) {\n                System.err.println(\"Error on OutputStream.write(): \" + t);\n                return -1;\n            }\n        }\n    }\n\n    static WriteCallback writeCallback = new WriteCallback().retainReference();\n\n    static class SeekCallback extends Seek_Pointer_long_int {\n\n        @Override public long call(Pointer opaque, long offset, int whence) {\n            try {\n                OutputStream os = outputStreams.get(opaque);\n                ((Seekable)os).seek(offset, whence);\n                return 0;\n            }\n            catch (Throwable t) {\n                System.err.println(\"Error on OutputStream.seek(): \" + t);\n                return -1;\n            }\n        }\n    }\n\n    static SeekCallback seekCallback = new SeekCallback().retainReference();\n\n    private OutputStream outputStream;\n    private boolean closeOutputStream;\n    private AVIOContext avio;\n    private String filename;\n    private AVFrame picture, tmp_picture;\n    private BytePointer picture_buf;\n    private BytePointer video_outbuf;\n    private int video_outbuf_size;\n    private AVFrame frame;\n    private Pointer[] samples_in;\n    private BytePointer[] samples_out;\n    private BytePointer audio_outbuf;\n    private int audio_outbuf_size;\n    private int audio_input_frame_size;\n    private AVOutputFormat oformat;\n    private AVFormatContext oc;\n    private AVCodec video_codec, audio_codec;\n    private AVCodecContext video_c, audio_c;\n    private AVStream video_st, audio_st;\n    private SwsContext img_convert_ctx;\n    private SwrContext samples_convert_ctx;\n    private int samples_channels, samples_format, samples_rate;\n    private PointerPointer plane_ptr, plane_ptr2;\n    private AVPacket video_pkt, audio_pkt;\n    private int[] got_video_packet, got_audio_packet;\n    private boolean wrote_samples = false;\n    private AVFormatContext ifmt_ctx;\n    private IntPointer display_matrix;\n    private AVChannelLayout default_layout;\n\n    private volatile boolean started = false;\n\n    public boolean isCloseOutputStream() {\n        return closeOutputStream;\n    }\n    public void setCloseOutputStream(boolean closeOutputStream) {\n        this.closeOutputStream = closeOutputStream;\n    }\n\n    /** Sets the rotation in degrees to the side data of the video stream. */\n    public void setDisplayRotation(double angle) {\n        if (display_matrix == null) {\n            display_matrix = new IntPointer(9).retainReference();\n        }\n        av_display_rotation_set(display_matrix, -angle);\n        setVideoSideData(\"Display Matrix\", display_matrix.asByteBuffer());\n    }\n\n    @Override public int getFrameNumber() {\n        return picture == null ? super.getFrameNumber() : (int)picture.pts();\n    }\n    @Override public void setFrameNumber(int frameNumber) {\n        if (picture == null) { super.setFrameNumber(frameNumber); } else { picture.pts(frameNumber); }\n    }\n\n    /** Returns best guess for timestamp in microseconds... */\n    @Override public long getTimestamp() {\n        return Math.round(getFrameNumber() * 1000000L / getFrameRate());\n    }\n    @Override public void setTimestamp(long timestamp)  {\n        setFrameNumber((int)Math.round(timestamp * getFrameRate() / 1000000L));\n    }\n\n    public void start(AVFormatContext inputFormatContext) throws Exception {\n        this.ifmt_ctx = inputFormatContext;\n        start();\n    }\n\n    @Override public void start() throws Exception {\n        synchronized (org.bytedeco.ffmpeg.global.avcodec.class) {\n            startUnsafe();\n        }\n    }\n\n    public synchronized void startUnsafe() throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (oc != null && !oc.isNull()) {\n            throw new Exception(\"start() has already been called: Call stop() before calling start() again.\");\n        }\n\n        int ret;\n        picture = null;\n        tmp_picture = null;\n        picture_buf = null;\n        frame = null;\n        video_outbuf = null;\n        audio_outbuf = null;\n        oc = new AVFormatContext(null);\n        video_c = null;\n        audio_c = null;\n        video_st = null;\n        audio_st = null;\n        plane_ptr  = new PointerPointer(AVFrame.AV_NUM_DATA_POINTERS).retainReference();\n        plane_ptr2 = new PointerPointer(AVFrame.AV_NUM_DATA_POINTERS).retainReference();\n        video_pkt = new AVPacket().retainReference();\n        audio_pkt = new AVPacket().retainReference();\n        wrote_samples = false;\n        got_video_packet = new int[1];\n        got_audio_packet = new int[1];\n        default_layout = new AVChannelLayout().retainReference();\n\n        /* auto detect the output format from the name. */\n        String format_name = format == null || format.length() == 0 ? null : format;\n        if ((oformat = av_guess_format(format_name, filename, null)) == null) {\n            int proto = filename.indexOf(\"://\");\n            if (proto > 0) {\n                format_name = filename.substring(0, proto);\n            }\n            if ((oformat = av_guess_format(format_name, filename, null)) == null) {\n                throw new Exception(\"av_guess_format() error: Could not guess output format for \\\"\" + filename + \"\\\" and \" + format + \" format.\");\n            }\n        }\n        format_name = oformat.name().getString();\n\n        /* allocate the output media context */\n        if (avformat_alloc_output_context2(oc, null, format_name, filename) < 0) {\n            throw new Exception(\"avformat_alloc_context2() error:\\tCould not allocate format context\");\n        }\n\n        if (outputStream != null) {\n            avio = avio_alloc_context(new BytePointer(av_malloc(4096)), 4096, 1, oc, null, writeCallback, outputStream instanceof Seekable ? seekCallback : null);\n            oc.pb(avio);\n\n            filename = outputStream.toString();\n            outputStreams.put(oc, outputStream);\n        }\n        oc.oformat(oformat);\n        oc.url(new BytePointer(av_malloc(filename.getBytes().length + 1)).putString(filename));\n        oc.max_delay(maxDelay);\n\n        /* add the audio and video streams using the format codecs\n           and initialize the codecs */\n        AVStream inpVideoStream = null, inpAudioStream = null;\n        if (ifmt_ctx != null) {\n            // get input video and audio stream indices from ifmt_ctx\n            for (int idx = 0; idx < ifmt_ctx.nb_streams(); idx++) {\n                AVStream inputStream = ifmt_ctx.streams(idx);\n                if (inputStream.codecpar().codec_type() == AVMEDIA_TYPE_VIDEO) {\n                    inpVideoStream = inputStream;\n                    videoCodec = inpVideoStream.codecpar().codec_id();\n                    if (inpVideoStream.r_frame_rate().num() != AV_NOPTS_VALUE && inpVideoStream.r_frame_rate().den() != 0) {\n                        frameRate = (inpVideoStream.r_frame_rate().num())*1.0d / (inpVideoStream.r_frame_rate().den());\n                    }\n\n                } else if (inputStream.codecpar().codec_type() == AVMEDIA_TYPE_AUDIO) {\n                    inpAudioStream = inputStream;\n                    audioCodec = inpAudioStream.codecpar().codec_id();\n                }\n            }\n        }\n\n        if (imageWidth > 0 && imageHeight > 0) {\n            if (videoCodec == AV_CODEC_ID_NONE) {\n                videoCodec = oformat.video_codec();\n            }\n//            if (videoCodec != AV_CODEC_ID_NONE) {\n//                oformat.video_codec(videoCodec);\n//            } else if (\"flv\".equals(format_name)) {\n//                oformat.video_codec(AV_CODEC_ID_FLV1);\n//            } else if (\"mp4\".equals(format_name)) {\n//                oformat.video_codec(AV_CODEC_ID_MPEG4);\n//            } else if (\"3gp\".equals(format_name)) {\n//                oformat.video_codec(AV_CODEC_ID_H263);\n//            } else if (\"avi\".equals(format_name)) {\n//                oformat.video_codec(AV_CODEC_ID_HUFFYUV);\n//            }\n\n            /* find the video encoder */\n            if ((video_codec = avcodec_find_encoder_by_name(videoCodecName)) == null &&\n                (video_codec = avcodec_find_encoder(videoCodec)) == null) {\n                releaseUnsafe();\n                throw new Exception(\"avcodec_find_encoder() error: Video codec not found.\");\n            }\n//            oformat.video_codec(video_codec.id());\n\n            AVRational frame_rate = av_d2q(frameRate, 1001000);\n            AVRational supported_framerates = video_codec.supported_framerates();\n            if (supported_framerates != null) {\n                int idx = av_find_nearest_q_idx(frame_rate, supported_framerates);\n                frame_rate = supported_framerates.position(idx);\n            }\n\n            /* add a video output stream */\n            if ((video_st = avformat_new_stream(oc, null)) == null) {\n                releaseUnsafe();\n                throw new Exception(\"avformat_new_stream() error: Could not allocate video stream.\");\n            }\n\n            if ((video_c = avcodec_alloc_context3(video_codec)) == null) {\n                releaseUnsafe();\n                throw new Exception(\"avcodec_alloc_context3() error: Could not allocate video encoding context.\");\n            }\n\n            if (inpVideoStream != null) {\n                if ((ret = avcodec_parameters_copy(video_st.codecpar(), inpVideoStream.codecpar())) < 0) {\n                    releaseUnsafe();\n                    throw new Exception(\"avcodec_parameters_copy() error \" + ret + \": Failed to copy video stream codec parameters from input to output\");\n                }\n\n                videoBitrate = (int)inpVideoStream.codecpar().bit_rate();\n                pixelFormat = inpVideoStream.codecpar().format();\n                aspectRatio = inpVideoStream.codecpar().sample_aspect_ratio().num()*1.0d/ inpVideoStream.codecpar().sample_aspect_ratio().den();\n//                videoQuality = inpVideoStream.codecpar().global_quality();\n                video_c.codec_tag(0);\n            }\n\n            video_c.codec_id(video_codec.id());\n            video_c.codec_type(AVMEDIA_TYPE_VIDEO);\n\n\n            /* put sample parameters */\n            video_c.bit_rate(videoBitrate);\n            /* resolution must be a multiple of two. Scale height to maintain the aspect ratio. */\n            if (imageWidth % 2 == 1) {\n                int roundedWidth = imageWidth + 1;\n                imageHeight = (roundedWidth * imageHeight + imageWidth / 2) / imageWidth;\n                imageWidth = roundedWidth;\n            }\n            video_c.width(imageWidth);\n            video_c.height(imageHeight);\n            if (aspectRatio > 0) {\n                AVRational r = av_d2q(aspectRatio, 255);\n                video_c.sample_aspect_ratio(r);\n                video_st.sample_aspect_ratio(r);\n            }\n            /* time base: this is the fundamental unit of time (in seconds) in terms\n               of which frame timestamps are represented. for fixed-fps content,\n               timebase should be 1/framerate and timestamp increments should be\n               identically 1. */\n            AVRational time_base = av_inv_q(frame_rate);\n            video_c.time_base(time_base);\n            video_st.time_base(time_base);\n            video_st.avg_frame_rate(frame_rate);\n//            video_st.codec().time_base(time_base); // \"deprecated\", but this is actually required\n            if (gopSize >= 0) {\n                video_c.gop_size(gopSize); /* emit one intra frame every gopSize frames at most */\n            }\n            if (videoProfile >= 0) {\n                video_c.profile(videoProfile);\n            }\n            if (videoQuality >= 0) {\n                video_c.flags(video_c.flags() | AV_CODEC_FLAG_QSCALE);\n                video_c.global_quality((int)Math.round(FF_QP2LAMBDA * videoQuality));\n            }\n\n            if (pixelFormat != AV_PIX_FMT_NONE) {\n                video_c.pix_fmt(pixelFormat);\n            } else if (video_c.codec_id() == AV_CODEC_ID_RAWVIDEO || video_c.codec_id() == AV_CODEC_ID_PNG ||\n                       video_c.codec_id() == AV_CODEC_ID_HUFFYUV  || video_c.codec_id() == AV_CODEC_ID_FFV1) {\n                video_c.pix_fmt(AV_PIX_FMT_RGB32);   // appropriate for common lossless formats\n            } else if (video_c.codec_id() == AV_CODEC_ID_JPEGLS) {\n                video_c.pix_fmt(AV_PIX_FMT_BGR24);\n            } else if (video_c.codec_id() == AV_CODEC_ID_MJPEG || video_c.codec_id() == AV_CODEC_ID_MJPEGB) {\n                video_c.pix_fmt(AV_PIX_FMT_YUVJ420P);\n            } else {\n                video_c.pix_fmt(AV_PIX_FMT_YUV420P); // lossy, but works with about everything\n            }\n\n            if (video_c.codec_id() == AV_CODEC_ID_MPEG2VIDEO) {\n                /* just for testing, we also add B frames */\n                video_c.max_b_frames(2);\n            } else if (video_c.codec_id() == AV_CODEC_ID_MPEG1VIDEO) {\n                /* Needed to avoid using macroblocks in which some coeffs overflow.\n                   This does not happen with normal video, it just happens here as\n                   the motion of the chroma plane does not match the luma plane. */\n                video_c.mb_decision(2);\n            } else if (video_c.codec_id() == AV_CODEC_ID_H263) {\n                // H.263 does not support any other resolution than the following\n                if (imageWidth <= 128 && imageHeight <= 96) {\n                    video_c.width(128).height(96);\n                } else if (imageWidth <= 176 && imageHeight <= 144) {\n                    video_c.width(176).height(144);\n                } else if (imageWidth <= 352 && imageHeight <= 288) {\n                    video_c.width(352).height(288);\n                } else if (imageWidth <= 704 && imageHeight <= 576) {\n                    video_c.width(704).height(576);\n                } else {\n                    video_c.width(1408).height(1152);\n                }\n            } else if (video_c.codec_id() == AV_CODEC_ID_H264) {\n                // default to constrained baseline to produce content that plays back on anything,\n                // without any significant tradeoffs for most use cases\n                if (videoProfile < 0) {\n                    video_c.profile(AV_PROFILE_H264_CONSTRAINED_BASELINE);\n                }\n            }\n\n            // some formats want stream headers to be separate\n            if ((oformat.flags() & AVFMT_GLOBALHEADER) != 0) {\n                video_c.flags(video_c.flags() | AV_CODEC_FLAG_GLOBAL_HEADER);\n            }\n\n            if ((video_codec.capabilities() & AV_CODEC_CAP_EXPERIMENTAL) != 0) {\n                video_c.strict_std_compliance(FF_COMPLIANCE_EXPERIMENTAL);\n            }\n\n            if (maxBFrames >= 0) {\n                video_c.max_b_frames(maxBFrames);\n                video_c.has_b_frames(maxBFrames == 0 ? 0 : 1);\n            }\n\n            if (trellis >= 0) {\n                video_c.trellis(trellis);\n            }\n        }\n\n        /*\n         * add an audio output stream\n         */\n        if (audioChannels > 0 && audioBitrate > 0 && sampleRate > 0) {\n            if (audioCodec == AV_CODEC_ID_NONE) {\n                audioCodec = oformat.audio_codec();\n            }\n//            if (audioCodec != AV_CODEC_ID_NONE) {\n//                oformat.audio_codec(audioCodec);\n//            } else if (\"flv\".equals(format_name) || \"mp4\".equals(format_name) || \"3gp\".equals(format_name)) {\n//                oformat.audio_codec(AV_CODEC_ID_AAC);\n//            } else if (\"avi\".equals(format_name)) {\n//                oformat.audio_codec(AV_CODEC_ID_PCM_S16LE);\n//            }\n\n            /* find the audio encoder */\n            if ((audio_codec = avcodec_find_encoder_by_name(audioCodecName)) == null &&\n                (audio_codec = avcodec_find_encoder(audioCodec)) == null) {\n                releaseUnsafe();\n                throw new Exception(\"avcodec_find_encoder() error: Audio codec not found.\");\n            }\n//            oformat.audio_codec(audio_codec.id());\n\n            AVRational sample_rate = av_d2q(sampleRate, 1001000);\n\n            if ((audio_st = avformat_new_stream(oc, null)) == null) {\n                releaseUnsafe();\n                throw new Exception(\"avformat_new_stream() error: Could not allocate audio stream.\");\n            }\n\n            if ((audio_c = avcodec_alloc_context3(audio_codec)) == null) {\n                releaseUnsafe();\n                throw new Exception(\"avcodec_alloc_context3() error: Could not allocate audio encoding context.\");\n            }\n\n            if (inpAudioStream != null && audioChannels > 0) {\n                if ((ret = avcodec_parameters_copy(audio_st.codecpar(), inpAudioStream.codecpar())) < 0) {\n                    throw new Exception(\"avcodec_parameters_copy() error \" + ret + \": Failed to copy audio stream codec parameters from input to output\");\n                }\n\n                audioBitrate = (int) inpAudioStream.codecpar().bit_rate();\n                sampleRate = inpAudioStream.codecpar().sample_rate();\n                audioChannels = inpAudioStream.codecpar().ch_layout().nb_channels();\n                sampleFormat = inpAudioStream.codecpar().format();\n//                audioQuality = inpAudioStream.codecpar().global_quality();\n                audio_c.codec_tag(0);\n//                audio_st.pts(inpAudioStream.pts());\n                audio_st.duration(inpAudioStream.duration());\n                audio_st.time_base().num(inpAudioStream.time_base().num());\n                audio_st.time_base().den(inpAudioStream.time_base().den());\n            }\n\n            audio_c.codec_id(audio_codec.id());\n            audio_c.codec_type(AVMEDIA_TYPE_AUDIO);\n\n\n            /* put sample parameters */\n            audio_c.bit_rate(audioBitrate);\n            audio_c.sample_rate(sampleRate);\n            av_channel_layout_default(default_layout, audioChannels);\n            audio_c.ch_layout(default_layout);\n            if (sampleFormat != AV_SAMPLE_FMT_NONE) {\n                audio_c.sample_fmt(sampleFormat);\n            } else {\n                // use AV_SAMPLE_FMT_S16 by default, if available\n                audio_c.sample_fmt(AV_SAMPLE_FMT_FLTP);\n                IntPointer formats = audio_c.codec().sample_fmts();\n                for (int i = 0; formats.get(i) != -1; i++) {\n                    if (formats.get(i) == AV_SAMPLE_FMT_S16) {\n                        audio_c.sample_fmt(AV_SAMPLE_FMT_S16);\n                        break;\n                    }\n                }\n            }\n            AVRational time_base = av_inv_q(sample_rate);\n            audio_c.time_base(time_base);\n            audio_st.time_base(time_base);\n//            audio_st.codec().time_base(time_base); // \"deprecated\", but this is actually required\n            switch (audio_c.sample_fmt()) {\n                case AV_SAMPLE_FMT_U8:\n                case AV_SAMPLE_FMT_U8P:  audio_c.bits_per_raw_sample(8);  break;\n                case AV_SAMPLE_FMT_S16:\n                case AV_SAMPLE_FMT_S16P: audio_c.bits_per_raw_sample(16); break;\n                case AV_SAMPLE_FMT_S32:\n                case AV_SAMPLE_FMT_S32P: audio_c.bits_per_raw_sample(32); break;\n                case AV_SAMPLE_FMT_FLT:\n                case AV_SAMPLE_FMT_FLTP: audio_c.bits_per_raw_sample(32); break;\n                case AV_SAMPLE_FMT_DBL:\n                case AV_SAMPLE_FMT_DBLP: audio_c.bits_per_raw_sample(64); break;\n                default: assert false;\n            }\n            if (audioQuality >= 0) {\n                audio_c.flags(audio_c.flags() | AV_CODEC_FLAG_QSCALE);\n                audio_c.global_quality((int)Math.round(FF_QP2LAMBDA * audioQuality));\n            }\n\n            // some formats want stream headers to be separate\n            if ((oformat.flags() & AVFMT_GLOBALHEADER) != 0) {\n                audio_c.flags(audio_c.flags() | AV_CODEC_FLAG_GLOBAL_HEADER);\n            }\n\n            if ((audio_codec.capabilities() & AV_CODEC_CAP_EXPERIMENTAL) != 0) {\n                audio_c.strict_std_compliance(FF_COMPLIANCE_EXPERIMENTAL);\n            }\n        }\n\n        /* now that all the parameters are set, we can open the audio and\n           video codecs and allocate the necessary encode buffers */\n        if (video_st != null && inpVideoStream == null) {\n            AVDictionary options = new AVDictionary(null);\n            if (videoQuality >= 0) {\n                av_dict_set(options, \"crf\", \"\" + videoQuality, 0);\n            }\n            for (Entry<String, String> e : videoOptions.entrySet()) {\n                av_dict_set(options, e.getKey(), e.getValue(), 0);\n            }\n\n            // Enable multithreading when available\n            video_c.thread_count(0);\n\n            /* open the codec */\n            if ((ret = avcodec_open2(video_c, video_codec, options)) < 0) {\n                releaseUnsafe();\n                av_dict_free(options);\n                throw new Exception(\"avcodec_open2() error \" + ret + \": Could not open video codec.\");\n            }\n            FFmpegLogCallback.logRejectedOptions(options, \"avcodec_open2\");\n            av_dict_free(options);\n\n            video_outbuf = null;\n//            if ((oformat.flags() & AVFMT_RAWPICTURE) == 0) {\n//                /* allocate output buffer */\n//                /* XXX: API change will be done */\n//                /* buffers passed into lav* can be allocated any way you prefer,\n//                   as long as they're aligned enough for the architecture, and\n//                   they're freed appropriately (such as using av_free for buffers\n//                   allocated with av_malloc) */\n//                video_outbuf_size = Math.max(256 * 1024, 8 * video_c.width() * video_c.height()); // a la ffmpeg.c\n//                video_outbuf = new BytePointer(av_malloc(video_outbuf_size));\n//            }\n\n            /* allocate the encoded raw picture */\n            if ((picture = av_frame_alloc()) == null) {\n                releaseUnsafe();\n                throw new Exception(\"av_frame_alloc() error: Could not allocate picture.\");\n            }\n            picture.pts(0); // magic required by libx264\n\n            int size = av_image_get_buffer_size(video_c.pix_fmt(), video_c.width(), video_c.height(), 1);\n            if ((picture_buf = new BytePointer(av_malloc(size))).isNull()) {\n                releaseUnsafe();\n                throw new Exception(\"av_malloc() error: Could not allocate picture buffer.\");\n            }\n\n            /* if the output format is not equal to the image format, then a temporary\n               picture is needed too. It is then converted to the required output format */\n            if ((tmp_picture = av_frame_alloc()) == null) {\n                releaseUnsafe();\n                throw new Exception(\"av_frame_alloc() error: Could not allocate temporary picture.\");\n            }\n\n            /* copy the stream parameters to the muxer */\n            if ((ret = avcodec_parameters_from_context(video_st.codecpar(), video_c)) < 0) {\n                releaseUnsafe();\n                throw new Exception(\"avcodec_parameters_from_context() error \" + ret + \": Could not copy the video stream parameters.\");\n            }\n\n            AVDictionary metadata = new AVDictionary(null);\n            for (Entry<String, String> e : videoMetadata.entrySet()) {\n                av_dict_set(metadata, new BytePointer(e.getKey(), charset), new BytePointer(e.getValue(), charset), 0);\n            }\n            video_st.metadata(metadata);\n\n            for (Entry<String, Buffer> e : videoSideData.entrySet()) {\n                int type = -1;\n                for (int i = 0; i < AV_PKT_DATA_NB; i++) {\n                    BytePointer s = av_packet_side_data_name(i);\n                    if (s != null && !s.isNull() && e.getKey().equals(s.getString())) {\n                        type = i;\n                        break;\n                    }\n                }\n                Pointer p = new Pointer(e.getValue());\n                AVPacketSideData coded_side_data = video_st.codecpar().coded_side_data();\n                int[] nb_coded_side_data = {video_st.codecpar().nb_coded_side_data()};\n                AVPacketSideData b = av_packet_side_data_new(coded_side_data, nb_coded_side_data, type, p.capacity(), 0);\n                video_st.codecpar().coded_side_data(coded_side_data);\n                video_st.codecpar().nb_coded_side_data(nb_coded_side_data[0]);\n                if (b != null && !b.isNull() && b.data() != null && !b.data().isNull()) {\n                    b.data().capacity(p.capacity()).put(p);\n                }\n            }\n        }\n\n        if (audio_st != null && inpAudioStream == null) {\n            AVDictionary options = new AVDictionary(null);\n            if (audioQuality >= 0) {\n                av_dict_set(options, \"crf\", \"\" + audioQuality, 0);\n            }\n            for (Entry<String, String> e : audioOptions.entrySet()) {\n                av_dict_set(options, e.getKey(), e.getValue(), 0);\n            }\n\n            // Enable multithreading when available\n            audio_c.thread_count(0);\n\n            /* open the codec */\n            if ((ret = avcodec_open2(audio_c, audio_codec, options)) < 0) {\n                releaseUnsafe();\n                av_dict_free(options);\n                throw new Exception(\"avcodec_open2() error \" + ret + \": Could not open audio codec.\");\n            }\n            FFmpegLogCallback.logRejectedOptions(options, \"avcodec_open2\");\n            av_dict_free(options);\n\n            audio_outbuf_size = 256 * 1024;\n            audio_outbuf = new BytePointer(av_malloc(audio_outbuf_size));\n\n            /* ugly hack for PCM codecs (will be removed ASAP with new PCM\n               support to compute the input frame size in samples */\n            if (audio_c.frame_size() <= 1) {\n                audio_outbuf_size = 16 * 1024; // AV_INPUT_BUFFER_MIN_SIZE;\n                audio_input_frame_size = audio_outbuf_size / audio_c.ch_layout().nb_channels();\n                switch (audio_c.codec_id()) {\n                    case AV_CODEC_ID_PCM_S16LE:\n                    case AV_CODEC_ID_PCM_S16BE:\n                    case AV_CODEC_ID_PCM_U16LE:\n                    case AV_CODEC_ID_PCM_U16BE:\n                        audio_input_frame_size >>= 1;\n                        break;\n                    default:\n                        break;\n                }\n            } else {\n                audio_input_frame_size = audio_c.frame_size();\n            }\n            //int bufferSize = audio_input_frame_size * audio_c.bits_per_raw_sample()/8 * audio_c.ch_layout().nb_channels();\n            int planes = av_sample_fmt_is_planar(audio_c.sample_fmt()) != 0 ? (int)audio_c.ch_layout().nb_channels() : 1;\n            int data_size = av_samples_get_buffer_size((IntPointer)null, audio_c.ch_layout().nb_channels(),\n                    audio_input_frame_size, audio_c.sample_fmt(), 1) / planes;\n            samples_out = new BytePointer[planes];\n            for (int i = 0; i < samples_out.length; i++) {\n                samples_out[i] = new BytePointer(av_malloc(data_size)).capacity(data_size);\n            }\n            samples_in = new Pointer[AVFrame.AV_NUM_DATA_POINTERS];\n\n            /* allocate the audio frame */\n            if ((frame = av_frame_alloc()) == null) {\n                releaseUnsafe();\n                throw new Exception(\"av_frame_alloc() error: Could not allocate audio frame.\");\n            }\n            frame.pts(0); // magic required by libvorbis and webm\n\n            /* copy the stream parameters to the muxer */\n            if ((ret = avcodec_parameters_from_context(audio_st.codecpar(), audio_c)) < 0) {\n                releaseUnsafe();\n                throw new Exception(\"avcodec_parameters_from_context() error \" + ret + \": Could not copy the audio stream parameters.\");\n            }\n\n            AVDictionary metadata = new AVDictionary(null);\n            for (Entry<String, String> e : audioMetadata.entrySet()) {\n                av_dict_set(metadata, new BytePointer(e.getKey(), charset), new BytePointer(e.getValue(), charset), 0);\n            }\n            audio_st.metadata(metadata);\n\n            for (Entry<String, Buffer> e : audioSideData.entrySet()) {\n                int type = -1;\n                for (int i = 0; i < AV_PKT_DATA_NB; i++) {\n                    BytePointer s = av_packet_side_data_name(i);\n                    if (s != null && !s.isNull() && e.getKey().equals(s.getString())) {\n                        type = i;\n                        break;\n                    }\n                }\n                Pointer p = new Pointer(e.getValue());\n                AVPacketSideData coded_side_data = audio_st.codecpar().coded_side_data();\n                int[] nb_coded_side_data = {audio_st.codecpar().nb_coded_side_data()};\n                AVPacketSideData b = av_packet_side_data_new(coded_side_data, nb_coded_side_data, type, p.capacity(), 0);\n                audio_st.codecpar().coded_side_data(coded_side_data);\n                audio_st.codecpar().nb_coded_side_data(nb_coded_side_data[0]);\n                if (b != null && !b.isNull() && b.data() != null && !b.data().isNull()) {\n                    b.data().capacity(p.capacity()).put(p);\n                }\n            }\n        }\n\n        AVDictionary options = new AVDictionary(null);\n        for (Entry<String, String> e : this.options.entrySet()) {\n            av_dict_set(options, e.getKey(), e.getValue(), 0);\n        }\n\n        /* open the output file, if needed */\n        if (outputStream == null && (oformat.flags() & AVFMT_NOFILE) == 0) {\n            AVIOContext pb = new AVIOContext(null);\n            if ((ret = avio_open2(pb, filename, AVIO_FLAG_WRITE, null, options)) < 0) {\n                String errorMsg = \"avio_open2 error() error \" + ret + \": Could not open '\" + filename + \"'\";\n                releaseUnsafe();\n                av_dict_free(options);\n                throw new Exception(errorMsg);\n            }\n            FFmpegLogCallback.logRejectedOptions(options, \"avio_open2\");\n            oc.pb(pb);\n        }\n\n        AVDictionary metadata = new AVDictionary(null);\n        for (Entry<String, String> e : this.metadata.entrySet()) {\n            av_dict_set(metadata, new BytePointer(e.getKey(), charset), new BytePointer(e.getValue(), charset), 0);\n        }\n        /* write the stream header, if any */\n        if ((ret = avformat_write_header(oc.metadata(metadata), options)) < 0) {\n            String errorMsg = \"avformat_write_header error() error \" + ret + \": Could not write header to '\" + filename + \"'\";\n            releaseUnsafe();\n            av_dict_free(options);\n            throw new Exception(errorMsg);\n        }\n        FFmpegLogCallback.logRejectedOptions(options, \"avformat_write_header\");\n        av_dict_free(options);\n\n        if (av_log_get_level() >= AV_LOG_INFO) {\n            av_dump_format(oc, 0, filename, 1);\n        }\n\n        started = true;\n\n        }\n    }\n\n    public synchronized void flush() throws Exception {\n        synchronized (oc) {\n            /* flush all the buffers */\n            while (video_st != null && ifmt_ctx == null && recordImage(0, 0, 0, 0, 0, AV_PIX_FMT_NONE, (Buffer[])null));\n            while (audio_st != null && ifmt_ctx == null && recordSamples(0, 0, (Buffer[])null));\n\n            if (interleaved && (video_st != null || audio_st != null)) {\n                av_interleaved_write_frame(oc, null);\n            } else {\n                av_write_frame(oc, null);\n            }\n        }\n    }\n\n    public void stop() throws Exception {\n        if (oc != null) {\n            try {\n                flush();\n\n                /* write the trailer, if any */\n                av_write_trailer(oc);\n            } finally {\n                release();\n            }\n        }\n    }\n\n    @Override public void record(Frame frame) throws Exception {\n        record(frame, frame != null && frame.opaque instanceof AVFrame ? ((AVFrame)frame.opaque).format() : AV_PIX_FMT_NONE);\n    }\n    public synchronized void record(Frame frame, int pixelFormat) throws Exception {\n        if (frame == null || (frame.image == null && frame.samples == null && frame.data == null)) {\n            recordImage(0, 0, 0, 0, 0, pixelFormat, (Buffer[])null);\n        } else {\n            if (frame.image != null) {\n                frame.keyFrame = recordImage(frame.imageWidth, frame.imageHeight, frame.imageDepth,\n                        frame.imageChannels, frame.imageStride, pixelFormat, frame.image);\n            }\n            if (frame.samples != null) {\n                frame.keyFrame = recordSamples(frame.sampleRate, frame.audioChannels, frame.samples);\n            }\n        }\n    }\n\n    public synchronized boolean recordImage(int width, int height, int depth, int channels, int stride, int pixelFormat, Buffer ... image) throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (video_st == null) {\n            throw new Exception(\"No video output stream (Is imageWidth > 0 && imageHeight > 0 and has start() been called?)\");\n        }\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n        int ret;\n\n        if (image == null || image.length == 0) {\n            /* no more frame to compress. The codec has a latency of a few\n               frames if using B frames, so we get the last frames by\n               passing the same picture again */\n        } else {\n            int step = stride * Math.abs(depth) / 8;\n            BytePointer data = image[0] instanceof ByteBuffer\n                    ? new BytePointer((ByteBuffer)image[0]).position(0)\n                    : new BytePointer(new Pointer(image[0]).position(0));\n\n            if (pixelFormat == AV_PIX_FMT_NONE) {\n                if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 3) {\n                    pixelFormat = AV_PIX_FMT_BGR24;\n                } else if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 1) {\n                    pixelFormat = AV_PIX_FMT_GRAY8;\n                } else if ((depth == Frame.DEPTH_USHORT || depth == Frame.DEPTH_SHORT) && channels == 1) {\n                    pixelFormat = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN) ?\n                            AV_PIX_FMT_GRAY16BE : AV_PIX_FMT_GRAY16LE;\n                } else if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 4) {\n                    pixelFormat = AV_PIX_FMT_RGBA;\n                } else if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 2) {\n                    pixelFormat = AV_PIX_FMT_NV21; // Android's camera capture format\n                } else {\n                    throw new Exception(\"Could not guess pixel format of image: depth=\" + depth + \", channels=\" + channels);\n                }\n            }\n\n            if (pixelFormat == AV_PIX_FMT_NV21) {\n                step = width;\n            }\n\n            if (video_c.pix_fmt() != pixelFormat || video_c.width() != width || video_c.height() != height) {\n                /* convert to the codec pixel format if needed */\n                img_convert_ctx = sws_getCachedContext(img_convert_ctx, width, height, pixelFormat,\n                        video_c.width(), video_c.height(), video_c.pix_fmt(),\n                        imageScalingFlags != 0 ? imageScalingFlags : SWS_BILINEAR,\n                        null, null, (DoublePointer)null);\n                if (img_convert_ctx == null) {\n                    throw new Exception(\"sws_getCachedContext() error: Cannot initialize the conversion context.\");\n                }\n                av_image_fill_arrays(new PointerPointer(tmp_picture), tmp_picture.linesize(), data, pixelFormat, width, height, 1);\n                av_image_fill_arrays(new PointerPointer(picture), picture.linesize(), picture_buf, video_c.pix_fmt(), video_c.width(), video_c.height(), 1);\n                tmp_picture.linesize(0, step);\n                tmp_picture.format(pixelFormat);\n                tmp_picture.width(width);\n                tmp_picture.height(height);\n                picture.format(video_c.pix_fmt());\n                picture.width(video_c.width());\n                picture.height(video_c.height());\n                sws_scale(img_convert_ctx, new PointerPointer(tmp_picture), tmp_picture.linesize(),\n                          0, height, new PointerPointer(picture), picture.linesize());\n            } else {\n                av_image_fill_arrays(new PointerPointer(picture), picture.linesize(), data, pixelFormat, width, height, 1);\n                picture.linesize(0, step);\n                picture.format(pixelFormat);\n                picture.width(width);\n                picture.height(height);\n            }\n        }\n\n//        if ((oformat.flags() & AVFMT_RAWPICTURE) != 0) {\n//            if (image == null || image.length == 0) {\n//                return false;\n//            }\n//            /* raw video case. The API may change slightly in the future for that? */\n//            av_init_packet(video_pkt);\n//            video_pkt.flags(video_pkt.flags() | AV_PKT_FLAG_KEY);\n//            video_pkt.stream_index(video_st.index());\n//            video_pkt.data(new BytePointer(picture));\n//            video_pkt.size(Loader.sizeof(AVFrame.class));\n//        } else {\n            /* encode the image */\n            picture.quality(video_c.global_quality());\n            if ((ret = avcodec_send_frame(video_c, image == null || image.length == 0 ? null : picture)) < 0\n                    && image != null && image.length != 0) {\n                throw new Exception(\"avcodec_send_frame() error \" + ret + \": Error sending a video frame for encoding.\");\n            }\n            picture.pts(picture.pts() + 1); // magic required by libx264\n\n            /* if zero size, it means the image was buffered */\n            got_video_packet[0] = 0;\n            while (ret >= 0) {\n                av_new_packet(video_pkt, video_outbuf_size);\n                ret = avcodec_receive_packet(video_c, video_pkt);\n                if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF()) {\n                    av_packet_unref(video_pkt);\n                    break;\n                } else if (ret < 0) {\n                    av_packet_unref(video_pkt);\n                    throw new Exception(\"avcodec_receive_packet() error \" + ret + \": Error during video encoding.\");\n                }\n                got_video_packet[0] = 1;\n\n                if (video_pkt.pts() != AV_NOPTS_VALUE) {\n                    video_pkt.pts(av_rescale_q(video_pkt.pts(), video_c.time_base(), video_st.time_base()));\n                }\n                if (video_pkt.dts() != AV_NOPTS_VALUE) {\n                    video_pkt.dts(av_rescale_q(video_pkt.dts(), video_c.time_base(), video_st.time_base()));\n                }\n                video_pkt.stream_index(video_st.index());\n\n                /* write the compressed frame in the media file */\n                writePacket(AVMEDIA_TYPE_VIDEO, video_pkt);\n            }\n//        }\n        return image != null ? (video_pkt.flags() & AV_PKT_FLAG_KEY) != 0 : got_video_packet[0] != 0;\n\n        }\n    }\n\n    public boolean recordSamples(Buffer ... samples) throws Exception {\n        return recordSamples(0, 0, samples);\n    }\n    public synchronized boolean recordSamples(int sampleRate, int audioChannels, Buffer ... samples) throws Exception {\n        try (PointerScope scope = new PointerScope()) {\n\n        if (audio_st == null) {\n            throw new Exception(\"No audio output stream (Is audioChannels > 0 and has start() been called?)\");\n        }\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        if (samples == null && samples_convert_ctx == null) {\n            // We haven't tried to record any samples yet so we don't need to flush.\n            return false;\n        }\n\n        int ret;\n\n        if (sampleRate <= 0) {\n            sampleRate = audio_c.sample_rate();\n        }\n        if (audioChannels <= 0) {\n            audioChannels = audio_c.ch_layout().nb_channels();\n        }\n        int inputSize = samples != null ? samples[0].limit() - samples[0].position() : 0;\n        int inputFormat = samples_format;\n        int inputChannels = samples != null && samples.length > 1 ? 1 : audioChannels;\n        int inputDepth = 0;\n        int outputFormat = audio_c.sample_fmt();\n        int outputChannels = samples_out.length > 1 ? 1 : audio_c.ch_layout().nb_channels();\n        int outputDepth = av_get_bytes_per_sample(outputFormat);\n        if (samples != null && samples[0] instanceof ByteBuffer) {\n            inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_U8P : AV_SAMPLE_FMT_U8;\n            inputDepth = 1;\n            for (int i = 0; i < samples.length; i++) {\n                ByteBuffer b = (ByteBuffer)samples[i];\n                if (samples_in[i] instanceof BytePointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {\n                    ((BytePointer)samples_in[i]).position(0).put(b.array(), b.position(), inputSize);\n                } else {\n                    if (samples_in[i] != null) {\n                        samples_in[i].releaseReference();\n                    }\n                    samples_in[i] = new BytePointer(b).retainReference();\n                }\n            }\n        } else if (samples != null && samples[0] instanceof ShortBuffer) {\n            inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_S16P : AV_SAMPLE_FMT_S16;\n            inputDepth = 2;\n            for (int i = 0; i < samples.length; i++) {\n                ShortBuffer b = (ShortBuffer)samples[i];\n                if (samples_in[i] instanceof ShortPointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {\n                    ((ShortPointer)samples_in[i]).position(0).put(b.array(), samples[i].position(), inputSize);\n                } else {\n                    if (samples_in[i] != null) {\n                        samples_in[i].releaseReference();\n                    }\n                    samples_in[i] = new ShortPointer(b).retainReference();\n                }\n            }\n        } else if (samples != null && samples[0] instanceof IntBuffer) {\n            inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_S32P : AV_SAMPLE_FMT_S32;\n            inputDepth = 4;\n            for (int i = 0; i < samples.length; i++) {\n                IntBuffer b = (IntBuffer)samples[i];\n                if (samples_in[i] instanceof IntPointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {\n                    ((IntPointer)samples_in[i]).position(0).put(b.array(), samples[i].position(), inputSize);\n                } else {\n                    if (samples_in[i] != null) {\n                        samples_in[i].releaseReference();\n                    }\n                    samples_in[i] = new IntPointer(b).retainReference();\n                }\n            }\n        } else if (samples != null && samples[0] instanceof FloatBuffer) {\n            inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_FLTP : AV_SAMPLE_FMT_FLT;\n            inputDepth = 4;\n            for (int i = 0; i < samples.length; i++) {\n                FloatBuffer b = (FloatBuffer)samples[i];\n                if (samples_in[i] instanceof FloatPointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {\n                    ((FloatPointer)samples_in[i]).position(0).put(b.array(), b.position(), inputSize);\n                } else {\n                    if (samples_in[i] != null) {\n                        samples_in[i].releaseReference();\n                    }\n                    samples_in[i] = new FloatPointer(b).retainReference();\n                }\n            }\n        } else if (samples != null && samples[0] instanceof DoubleBuffer) {\n            inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_DBLP : AV_SAMPLE_FMT_DBL;\n            inputDepth = 8;\n            for (int i = 0; i < samples.length; i++) {\n                DoubleBuffer b = (DoubleBuffer)samples[i];\n                if (samples_in[i] instanceof DoublePointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {\n                    ((DoublePointer)samples_in[i]).position(0).put(b.array(), b.position(), inputSize);\n                } else {\n                    if (samples_in[i] != null) {\n                        samples_in[i].releaseReference();\n                    }\n                    samples_in[i] = new DoublePointer(b).retainReference();\n                }\n            }\n        } else if (samples != null) {\n            throw new Exception(\"Audio samples Buffer has unsupported type: \" + samples);\n        }\n\n        boolean formatChanged = samples_channels != audioChannels || samples_format != inputFormat || samples_rate != sampleRate;\n        if (samples != null && (samples_convert_ctx == null || formatChanged)) {\n            if (samples_convert_ctx == null) {\n                samples_convert_ctx = new SwrContext().retainReference();\n            }\n            if ((ret = swr_alloc_set_opts2(samples_convert_ctx, audio_c.ch_layout(), outputFormat, audio_c.sample_rate(),\n                    default_layout, inputFormat, sampleRate, 0, null)) < 0) {\n                throw new Exception(\"swr_alloc_set_opts2() error \" + ret + \": Cannot allocate the conversion context.\");\n            } else if ((ret = swr_init(samples_convert_ctx)) < 0) {\n                throw new Exception(\"swr_init() error \" + ret + \": Cannot initialize the conversion context.\");\n            }\n            samples_channels = audioChannels;\n            samples_format = inputFormat;\n            samples_rate = sampleRate;\n        }\n\n        for (int i = 0; samples != null && i < samples.length; i++) {\n            samples_in[i].position(samples_in[i].position() * inputDepth).\n                    limit((samples_in[i].position() + inputSize) * inputDepth);\n        }\n        while (true) {\n            int inputCount = (int)Math.min(samples != null ? (samples_in[0].limit() - samples_in[0].position()) / (inputChannels * inputDepth) : 0, Integer.MAX_VALUE);\n            int outputCount = (int)Math.min((samples_out[0].limit() - samples_out[0].position()) / (outputChannels * outputDepth), Integer.MAX_VALUE);\n            inputCount = Math.min(inputCount, (outputCount * sampleRate + audio_c.sample_rate() - 1) / audio_c.sample_rate());\n            for (int i = 0; samples != null && i < samples.length; i++) {\n                plane_ptr.put(i, samples_in[i]);\n            }\n            for (int i = 0; i < samples_out.length; i++) {\n                plane_ptr2.put(i, samples_out[i]);\n            }\n            if (samples == null && inputCount == 0 && plane_ptr != null) {\n                plane_ptr.releaseReference();\n                // needs to be null to flush swr context.\n                plane_ptr = null;\n            }\n            if ((ret = swr_convert(samples_convert_ctx, plane_ptr2, outputCount, plane_ptr, inputCount)) < 0) {\n                throw new Exception(\"swr_convert() error \" + ret + \": Cannot convert audio samples.\");\n            } else if (ret == 0 && inputCount == 0) {\n                break;\n            }\n            for (int i = 0; samples != null && i < samples.length; i++) {\n                samples_in[i].position(samples_in[i].position() + inputCount * inputChannels * inputDepth);\n            }\n            for (int i = 0; i < samples_out.length; i++) {\n                samples_out[i].position(samples_out[i].position() + ret * outputChannels * outputDepth);\n            }\n\n            if (samples == null || samples_out[0].position() >= samples_out[0].limit()) {\n                writeSamples(audio_input_frame_size);\n            }\n        }\n\n        if (samples == null && samples_out[0].position() > 0) {\n            // Typically samples_out[0].limit() is double the audio_input_frame_size --> sampleDivisor = 2\n            double sampleDivisor = Math.floor((int)Math.min(samples_out[0].limit(), Integer.MAX_VALUE) / audio_input_frame_size);\n            writeSamples((int)Math.floor((int)samples_out[0].position() / sampleDivisor));\n            return writeFrame((AVFrame)null);\n        }\n\n        return samples != null ? (frame.flags() & AVFrame.AV_FRAME_FLAG_KEY) != 0 : writeFrame((AVFrame)null);\n\n        }\n    }\n\n    private void writeSamples(int nb_samples) throws Exception {\n        if (samples_out == null || samples_out.length == 0) {\n            return;\n        }\n\n        frame.nb_samples(nb_samples);\n        avcodec_fill_audio_frame(frame, audio_c.ch_layout().nb_channels(), audio_c.sample_fmt(), samples_out[0], (int)samples_out[0].position(), 0);\n        for (int i = 0; i < samples_out.length; i++) {\n            int linesize = 0;\n            if (samples_out[0].position() > 0 && samples_out[0].position() < samples_out[0].limit()) {\n                // align the end of the buffer to a 32-byte boundary as sometimes required by FFmpeg\n                linesize = ((int)samples_out[i].position() + 31) & ~31;\n            } else {\n                linesize = (int)Math.min(samples_out[i].limit(), Integer.MAX_VALUE);\n            }\n\n            frame.data(i, samples_out[i].position(0));\n            frame.linesize(i, linesize);\n        }\n        frame.ch_layout(audio_c.ch_layout());\n        frame.format(audio_c.sample_fmt());\n        frame.quality(audio_c.global_quality());\n        writeFrame(frame);\n        wrote_samples = true;\n    }\n\n    private boolean writeFrame(AVFrame frame) throws Exception {\n        int ret;\n\n        if ((ret = avcodec_send_frame(audio_c, frame)) < 0 && frame != null) {\n            throw new Exception(\"avcodec_send_frame() error \" + ret + \": Error sending an audio frame for encoding.\");\n        }\n        if (frame != null) {\n            frame.pts(frame.pts() + frame.nb_samples()); // magic required by libvorbis and webm\n        }\n\n        /* if zero size, it means the image was buffered */\n        got_audio_packet[0] = 0;\n        while (ret >= 0) {\n            av_new_packet(audio_pkt, audio_outbuf_size);\n            ret = avcodec_receive_packet(audio_c, audio_pkt);\n            if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF()) {\n                av_packet_unref(audio_pkt);\n                break;\n            } else if (ret < 0) {\n                av_packet_unref(audio_pkt);\n                throw new Exception(\"avcodec_receive_packet() error \" + ret + \": Error during audio encoding.\");\n            }\n            got_audio_packet[0] = 1;\n\n            if (audio_pkt.pts() != AV_NOPTS_VALUE) {\n                audio_pkt.pts(av_rescale_q(audio_pkt.pts(), audio_c.time_base(), audio_st.time_base()));\n            }\n            if (audio_pkt.dts() != AV_NOPTS_VALUE) {\n                audio_pkt.dts(av_rescale_q(audio_pkt.dts(), audio_c.time_base(), audio_st.time_base()));\n            }\n            audio_pkt.flags(audio_pkt.flags() | AV_PKT_FLAG_KEY);\n            audio_pkt.stream_index(audio_st.index());\n\n            /* write the compressed frame in the media file */\n            writePacket(AVMEDIA_TYPE_AUDIO, audio_pkt);\n\n            if (frame == null && !wrote_samples) {\n                break;\n            }\n        }\n\n        return got_audio_packet[0] != 0;\n    }\n\n    private void writePacket(int mediaType, AVPacket avPacket) throws Exception {\n        AVStream avStream = (mediaType == AVMEDIA_TYPE_VIDEO) ? video_st : (mediaType == AVMEDIA_TYPE_AUDIO) ? audio_st : null;\n        String mediaTypeStr = (mediaType == AVMEDIA_TYPE_VIDEO) ? \"video\" : (mediaType == AVMEDIA_TYPE_AUDIO) ? \"audio\" : \"unsupported media stream type\";\n\n        synchronized (oc) {\n            int ret;\n            if (interleaved && avStream != null) {\n                if ((ret = av_interleaved_write_frame(oc, avPacket)) < 0) {\n                    av_packet_unref(avPacket);\n                    throw new Exception(\"av_interleaved_write_frame() error \" + ret + \" while writing interleaved \" + mediaTypeStr + \" packet.\");\n                }\n            } else {\n                if ((ret = av_write_frame(oc, avPacket)) < 0) {\n                    av_packet_unref(avPacket);\n                    throw new Exception(\"av_write_frame() error \" + ret + \" while writing \" + mediaTypeStr + \" packet.\");\n                }\n            }\n        }\n        av_packet_unref(avPacket);\n    }\n\n    public synchronized boolean recordPacket(AVPacket pkt) throws Exception {\n        if (ifmt_ctx == null) {\n            throw new Exception(\"No input format context (Has start(AVFormatContext) been called?)\");\n        }\n        if (!started) {\n            throw new Exception(\"start() was not called successfully!\");\n        }\n\n        if (pkt == null) {\n            return false;\n        }\n\n        AVStream in_stream = ifmt_ctx.streams(pkt.stream_index());\n/**\n * Repair the problem of error decoding and playback caused by the absence of dts/pts \n * in the output audio/video file or audio/video stream,\n * Comment out this line of code so that PTS / DTS can specify the timestamp manually.\n */\n//        pkt.dts(AV_NOPTS_VALUE);\n//        pkt.pts(AV_NOPTS_VALUE);\n        pkt.pos(-1);\n        if (in_stream.codecpar().codec_type() == AVMEDIA_TYPE_VIDEO && video_st != null) {\n            pkt.stream_index(video_st.index());\n            pkt.duration((int) av_rescale_q(pkt.duration(), in_stream.time_base(), video_st.time_base()));\n            pkt.pts(av_rescale_q_rnd(pkt.pts(), in_stream.time_base(), video_st.time_base(),(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)));//Increase pts calculation\n            pkt.dts(av_rescale_q_rnd(pkt.dts(), in_stream.time_base(), video_st.time_base(),(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)));//Increase dts calculation\n            writePacket(AVMEDIA_TYPE_VIDEO, pkt);\n        } else if (in_stream.codecpar().codec_type() == AVMEDIA_TYPE_AUDIO && audio_st != null && (audioChannels > 0)) {\n            pkt.stream_index(audio_st.index());\n            pkt.duration((int) av_rescale_q(pkt.duration(), in_stream.time_base(), audio_st.time_base()));\n            pkt.pts(av_rescale_q_rnd(pkt.pts(), in_stream.time_base(), audio_st.time_base(),(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)));//Increase pts calculation\n            pkt.dts(av_rescale_q_rnd(pkt.dts(), in_stream.time_base(), audio_st.time_base(),(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)));//Increase dts calculation\n            writePacket(AVMEDIA_TYPE_AUDIO, pkt);\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FFmpegLockCallback.java",
    "content": "package org.bytedeco.javacv;\n\nimport java.util.HashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.PointerPointer;\nimport org.bytedeco.javacpp.annotation.Cast;\n\nimport org.bytedeco.ffmpeg.avcodec.*;\nimport static org.bytedeco.ffmpeg.global.avcodec.*;\n\npublic class FFmpegLockCallback {\n    private static boolean initialized = false;\n\n    private static AtomicInteger lockCounter = new AtomicInteger(0);\n    private static HashMap<Integer, Lock> lockArray = new HashMap<>();\n    private static Cb_PointerPointer_int lockCallback = new Cb_PointerPointer_int() {\n        @Override\n        public int call(@SuppressWarnings(\"rawtypes\") @Cast(\"void**\") PointerPointer mutex, @Cast(\"AVLockOp\") int op) {\n            int number;\n            Lock l;\n            // System.out.println \"Locking: \" + op);\n            switch (op) {\n            case AV_LOCK_CREATE:\n                number = lockCounter.incrementAndGet();\n                // System.out.println(\"Command: \" + op + \" number: \" + number);\n                new IntPointer(mutex).put(0, number);\n                lockArray.put(number, new ReentrantLock());\n                return 0;\n            case AV_LOCK_OBTAIN:\n                number = new IntPointer(mutex).get(0);\n                // System.out.println(\"Command: \" + op + \" number: \" + number);\n                l = lockArray.get(number);\n                if (l == null) {\n                    System.err.println(\"Lock not found!\");\n                    return -1;\n                }\n                l.lock();\n                return 0;\n            case AV_LOCK_RELEASE:\n                number = new IntPointer(mutex).get(0);\n                // System.out.println(\"Command: \" + op + \" number: \" + number);\n                l = lockArray.get(number);\n                if (l == null) {\n                    System.err.println(\"Lock not found!\");\n                    return -1;\n                }\n                l.unlock();\n                return 0;\n            case AV_LOCK_DESTROY:\n                number = new IntPointer(mutex).get(0);\n                // System.out.println(\"Command: \" + op + \" number: \" + number);\n                lockArray.remove(number);\n                mutex.put(0, null);\n                return 0;\n            default:\n                return -1;\n            }\n        }\n    }.retainReference();\n\n    public static synchronized void init() {\n        if (!initialized) {\n            initialized = true;\n            av_lockmgr_register(lockCallback);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FFmpegLogCallback.java",
    "content": "/*\n * Copyright (C) 2015-2021 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.tools.Logger;\n\nimport org.bytedeco.ffmpeg.avutil.*;\nimport static org.bytedeco.ffmpeg.global.avutil.*;\n\n/**\n * A utility class to redirect to Java log messages from FFmpeg.\n *\n * @see Logger\n *\n * @author Samuel Audet\n */\npublic class FFmpegLogCallback extends LogCallback {\n\n    private static final Logger logger = Logger.create(FFmpegLogCallback.class);\n\n    static final FFmpegLogCallback instance = new FFmpegLogCallback().retainReference();\n\n    /** Returns an instance that can be used with {@link org.bytedeco.ffmpeg.global.avutil#setLogCallback(LogCallback)}. */\n    public static FFmpegLogCallback getInstance() {\n        return instance;\n    }\n\n    /** Calls {@code avutil.setLogCallback(getInstance())}. */\n    public static void set() {\n        setLogCallback(getInstance());\n    }\n\n    /** Returns {@code av_log_get_level()}. **/\n    public static int getLevel() {\n        return av_log_get_level();\n    }\n\n    /** Calls {@code av_log_set_level(level)}. **/\n    public static void setLevel(int level) {\n        av_log_set_level(level);\n    }\n\n    /** Logs the given rejected options regarding the given command */\n    public static void logRejectedOptions(final AVDictionary options, final String command) {\n        if (getLevel() >= AV_LOG_INFO && av_dict_count(options) > 0) {\n            final StringBuilder sb = new StringBuilder(command + \" rejected some options:\");\n            AVDictionaryEntry e = null;\n            while ((e = av_dict_iterate(options, e)) != null) {\n                sb.append(\"\\tOption: \").append(e.key().getString()).append(\", value: \").append(e.value().getString());\n            }\n            logger.info(sb.toString());\n        }\n    }\n\n    @Override public void call(int level, BytePointer msg) {\n        switch (level) {\n            case AV_LOG_PANIC:\n            case AV_LOG_FATAL:\n            case AV_LOG_ERROR:\n                logger.error(msg.getString());\n                break;\n            case AV_LOG_WARNING:\n                logger.warn(msg.getString());\n                break;\n            case AV_LOG_INFO:\n                logger.info(msg.getString());\n                break;\n            case AV_LOG_VERBOSE:\n            case AV_LOG_DEBUG:\n            case AV_LOG_TRACE:\n                logger.debug(msg.getString());\n                break;\n            default:\n                assert false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FlyCapture2FrameGrabber.java",
    "content": "/*\n * Copyright (C) 2014,2017 Jeremy Laviole, Samuel Audet, Jarek Sacha\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.ShortBuffer;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.Loader;\n\nimport org.bytedeco.flycapture.FlyCapture2.*;\nimport org.bytedeco.flycapture.FlyCapture2.Error;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.flycapture.global.FlyCapture2.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Jeremy Laviole\n * @author Jarek Sacha\n */\npublic class FlyCapture2FrameGrabber extends FrameGrabber {\n\n    public static String[] getDeviceDescriptions() throws FrameGrabber.Exception {\n        tryLoad();\n\n        BusManager busMgr = new BusManager();\n\n        int[] numCameras = new int[1];\n        busMgr.GetNumOfCameras(numCameras);\n\n        String[] descriptions = new String[numCameras[0]];\n\n        for (int i = 0; i < numCameras[0]; i++) {\n            PGRGuid guid = new PGRGuid();\n            Error error = busMgr.GetCameraFromIndex(i, guid);\n            if (error.notEquals(PGRERROR_OK)) {\n                PrintError(error);\n                System.exit(-1);\n            }\n\n            Camera cam = new Camera();\n            // Connect to a camera\n            error = cam.Connect(guid);\n            if (error.notEquals(PGRERROR_OK)) {\n                PrintError(error);\n            }\n\n            // Get the camera information\n            CameraInfo camInfo = new CameraInfo();\n            error = cam.GetCameraInfo(camInfo);\n            if (error.notEquals(PGRERROR_OK)) {\n                PrintError(error);\n            }\n            descriptions[i] = CameraInfo(camInfo);\n        }\n\n        return descriptions;\n    }\n\n    static void PrintError(Error error) {\n        error.PrintErrorTrace();\n    }\n\n    static String CameraInfo(CameraInfo pCamInfo) {\n        return \"\\n*** CAMERA INFORMATION ***\\n\"\n                + \"Serial number - \" + pCamInfo.serialNumber() + \"\\n\"\n                + \"Camera model - \" + pCamInfo.modelName().getString() + \"\\n\"\n                + \"Camera vendor - \" + pCamInfo.vendorName().getString() + \"\\n\"\n                + \"Sensor - \" + pCamInfo.sensorInfo().getString() + \"\\n\"\n                + \"Resolution - \" + pCamInfo.sensorResolution().getString() + \"\\n\"\n                + \"Firmware version - \" + pCamInfo.firmwareVersion().getString() + \"\\n\"\n                + \"Firmware build time - \" + pCamInfo.firmwareBuildTime().getString() + \"\\n\";\n    }\n\n    public static FlyCapture2FrameGrabber createDefault(File deviceFile) throws FrameGrabber.Exception {\n        return null;\n    }\n\n    public static FlyCapture2FrameGrabber createDefault(String devicePath) throws FrameGrabber.Exception {\n        return null;\n    }\n\n    public static FlyCapture2FrameGrabber createDefault(int deviceNumber) throws FrameGrabber.Exception {\n        return new FlyCapture2FrameGrabber(deviceNumber);\n    }\n\n    private static FrameGrabber.Exception loadingException = null;\n\n    public static void tryLoad() throws FrameGrabber.Exception {\n        if (loadingException != null) {\n            loadingException.printStackTrace();\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.flycapture.global.FlyCapture2.class);\n            } catch (Throwable t) {\n                throw loadingException = new FrameGrabber.Exception(\"Failed to load \" + FlyCapture2FrameGrabber.class, t);\n            }\n        }\n    }\n\n    public FlyCapture2FrameGrabber(int deviceNumber) throws FrameGrabber.Exception {\n        int[] numCameras = new int[1];\n        busMgr.GetNumOfCameras(numCameras);\n\n        // Get the camera\n        PGRGuid guid = new PGRGuid();\n        Error error = busMgr.GetCameraFromIndex(deviceNumber, guid);\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n            System.exit(-1);\n        }\n\n        camera = new Camera();\n\n        // Connect to a camera\n        error = camera.Connect(guid);\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n        }\n\n        // Get the camera information\n        cameraInfo = new CameraInfo();\n        error = camera.GetCameraInfo(cameraInfo);\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n        }\n\n    }\n\n    public void release() throws FrameGrabber.Exception {\n        if (camera != null) {\n            stop();\n            camera.Disconnect();\n            camera = null;\n        }\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    public static final int INITIALIZE = 0x000,\n            TRIGGER_INQ = 0x530,\n            IS_CAMERA_POWER = 0x400,\n            CAMERA_POWER = 0x610,\n            SOFTWARE_TRIGGER = 0x62C,\n            SOFT_ASYNC_TRIGGER = 0x102C,\n            IMAGE_DATA_FORMAT = 0x1048;\n\n    private BusManager busMgr = new BusManager();\n    private Camera camera;\n    private CameraInfo cameraInfo;\n    private Image raw_image = new Image();\n    private Image conv_image = new Image();\n    private IplImage temp_image, return_image = null;\n    private FrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n    private final int[] regOut = new int[1];\n    private final float[] outFloat = new float[1];\n    private final float[] gammaOut = new float[1];\n\n    @Override\n    public double getGamma() {\n        return Float.isNaN(gammaOut[0]) || Float.isInfinite(gammaOut[0]) || gammaOut[0] == 0.0f ? 2.2 : gammaOut[0];\n    }\n\n    @Override\n    public int getImageWidth() {\n        return return_image == null ? super.getImageWidth() : return_image.width();\n    }\n\n    @Override\n    public int getImageHeight() {\n        return return_image == null ? super.getImageHeight() : return_image.height();\n    }\n\n    @Override\n    public double getFrameRate() {\n        if (camera == null || camera.isNull()) {\n            return super.getFrameRate();\n        } else {\n            IntPointer videoMode = new IntPointer(1L);\n            IntPointer frameRate = new IntPointer(1L);\n            camera.GetVideoModeAndFrameRate(videoMode, frameRate);\n            return frameRate.get(0);\n        }\n    }\n\n    @Override\n    public void setImageMode(FrameGrabber.ImageMode imageMode) {\n        if (imageMode != this.imageMode) {\n            temp_image = null;\n            return_image = null;\n        }\n        super.setImageMode(imageMode);\n    }\n    static final int VIDEOMODE_ANY = -1;\n\n    public void start() throws FrameGrabber.Exception {\n        int f = FRAMERATE_30;  // TODO: Default 30 ? \n        if (frameRate <= 0) {\n            f = FRAMERATE_30;\n        } else if (frameRate <= 1.876) {\n            f = FRAMERATE_1_875;\n        } else if (frameRate <= 3.76) {\n            f = FRAMERATE_3_75;\n        } else if (frameRate <= 7.51) {\n            f = FRAMERATE_7_5;\n        } else if (frameRate <= 15.01) {\n            f = FRAMERATE_15;\n        } else if (frameRate <= 30.01) {\n            f = FRAMERATE_30;\n        } else if (frameRate <= 60.01) {\n            f = FRAMERATE_60;\n        } else if (frameRate <= 120.01) {\n            f = FRAMERATE_120;\n        } else if (frameRate <= 240.01) {\n            f = FRAMERATE_240;\n        }\n\n        int c = VIDEOMODE_ANY;\n        if (imageMode == FrameGrabber.ImageMode.COLOR || imageMode == FrameGrabber.ImageMode.RAW) {\n            if (imageWidth <= 0 || imageHeight <= 0) {\n                c = VIDEOMODE_ANY;\n            } else if (imageWidth <= 640 && imageHeight <= 480) {\n                c = VIDEOMODE_640x480RGB;\n            } else if (imageWidth <= 800 && imageHeight <= 600) {\n                c = VIDEOMODE_800x600RGB;\n            } else if (imageWidth <= 1024 && imageHeight <= 768) {\n                c = VIDEOMODE_1024x768RGB;\n            } else if (imageWidth <= 1280 && imageHeight <= 960) {\n                c = VIDEOMODE_1280x960RGB;\n            } else if (imageWidth <= 1600 && imageHeight <= 1200) {\n                c = VIDEOMODE_1600x1200RGB;\n            }\n        } else if (imageMode == FrameGrabber.ImageMode.GRAY) {\n            if (imageWidth <= 0 || imageHeight <= 0) {\n                c = VIDEOMODE_ANY;\n            } else if (imageWidth <= 640 && imageHeight <= 480) {\n                c = bpp > 8 ? VIDEOMODE_640x480Y16 : VIDEOMODE_640x480Y8;\n            } else if (imageWidth <= 800 && imageHeight <= 600) {\n                c = bpp > 8 ? VIDEOMODE_800x600Y16 : VIDEOMODE_800x600Y8;\n            } else if (imageWidth <= 1024 && imageHeight <= 768) {\n                c = bpp > 8 ? VIDEOMODE_1024x768Y16 : VIDEOMODE_1024x768Y8;\n            } else if (imageWidth <= 1280 && imageHeight <= 960) {\n                c = bpp > 8 ? VIDEOMODE_1280x960Y16 : VIDEOMODE_1280x960Y8;\n            } else if (imageWidth <= 1600 && imageHeight <= 1200) {\n                c = bpp > 8 ? VIDEOMODE_1600x1200Y16 : VIDEOMODE_1600x1200Y8;\n            }\n        }\n\n        // set or reset trigger mode\n        TriggerMode tm = new TriggerMode();\n        Error error = camera.GetTriggerMode(tm);\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n            throw new FrameGrabber.Exception(\"GetTriggerMode() Error \" + error.GetDescription());\n        }\n        tm.onOff(triggerMode);\n        tm.source(7);\n        tm.mode(14);\n        tm.parameter(0);\n        error = camera.SetTriggerMode(tm);\n        if (error.notEquals(PGRERROR_OK)) {\n            // try with trigger mode 0 instead\n            tm.onOff(true);\n            tm.source(7);\n            tm.mode(0);\n            tm.parameter(0);\n            error = camera.SetTriggerMode(tm);\n            if (error.notEquals(PGRERROR_OK)) {\n                PrintError(error);\n                throw new FrameGrabber.Exception(\"SetTriggerMode() Error \" + error.GetDescription());\n            }\n        }\n        if (triggerMode) {\n            waitForTriggerReady();\n        }\n\n        // try to match the endianness to our platform\n        error = camera.ReadRegister(IMAGE_DATA_FORMAT, regOut);\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n            throw new FrameGrabber.Exception(\"ReadRegister(IMAGE_DATA_FORMAT, regOut) Error \" + error.GetDescription());\n        }\n        int reg;\n        if (ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)) {\n            reg = regOut[0] | 0x1;\n        } else {\n            reg = regOut[0] & ~0x1;\n        }\n        error = camera.WriteRegister(IMAGE_DATA_FORMAT, reg);\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n            throw new FrameGrabber.Exception(\"WriteRegister(IMAGE_DATA_FORMAT, reg) Error \" + error.GetDescription());\n        }\n\n        // TODO: set fastest bus speed ? This may lead to system instability. Use default.\n\n        // set `gamma`\n        Property gammaProp = new Property(GAMMA);\n        if (gamma != 0.0) {\n            error = camera.GetProperty(gammaProp);\n            if (error.notEquals(PGRERROR_OK)) {\n                throw new FrameGrabber.Exception(\"GetProperty(gammaProp) Error \" + error.GetDescription());\n            }\n            gammaProp.onOff(true);\n            gammaProp.absControl(true);\n            gammaProp.absValue((float)gamma);\n            camera.SetProperty(gammaProp);\n            error = camera.SetProperty(gammaProp);\n            if (error.notEquals(PGRERROR_OK)) {\n                PrintError(error);\n                throw new FrameGrabber.Exception(\"SetProperty(gammaProp) Error \" + error.GetDescription());\n            }\n        }\n        error = camera.GetProperty(gammaProp);\n        if (error.notEquals(PGRERROR_OK)) {\n            gammaOut[0] = 2.2f;\n        } else {\n            gammaOut[0] = gammaProp.absValue();\n        }\n\n        // set `timeout`\n        error = camera.StartCapture();\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n            throw new FrameGrabber.Exception(\"StartCapture() Error \" + error.GetDescription());\n        }\n\n        // Get the camera configuration\n        FC2Config config = new FC2Config();\n        error = camera.GetConfiguration(config);\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n            throw new FrameGrabber.Exception(\"GetConfiguration() Error \" + error.GetDescription());\n        }\n\n        // Set the grab timeout to 5 seconds\n        config.grabTimeout(timeout);\n\n        // Set the camera configuration\n        error = camera.SetConfiguration(config);\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n            throw new FrameGrabber.Exception(\"SetConfiguration() Error \" + error.GetDescription());\n        }\n    }\n\n    private void waitForTriggerReady() throws Exception {\n        // wait for trigger to be ready...\n        long time = System.currentTimeMillis();\n        do {\n            Error error = camera.ReadRegister(SOFTWARE_TRIGGER, regOut);\n            if (error.notEquals(PGRERROR_OK)) {\n                PrintError(error);\n                throw new FrameGrabber.Exception(\"GetTriggerMode() Error \" + error.GetDescription());\n            }\n            if (System.currentTimeMillis() - time > timeout) {\n                break;\n                //throw new Exception(\"waitForTriggerReady() Error: Timeout occured.\");\n            }\n        } while((regOut[0] >>> 31) != 0);\n    }\n\n    public void stop() throws FrameGrabber.Exception {\n        Error error = camera.StopCapture();\n        if (error.notEquals(PGRERROR_OK)) {\n            PrintError(error);\n            throw new FrameGrabber.Exception(\"flycapture camera StopCapture() Error \" + error);\n        }\n        temp_image = null;\n        return_image = null;\n        timestamp = 0;\n        frameNumber = 0;\n    }\n\n    /**\n     * @throws org.bytedeco.javacv.FrameGrabber.Exception\n     */\n    public void trigger() throws FrameGrabber.Exception {\n        waitForTriggerReady();\n        Error error = camera.FireSoftwareTrigger();\n        if (error.notEquals(PGRERROR_OK)) {\n            throw new FrameGrabber.Exception(\"flycaptureSetCameraRegister() Error \" + error);\n        }\n\n    }\n\n    private int getNumChannels(int pixelFormat) {\n        switch (pixelFormat) {\n            case PIXEL_FORMAT_BGR:\n            case PIXEL_FORMAT_RGB8:\n            case PIXEL_FORMAT_RGB16:\n            case PIXEL_FORMAT_S_RGB16:\n                return 3;\n\n            case PIXEL_FORMAT_MONO8:\n            case PIXEL_FORMAT_MONO16:\n            case PIXEL_FORMAT_RAW8:\n            case PIXEL_FORMAT_RAW16:\n            case PIXEL_FORMAT_S_MONO16:\n                return 1;\n\n            case PIXEL_FORMAT_BGRU:\n                return 4;\n\n            case PIXEL_FORMAT_411YUV8:\n            case PIXEL_FORMAT_422YUV8:\n            case PIXEL_FORMAT_444YUV8:\n            default:\n                return -1;\n        }\n    }\n\n    private int getDepth(int pixelFormat) {\n        switch (pixelFormat) {\n            case PIXEL_FORMAT_BGR:\n            case PIXEL_FORMAT_RGB8:\n            case PIXEL_FORMAT_MONO8:\n            case PIXEL_FORMAT_RAW8:\n            case PIXEL_FORMAT_BGRU:\n                return IPL_DEPTH_8U;\n\n            case PIXEL_FORMAT_MONO16:\n            case PIXEL_FORMAT_RAW16:\n            case PIXEL_FORMAT_RGB16:\n                return IPL_DEPTH_16U;\n\n            case PIXEL_FORMAT_S_MONO16:\n            case PIXEL_FORMAT_S_RGB16:\n                return IPL_DEPTH_16S;\n\n            case PIXEL_FORMAT_411YUV8:\n            case PIXEL_FORMAT_422YUV8:\n            case PIXEL_FORMAT_444YUV8:\n            default:\n                return IPL_DEPTH_8U;\n        }\n    }\n\n    private void setPixelFormat(Image image, int pixelFormat) {\n        image.SetDimensions(image.GetRows(),\n                image.GetCols(),\n                image.GetStride(),\n                pixelFormat,\n                image.GetBayerTileFormat());\n    }\n\n    private void setStride(Image image, int stride) {\n        image.SetDimensions(image.GetRows(),\n                image.GetCols(),\n                stride,\n                image.GetPixelFormat(),\n                image.GetBayerTileFormat());\n    }\n\n    public Frame grab() throws FrameGrabber.Exception {\n        Error error = camera.RetrieveBuffer(raw_image);\n        if (error.notEquals(PGRERROR_OK)) {\n            throw new FrameGrabber.Exception(\"flycaptureGrabImage2() Error \" + error + \" (Has start() been called?)\");\n        }\n        int w = raw_image.GetCols();\n        int h = raw_image.GetRows();\n        int format = raw_image.GetPixelFormat();\n        int depth = getDepth(format);\n        int stride = raw_image.GetStride();\n        int size = h * stride;\n        int numChannels = getNumChannels(format);\n        error = camera.ReadRegister(IMAGE_DATA_FORMAT, regOut);\n        if (error.notEquals(PGRERROR_OK)) {\n            throw new FrameGrabber.Exception(\"flycaptureGetCameraRegister() Error \" + error);\n        }\n        ByteOrder frameEndian = (regOut[0] & 0x1) != 0\n                ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;\n        boolean alreadySwapped = false;\n\n        boolean colorbayer = raw_image.GetBayerTileFormat() != NONE;\n\n        boolean colorrgb = format == PIXEL_FORMAT_RGB8 || format == PIXEL_FORMAT_RGB16\n                || format == PIXEL_FORMAT_BGR || format == PIXEL_FORMAT_BGRU;\n        boolean coloryuv = format == PIXEL_FORMAT_411YUV8 || format == PIXEL_FORMAT_422YUV8\n                || format == PIXEL_FORMAT_444YUV8;\n        BytePointer imageData = raw_image.GetData().capacity(raw_image.GetDataSize());\n\n        if ((depth == IPL_DEPTH_8U || frameEndian.equals(ByteOrder.nativeOrder()))\n                && (imageMode == FrameGrabber.ImageMode.RAW || (imageMode == FrameGrabber.ImageMode.COLOR && numChannels == 3)\n                || (imageMode == FrameGrabber.ImageMode.GRAY && numChannels == 1 && !colorbayer))) {\n            if (return_image == null) {\n                return_image = IplImage.createHeader(w, h, depth, numChannels);\n            }\n            return_image.widthStep(stride);\n            return_image.imageSize(size);\n            return_image.imageData(imageData);\n        } else {\n            if (return_image == null) {\n                return_image = IplImage.create(w, h, depth, imageMode == FrameGrabber.ImageMode.COLOR ? 3 : 1);\n            }\n            if (temp_image == null) {\n                if (imageMode == FrameGrabber.ImageMode.COLOR\n                        && (numChannels > 1 || depth > 8) && !coloryuv && !colorbayer) {\n                    temp_image = IplImage.create(w, h, depth, numChannels);\n                } else if (imageMode == FrameGrabber.ImageMode.GRAY && colorbayer) {\n                    temp_image = IplImage.create(w, h, depth, 3);\n                } else if (imageMode == FrameGrabber.ImageMode.GRAY && colorrgb) {\n                    temp_image = IplImage.createHeader(w, h, depth, 3);\n                } else if (imageMode == FrameGrabber.ImageMode.COLOR && numChannels == 1 && !coloryuv && !colorbayer) {\n                    temp_image = IplImage.createHeader(w, h, depth, 1);\n                } else {\n                    temp_image = return_image;\n                }\n            }\n\n            setStride(conv_image, temp_image.widthStep());\n            conv_image.SetData(temp_image.imageData(), temp_image.width() * temp_image.height() * temp_image.depth());\n\n            if (depth == IPL_DEPTH_8U) {\n\n                setPixelFormat(conv_image, imageMode == FrameGrabber.ImageMode.RAW ? PIXEL_FORMAT_RAW8\n                        : temp_image.nChannels() == 1 ? PIXEL_FORMAT_MONO8 : PIXEL_FORMAT_BGR);\n            } else {\n                setPixelFormat(conv_image, imageMode == FrameGrabber.ImageMode.RAW ? PIXEL_FORMAT_RAW16\n                        : temp_image.nChannels() == 1 ? PIXEL_FORMAT_MONO16 : PIXEL_FORMAT_RGB16);\n            }\n            if (depth != IPL_DEPTH_8U && conv_image.GetPixelFormat() == format && conv_image.GetStride() == stride) {\n                // we just need a copy to swap bytes..\n                ShortBuffer in = imageData.asByteBuffer().order(frameEndian).asShortBuffer();\n                ShortBuffer out = temp_image.getByteBuffer().order(ByteOrder.nativeOrder()).asShortBuffer();\n                out.put(in);\n                alreadySwapped = true;\n            } else if ((imageMode == FrameGrabber.ImageMode.GRAY && colorrgb)\n                    || (imageMode == FrameGrabber.ImageMode.COLOR && numChannels == 1 && !coloryuv && !colorbayer)) {\n                temp_image.widthStep(stride);\n                temp_image.imageSize(size);\n                temp_image.imageData(imageData);\n            } else if (!colorrgb && (colorbayer || coloryuv || numChannels > 1)) {\n\n                error = raw_image.Convert(conv_image);\n//                error = flycaptureConvertImage(context, raw_image, conv_image);\n                if (error.notEquals(PGRERROR_OK)) {\n                    PrintError(error);\n                    throw new FrameGrabber.Exception(\"raw_image.Convert Error \" + error);\n                }\n            }\n            if (!alreadySwapped && depth != IPL_DEPTH_8U\n                    && !frameEndian.equals(ByteOrder.nativeOrder())) {\n\n                // ack, the camera's endianness doesn't correspond to our machine ...\n                // swap bytes of 16-bit images\n                ByteBuffer bb = temp_image.getByteBuffer();\n                ShortBuffer in = bb.order(frameEndian).asShortBuffer();\n                ShortBuffer out = bb.order(ByteOrder.nativeOrder()).asShortBuffer();\n                out.put(in);\n            }\n            if (imageMode == FrameGrabber.ImageMode.COLOR && numChannels == 1 && !coloryuv && !colorbayer) {\n                cvCvtColor(temp_image, return_image, CV_GRAY2BGR);\n            } else if (imageMode == FrameGrabber.ImageMode.GRAY && (colorbayer || colorrgb)) {\n                cvCvtColor(temp_image, return_image, CV_BGR2GRAY);\n            }\n        }\n\n        int bayerFormat = cameraInfo.bayerTileFormat();\n        switch (bayerFormat) {\n            case BGGR:\n                sensorPattern = SENSOR_PATTERN_BGGR;\n                break;\n            case GBRG:\n                sensorPattern = SENSOR_PATTERN_GBRG;\n                break;\n            case GRBG:\n                sensorPattern = SENSOR_PATTERN_GRBG;\n                break;\n            case RGGB:\n                sensorPattern = SENSOR_PATTERN_RGGB;\n                break;\n            default:\n                sensorPattern = -1L;\n        }\n\n        TimeStamp timeStamp = raw_image.GetTimeStamp();\n        timestamp = timeStamp.seconds() * 1000000L + timeStamp.microSeconds();\n        return converter.convert(return_image);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FlyCaptureFrameGrabber.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.ShortBuffer;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Loader;\n\nimport org.bytedeco.flycapture.PGRFlyCapture.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.flycapture.global.PGRFlyCapture.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class FlyCaptureFrameGrabber extends FrameGrabber {\n    public static String[] getDeviceDescriptions() throws Exception {\n        tryLoad();\n\n        int[] count = new int[1];\n        int error = flycaptureBusCameraCount(count);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureBusCameraCount() Error \" + error);\n        }\n        int c = count[0];\n        String[] descriptions = new String[c];\n\n        if (c > 0) {\n            FlyCaptureInfoEx info = new FlyCaptureInfoEx(c);\n            error = flycaptureBusEnumerateCamerasEx(info, count);\n            if (error != FLYCAPTURE_OK) {\n                throw new Exception(\"flycaptureBusEnumerateCamerasEx() Error \" + error);\n            }\n\n            for (int i = 0; i < descriptions.length; i++) {\n                info.position(i);\n                descriptions[i] = info.pszVendorName() + \" \" +\n                        info.pszModelName() + \" \" + info.SerialNumber();\n            }\n        }\n\n        return descriptions;\n    }\n\n    public static FlyCaptureFrameGrabber createDefault(File deviceFile)   throws Exception { throw new Exception(FlyCaptureFrameGrabber.class + \" does not support device files.\"); }\n    public static FlyCaptureFrameGrabber createDefault(String devicePath) throws Exception { throw new Exception(FlyCaptureFrameGrabber.class + \" does not support device paths.\"); }\n    public static FlyCaptureFrameGrabber createDefault(int deviceNumber)  throws Exception { return new FlyCaptureFrameGrabber(deviceNumber); }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.javacpp.PGRFlyCapture.class);\n            } catch (Throwable t) {\n                throw loadingException = new Exception(\"Failed to load \" + FlyCaptureFrameGrabber.class, t);\n            }\n        }\n    }\n\n    public FlyCaptureFrameGrabber(int deviceNumber) throws Exception {\n        int error = flycaptureCreateContext(context);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureCreateContext() Error \" + error);\n        }\n        error = flycaptureInitializePlus(context, deviceNumber, numBuffers, (BytePointer)null);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureInitialize() Error \" + error);\n        }\n    }\n    public void release() throws Exception {\n        if (context != null) {\n            stop();\n            int error = flycaptureDestroyContext(context);\n            context = null;\n            if (error != FLYCAPTURE_OK) {\n                throw new Exception(\"flycaptureDestroyContext() Error \" + error);\n            }\n        }\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    public static final int\n            INITIALIZE         = 0x000,\n            TRIGGER_INQ        = 0x530,\n            IS_CAMERA_POWER    = 0x400,\n            CAMERA_POWER       = 0x610,\n            SOFTWARE_TRIGGER   = 0x62C,\n            SOFT_ASYNC_TRIGGER = 0x102C,\n            IMAGE_DATA_FORMAT  = 0x1048;\n\n    private FlyCaptureContext context = new FlyCaptureContext(null);\n    private FlyCaptureImage raw_image = new FlyCaptureImage();\n    private FlyCaptureImage conv_image = new FlyCaptureImage();\n    private IplImage temp_image, return_image = null;\n    private FrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n    private final int[] regOut = new int[1];\n    private final float[] outFloat = new float[1];\n    private final float[] gammaOut = new float[1];\n\n    @Override public double getGamma() {\n        return Float.isNaN(gammaOut[0]) || Float.isInfinite(gammaOut[0]) || gammaOut[0] == 0.0f ? 2.2 : gammaOut[0];\n    }\n\n    @Override public int getImageWidth() {\n        return return_image == null ? super.getImageWidth() : return_image.width();\n    }\n\n    @Override public int getImageHeight() {\n        return return_image == null ? super.getImageHeight() : return_image.height();\n    }\n\n    @Override public double getFrameRate() {\n        if (context == null || context.isNull()) {\n            return super.getFrameRate();\n        } else {\n            flycaptureGetCameraAbsProperty(context, FLYCAPTURE_FRAME_RATE, outFloat);\n            return outFloat[0];\n        }\n    }\n\n    @Override public void setImageMode(ImageMode imageMode) {\n        if (imageMode != this.imageMode) {\n            temp_image = null;\n            return_image = null;\n        }\n        super.setImageMode(imageMode);\n    }\n\n    public void start() throws Exception {\n        int f = FLYCAPTURE_FRAMERATE_ANY;\n        if (frameRate <= 0) {\n            f = FLYCAPTURE_FRAMERATE_ANY;\n        } else if (frameRate <= 1.876) {\n            f = FLYCAPTURE_FRAMERATE_1_875;\n        } else if (frameRate <= 3.76) {\n            f = FLYCAPTURE_FRAMERATE_3_75;\n        } else if (frameRate <= 7.51) {\n            f = FLYCAPTURE_FRAMERATE_7_5;\n        } else if (frameRate <= 15.01) {\n            f = FLYCAPTURE_FRAMERATE_15;\n        } else if (frameRate <= 30.01) {\n            f = FLYCAPTURE_FRAMERATE_30;\n        } else if (frameRate <= 60.01) {\n            f = FLYCAPTURE_FRAMERATE_60;\n        } else if (frameRate <= 120.01) {\n            f = FLYCAPTURE_FRAMERATE_120;\n        } else if (frameRate <= 240.01) {\n            f = FLYCAPTURE_FRAMERATE_240;\n        }\n\n        int c = FLYCAPTURE_VIDEOMODE_ANY;\n        if (imageMode == ImageMode.COLOR || imageMode == ImageMode.RAW) {\n            if (imageWidth <= 0 || imageHeight <= 0) {\n                c = FLYCAPTURE_VIDEOMODE_ANY;\n            } else if (imageWidth <= 640 && imageHeight <= 480) {\n                c = FLYCAPTURE_VIDEOMODE_640x480RGB;\n            } else if (imageWidth <= 800 && imageHeight <= 600) {\n                c = FLYCAPTURE_VIDEOMODE_800x600RGB;\n            } else if (imageWidth <= 1024 && imageHeight <= 768) {\n                c = FLYCAPTURE_VIDEOMODE_1024x768RGB;\n            } else if (imageWidth <= 1280 && imageHeight <= 960) {\n                c = FLYCAPTURE_VIDEOMODE_1280x960RGB;\n            } else if (imageWidth <= 1600 && imageHeight <= 1200) {\n                c = FLYCAPTURE_VIDEOMODE_1600x1200RGB;\n            }\n        } else if (imageMode == ImageMode.GRAY) {\n            if (imageWidth <= 0 || imageHeight <= 0) {\n                c = FLYCAPTURE_VIDEOMODE_ANY;\n            } else if (imageWidth <= 640 && imageHeight <= 480) {\n                c = bpp > 8 ? FLYCAPTURE_VIDEOMODE_640x480Y16 : FLYCAPTURE_VIDEOMODE_640x480Y8;\n            } else if (imageWidth <= 800 && imageHeight <= 600) {\n                c = bpp > 8 ? FLYCAPTURE_VIDEOMODE_800x600Y16 : FLYCAPTURE_VIDEOMODE_800x600Y8;\n            } else if (imageWidth <= 1024 && imageHeight <= 768) {\n                c = bpp > 8 ? FLYCAPTURE_VIDEOMODE_1024x768Y16 : FLYCAPTURE_VIDEOMODE_1024x768Y8;\n            } else if (imageWidth <= 1280 && imageHeight <= 960) {\n                c = bpp > 8 ? FLYCAPTURE_VIDEOMODE_1280x960Y16 : FLYCAPTURE_VIDEOMODE_1280x960Y8;\n            } else if (imageWidth <= 1600 && imageHeight <= 1200) {\n                c = bpp > 8 ? FLYCAPTURE_VIDEOMODE_1600x1200Y16 : FLYCAPTURE_VIDEOMODE_1600x1200Y8;\n            }\n        }\n\n        // set or reset trigger mode\n        int[] iPolarity = new int[1];\n        int[] iSource   = new int[1];\n        int[] iRawValue = new int[1];\n        int[] iMode     = new int[1];\n        int error = flycaptureGetTrigger(context, (boolean[])null, iPolarity, iSource, iRawValue, iMode, null);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureGetTrigger() Error \" + error);\n        }\n        error = flycaptureSetTrigger(context, triggerMode, iPolarity[0], 7, 14, 0);\n        if (error != FLYCAPTURE_OK) {\n            // try with trigger mode 0 instead\n            error = flycaptureSetTrigger(context, true, iPolarity[0], 7, 0, 0);\n        }\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureSetTrigger() Error \" + error);\n        }\n        if (triggerMode) {\n            waitForTriggerReady();\n        }\n\n        // try to match the endianness to our platform\n        error = flycaptureGetCameraRegister(context, IMAGE_DATA_FORMAT, regOut);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureGetCameraRegister() Error \" + error);\n        }\n        int reg;\n        if (ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)) {\n            reg = regOut[0] | 0x1;\n        } else {\n            reg = regOut[0] & ~0x1;\n        }\n        error = flycaptureSetCameraRegister(context, IMAGE_DATA_FORMAT, reg);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureSetCameraRegister() Error \" + error);\n        }\n\n        error = flycaptureSetBusSpeed(context, FLYCAPTURE_S_FASTEST, FLYCAPTURE_S_FASTEST);\n        if (error != FLYCAPTURE_OK) {\n            error = flycaptureSetBusSpeed(context,\n                    FLYCAPTURE_ANY, FLYCAPTURE_ANY);\n            if (error != FLYCAPTURE_OK) {\n                throw new Exception(\"flycaptureSetBusSpeed() Error \" + error);\n            }\n        }\n\n        if (gamma != 0.0) {\n            error = flycaptureSetCameraAbsProperty(context, FLYCAPTURE_GAMMA, (float)gamma);\n            if (error != FLYCAPTURE_OK) {\n                throw new Exception(\"flycaptureSetCameraAbsProperty() Error \" + error + \": Could not set gamma.\");\n            }\n        }\n        error = flycaptureGetCameraAbsProperty(context, FLYCAPTURE_GAMMA, gammaOut);\n        if (error != FLYCAPTURE_OK) {\n            gammaOut[0] = 2.2f;\n        }\n\n        error = flycaptureStart(context, c, f);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureStart() Error \" + error);\n        }\n        error = flycaptureSetGrabTimeoutEx(context, timeout);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureSetGrabTimeoutEx() Error \" + error);\n        }\n    }\n\n    private void waitForTriggerReady() throws Exception {\n        // wait for trigger to be ready...\n        long time = System.currentTimeMillis();\n        do {\n            int error = flycaptureGetCameraRegister(context, SOFTWARE_TRIGGER, regOut);\n            if (error != FLYCAPTURE_OK) {\n                throw new Exception(\"flycaptureGetCameraRegister() Error \" + error);\n            }\n            if (System.currentTimeMillis() - time > timeout) {\n                break;\n                //throw new Exception(\"waitForTriggerReady() Error: Timeout occured.\");\n            }\n        } while((regOut[0] >>> 31) != 0);\n    }\n\n    public void stop() throws Exception {\n        int error = flycaptureStop(context);\n        if (error != FLYCAPTURE_OK && error != FLYCAPTURE_FAILED) {\n            throw new Exception(\"flycaptureStop() Error \" + error);\n        }\n        temp_image    = null;\n        return_image  = null;\n        timestamp   = 0;\n        frameNumber = 0;\n    }\n\n    public void trigger() throws Exception {\n        waitForTriggerReady();\n        int error = flycaptureSetCameraRegister(context, SOFT_ASYNC_TRIGGER, 0x80000000);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureSetCameraRegister() Error \" + error);\n        }\n    }\n\n    private int getNumChannels(int pixelFormat) {\n        switch (pixelFormat) {\n            case FLYCAPTURE_BGR:\n            case FLYCAPTURE_RGB8:\n            case FLYCAPTURE_RGB16:\n            case FLYCAPTURE_S_RGB16:\n                return 3;\n\n            case FLYCAPTURE_MONO8:\n            case FLYCAPTURE_MONO16:\n            case FLYCAPTURE_RAW8:\n            case FLYCAPTURE_RAW16:\n            case FLYCAPTURE_S_MONO16:\n                return 1;\n\n            case FLYCAPTURE_BGRU:\n                return 4;\n\n            case FLYCAPTURE_411YUV8:\n            case FLYCAPTURE_422YUV8:\n            case FLYCAPTURE_444YUV8:\n            default:\n                return -1;\n        }\n    }\n    private int getDepth(int pixelFormat) {\n        switch (pixelFormat) {\n            case FLYCAPTURE_BGR:\n            case FLYCAPTURE_RGB8:\n            case FLYCAPTURE_MONO8:\n            case FLYCAPTURE_RAW8:\n            case FLYCAPTURE_BGRU:\n                return IPL_DEPTH_8U;\n\n            case FLYCAPTURE_MONO16:\n            case FLYCAPTURE_RAW16:\n            case FLYCAPTURE_RGB16:\n                return IPL_DEPTH_16U;\n\n            case FLYCAPTURE_S_MONO16:\n            case FLYCAPTURE_S_RGB16:\n                return IPL_DEPTH_16S;\n\n            case FLYCAPTURE_411YUV8:\n            case FLYCAPTURE_422YUV8:\n            case FLYCAPTURE_444YUV8:\n            default:\n                return IPL_DEPTH_8U;\n        }\n    }\n\n    public Frame grab() throws Exception {\n        int error = flycaptureGrabImage2(context, raw_image);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureGrabImage2() Error \" + error + \" (Has start() been called?)\");\n        }\n\n        int w = raw_image.iCols();\n        int h = raw_image.iRows();\n        int format = raw_image.pixelFormat();\n        int depth = getDepth(format);\n        int stride = raw_image.iRowInc();\n        int size = h*stride;\n        int numChannels = getNumChannels(format);\n        error = flycaptureGetCameraRegister(context, IMAGE_DATA_FORMAT, regOut);\n        if (error != FLYCAPTURE_OK) {\n            throw new Exception(\"flycaptureGetCameraRegister() Error \" + error);\n        }\n        ByteOrder frameEndian = (regOut[0] & 0x1) != 0 ?\n                ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;\n        boolean alreadySwapped = false;\n        boolean colorbayer = raw_image.bStippled();\n        boolean colorrgb = format == FLYCAPTURE_RGB8 || format == FLYCAPTURE_RGB16 ||\n                           format == FLYCAPTURE_BGR  || format == FLYCAPTURE_BGRU;\n        boolean coloryuv = format == FLYCAPTURE_411YUV8 || format == FLYCAPTURE_422YUV8 ||\n                           format == FLYCAPTURE_444YUV8;\n        BytePointer imageData = raw_image.pData();\n\n        if ((depth == IPL_DEPTH_8U || frameEndian.equals(ByteOrder.nativeOrder())) &&\n                (imageMode == ImageMode.RAW || (imageMode == ImageMode.COLOR && numChannels == 3) ||\n                (imageMode == ImageMode.GRAY && numChannels == 1 && !colorbayer))) {\n            if (return_image == null) {\n                return_image = IplImage.createHeader(w, h, depth, numChannels);\n            }\n            return_image.widthStep(stride);\n            return_image.imageSize(size);\n            return_image.imageData(imageData);\n        } else {\n            if (return_image == null) {\n                return_image = IplImage.create(w, h, depth, imageMode == ImageMode.COLOR ? 3 : 1);\n            }\n            if (temp_image == null) {\n                if (imageMode == ImageMode.COLOR &&\n                        (numChannels > 1 || depth > 8) && !coloryuv && !colorbayer) {\n                    temp_image = IplImage.create(w, h, depth, numChannels);\n                } else if (imageMode == ImageMode.GRAY && colorbayer) {\n                    temp_image = IplImage.create(w, h, depth, 3);\n                } else if (imageMode == ImageMode.GRAY && colorrgb) {\n                    temp_image = IplImage.createHeader(w, h, depth, 3);\n                } else if (imageMode == ImageMode.COLOR && numChannels == 1 && !coloryuv && !colorbayer) {\n                    temp_image = IplImage.createHeader(w, h, depth, 1);\n                } else {\n                    temp_image = return_image;\n                }\n            }\n            conv_image.iRowInc(temp_image.widthStep());\n            conv_image.pData(temp_image.imageData());\n            if (depth == IPL_DEPTH_8U) {\n                conv_image.pixelFormat(imageMode == ImageMode.RAW ? FLYCAPTURE_RAW8 :\n                                       temp_image.nChannels() == 1  ? FLYCAPTURE_MONO8 : FLYCAPTURE_BGR);\n            } else {\n                conv_image.pixelFormat(imageMode == ImageMode.RAW ? FLYCAPTURE_RAW16 :\n                                       temp_image.nChannels() == 1  ? FLYCAPTURE_MONO16 : FLYCAPTURE_RGB16);\n            }\n\n            if (depth != IPL_DEPTH_8U && conv_image.pixelFormat() == format && conv_image.iRowInc() == stride) {\n                // we just need a copy to swap bytes..\n                ShortBuffer in  = raw_image.getByteBuffer().order(frameEndian).asShortBuffer();\n                ShortBuffer out = temp_image.getByteBuffer().order(ByteOrder.nativeOrder()).asShortBuffer();\n                out.put(in);\n                alreadySwapped = true;\n            } else if ((imageMode == ImageMode.GRAY && colorrgb) ||\n                    (imageMode == ImageMode.COLOR && numChannels == 1 && !coloryuv && !colorbayer)) {\n                temp_image.widthStep(stride);\n                temp_image.imageSize(size);\n                temp_image.imageData(imageData);\n            } else if (!colorrgb && (colorbayer || coloryuv || numChannels > 1)) {\n                error = flycaptureConvertImage(context, raw_image, conv_image);\n                if (error != FLYCAPTURE_OK) {\n                    throw new Exception(\"flycaptureConvertImage() Error \" + error);\n                }\n            }\n\n            if (!alreadySwapped && depth != IPL_DEPTH_8U &&\n                    !frameEndian.equals(ByteOrder.nativeOrder())) {\n                // ack, the camera's endianness doesn't correspond to our machine ...\n                // swap bytes of 16-bit images\n                ByteBuffer  bb  = temp_image.getByteBuffer();\n                ShortBuffer in  = bb.order(frameEndian).asShortBuffer();\n                ShortBuffer out = bb.order(ByteOrder.nativeOrder()).asShortBuffer();\n                out.put(in);\n            }\n\n            if (imageMode == ImageMode.COLOR && numChannels == 1 && !coloryuv && !colorbayer) {\n                cvCvtColor(temp_image, return_image, CV_GRAY2BGR);\n            } else if (imageMode == ImageMode.GRAY && (colorbayer || colorrgb)) {\n                cvCvtColor(temp_image, return_image, CV_BGR2GRAY);\n            }\n        }\n\n        error = flycaptureGetColorTileFormat(context, regOut);\n        if (error != FLYCAPTURE_OK) {\n            sensorPattern = -1L;\n        } else switch (regOut[0]) {\n            case FLYCAPTURE_STIPPLEDFORMAT_BGGR: sensorPattern = SENSOR_PATTERN_BGGR; break;\n            case FLYCAPTURE_STIPPLEDFORMAT_GBRG: sensorPattern = SENSOR_PATTERN_GBRG; break;\n            case FLYCAPTURE_STIPPLEDFORMAT_GRBG: sensorPattern = SENSOR_PATTERN_GRBG; break;\n            case FLYCAPTURE_STIPPLEDFORMAT_RGGB: sensorPattern = SENSOR_PATTERN_RGGB; break;\n            default: sensorPattern = -1L;\n        }\n\n        FlyCaptureTimestamp timeStamp = raw_image.timeStamp();\n        timestamp = timeStamp.ulSeconds() * 1000000L + timeStamp.ulMicroSeconds();\n        return converter.convert(return_image);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/Frame.java",
    "content": "/*\n * Copyright (C) 2015-2021 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport java.nio.DoubleBuffer;\nimport java.nio.FloatBuffer;\nimport java.nio.IntBuffer;\nimport java.nio.LongBuffer;\nimport java.nio.ShortBuffer;\nimport java.util.EnumSet;\n\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.DoublePointer;\nimport org.bytedeco.javacpp.FloatPointer;\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.LongPointer;\nimport org.bytedeco.javacpp.Pointer;\nimport org.bytedeco.javacpp.ShortPointer;\nimport org.bytedeco.javacpp.indexer.ByteIndexer;\nimport org.bytedeco.javacpp.indexer.DoubleIndexer;\nimport org.bytedeco.javacpp.indexer.FloatIndexer;\nimport org.bytedeco.javacpp.indexer.Indexable;\nimport org.bytedeco.javacpp.indexer.Indexer;\nimport org.bytedeco.javacpp.indexer.IntIndexer;\nimport org.bytedeco.javacpp.indexer.LongIndexer;\nimport org.bytedeco.javacpp.indexer.ShortIndexer;\nimport org.bytedeco.javacpp.indexer.UByteIndexer;\nimport org.bytedeco.javacpp.indexer.UShortIndexer;\n\n/**\n * A class to manage the data of audio and video frames. It it used by\n * {@link CanvasFrame}, {@link FrameGrabber}, {@link FrameRecorder}, and their\n * subclasses. We can also make the link with other APIs, such as Android,\n * Java 2D, FFmpeg, and OpenCV, via a {@link FrameConverter}.\n *\n * @author Samuel Audet\n */\npublic class Frame implements AutoCloseable, Indexable {\n    /** A flag set by a FrameGrabber or a FrameRecorder to indicate a key frame. */\n    public boolean keyFrame;\n\n    /** The type of the image frame ('I', 'P', 'B', etc). */\n    public char pictType;\n\n    /** Constants to be used for {@link #imageDepth}. */\n    public static final int\n            DEPTH_BYTE   =  -8,\n            DEPTH_UBYTE  =   8,\n            DEPTH_SHORT  = -16,\n            DEPTH_USHORT =  16,\n            DEPTH_INT    = -32,\n            DEPTH_LONG   = -64,\n            DEPTH_FLOAT  =  32,\n            DEPTH_DOUBLE =  64;\n\n    /** Constants defining data type in the frame. */\n    public static enum Type {\n        VIDEO,\n        AUDIO,\n        DATA,\n        SUBTITLE,\n        ATTACHMENT\n    }\n\n    /** Information associated with the {@link #image} field. */\n    public int imageWidth, imageHeight, imageDepth, imageChannels, imageStride;\n\n    /**\n     * Buffers to hold image pixels from multiple channels for a video frame.\n     * Most of the software supports packed data only, but an array is provided\n     * to allow users to store images in a planar format as well.\n     */\n    public Buffer[] image;\n\n    /** Information associated with the {@link #samples} field. */\n    public int sampleRate, audioChannels;\n\n    /** Buffers to hold audio samples from multiple channels for an audio frame. */\n    public Buffer[] samples;\n\n    /** Buffer to hold a data stream associated with a frame. */\n    public ByteBuffer data;\n\n    /** Stream number the audio|video|other data is associated with. */\n    public int streamIndex;\n\n    /** The type of the stream. */\n    public Type type;\n\n    /** The underlying data object, for example, Pointer, AVFrame, IplImage, or Mat. */\n    public Object opaque;\n\n    /** Timestamp of the frame creation in microseconds. */\n    public long timestamp;\n\n    /** Returns {@code Math.abs(depth) / 8}. */\n    public static int pixelSize(int depth) {\n        return Math.abs(depth) / 8;\n    }\n\n    /** Empty constructor. */\n    public Frame() { }\n\n    /** Allocates a new packed image frame in native memory where rows are 8-byte aligned. */\n    public Frame(int width, int height, int depth, int channels) {\n        this(width, height, depth, channels, ((width * channels * pixelSize(depth) + 7) & ~7) / pixelSize(depth));\n    }\n    public Frame(int width, int height, int depth, int channels, int imageStride) {\n        this.imageWidth = width;\n        this.imageHeight = height;\n        this.imageDepth = depth;\n        this.imageChannels = channels;\n        this.imageStride = imageStride;\n        this.pictType = '\\0';\n        this.image = new Buffer[1];\n        this.data = null;\n        this.streamIndex = -1;\n        this.type = null;\n\n        Pointer pointer = new BytePointer(imageHeight * imageStride * pixelSize(depth));\n        ByteBuffer buffer = pointer.asByteBuffer();\n        switch (imageDepth) {\n            case DEPTH_BYTE:\n            case DEPTH_UBYTE:  image[0] = buffer;                  break;\n            case DEPTH_SHORT:\n            case DEPTH_USHORT: image[0] = buffer.asShortBuffer();  break;\n            case DEPTH_INT:    image[0] = buffer.asIntBuffer();    break;\n            case DEPTH_LONG:   image[0] = buffer.asLongBuffer();   break;\n            case DEPTH_FLOAT:  image[0] = buffer.asFloatBuffer();  break;\n            case DEPTH_DOUBLE: image[0] = buffer.asDoubleBuffer(); break;\n            default: throw new UnsupportedOperationException(\"Unsupported depth value: \" + imageDepth);\n        }\n        opaque = new Pointer[] {pointer.retainReference()};\n    }\n\n    /** Returns {@code createIndexer(true, 0)}. */\n    public <I extends Indexer> I createIndexer() {\n        return (I)createIndexer(true, 0);\n    }\n    @Override public <I extends Indexer> I createIndexer(boolean direct) {\n        return (I)createIndexer(direct, 0);\n    }\n    /** Returns an {@link Indexer} for the <i>i</i>th image plane. */\n    public <I extends Indexer> I createIndexer(boolean direct, int i) {\n        long[] sizes = {imageHeight, imageWidth, imageChannels};\n        long[] strides = {imageStride, imageChannels, 1};\n        Buffer buffer = image[i];\n        Object array = buffer.hasArray() ? buffer.array() : null;\n        switch (imageDepth) {\n            case DEPTH_UBYTE:\n                return array != null ? (I)UByteIndexer.create((byte[])array, sizes, strides).indexable(this)\n                            : direct ? (I)UByteIndexer.create((ByteBuffer)buffer, sizes, strides).indexable(this)\n                                     : (I)UByteIndexer.create(new BytePointer((ByteBuffer)buffer), sizes, strides, false).indexable(this);\n            case DEPTH_BYTE:\n                return array != null ? (I)ByteIndexer.create((byte[])array, sizes, strides).indexable(this)\n                            : direct ? (I)ByteIndexer.create((ByteBuffer)buffer, sizes, strides).indexable(this)\n                                     : (I)ByteIndexer.create(new BytePointer((ByteBuffer)buffer), sizes, strides, false).indexable(this);\n            case DEPTH_USHORT:\n                return array != null ? (I)UShortIndexer.create((short[])array, sizes, strides).indexable(this)\n                            : direct ? (I)UShortIndexer.create((ShortBuffer)buffer, sizes, strides).indexable(this)\n                                     : (I)UShortIndexer.create(new ShortPointer((ShortBuffer)buffer), sizes, strides, false).indexable(this);\n            case DEPTH_SHORT:\n                return array != null ? (I)ShortIndexer.create((short[])array, sizes, strides).indexable(this)\n                            : direct ? (I)ShortIndexer.create((ShortBuffer)buffer, sizes, strides).indexable(this)\n                                     : (I)ShortIndexer.create(new ShortPointer((ShortBuffer)buffer), sizes, strides, false).indexable(this);\n            case DEPTH_INT:\n                return array != null ? (I)IntIndexer.create((int[])array, sizes, strides).indexable(this)\n                            : direct ? (I)IntIndexer.create((IntBuffer)buffer, sizes, strides).indexable(this)\n                                     : (I)IntIndexer.create(new IntPointer((IntBuffer)buffer), sizes, strides, false).indexable(this);\n            case DEPTH_LONG:\n                return array != null ? (I)LongIndexer.create((long[])array, sizes, strides).indexable(this)\n                            : direct ? (I)LongIndexer.create((LongBuffer)buffer, sizes, strides).indexable(this)\n                                     : (I)LongIndexer.create(new LongPointer((LongBuffer)buffer), sizes, strides, false).indexable(this);\n            case DEPTH_FLOAT:\n                return array != null ? (I)FloatIndexer.create((float[])array, sizes, strides).indexable(this)\n                            : direct ? (I)FloatIndexer.create((FloatBuffer)buffer, sizes, strides).indexable(this)\n                                     : (I)FloatIndexer.create(new FloatPointer((FloatBuffer)buffer), sizes, strides, false).indexable(this);\n            case DEPTH_DOUBLE:\n                return array != null ? (I)DoubleIndexer.create((double[])array, sizes, strides).indexable(this)\n                            : direct ? (I)DoubleIndexer.create((DoubleBuffer)buffer, sizes, strides).indexable(this)\n                                     : (I)DoubleIndexer.create(new DoublePointer((DoubleBuffer)buffer), sizes, strides, false).indexable(this);\n            default: assert false;\n        }\n        return null;\n    }\n\n    /**Care must be taken if this method is to be used in conjunction with movie recordings.\n     *  Cloning a frame containing a full HD picture (alpha channel included) would take 1920 x 1080 * 4 = 8.294.400 Bytes.\n     *  Expect a heap overflow exception when using this method without cleaning up.\n     *\n     * @return A deep copy of this frame.\n     * @see {@link #cloneBufferArray}\n     *\n     * Extension proposed by Dragos Dutu\n     * */\n    @Override\n    public Frame clone() {\n        Frame newFrame = new Frame();\n\n        // Video part\n        newFrame.imageWidth = imageWidth;\n        newFrame.imageHeight = imageHeight;\n        newFrame.imageDepth = imageDepth;\n        newFrame.imageChannels = imageChannels;\n        newFrame.imageStride = imageStride;\n        newFrame.keyFrame = keyFrame;\n        newFrame.pictType = pictType;\n        newFrame.streamIndex = streamIndex;\n        newFrame.type = type;\n        newFrame.opaque = new Pointer[3];\n        if (image != null) {\n            newFrame.image = new Buffer[image.length];\n            ((Pointer[])newFrame.opaque)[0] = cloneBufferArray(image, newFrame.image);\n        }\n\n        // Audio part\n        newFrame.audioChannels = audioChannels;\n        newFrame.sampleRate = sampleRate;\n        if (samples != null) {\n            newFrame.samples = new Buffer[samples.length];\n            ((Pointer[])newFrame.opaque)[1] = cloneBufferArray(samples, newFrame.samples);\n        }\n\n        // Other data streams\n        if (data != null) {\n            ByteBuffer[] dst = new ByteBuffer[1];\n            ((Pointer[])newFrame.opaque)[2] = cloneBufferArray(new ByteBuffer[]{data}, dst);\n            newFrame.data = dst[0];\n        }\n\n        // Add timestamp\n        newFrame.timestamp = timestamp;\n\n        return newFrame;\n    }\n\n    /**\n     * This private method takes a buffer array as input and returns a deep copy.\n     * It is assumed that all buffers in the input array are of the same subclass.\n     *\n     * @param srcBuffers - Buffer array to be cloned\n     * @param clonedBuffers - Buffer array to fill with clones\n     * @return Opaque object to store\n     *\n     *  @author Extension proposed by Dragos Dutu\n     */\n    private static Pointer cloneBufferArray(Buffer[] srcBuffers, Buffer[] clonedBuffers) {\n        Pointer opaque = null;\n\n        if (srcBuffers != null && srcBuffers.length > 0) {\n            int totalCapacity = 0;\n            for (int i = 0; i < srcBuffers.length; i++) {\n                srcBuffers[i].rewind();\n                totalCapacity += srcBuffers[i].capacity();\n            }\n\n            /*\n             * In order to optimize the transfer we need a type check.\n             *\n             * Most CPUs support hardware memory transfer for different data\n             * types, so it's faster to copy more bytes at once rather\n             * than one byte per iteration as in case of ByteBuffer.\n             *\n             * For example, Intel CPUs support MOVSB (byte transfer), MOVSW\n             * (word transfer), MOVSD (double word transfer), MOVSS (32 bit\n             * scalar single precision floating point), MOVSQ (quad word\n             * transfer) and so on...\n             *\n             * Type checking may be improved by changing the order in\n             * which a buffer is checked against. If it's likely that the\n             * expected buffer is of type \"ShortBuffer\", then it should be\n             * checked at first place.\n             *\n             */\n\n            if (srcBuffers[0] instanceof ByteBuffer) {\n                BytePointer pointer = new BytePointer(totalCapacity);\n                for (int i = 0; i < srcBuffers.length; i++) {\n                    clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())\n                            .asBuffer().put((ByteBuffer)srcBuffers[i]);\n                    pointer.position(pointer.limit());\n                }\n                opaque = pointer;\n            } else if (srcBuffers[0] instanceof ShortBuffer) {\n                ShortPointer pointer = new ShortPointer(totalCapacity);\n                for (int i = 0; i < srcBuffers.length; i++) {\n                    clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())\n                            .asBuffer().put((ShortBuffer)srcBuffers[i]);\n                    pointer.position(pointer.limit());\n                }\n                opaque = pointer;\n            } else if (srcBuffers[0] instanceof IntBuffer) {\n                IntPointer pointer = new IntPointer(totalCapacity);\n                for (int i = 0; i < srcBuffers.length; i++) {\n                    clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())\n                            .asBuffer().put((IntBuffer)srcBuffers[i]);\n                    pointer.position(pointer.limit());\n                }\n                opaque = pointer;\n            } else if (srcBuffers[0] instanceof LongBuffer) {\n                LongPointer pointer = new LongPointer(totalCapacity);\n                for (int i = 0; i < srcBuffers.length; i++) {\n                    clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())\n                            .asBuffer().put((LongBuffer)srcBuffers[i]);\n                    pointer.position(pointer.limit());\n                }\n                opaque = pointer;\n            } else if (srcBuffers[0] instanceof FloatBuffer) {\n                FloatPointer pointer = new FloatPointer(totalCapacity);\n                for (int i = 0; i < srcBuffers.length; i++) {\n                    clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())\n                            .asBuffer().put((FloatBuffer)srcBuffers[i]);\n                    pointer.position(pointer.limit());\n                }\n                opaque = pointer;\n            } else if (srcBuffers[0] instanceof DoubleBuffer) {\n                DoublePointer pointer = new DoublePointer(totalCapacity);\n                for (int i = 0; i < srcBuffers.length; i++) {\n                    clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())\n                            .asBuffer().put((DoubleBuffer)srcBuffers[i]);\n                    pointer.position(pointer.limit());\n                }\n                opaque = pointer;\n            }\n\n            for (int i = 0; i < srcBuffers.length; i++) {\n                srcBuffers[i].rewind();\n                clonedBuffers[i].rewind();\n            }\n        }\n\n        if (opaque != null) {\n            opaque.retainReference();\n        }\n        return opaque;\n    }\n\n    /** Returns types of data containing in the frame */\n    public EnumSet<Type> getTypes() {\n        EnumSet<Type> type = EnumSet.noneOf(Type.class);\n        if (image != null) type.add(Type.VIDEO);\n        if (samples != null) type.add(Type.AUDIO);\n        if (data != null) type.add(Type.DATA);\n        return type;\n    }\n\n    @Override public void close() {\n        if (opaque instanceof Pointer[]) {\n            for (Pointer p : (Pointer[])opaque) {\n                if (p != null) {\n                    p.releaseReference();\n                    p = null;\n                }\n            }\n            opaque = null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FrameConverter.java",
    "content": "/*\n * Copyright (C) 2015-2021 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\n/**\n * Defines two methods to convert between a {@link Frame} and another generic\n * data object that can contain the same data. The idea with this design is\n * to allow users to convert easily between multiple potentially mutually\n * exclusive types of image data objects over which we have no control. Because\n * of this, and for performance reasons, any object returned by this class is\n * guaranteed to remain valid only until the next call to {@code convert()},\n * anywhere in a chain of {@code FrameConverter} objects, and only as long as\n * the latter themselves are not closed or garbage collected.\n *\n * @author Samuel Audet\n */\npublic abstract class FrameConverter<F> implements AutoCloseable {\n    protected Frame frame;\n\n    public abstract Frame convert(F f);\n    public abstract F convert(Frame frame);\n\n    @Override public void close() {\n        if (frame != null) {\n            frame.close();\n            frame = null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FrameFilter.java",
    "content": "/*\n * Copyright (C) 2015-2018 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.bytedeco.javacv;\n\nimport java.io.Closeable;\nimport java.io.IOException;\n\n/**\n * A frame processor that may filter video and audio frames, or both.\n * After calling {@link #start()}, we can add frames to the graph with\n * {@link #push(Frame)} and get the filtered ones with {@link #pull()}.\n *\n * @author Samuel Audet\n */\npublic abstract class FrameFilter implements Closeable {\n    public static FrameFilter createDefault(String filtersDescr, int imageWidth, int imageHeight) throws Exception {\n        return new FFmpegFrameFilter(filtersDescr, imageWidth, imageHeight);\n    }\n\n    protected String filters;\n    protected int imageWidth;\n    protected int imageHeight;\n    protected int pixelFormat;\n    protected double frameRate;\n    protected double aspectRatio;\n    protected int videoInputs;\n    protected String[] videoFilterArgs;\n\n    protected String afilters;\n    protected int audioChannels;\n    protected int sampleFormat;\n    protected int sampleRate;\n    protected int audioInputs;\n    protected String[] audioFilterArgs;\n\n    public String getFilters() {\n        return filters;\n    }\n    public void setFilters(String filters) {\n        this.filters = filters;\n    }\n\n    public int getImageWidth() {\n        return imageWidth;\n    }\n    public void setImageWidth(int imageWidth) {\n        this.imageWidth = imageWidth;\n    }\n\n    public int getImageHeight() {\n        return imageHeight;\n    }\n    public void setImageHeight(int imageHeight) {\n        this.imageHeight = imageHeight;\n    }\n\n    public int getPixelFormat() {\n        return pixelFormat;\n    }\n    public void setPixelFormat(int pixelFormat) {\n        this.pixelFormat = pixelFormat;\n    }\n\n    public double getFrameRate() {\n        return frameRate;\n    }\n    public void setFrameRate(double frameRate) {\n        this.frameRate = frameRate;\n    }\n\n    public double getAspectRatio() {\n        return aspectRatio;\n    }\n    public void setAspectRatio(double aspectRatio) {\n        this.aspectRatio = aspectRatio;\n    }\n\n    public int getVideoInputs() {\n        return videoInputs;\n    }\n    public void setVideoInputs(int videoInputs) {\n        this.videoInputs = videoInputs;\n    }\n\n    public String[] getVideoFilterArgs() {\n        return videoFilterArgs;\n    }\n\n    public void setVideoFilterArgs(String[] videoFilterArgs) {\n        this.videoFilterArgs = videoFilterArgs;\n    }\n\n    public int getAudioChannels() {\n        return audioChannels;\n    }\n    public void setAudioChannels(int audioChannels) {\n        this.audioChannels = audioChannels;\n    }\n\n    public int getSampleFormat() {\n        return sampleFormat;\n    }\n    public void setSampleFormat(int sampleFormat) {\n        this.sampleFormat = sampleFormat;\n    }\n\n    public int getSampleRate() {\n        return sampleRate;\n    }\n    public void setSampleRate(int sampleRate) {\n        this.sampleRate = sampleRate;\n    }\n\n    public int getAudioInputs() {\n        return audioInputs;\n    }\n    public void setAudioInputs(int audioInputs) {\n        this.audioInputs = audioInputs;\n    }\n\n    public String[] getAudioFilterArgs() {\n        return audioFilterArgs;\n    }\n\n    public void setAudioFilterArgs(String[] audioFilterArgs) {\n        this.audioFilterArgs = audioFilterArgs;\n    }\n\n    public static class Exception extends IOException {\n        public Exception(String message) { super(message); }\n        public Exception(String message, Throwable cause) { super(message, cause); }\n    }\n\n    public abstract void start() throws Exception;\n    public abstract void stop() throws Exception;\n    public abstract void push(Frame frame) throws Exception;\n    public abstract Frame pull() throws Exception;\n    public abstract void release() throws Exception;\n\n    @Override public void close() throws Exception {\n        stop();\n        release();\n    }\n\n    public void restart() throws Exception {\n        stop();\n        start();\n    }\n    public void flush() throws Exception {\n        while (pull() != null);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FrameGrabber.java",
    "content": "/*\n * Copyright (C) 2009-2022 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.beans.PropertyEditorSupport;\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.InvocationTargetException;\nimport java.nio.Buffer;\nimport java.nio.charset.Charset;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\n/**\n *\n * @author Samuel Audet\n */\npublic abstract class FrameGrabber implements Closeable {\n\n    public static final List<String> list = new LinkedList<String>(Arrays.asList(new String[] {\n        \"DC1394\", \"FlyCapture\", \"FlyCapture2\", \"OpenKinect\", \"OpenKinect2\", \"RealSense\", \"RealSense2\", \"PS3Eye\", \"VideoInput\", \"OpenCV\", \"FFmpeg\", \"IPCamera\" }));\n    public static void init() {\n        for (String name : list) {\n            try {\n                Class<? extends FrameGrabber> c = get(name);\n                c.getMethod(\"tryLoad\").invoke(null);\n            } catch (Throwable t) {\n                continue;\n            }\n        }\n    }\n    public static Class<? extends FrameGrabber> getDefault() {\n        // select first frame grabber that can load and that may have some cameras..\n        for (String name : list) {\n            try {\n                Class<? extends FrameGrabber> c = get(name);\n                c.getMethod(\"tryLoad\").invoke(null);\n                boolean mayContainCameras = false;\n                try {\n                    String[] s = (String[])c.getMethod(\"getDeviceDescriptions\").invoke(null);\n                    if (s.length > 0) {\n                        mayContainCameras = true;\n                    }\n                } catch (Throwable t) {\n                    if (t.getCause() instanceof UnsupportedOperationException) {\n                        mayContainCameras = true;\n                    }\n                }\n                if (mayContainCameras) {\n                    return c;\n                }\n            } catch (Throwable t) {\n                continue;\n            }\n        }\n        return null;\n    }\n    public static Class<? extends FrameGrabber> get(String className) throws Exception {\n        className = FrameGrabber.class.getPackage().getName() + \".\" + className;\n        try {\n            return Class.forName(className).asSubclass(FrameGrabber.class);\n        } catch (ClassNotFoundException e) {\n            String className2 = className + \"FrameGrabber\";\n            try {\n                return Class.forName(className2).asSubclass(FrameGrabber.class);\n            } catch (ClassNotFoundException ex) {\n                throw new Exception(\"Could not get FrameGrabber class for \" + className + \" or \" + className2, e);\n            }\n        }\n    }\n\n    public static FrameGrabber create(Class<? extends FrameGrabber> c, Class p, Object o) throws Exception {\n        Throwable cause = null;\n        try {\n            return c.getConstructor(p).newInstance(o);\n        } catch (InstantiationException ex) {\n            cause = ex;\n        } catch (IllegalAccessException ex) {\n            cause = ex;\n        } catch (IllegalArgumentException ex) {\n            cause = ex;\n        } catch (NoSuchMethodException ex) {\n            cause = ex;\n        } catch (InvocationTargetException ex) {\n            cause = ex.getCause();\n        }\n        throw new Exception(\"Could not create new \" + c.getSimpleName() + \"(\" + o + \")\", cause);\n    }\n\n    public static FrameGrabber createDefault(File deviceFile) throws Exception {\n        return create(getDefault(), File.class, deviceFile);\n    }\n    public static FrameGrabber createDefault(String devicePath) throws Exception {\n        return create(getDefault(), String.class, devicePath);\n    }\n    public static FrameGrabber createDefault(int deviceNumber) throws Exception {\n        try {\n            return create(getDefault(), int.class, deviceNumber);\n        } catch (Exception ex) {\n            return create(getDefault(), Integer.class, deviceNumber);\n        }\n    }\n\n    public static FrameGrabber create(String className, File deviceFile) throws Exception {\n        return create(get(className), File.class, deviceFile);\n    }\n    public static FrameGrabber create(String className, String devicePath) throws Exception {\n        return create(get(className), String.class, devicePath);\n    }\n    public static FrameGrabber create(String className, int deviceNumber) throws Exception {\n        try {\n            return create(get(className), int.class, deviceNumber);\n        } catch (Exception ex) {\n            return create(get(className), Integer.class, deviceNumber);\n        }\n    }\n\n    public static class PropertyEditor extends PropertyEditorSupport {\n        @Override public String getAsText() {\n            Class c = (Class)getValue();\n            return c == null ? \"null\" : c.getSimpleName().split(\"FrameGrabber\")[0];\n        }\n        @Override public void setAsText(String s) {\n            if (s == null) {\n                setValue(null);\n            }\n            try {\n                setValue(get(s));\n            } catch (Exception ex) {\n                throw new IllegalArgumentException(ex);\n            }\n        }\n        @Override public String[] getTags() {\n            return list.toArray(new String[list.size()]);\n        }\n    }\n\n\n    public static enum ImageMode {\n        COLOR, GRAY, RAW\n    }\n\n    public static enum SampleMode {\n        SHORT, FLOAT, RAW\n    }\n\n    public static final long\n            SENSOR_PATTERN_RGGB = 0,\n            SENSOR_PATTERN_GBRG = (1L << 32),\n            SENSOR_PATTERN_GRBG = 1,\n            SENSOR_PATTERN_BGGR = (1L << 32) | 1;\n\n    protected int videoStream = -1, audioStream = -1;\n    protected int videoDisposition = 0, audioDisposition = 0;\n    protected String format = null, videoCodecName = null, audioCodecName = null;\n    protected int imageWidth = 0, imageHeight = 0, audioChannels = 0;\n    protected ImageMode imageMode = ImageMode.COLOR;\n    protected long sensorPattern = -1L;\n    protected int pixelFormat = -1, videoCodec, videoBitrate = 0, imageScalingFlags = 0;\n    protected double aspectRatio = 0, frameRate = 0;\n    protected SampleMode sampleMode = SampleMode.SHORT;\n    protected int sampleFormat = -1, audioCodec, audioBitrate = 0, sampleRate = 0;\n    protected boolean triggerMode = false;\n    protected int bpp = 0;\n    protected int timeout = 10000;\n    protected int numBuffers = 4;\n    protected double gamma = 0.0;\n    protected boolean deinterlace = false;\n    protected Charset charset = Charset.defaultCharset();\n    protected Map<String, String> options = new HashMap<String, String>();\n    protected Map<String, String> videoOptions = new HashMap<String, String>();\n    protected Map<String, String> audioOptions = new HashMap<String, String>();\n    protected Map<String, String> metadata = new HashMap<String, String>();\n    protected Map<String, String> videoMetadata = new HashMap<String, String>();\n    protected Map<String, String> audioMetadata = new HashMap<String, String>();\n    protected Map<String, Buffer> videoSideData = new HashMap<String, Buffer>();\n    protected Map<String, Buffer> audioSideData = new HashMap<String, Buffer>();\n    protected int frameNumber = 0;\n    protected long timestamp = 0;\n    protected int maxDelay = -1;\n    protected long startTime = 0;\n\n    public int getVideoStream() {\n        return videoStream;\n    }\n    public void setVideoStream(int videoStream) {\n        this.videoStream = videoStream;\n    }\n\n    public int getAudioStream() {\n        return audioStream;\n    }\n    public void setAudioStream(int audioStream) {\n        this.audioStream = audioStream;\n    }\n\n    public void setVideoDisposition(int videoDisposition) {\n        this.videoDisposition = videoDisposition;\n    }\n    public int getVideoDisposition() {\n        return videoDisposition;\n    }\n\n    public void setAudioDisposition(int audioDisposition) {\n        this.audioDisposition = audioDisposition;\n    }\n    public int getAudioDisposition() {\n        return audioDisposition;\n    }\n\n    public String getFormat() {\n        return format;\n    }\n    public void setFormat(String format) {\n        this.format = format;\n    }\n\n    public String getVideoCodecName() {\n        return videoCodecName;\n    }\n    public void setVideoCodecName(String videoCodecName) {\n        this.videoCodecName = videoCodecName;\n    }\n\n    public String getAudioCodecName() {\n        return audioCodecName;\n    }\n    public void setAudioCodecName(String audioCodecName) {\n        this.audioCodecName = audioCodecName;\n    }\n\n    public int getImageWidth() {\n        return imageWidth;\n    }\n    public void setImageWidth(int imageWidth) {\n        this.imageWidth = imageWidth;\n    }\n\n    public int getImageHeight() {\n        return imageHeight;\n    }\n    public void setImageHeight(int imageHeight) {\n        this.imageHeight = imageHeight;\n    }\n\n    public int getAudioChannels() {\n        return audioChannels;\n    }\n    public void setAudioChannels(int audioChannels) {\n        this.audioChannels = audioChannels;\n    }\n\n    public ImageMode getImageMode() {\n        return imageMode;\n    }\n    public void setImageMode(ImageMode imageMode) {\n        this.imageMode = imageMode;\n    }\n\n    public long getSensorPattern() {\n        return sensorPattern;\n    }\n    public void setSensorPattern(long sensorPattern) {\n        this.sensorPattern = sensorPattern;\n    }\n\n    public int getPixelFormat() {\n        return pixelFormat;\n    }\n    public void setPixelFormat(int pixelFormat) {\n        this.pixelFormat = pixelFormat;\n    }\n\n    public int getVideoCodec() {\n        return videoCodec;\n    }\n    public void setVideoCodec(int videoCodec) {\n        this.videoCodec = videoCodec;\n    }\n\n    public int getVideoBitrate() {\n        return videoBitrate;\n    }\n    public void setVideoBitrate(int videoBitrate) {\n        this.videoBitrate = videoBitrate;\n    }\n\n    public int getImageScalingFlags() {\n        return imageScalingFlags;\n    }\n    public void setImageScalingFlags(int imageScalingFlags) {\n        this.imageScalingFlags = imageScalingFlags;\n    }\n\n    public double getAspectRatio() {\n        return aspectRatio;\n    }\n    public void setAspectRatio(double aspectRatio) {\n        this.aspectRatio = aspectRatio;\n    }\n\n    public double getFrameRate() {\n        return frameRate;\n    }\n    public void setFrameRate(double frameRate) {\n        this.frameRate = frameRate;\n    }\n\n    public int getAudioCodec() {\n        return audioCodec;\n    }\n    public void setAudioCodec(int audioCodec) {\n        this.audioCodec = audioCodec;\n    }\n\n    public int getAudioBitrate() {\n        return audioBitrate;\n    }\n    public void setAudioBitrate(int audioBitrate) {\n        this.audioBitrate = audioBitrate;\n    }\n\n    public SampleMode getSampleMode() {\n        return sampleMode;\n    }\n    public void setSampleMode(SampleMode samplesMode) {\n        this.sampleMode = samplesMode;\n    }\n\n    public int getSampleFormat() {\n        return sampleFormat;\n    }\n    public void setSampleFormat(int sampleFormat) {\n        this.sampleFormat = sampleFormat;\n    }\n\n    public int getSampleRate() {\n        return sampleRate;\n    }\n    public void setSampleRate(int sampleRate) {\n        this.sampleRate = sampleRate;\n    }\n\n    public boolean isTriggerMode() {\n        return triggerMode;\n    }\n    public void setTriggerMode(boolean triggerMode) {\n        this.triggerMode = triggerMode;\n    }\n\n    public int getBitsPerPixel() {\n        return bpp;\n    }\n    public void setBitsPerPixel(int bitsPerPixel) {\n        this.bpp = bitsPerPixel;\n    }\n\n    public int getTimeout() {\n        return timeout;\n    }\n    public void setTimeout(int timeout) {\n        this.timeout = timeout;\n    }\n\n    public int getNumBuffers() {\n        return numBuffers;\n    }\n    public void setNumBuffers(int numBuffers) {\n        this.numBuffers = numBuffers;\n    }\n\n    public double getGamma() {\n        return gamma;\n    }\n    public void setGamma(double gamma) {\n        this.gamma = gamma;\n    }\n\n    public boolean isDeinterlace() {\n        return deinterlace;\n    }\n    public void setDeinterlace(boolean deinterlace) {\n        this.deinterlace = deinterlace;\n    }\n\n    public Charset getCharset() {\n        return charset;\n    }\n    public void setCharset(Charset charset) {\n        this.charset = charset;\n    }\n\n    public Map<String, String> getOptions() {\n        return options;\n    }\n    public void setOptions(Map<String, String> options) {\n        this.options = options;\n    }\n\n    public Map<String, String> getVideoOptions() {\n        return videoOptions;\n    }\n    public void setVideoOptions(Map<String, String> options) {\n        this.videoOptions = options;\n    }\n\n    public Map<String, String> getAudioOptions() {\n        return audioOptions;\n    }\n    public void setAudioOptions(Map<String, String> options) {\n        this.audioOptions = options;\n    }\n\n    public Map<String, String> getMetadata() {\n        return metadata;\n    }\n    public void setMetadata(Map<String, String> metadata) {\n        this.metadata = metadata;\n    }\n\n    public Map<String, String> getVideoMetadata() {\n        return videoMetadata;\n    }\n    public void setVideoMetadata(Map<String, String> metadata) {\n        this.videoMetadata = metadata;\n    }\n\n    public Map<String, String> getAudioMetadata() {\n        return audioMetadata;\n    }\n    public void setAudioMetadata(Map<String, String> metadata) {\n        this.audioMetadata = metadata;\n    }\n\n    public String getOption(String key) {\n        return options.get(key);\n    }\n    public void setOption(String key, String value) {\n        options.put(key, value);\n    }\n\n    public String getVideoOption(String key) {\n        return videoOptions.get(key);\n    }\n    public void setVideoOption(String key, String value) {\n        videoOptions.put(key, value);\n    }\n\n    public String getAudioOption(String key) {\n        return audioOptions.get(key);\n    }\n    public void setAudioOption(String key, String value) {\n        audioOptions.put(key, value);\n    }\n\n    public String getMetadata(String key) {\n        return metadata.get(key);\n    }\n    public void setMetadata(String key, String value) {\n        metadata.put(key, value);\n    }\n\n    public String getVideoMetadata(String key) {\n        return videoMetadata.get(key);\n    }\n    public void setVideoMetadata(String key, String value) {\n        videoMetadata.put(key, value);\n    }\n\n    public String getAudioMetadata(String key) {\n        return audioMetadata.get(key);\n    }\n    public void setAudioMetadata(String key, String value) {\n        audioMetadata.put(key, value);\n    }\n\n    public Map<String, Buffer> getVideoSideData() {\n        return videoSideData;\n    }\n    public void setVideoSideData(Map<String, Buffer> videoSideData) {\n        this.videoSideData = videoSideData;\n    }\n\n    public Buffer getVideoSideData(String key) {\n        return videoSideData.get(key);\n    }\n    public void setVideoSideData(String key, Buffer value) {\n        videoSideData.put(key, value);\n    }\n\n    public Map<String, Buffer> getAudioSideData() {\n        return audioSideData;\n    }\n    public void setAudioSideData(Map<String, Buffer> audioSideData) {\n        this.audioSideData = audioSideData;\n    }\n\n    public Buffer getAudioSideData(String key) {\n        return audioSideData.get(key);\n    }\n    public void setAudioSideData(String key, Buffer value) {\n        audioSideData.put(key, value);\n    }\n\n    public int getFrameNumber() {\n        return frameNumber;\n    }\n    public void setFrameNumber(int frameNumber) throws Exception {\n        this.frameNumber = frameNumber;\n    }\n\n    public long getTimestamp() {\n        return timestamp;\n    }\n    public void setTimestamp(long timestamp) throws Exception {\n        this.timestamp = timestamp;\n    }\n\n    public int getMaxDelay() {\n        return maxDelay;\n    }\n    public void setMaxDelay(int maxDelay) {\n        this.maxDelay = maxDelay;\n    }\n\n    public int getLengthInFrames() {\n        return 0;\n    }\n    public long getLengthInTime() {\n        return 0;\n    }\n\n    public static class Exception extends IOException {\n        public Exception(String message) { super(message); }\n        public Exception(String message, Throwable cause) { super(message, cause); }\n    }\n\n    public abstract void start() throws Exception;\n    public abstract void stop() throws Exception;\n    public abstract void trigger() throws Exception;\n\n    @Override public void close() throws Exception {\n        stop();\n        release();\n    }\n\n    /**\n     * Each call to grab stores the new image in the memory address for the previously returned frame. <br/>\n     * IE.<br/>\n     * <code>\n     * grabber.grab() == grabber.grab()\n     * </code>\n     * <br/>\n     * This means that if you need to cache images returned from grab you should {@link Frame#clone()} the\n     * returned frame as the next call to grab will overwrite your existing image's memory.\n     * <br/>\n     * <b>Why?</b><br/>\n     * Using this method instead of allocating a new buffer every time a frame\n     * is grabbed improves performance by reducing the frequency of garbage collections.\n     * Almost no additional heap space is typically allocated per frame.\n     *\n     * @return The frame returned from the grabber\n     * @throws Exception If there is a problem grabbing the frame.\n     */\n    public abstract Frame grab() throws Exception;\n    public Frame grabFrame() throws Exception { return grab(); }\n    public abstract void release() throws Exception;\n\n    public void restart() throws Exception {\n        stop();\n        start();\n    }\n    public void flush() throws Exception {\n        for (int i = 0; i < numBuffers+1; i++) {\n            grab();\n        }\n    }\n\n    private ExecutorService executor = Executors.newSingleThreadExecutor();\n    private Future<Void> future = null;\n    private Frame delayedFrame = null;\n    private long delayedTime = 0;\n    public void delayedGrab(final long delayTime) {\n        delayedFrame = null;\n        delayedTime = 0;\n        final long start = System.nanoTime()/1000;\n        if (future != null && !future.isDone()) {\n            return;\n        }\n        future = executor.submit(new Callable<Void>() { public Void call() throws Exception {\n            do {\n                delayedFrame = grab();\n                delayedTime = System.nanoTime()/1000 - start;\n            } while (delayedTime < delayTime);\n            return null;\n        }});\n    }\n    public long getDelayedTime() throws InterruptedException, ExecutionException {\n        if (future == null) {\n            return 0;\n        }\n        future.get();\n        return delayedTime;\n    }\n    public Frame getDelayedFrame() throws InterruptedException, ExecutionException {\n        if (future == null) {\n            return null;\n        }\n        future.get();\n        return delayedFrame;\n    }\n\n    public static class Array {\n        // declared protected to force users to use createArray(), which\n        // can be overridden without changing the calling code...\n        protected Array(FrameGrabber[] frameGrabbers) {\n            setFrameGrabbers(frameGrabbers);\n        }\n\n        private Frame[] grabbedFrames = null;\n        private long[] latencies = null;\n        private long[] bestLatencies = null;\n        private long lastNewestTimestamp = 0;\n        private long bestInterval = Long.MAX_VALUE;\n\n        protected FrameGrabber[] frameGrabbers = null;\n        public FrameGrabber[] getFrameGrabbers() {\n            return frameGrabbers;\n        }\n        public void setFrameGrabbers(FrameGrabber[] frameGrabbers) {\n            this.frameGrabbers = frameGrabbers;\n            grabbedFrames = new Frame[frameGrabbers.length];\n            latencies = new long[frameGrabbers.length];\n            bestLatencies = null;\n            lastNewestTimestamp = 0;\n        }\n        public int size() {\n            return frameGrabbers.length;\n        }\n\n        public void start() throws Exception {\n            for (FrameGrabber f : frameGrabbers) {\n                f.start();\n            }\n        }\n        public void stop() throws Exception {\n            for (FrameGrabber f : frameGrabbers) {\n                f.stop();\n            }\n        }\n        // should be overriden to implement a broadcast trigger...\n        public void trigger() throws Exception {\n            for (FrameGrabber f : frameGrabbers) {\n                if (f.isTriggerMode()) {\n                    f.trigger();\n                }\n            }\n        }\n        // should be overriden to implement a broadcast grab...\n        public Frame[] grab() throws Exception {\n            if (frameGrabbers.length == 1) {\n                grabbedFrames[0] = frameGrabbers[0].grab();\n                return grabbedFrames;\n            }\n\n            // assume we sometimes get perfectly synchronized images,\n            // so save the best latencies we find as the perfectly\n            // synchronized case, so we know what to aim for in\n            // cases of missing/dropped frames ...\n            long newestTimestamp = 0;\n            boolean unsynchronized = false;\n            for (int i = 0; i < frameGrabbers.length; i++) {\n                grabbedFrames[i] = frameGrabbers[i].grab();\n                if (grabbedFrames[i] != null) {\n                    newestTimestamp = Math.max(newestTimestamp, frameGrabbers[i].getTimestamp());\n                }\n                if (frameGrabbers[i].getClass() != frameGrabbers[(i + 1) % frameGrabbers.length].getClass()) {\n                    // assume we can't synchronize different types of cameras with each other\n                    unsynchronized = true;\n                }\n            }\n            if (unsynchronized) {\n                return grabbedFrames;\n            }\n            for (int i = 0; i < frameGrabbers.length; i++) {\n                if (grabbedFrames[i] != null) {\n                    latencies[i] = newestTimestamp - Math.max(0, frameGrabbers[i].getTimestamp());\n                }\n            }\n            if (bestLatencies == null) {\n                bestLatencies = Arrays.copyOf(latencies, latencies.length);\n            } else {\n                int sum1 = 0, sum2 = 0;\n                for (int i = 0; i < frameGrabbers.length; i++) {\n                    sum1 += latencies[i];\n                    sum2 += bestLatencies[i];\n                }\n                if (sum1 < sum2) {\n                    bestLatencies = Arrays.copyOf(latencies, latencies.length);\n                }\n            }\n\n            // we cannot have latencies higher than the time between frames..\n            // or something too close to it anyway... 90% is good?\n            bestInterval = Math.min(bestInterval, newestTimestamp-lastNewestTimestamp);\n            for (int i = 0; i < bestLatencies.length; i++) {\n                bestLatencies[i] = Math.min(bestLatencies[i], bestInterval*9/10);\n            }\n\n            // try to synchronize by attempting to land within 10% of\n            // the bestLatencies looking up to 2 frames ahead ...\n            for (int j = 0; j < 2; j++) {\n                for (int i = 0; i < frameGrabbers.length; i++) {\n                    if (frameGrabbers[i].isTriggerMode() || grabbedFrames[i] == null) {\n                        continue;\n                    }\n                    int latency = (int)(newestTimestamp - Math.max(0, frameGrabbers[i].getTimestamp()));\n                    while (latency-bestLatencies[i] > 0.1*bestLatencies[i]) {\n                        grabbedFrames[i] = frameGrabbers[i].grab();\n                        if (grabbedFrames[i] == null) {\n                            break;\n                        }\n                        latency = (int)(newestTimestamp - Math.max(0, frameGrabbers[i].getTimestamp()));\n                        if (latency < 0) {\n                            // woops, a camera seems to have dropped a frame somewhere...\n                            // bump up the newestTimestamp\n                            newestTimestamp = Math.max(0, frameGrabbers[i].getTimestamp());\n                            break;\n                        }\n                    }\n                }\n            }\n\n//for (int i = 0; i < frameGrabbers.length; i++) {\n//    long latency = newestTimestamp - Math.max(0, frameGrabbers[i].getTimestamp());\n//    System.out.print(bestLatencies[i] + \" \" + latency + \"  \");\n//}\n//System.out.println(\"  \" + bestInterval);\n\n            lastNewestTimestamp = newestTimestamp;\n\n            return grabbedFrames;\n        }\n        public void release() throws Exception {\n            for (FrameGrabber f : frameGrabbers) {\n                f.release();\n            }\n        }\n    }\n\n    public Array createArray(FrameGrabber[] frameGrabbers) {\n        return new Array(frameGrabbers);\n    }\n\n    /** Returns {@code frame = grab()} after {@code waitForTimestamp(frame)}. */\n    public Frame grabAtFrameRate() throws Exception, InterruptedException {\n        Frame frame = grab();\n        if (frame != null) {\n            waitForTimestamp(frame);\n        }\n        return frame;\n    }\n\n    /** Returns true if {@code Thread.sleep()} had to be called. */\n    public boolean waitForTimestamp(Frame frame) throws InterruptedException {\n        if (startTime == 0) {\n            startTime = System.nanoTime() / 1000 - frame.timestamp;\n        } else {\n            long delay = frame.timestamp - (System.nanoTime() / 1000 - startTime);\n            if (delay > 0) {\n                Thread.sleep(delay / 1000, (int)(delay % 1000) * 1000);\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public void resetStartTime() {\n        startTime = 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/FrameRecorder.java",
    "content": "/*\n * Copyright (C) 2009-2023 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.InvocationTargetException;\nimport java.nio.Buffer;\nimport java.nio.charset.Charset;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n *\n * @author Samuel Audet\n */\npublic abstract class FrameRecorder implements Closeable {\n\n    public static final List<String> list = new LinkedList<String>(Arrays.asList(new String[] { \"FFmpeg\", \"OpenCV\" }));\n    public static void init() {\n        for (String name : list) {\n            try {\n                Class<? extends FrameRecorder> c = get(name);\n                c.getMethod(\"tryLoad\").invoke(null);\n            } catch (Throwable t) { }\n        }\n    }\n    public static Class<? extends FrameRecorder> getDefault() {\n        // select first frame recorder that can load..\n        for (String name : list) {\n            try {\n                Class<? extends FrameRecorder> c = get(name);\n                c.getMethod(\"tryLoad\").invoke(null);\n                return c;\n            } catch (Throwable t) { }\n        }\n        return null;\n    }\n    public static Class<? extends FrameRecorder> get(String className) throws Exception {\n        className = FrameRecorder.class.getPackage().getName() + \".\" + className;\n        try {\n            return Class.forName(className).asSubclass(FrameRecorder.class);\n        } catch (ClassNotFoundException e) {\n            String className2 = className + \"FrameRecorder\";\n            try {\n                return Class.forName(className2).asSubclass(FrameRecorder.class);\n            } catch (ClassNotFoundException ex) {\n                throw new Exception(\"Could not get FrameRecorder class for \" + className + \" or \" + className2, e);\n            }\n        }\n    }\n\n    public static FrameRecorder create(Class<? extends FrameRecorder> c, Class p, Object o, int w, int h) throws Exception {\n        Throwable cause = null;\n        try {\n            return (FrameRecorder)c.getConstructor(p, int.class, int.class).newInstance(o, w, h);\n        } catch (InstantiationException ex) {\n            cause = ex;\n        } catch (IllegalAccessException ex) {\n            cause = ex;\n        } catch (IllegalArgumentException ex) {\n            cause = ex;\n        } catch (NoSuchMethodException ex) {\n            cause = ex;\n        } catch (InvocationTargetException ex) {\n            cause = ex.getCause();\n        }\n        throw new Exception(\"Could not create new \" + c.getSimpleName() + \"(\" + o + \", \" + w + \", \" + h + \")\", cause);\n    }\n\n    public static FrameRecorder createDefault(File file, int width, int height) throws Exception {\n        return create(getDefault(), File.class, file, width, height);\n    }\n    public static FrameRecorder createDefault(String filename, int width, int height) throws Exception {\n        return create(getDefault(), String.class, filename, width, height);\n    }\n\n    public static FrameRecorder create(String className, File file, int width, int height) throws Exception {\n        return create(get(className), File.class, file, width, height);\n    }\n    public static FrameRecorder create(String className, String filename, int width, int height) throws Exception {\n        return create(get(className), String.class, filename, width, height);\n    }\n\n    protected String format, videoCodecName, audioCodecName;\n    protected int imageWidth, imageHeight, audioChannels;\n    protected int pixelFormat, videoCodec, videoBitrate, imageScalingFlags, gopSize = -1, videoProfile = -1;\n    protected double aspectRatio, frameRate, videoQuality = -1;\n    protected int sampleFormat, audioCodec, audioBitrate, sampleRate;\n    protected double audioQuality = -1;\n    protected boolean interleaved;\n    protected Charset charset = Charset.defaultCharset();\n    protected Map<String, String> options = new HashMap<String, String>();\n    protected Map<String, String> videoOptions = new HashMap<String, String>();\n    protected Map<String, String> audioOptions = new HashMap<String, String>();\n    protected Map<String, String> metadata = new HashMap<String, String>();\n    protected Map<String, String> videoMetadata = new HashMap<String, String>();\n    protected Map<String, String> audioMetadata = new HashMap<String, String>();\n    protected Map<String, Buffer> videoSideData = new HashMap<String, Buffer>();\n    protected Map<String, Buffer> audioSideData = new HashMap<String, Buffer>();\n    protected int frameNumber = 0;\n    protected long timestamp = 0;\n    protected int maxBFrames = -1;\n    protected int trellis = -1;\n    protected int maxDelay = -1;\n\n    public String getFormat() {\n        return format;\n    }\n    public void setFormat(String format) {\n        this.format = format;\n    }\n\n    public String getVideoCodecName() {\n        return videoCodecName;\n    }\n    public void setVideoCodecName(String videoCodecName) {\n        this.videoCodecName = videoCodecName;\n    }\n\n    public String getAudioCodecName() {\n        return audioCodecName;\n    }\n    public void setAudioCodecName(String audioCodecName) {\n        this.audioCodecName = audioCodecName;\n    }\n\n    public int getImageWidth() {\n        return imageWidth;\n    }\n    public void setImageWidth(int imageWidth) {\n        this.imageWidth = imageWidth;\n    }\n\n    public int getImageHeight() {\n        return imageHeight;\n    }\n    public void setImageHeight(int imageHeight) {\n        this.imageHeight = imageHeight;\n    }\n\n    public int getAudioChannels() {\n        return audioChannels;\n    }\n    public void setAudioChannels(int audioChannels) {\n        this.audioChannels = audioChannels;\n    }\n\n    public int getPixelFormat() {\n        return pixelFormat;\n    }\n    public void setPixelFormat(int pixelFormat) {\n        this.pixelFormat = pixelFormat;\n    }\n\n    public int getVideoCodec() {\n        return videoCodec;\n    }\n    public void setVideoCodec(int videoCodec) {\n        this.videoCodec = videoCodec;\n    }\n\n    public int getVideoBitrate() {\n        return videoBitrate;\n    }\n    public void setVideoBitrate(int videoBitrate) {\n        this.videoBitrate = videoBitrate;\n    }\n\n    public int getImageScalingFlags() {\n        return imageScalingFlags;\n    }\n    public void setImageScalingFlags(int imageScalingFlags) {\n        this.imageScalingFlags = imageScalingFlags;\n    }\n\n    public int getGopSize() {\n        return gopSize;\n    }\n    public void setGopSize(int gopSize) {\n        this.gopSize = gopSize;\n    }\n\n    public int getVideoProfile() {\n        return videoProfile;\n    }\n    public void setVideoProfile(int videoProfile) {\n        this.videoProfile = videoProfile;\n    }\n\n    public double getAspectRatio() {\n        return aspectRatio;\n    }\n    public void setAspectRatio(double aspectRatio) {\n        this.aspectRatio = aspectRatio;\n    }\n\n    public double getFrameRate() {\n        return frameRate;\n    }\n    public void setFrameRate(double frameRate) {\n        this.frameRate = frameRate;\n    }\n\n    public double getVideoQuality() {\n        return videoQuality;\n    }\n    public void setVideoQuality(double videoQuality) {\n        this.videoQuality = videoQuality;\n    }\n\n    public int getSampleFormat() {\n        return sampleFormat;\n    }\n    public void setSampleFormat(int sampleFormat) {\n        this.sampleFormat = sampleFormat;\n    }\n\n    public int getAudioCodec() {\n        return audioCodec;\n    }\n    public void setAudioCodec(int audioCodec) {\n        this.audioCodec = audioCodec;\n    }\n\n    public int getAudioBitrate() {\n        return audioBitrate;\n    }\n    public void setAudioBitrate(int audioBitrate) {\n        this.audioBitrate = audioBitrate;\n    }\n\n    public int getSampleRate() {\n        return sampleRate;\n    }\n    public void setSampleRate(int sampleRate) {\n        this.sampleRate = sampleRate;\n    }\n\n    public double getAudioQuality() {\n        return audioQuality;\n    }\n    public void setAudioQuality(double audioQuality) {\n        this.audioQuality = audioQuality;\n    }\n\n    public boolean isInterleaved() {\n        return interleaved;\n    }\n    public void setInterleaved(boolean interleaved) {\n        this.interleaved = interleaved;\n    }\n\n    public Charset getCharset() {\n        return charset;\n    }\n    public void setCharset(Charset charset) {\n        this.charset = charset;\n    }\n\n    public Map<String, String> getOptions() {\n        return options;\n    }\n    public void setOptions(Map<String, String> options) {\n        this.options = options;\n    }\n\n    public Map<String, String> getVideoOptions() {\n        return videoOptions;\n    }\n    public void setVideoOptions(Map<String, String> options) {\n        this.videoOptions = options;\n    }\n\n    public Map<String, String> getAudioOptions() {\n        return audioOptions;\n    }\n    public void setAudioOptions(Map<String, String> options) {\n        this.audioOptions = options;\n    }\n\n    public Map<String, String> getMetadata() {\n        return metadata;\n    }\n    public void setMetadata(Map<String, String> metadata) {\n        this.metadata = metadata;\n    }\n\n    public Map<String, String> getVideoMetadata() {\n        return videoMetadata;\n    }\n    public void setVideoMetadata(Map<String, String> metadata) {\n        this.videoMetadata = metadata;\n    }\n\n    public Map<String, String> getAudioMetadata() {\n        return audioMetadata;\n    }\n    public void setAudioMetadata(Map<String, String> metadata) {\n        this.audioMetadata = metadata;\n    }\n\n    public String getOption(String key) {\n        return options.get(key);\n    }\n    public void setOption(String key, String value) {\n        options.put(key, value);\n    }\n\n    public String getVideoOption(String key) {\n        return videoOptions.get(key);\n    }\n    public void setVideoOption(String key, String value) {\n        videoOptions.put(key, value);\n    }\n\n    public String getAudioOption(String key) {\n        return audioOptions.get(key);\n    }\n    public void setAudioOption(String key, String value) {\n        audioOptions.put(key, value);\n    }\n\n    public String getMetadata(String key) {\n        return metadata.get(key);\n    }\n    public void setMetadata(String key, String value) {\n        metadata.put(key, value);\n    }\n\n    public String getVideoMetadata(String key) {\n        return videoMetadata.get(key);\n    }\n    public void setVideoMetadata(String key, String value) {\n        videoMetadata.put(key, value);\n    }\n\n    public String getAudioMetadata(String key) {\n        return audioMetadata.get(key);\n    }\n    public void setAudioMetadata(String key, String value) {\n        audioMetadata.put(key, value);\n    }\n\n    public Map<String, Buffer> getVideoSideData() {\n        return videoSideData;\n    }\n    public void setVideoSideData(Map<String, Buffer> videoSideData) {\n        this.videoSideData = videoSideData;\n    }\n\n    public Buffer getVideoSideData(String key) {\n        return videoSideData.get(key);\n    }\n    public void setVideoSideData(String key, Buffer value) {\n        videoSideData.put(key, value);\n    }\n\n    public Map<String, Buffer> getAudioSideData() {\n        return audioSideData;\n    }\n    public void setAudioSideData(Map<String, Buffer> audioSideData) {\n        this.audioSideData = audioSideData;\n    }\n\n    public Buffer getAudioSideData(String key) {\n        return audioSideData.get(key);\n    }\n    public void setAudioSideData(String key, Buffer value) {\n        audioSideData.put(key, value);\n    }\n\n    public int getFrameNumber() {\n        return frameNumber;\n    }\n    public void setFrameNumber(int frameNumber) {\n        this.frameNumber = frameNumber;\n    }\n\n    public long getTimestamp() {\n        return timestamp;\n    }\n    public void setTimestamp(long timestamp) {\n        this.timestamp = timestamp;\n    }\n\n    public int getMaxBFrames() {\n    return maxBFrames;\n    }\n    public void setMaxBFrames(int maxBFrames) {\n    this.maxBFrames = maxBFrames;\n    }\n\n    public int getTrellis() {\n        return trellis;\n    }\n\n    public void setTrellis(int trellis) {\n        this.trellis = trellis;\n    }\n\n    public int getMaxDelay() {\n        return maxDelay;\n    }\n\n    public void setMaxDelay(int maxDelay) {\n        this.maxDelay = maxDelay;\n    }\n\n    public static class Exception extends IOException {\n        public Exception(String message) { super(message); }\n        public Exception(String message, Throwable cause) { super(message, cause); }\n    }\n\n    public abstract void start() throws Exception;\n    public abstract void flush() throws Exception;\n    public abstract void stop() throws Exception;\n    public abstract void record(Frame frame) throws Exception;\n    public abstract void release() throws Exception;\n\n    @Override public void close() throws Exception {\n        stop();\n        release();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/GLCanvasFrame.java",
    "content": "/*\n * Copyright (C) 2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport com.jogamp.opencl.CLImage2d;\nimport com.jogamp.opencl.gl.CLGLImage2d;\nimport com.jogamp.opengl.GL2;\nimport com.jogamp.opengl.GLAutoDrawable;\nimport com.jogamp.opengl.GLCapabilitiesImmutable;\nimport com.jogamp.opengl.GLContext;\nimport com.jogamp.opengl.GLEventListener;\nimport com.jogamp.opengl.awt.GLCanvas;\nimport com.jogamp.opengl.util.Gamma;\nimport java.awt.Color;\nimport java.awt.DisplayMode;\nimport java.awt.EventQueue;\nimport java.awt.GraphicsConfiguration;\nimport java.awt.Image;\nimport java.awt.image.BufferedImage;\nimport java.awt.image.DataBuffer;\nimport java.awt.image.DataBufferByte;\nimport java.awt.image.DataBufferDouble;\nimport java.awt.image.DataBufferFloat;\nimport java.awt.image.DataBufferInt;\nimport java.awt.image.DataBufferShort;\nimport java.awt.image.DataBufferUShort;\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport java.nio.DoubleBuffer;\nimport java.nio.FloatBuffer;\nimport java.nio.IntBuffer;\nimport java.nio.ShortBuffer;\nimport javax.swing.JFrame;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class GLCanvasFrame extends CanvasFrame {\n    public GLCanvasFrame(String title) {\n        this(title, 0.0);\n    }\n    public GLCanvasFrame(String title, double gamma) {\n        super(title, gamma);\n        init(false, null, null);\n    }\n\n    public GLCanvasFrame(String title, GraphicsConfiguration gc,\n            GLCapabilitiesImmutable caps, GLContext shareWith) {\n        this(title, gc, caps, shareWith, 0.0);\n    }\n    public GLCanvasFrame(String title, GraphicsConfiguration gc,\n            GLCapabilitiesImmutable caps, GLContext shareWith, double gamma) {\n        super(title, gc, gamma);\n        init(false, caps, shareWith);\n    }\n\n    public GLCanvasFrame(String title, int screenNumber, DisplayMode displayMode) throws Exception {\n        this(title, screenNumber, displayMode, 0.0);\n    }\n    public GLCanvasFrame(String title, int screenNumber, DisplayMode displayMode, double gamma) throws Exception {\n        super(title, screenNumber, displayMode, gamma);\n        init(true, null, null);\n    }\n\n    public GLCanvasFrame(String title, int screenNumber, DisplayMode displayMode,\n            GLCapabilitiesImmutable caps, GLContext shareWith) throws Exception {\n        this(title, screenNumber, displayMode, caps, shareWith, 0.0);\n    }\n    public GLCanvasFrame(String title, int screenNumber, DisplayMode displayMode,\n            GLCapabilitiesImmutable caps, GLContext shareWith, double gamma) throws Exception {\n        super(title, screenNumber, displayMode, gamma);\n        init(true, caps, shareWith);\n    }\n\n    private void init(final boolean fullScreen,\n            final GLCapabilitiesImmutable caps, final GLContext shareWith) {\n        Runnable r = new Runnable() { public void run() {\n            String wasErase = System.setProperty(\"sun.awt.noerasebackground\", \"true\");\n\n            canvas = new GLCanvas(caps);\n            if (shareWith != null) {\n                ((GLCanvas)canvas).setSharedContext(shareWith);\n            }\n            ((GLCanvas)canvas).addGLEventListener(eventListener);\n            if (fullScreen) {\n                canvas.setSize(getSize());\n                needInitialResize = false;\n            } else {\n                canvas.setSize(1, 1); // or we do not get a GLContext\n                needInitialResize = true;\n            }\n            getContentPane().add(canvas);\n            canvas.setVisible(true);\n\n            if (wasErase != null) {\n                System.setProperty(\"sun.awt.noerasebackground\", wasErase);\n            } else {\n                System.clearProperty(\"sun.awt.noerasebackground\");\n            }\n        }};\n\n        if (EventQueue.isDispatchThread()) {\n            r.run();\n        } else {\n            try {\n                EventQueue.invokeAndWait(r);\n            } catch (java.lang.Exception ex) { }\n        }\n    }\n\n    @Override protected void initCanvas(boolean fullScreen, DisplayMode displayMode, double gamma) { }\n\n    private int[] params = new int[2];\n    private Color color = null;\n    private int width, height, format, type;\n    private Buffer buffer = null;\n    private int frameBuffer = 0, renderBuffer = 0;\n\n    private GLEventListener eventListener = new GLEventListener() {\n        public void init(GLAutoDrawable drawable) {\n            GL2 gl = drawable.getGL().getGL2();\n\n            gl.setSwapInterval(1); // Sync to VBlank\n\n            if (inverseGamma != 1.0) {\n                // Yeah baby, gamma correction in hardware!\n                Gamma.setDisplayGamma(drawable, (float)inverseGamma, 0, 1);\n            }\n            gl.glGenFramebuffers(1, params, 0);\n            frameBuffer = params[0];\n        }\n        public void dispose(GLAutoDrawable drawable) {\n            GL2 gl = drawable.getGL().getGL2();\n\n            params[0] = frameBuffer;\n            gl.glDeleteFramebuffers(1, params, 0);\n            if (inverseGamma != 1.0) {\n                Gamma.resetDisplayGamma(drawable);\n            }\n        }\n        public void display(GLAutoDrawable drawable) {\n            GL2 gl = drawable.getGL().getGL2();\n\n            if (color != null) {\n                gl.glClearColor(color.getRed()/255f, color.getGreen()/255f, color.getBlue()/255f, 1f);\n                gl.glClear(GL2.GL_COLOR_BUFFER_BIT);\n            } else if (buffer != null) {\n                if (isResizable() && needInitialResize) {\n                    int w = (int)Math.round(width *initialScale);\n                    int h = (int)Math.round(height*initialScale);\n                    setCanvasSize(w, h);\n                }\n                gl.glWindowPos2i(0, canvas.getHeight());\n                gl.glPixelZoom((float)canvas.getWidth()/width, -(float)canvas.getHeight()/height);\n                // XXX: Tell OpenGL about the alignment of buffer via glPixelStore(), somehow...\n                gl.glDrawPixels(width, height, format, type, buffer);\n            } else if (renderBuffer > 0) {\n                gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, renderBuffer);\n                gl.glGetRenderbufferParameteriv(GL2.GL_RENDERBUFFER,\n                        GL2.GL_RENDERBUFFER_WIDTH, params, 0);\n                gl.glGetRenderbufferParameteriv(GL2.GL_RENDERBUFFER,\n                        GL2.GL_RENDERBUFFER_HEIGHT, params, 1);\n                if (isResizable() && needInitialResize) {\n                    int w = (int)Math.round(params[0]*initialScale);\n                    int h = (int)Math.round(params[1]*initialScale);\n                    setCanvasSize(w, h);\n                }\n                gl.glBindFramebuffer(GL2.GL_READ_FRAMEBUFFER, frameBuffer);\n                gl.glFramebufferRenderbuffer(GL2.GL_READ_FRAMEBUFFER,\n                        GL2.GL_COLOR_ATTACHMENT0, GL2.GL_RENDERBUFFER, renderBuffer);\n                // Often GL_RENDERBUFFER_WIDTH == 0 and GL_RENDERBUFFER_HEIGHT == 1,\n                // while glCheckFramebufferStatus() returns\n                // GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT here,\n                // but, for a given object, it either never happens or\n                // always happens ... NVIDIA driver bug?\n//System.out.println(params[0] + \" \" + params[1] +\n//        \" glCheckFramebufferStatus = \" + gl.glCheckFramebufferStatus(GL2.GL_READ_FRAMEBUFFER));\n                assert gl.glCheckFramebufferStatus(GL2.GL_READ_FRAMEBUFFER) == GL2.GL_FRAMEBUFFER_COMPLETE;\n                gl.glBlitFramebuffer(0, 0,  params[0], params[1],\n                        0, canvas.getHeight(),  canvas.getWidth(), 0,\n                        GL2.GL_COLOR_BUFFER_BIT, GL2.GL_LINEAR);\n            }\n        }\n        public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { }\n    };\n\n    public GLCanvas getGLCanvas() {\n        return (GLCanvas)canvas;\n    }\n\n    @Override public void showColor(Color color) {\n        this.color = color;\n        this.buffer = null;\n        getGLCanvas().display();\n    }\n\n    @Override public void showImage(Frame frame) {\n        showImage(frame, false);\n    }\n    @Override public void showImage(Frame frame, boolean flipChannels) {\n        if (flipChannels) {\n            throw new RuntimeException(\"GLCanvasFrame does not support channel flipping.\");\n        }\n        if (frame == null) {\n            return;\n        }\n        this.color  = null;\n        this.width  = frame.imageWidth;\n        this.height = frame.imageHeight;\n        this.buffer = frame.image[0];\n        switch (frame.imageDepth) {\n            case Frame.DEPTH_BYTE:   this.type = GL2.GL_BYTE;           break;\n            case Frame.DEPTH_UBYTE:  this.type = GL2.GL_UNSIGNED_BYTE;  break;\n            case Frame.DEPTH_SHORT:  this.type = GL2.GL_SHORT;          break;\n            case Frame.DEPTH_USHORT: this.type = GL2.GL_UNSIGNED_SHORT; break;\n            case Frame.DEPTH_INT:    this.type = GL2.GL_INT;            break;\n            case Frame.DEPTH_FLOAT:  this.type = GL2.GL_FLOAT;          break;\n            case Frame.DEPTH_DOUBLE: this.type = GL2.GL_DOUBLE;         break;\n            default: assert false;\n        }\n        switch (frame.imageChannels) {\n            case 1: this.format = GL2.GL_LUMINANCE; break;\n            case 2: this.format = GL2.GL_RG;        break;\n            case 3: this.format = GL2.GL_RGB;       break;\n            case 4: this.format = GL2.GL_RGBA;      break;\n            default: assert false;\n        }\n        getGLCanvas().display();\n    }\n    @Override public void showImage(Image image) {\n        if (!(image instanceof BufferedImage)) {\n            throw new RuntimeException(\"GLCanvasFrame does not support \" + image + \", BufferedImage required.\");\n        }\n        showImage((BufferedImage)image);\n    }\n    public void showImage(BufferedImage image) {\n        if (image == null) {\n            return;\n        }\n        this.color = null;\n        this.width  = image.getWidth();\n        this.height = image.getHeight();\n\n        DataBuffer buffer = image.getRaster().getDataBuffer();\n        if (buffer instanceof DataBufferByte) {\n            this.buffer = ByteBuffer.wrap(((DataBufferByte)buffer).getData());\n            this.type = GL2.GL_UNSIGNED_BYTE;\n        } else if (buffer instanceof DataBufferDouble) {\n            this.buffer = DoubleBuffer.wrap(((DataBufferDouble)buffer).getData());\n            this.type = GL2.GL_DOUBLE;\n        } else if (buffer instanceof DataBufferFloat) {\n            this.buffer = FloatBuffer.wrap(((DataBufferFloat)buffer).getData());\n            this.type = GL2.GL_FLOAT;\n        } else if (buffer instanceof DataBufferInt) {\n            this.buffer = IntBuffer.wrap(((DataBufferInt)buffer).getData());\n            this.type = GL2.GL_INT;\n        } else if (buffer instanceof DataBufferShort) {\n            this.buffer = ShortBuffer.wrap(((DataBufferShort)buffer).getData());\n            this.type = GL2.GL_SHORT;\n        } else if (buffer instanceof DataBufferUShort) {\n            this.buffer = ShortBuffer.wrap(((DataBufferUShort)buffer).getData());\n            this.type = GL2.GL_UNSIGNED_SHORT;\n        } else {\n            assert false;\n        }\n        switch (image.getSampleModel().getNumBands()) {\n            case 1: this.format = GL2.GL_LUMINANCE; break;\n            case 2: this.format = GL2.GL_RG;        break;\n            case 3: this.format = GL2.GL_RGB;       break;\n            case 4: this.format = GL2.GL_RGBA;      break;\n            default: assert false;\n        }\n        getGLCanvas().display();\n    }\n    public void showImage(int renderBuffer) {\n        if (renderBuffer <= 0) {\n            return;\n        }\n        this.color = null;\n        this.buffer = null;\n        this.renderBuffer = renderBuffer;\n        getGLCanvas().display();\n    }\n\n    private static GLCanvasFrame canvasFrame;\n    public static void main(String[] args) throws java.lang.Exception {\n        EventQueue.invokeAndWait(new Runnable() {\n            public void run() {\n                try {\n                    canvasFrame = new GLCanvasFrame(\"Some Title\"/*, context.getGLContext()*/);\n                    canvasFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n                    //canvasFrame.setCanvasSize(640, 480);\n                    canvasFrame.showColor(Color.BLUE);\n                } catch (java.lang.Exception ex) {\n                    ex.printStackTrace();\n                }\n            }\n        });\n\n        final JavaCVCL context = new JavaCVCL(canvasFrame.getGLCanvas().getContext());\n        final IplImage image = cvLoadImageBGRA(\"/usr/share/opencv/samples/c/lena.jpg\"/*args[0]*/);\n        //final IplImage image = cvLoadImage(\"/usr/share/opencv/samples/c/lena.jpg\"/*args[0]*/, 0);\n        //final IplImage image = IplImage.create(640, 480, IPL_DEPTH_32F, 4);\n        final CLGLImage2d imageCLGL = context.createCLGLImageFrom(image);\n        //final CLImage2d imageCL = context.createCLImageFrom(image);\n        context.acquireGLObject(imageCLGL);\n        context.writeImage(imageCLGL, image, true);\n        context.releaseGLObject(imageCLGL);\n        //System.out.println(imageCLGL.getFormat());\n        //System.exit(0);\n\n        canvasFrame.setCanvasScale(0.5);\n        for (int i = 0; i < 1000; i++) {\n            canvasFrame.showImage(imageCLGL.getGLObjectID());\n            Thread.sleep(10);\n            canvasFrame.showColor(Color.RED);\n            Thread.sleep(10);\n        }\n        canvasFrame.waitKey();\n        context.release();\n        System.exit(0);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/GNImageAligner.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.util.Arrays;\nimport org.bytedeco.javacv.ImageTransformer.Data;\nimport org.bytedeco.javacv.ImageTransformer.Parameters;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class GNImageAligner implements ImageAligner {\n    public GNImageAligner(ImageTransformer transformer, Parameters initialParameters,\n            IplImage template0, double[] roiPts, IplImage target0) {\n        this(transformer, initialParameters, template0, roiPts, target0, new Settings());\n    }\n    public GNImageAligner(ImageTransformer transformer, Parameters initialParameters,\n            IplImage template0, double[] roiPts, IplImage target0, Settings settings) {\n        this(transformer, initialParameters);\n        setSettings(settings);\n\n        final int minLevel = settings.pyramidLevelMin;\n        final int maxLevel = settings.pyramidLevelMax;\n\n        this.template    = new IplImage[maxLevel+1];\n        this.target      = new IplImage[maxLevel+1];\n        this.transformed = new IplImage[maxLevel+1];\n        this.residual    = new IplImage[maxLevel+1];\n        this.mask        = new IplImage[maxLevel+1];\n        int w = template0 != null ? template0.width()     : target0.width();\n        int h = template0 != null ? template0.height()    : target0.height();\n        int c = template0 != null ? template0.nChannels() : target0.nChannels();\n        int o = template0 != null ? template0.origin()    : target0.origin();\n        for (int i = minLevel; i <= maxLevel; i++) {\n            if (i == minLevel && (template0 != null && template0.depth() == IPL_DEPTH_32F)) {\n                template[i] = template0;\n            } else {\n                template[i] = IplImage.create(w, h, IPL_DEPTH_32F, c, o);\n            }\n            if (i == minLevel && (target0 != null && target0.depth() == IPL_DEPTH_32F)) {\n                target[i] = target0;\n            } else {\n                target[i] = IplImage.create(w, h, IPL_DEPTH_32F, c, o);\n            }\n            transformed[i] = IplImage.create(w, h, IPL_DEPTH_32F, c, o);\n            residual   [i] = IplImage.create(w, h, IPL_DEPTH_32F, c, o);\n            mask       [i] = IplImage.create(w, h, IPL_DEPTH_8U,  1, o);\n            w /= 2;\n            h /= 2;\n        }\n\n        this.hessianGradientTransformerData = new Data[n];\n        for (int i = 0; i < n; i++) {\n            hessianGradientTransformerData[i] = new Data(template[pyramidLevel],\n                    transformed[pyramidLevel], residual[pyramidLevel], mask[pyramidLevel],\n                    0, 0, pyramidLevel, null, null, n);\n        }\n        this.residualTransformerData = new Data[] { new Data(template[pyramidLevel],\n                    target[pyramidLevel], null, mask[pyramidLevel],\n                    0, 0, pyramidLevel, transformed[pyramidLevel], residual[pyramidLevel], 1) };\n\n        setConstrained(settings.constrained);\n        setTemplateImage(template0, roiPts);\n        setTargetImage(target0);\n    }\n    protected GNImageAligner(ImageTransformer transformer, Parameters initialParameters) {\n        this.n = initialParameters.size();\n\n        this.srcRoiPts = CvMat.create(4, 1, CV_64F, 2);\n        this.dstRoiPts = CvMat.create(4, 1, CV_64F, 2);\n        this.dstRoiPtsArray = new CvPoint(4);\n        this.roi     = new CvRect();\n        this.temproi = new CvRect();\n        this.transformer = transformer;\n\n        this.parameters      = initialParameters.clone();\n        this.parametersArray = new Parameters[] { parameters };\n        this.tempParameters  = new Parameters[n];\n        for (int i = 0; i < tempParameters.length; i++) {\n            this.tempParameters[i] = initialParameters.clone();\n        }\n\n        subspaceParameters = parameters.getSubspace();\n        if (subspaceParameters != null) {\n            tempSubspaceParameters = new double[Parallel.getNumThreads()][];\n            for (int i = 0; i < tempSubspaceParameters.length; i++) {\n                tempSubspaceParameters[i] = subspaceParameters.clone();\n            }\n//        for (double d : subspaceParameters) {\n//            System.out.print(d + \" \");\n//        }\n//        System.out.println();\n        }\n    }\n\n    public static class Settings extends ImageAligner.Settings implements Cloneable {\n        public Settings() { }\n        public Settings(Settings s) {\n            super(s);\n            stepSize        = s.stepSize;\n            lineSearch      = s.lineSearch;\n            deltaMin        = s.deltaMin;\n            deltaMax        = s.deltaMax;\n            displacementMax = s.displacementMax;\n            alphaSubspace   = s.alphaSubspace;\n            alphaTikhonov   = s.alphaTikhonov;\n            gammaTgamma     = s.gammaTgamma;\n            constrained     = s.constrained;\n        }\n\n        double stepSize        = 0.1;\n        double[] lineSearch    = {1.0, 0.25};\n        double deltaMin        = 10;\n        double deltaMax        = 300;\n        double displacementMax = 0.2;\n        double alphaSubspace   = 0.1;\n        double alphaTikhonov   = 0;\n        CvMat gammaTgamma      = null;\n        boolean constrained    = false;\n\n        public double getStepSize() {\n            return stepSize;\n        }\n        public void setStepSize(double stepSize) {\n            this.stepSize = stepSize;\n        }\n\n        public double[] getLineSearch() {\n            return lineSearch;\n        }\n        public void setLineSearch(double[] lineSearch) {\n            this.lineSearch = lineSearch;\n        }\n\n        public double getDeltaMin() {\n            return deltaMin;\n        }\n        public void setDeltaMin(double deltaMin) {\n            this.deltaMin = deltaMin;\n        }\n\n        public double getDeltaMax() {\n            return deltaMax;\n        }\n        public void setDeltaMax(double deltaMax) {\n            this.deltaMax = deltaMax;\n        }\n\n        public double getDisplacementMax() {\n            return displacementMax;\n        }\n        public void setDisplacementMax(double displacementMax) {\n            this.displacementMax = displacementMax;\n        }\n\n        public double getAlphaSubspace() {\n            return alphaSubspace;\n        }\n        public void setAlphaSubspace(double alphaSubspace) {\n            this.alphaSubspace = alphaSubspace;\n        }\n\n        public double getAlphaTikhonov() {\n            return alphaTikhonov;\n        }\n        public void setAlphaTikhonov(double alphaTikhonov) {\n            this.alphaTikhonov = alphaTikhonov;\n        }\n\n        public CvMat getGammaTgamma() {\n            return gammaTgamma;\n        }\n        public void setGammaTgamma(CvMat gammaTgamma) {\n            this.gammaTgamma = gammaTgamma;\n        }\n\n//        public boolean isConstrained() {\n//            return constrained;\n//        }\n//        public void setConstrained(boolean constrained) {\n//            this.constrained = constrained;\n//        }\n\n        @Override public Settings clone() {\n            return new Settings(this);\n        }\n    }\n\n    protected Settings settings;\n    public Settings getSettings() {\n        return settings;\n    }\n    public void setSettings(ImageAligner.Settings settings) {\n        this.settings = (Settings)settings;\n    }\n\n    protected final int n;\n    protected IplImage[] template, target, transformed, residual, mask;\n    protected IplImage[] images = new IplImage[5];\n    protected CvMat srcRoiPts, dstRoiPts;\n    protected CvPoint dstRoiPtsArray;\n    protected CvRect roi, temproi;\n    protected ImageTransformer transformer;\n    protected Data[] hessianGradientTransformerData, residualTransformerData;\n    protected Parameters parameters, parametersArray[], tempParameters[], priorParameters;\n    protected CvMat hessian, gradient, update, prior;\n    protected double[] constraintGrad, subspaceResidual, subspaceJacobian[], updateScale;\n    protected boolean[] subspaceCorrelated;\n    protected int pyramidLevel;\n    protected double RMSE;\n    protected boolean residualUpdateNeeded = true;\n    protected int lastLinePosition = 0;\n    protected int trials = 0;\n//    protected double prevOutlierRatio = 0;\n\n    protected double[] subspaceParameters, tempSubspaceParameters[];\n\n    public IplImage getTemplateImage() {\n        return template[pyramidLevel];\n    }\n    public void setTemplateImage(IplImage template0, double[] roiPts) {\n        final int minLevel = settings.pyramidLevelMin;\n        final int maxLevel = settings.pyramidLevelMax;\n\n        if (roiPts == null && template0 != null) {\n            int w = template0.width()  << minLevel;\n            int h = template0.height() << minLevel;\n            this.srcRoiPts.put(0.0, 0.0,  w, 0.0,  w, h,  0, h);\n        } else if (roiPts != null) {\n            this.srcRoiPts.put(roiPts);\n        }\n\n        if (template0 == null) {\n            return;\n        }\n\n        if (template0.depth() == IPL_DEPTH_32F) {\n            template[minLevel] = template0;\n        } else {\n            cvConvertScale(template0, template[minLevel], 1.0/template0.highValue(), 0);\n        }\n\n        for (int i = minLevel+1; i <= maxLevel; i++) {\n            cvPyrDown(template[i-1], template[i], CV_GAUSSIAN_5x5);\n        }\n        setPyramidLevel(maxLevel);\n    }\n\n    public IplImage getTargetImage() {\n        return target[pyramidLevel];\n    }\n    public void setTargetImage(IplImage target0) {\n        final int minLevel = settings.pyramidLevelMin;\n        final int maxLevel = settings.pyramidLevelMax;\n\n        if (target0 == null) {\n            return;\n        }\n\n        if (target0.depth() == IPL_DEPTH_32F) {\n            target[minLevel] = target0;\n        }\n\n        if (settings.displacementMax > 0) {\n            transformer.transform(srcRoiPts, dstRoiPts, parameters, false);\n            double[] pts = dstRoiPts.get();\n            for (int i = 0; i < pts.length; i++) {\n                pts[i] /= (1<<minLevel);\n            }\n            int width  = target[minLevel].width();\n            int height = target[minLevel].height();\n            temproi.x(0).y(0).width(width).height(height);\n            int padX = (int)Math.round(settings.displacementMax*width);\n            int padY = (int)Math.round(settings.displacementMax*height);\n            int align = 1<<(maxLevel+1);\n            // add +3 all around because cvPyrDown() needs it for smoothing\n            JavaCV.boundingRect(pts, temproi, padX+3, padY+3, align, align);\n            cvSetImageROI(target0, temproi);\n            cvSetImageROI(target[minLevel], temproi);\n        } else {\n            cvResetImageROI(target0);\n            cvResetImageROI(target[minLevel]);\n        }\n\n        if (target0.depth() != IPL_DEPTH_32F) {\n            cvConvertScale(target0, target[minLevel], 1.0/target0.highValue(), 0);\n            cvResetImageROI(target0);\n        }\n\n        for (int i = minLevel+1; i <= maxLevel; i++) {\n            IplROI ir = target[i-1].roi();\n            if (ir != null) {\n                temproi.x(ir.xOffset()/2); temproi.width (ir.width() /2);\n                temproi.y(ir.yOffset()/2); temproi.height(ir.height()/2);\n                cvSetImageROI(target[i], temproi);\n            } else {\n                cvResetImageROI(target[i]);\n            }\n//            if (i == 1) {\n//                cvResize(target[i-1], target[i], CV_INTER_NN);\n//            } else {\n//                cvPyrDown(target[i-1], target[i], CV_GAUSSIAN_5x5);\n//            }\n            cvPyrDown(target[i-1], target[i], CV_GAUSSIAN_5x5);\n        }\n\n        setPyramidLevel(maxLevel);\n    }\n\n    public int getPyramidLevel() {\n        return pyramidLevel;\n    }\n    public void setPyramidLevel(int pyramidLevel) {\n        this.pyramidLevel = pyramidLevel;\n        residualUpdateNeeded = true;\n        trials = 0;\n    }\n\n    public boolean isConstrained()  {\n        return settings.constrained;\n    }\n    public void setConstrained(boolean constrained) {\n        if (settings.constrained == constrained && hessian != null &&\n                gradient != null && update != null) {\n            return;\n        }\n        settings.constrained = constrained;\n        int m = constrained ? n+1 : n;\n        if (subspaceParameters != null && settings.alphaSubspace != 0.0) {\n            m += subspaceParameters.length;\n        }\n        hessian       = CvMat.create(m, m);\n        gradient      = CvMat.create(m, 1);\n        update        = CvMat.create(m, 1);\n        updateScale   = new double[m];\n        prior         = CvMat.create(n, 1);\n\n        constraintGrad = new double[n];\n        subspaceResidual = new double[n];\n        subspaceJacobian = new double[m][n];\n        subspaceCorrelated = new boolean[n];\n    }\n\n    public Parameters getParameters() {\n        return parameters;\n    }\n    public void setParameters(Parameters parameters) {\n        this.parameters.set(parameters);\n        subspaceParameters = parameters.getSubspace();\n        if (subspaceParameters != null && settings.alphaSubspace != 0.0) {\n            for (int i = 0; i < tempSubspaceParameters.length; i++) {\n                tempSubspaceParameters[i] = subspaceParameters.clone();\n            }\n        }\n        residualUpdateNeeded = true;\n    }\n\n    public Parameters getPriorParameters() {\n        return priorParameters;\n    }\n    public void setPriorParameters(Parameters priorParameters) {\n        this.priorParameters.set(priorParameters);\n    }\n\n    public double[] getTransformedRoiPts() {\n        if (residualUpdateNeeded) {\n            doRoi();\n            doResidual();\n        }\n        return dstRoiPts.get();\n    }\n\n    public IplImage getTransformedImage() {\n        if (residualUpdateNeeded) {\n            doRoi();\n            doResidual();\n        }\n        return transformed[pyramidLevel];\n    }\n    public IplImage getResidualImage() {\n        if (residualUpdateNeeded) {\n            doRoi();\n            doResidual();\n        }\n        return residual[pyramidLevel];\n    }\n    public IplImage getMaskImage() {\n        return mask[pyramidLevel];\n    }\n\n    public double getRMSE() {\n        if (residualUpdateNeeded) {\n            doRoi();\n            doResidual();\n        }\n        return RMSE;\n    }\n\n    public int getPixelCount() {\n        if (residualUpdateNeeded) {\n            doRoi();\n            doResidual();\n        }\n        return residualTransformerData[0].dstCount;\n    }\n\n    public int getOutlierCount() {\n        return hessianGradientTransformerData[0].dstCountOutlier;\n    }\n\n    public CvRect getRoi() {\n        if (residualUpdateNeeded) {\n            doRoi();\n        }\n        return roi;\n    }\n    public int getLastLinePosition() {\n        return lastLinePosition;\n    }\n\n    public IplImage[] getImages() {\n        images[0] = getTemplateImage();\n        images[1] = getTargetImage();\n        images[2] = getTransformedImage();\n        images[3] = getResidualImage();\n        images[4] = getMaskImage();\n        return images;\n    }\n\n    public boolean iterate(double[] delta) {\n        boolean converged = false;\n        final double prevRMSE = getRMSE();\n        final double[] prevParameters = parameters.get();\n        final double[] prevSubspaceParameters = subspaceParameters == null ? null : subspaceParameters.clone();\n\n        if (trials == 0 && parameters.preoptimize()) {\n            setParameters(parameters);\n            doResidual();\n        }\n        final double[] resetParameters = parameters.get();\n        final double[] resetSubspaceParameters = subspaceParameters == null ? null : subspaceParameters.clone();\n\n        doHessianGradient(updateScale);\n\n        lastLinePosition = 0;\n\n        // solve for optimal parameter update\n        cvSolve(hessian, gradient, update, CV_SVD);\n        for (int i = 0; i < n; i++) {\n            parameters.set(i, parameters.get(i) + settings.lineSearch[0]*update.get(i)*updateScale[i]);\n        }\n        for (int i = n; i < update.length(); i++) {\n            subspaceParameters[i-n] += settings.lineSearch[0]*update.get(i)*updateScale[i];\n        }\n        residualUpdateNeeded = true;\n\n        for (int j = 1; j < settings.lineSearch.length && getRMSE() > prevRMSE; j++) {\n            RMSE = prevRMSE;\n            parameters.set(resetParameters);\n            if (subspaceParameters != null) {\n                System.arraycopy(resetSubspaceParameters, 0, subspaceParameters, 0, subspaceParameters.length);\n            }\n            lastLinePosition = j;\n            for (int i = 0; i < n; i++) {\n                parameters.set(i, parameters.get(i) + settings.lineSearch[j]*update.get(i)*updateScale[i]);\n            }\n            for (int i = n; i < update.length(); i++) {\n                subspaceParameters[i-n] += settings.lineSearch[j]*update.get(i)*updateScale[i];\n            }\n            residualUpdateNeeded = true;\n        }\n\n        double deltaNorm = 0;\n        if (delta != null) {\n            for (int i = 0; i < delta.length && i < updateScale.length; i++) {\n                delta[i] = settings.lineSearch[lastLinePosition]*update.get(i)*updateScale[i];\n            }\n            deltaNorm = JavaCV.norm(Arrays.copyOf(delta, n));\n        }\n\n        boolean invalid = getRMSE() > prevRMSE || deltaNorm > settings.deltaMax ||\n                          Double.isNaN(RMSE) || Double.isInfinite(RMSE);\n        if (invalid) {\n            RMSE = prevRMSE;\n            parameters.set(prevParameters);\n            if (subspaceParameters != null) {\n                System.arraycopy(prevSubspaceParameters, 0, subspaceParameters, 0, subspaceParameters.length);\n            }\n            residualUpdateNeeded = true;\n        }\n        if (invalid && deltaNorm > settings.deltaMin && ++trials < 2) {\n            return false;\n        } else if (invalid || deltaNorm < settings.deltaMin) {\n            trials = 0;\n            if (pyramidLevel > settings.pyramidLevelMin) {\n                setPyramidLevel(pyramidLevel-1);\n            } else {\n                converged = true;\n            }\n        } else {\n            trials = 0;\n        }\n        return converged;\n    }\n\n    protected void doHessianGradient(final double[] scale) {\n        final double constraintError = parameters.getConstraintError();\n        final double stepSize = settings.stepSize;\n\n        cvSetZero(gradient);\n        cvSetZero(hessian);\n\n        Parallel.loop(0, n, new Parallel.Looper() {\n        public void loop(int from, int to, int looperID) {\n//        for (int i = 0; i < n; i++) {\n        for (int i = from; i < to; i++) {\n            tempParameters[i].set(parameters);\n            tempParameters[i].set(i, tempParameters[i].get(i) + /*(1<<pyramidLevel)**/stepSize);\n            scale[i] = tempParameters[i].get(i) - parameters.get(i);\n            constraintGrad[i] = tempParameters[i].getConstraintError() - constraintError;\n        }}});\n\n//        final double adjustedRMSE = (1-prevOutlierRatio)*RMSE;\n        for (int i = 0; i < n; i++) {\n            Data d = hessianGradientTransformerData[i];\n            d.srcImg    = template   [pyramidLevel];\n            d.subImg    = transformed[pyramidLevel];\n            d.srcDotImg = residual   [pyramidLevel];\n            d.transImg  = null;\n            d.dstImg    = null;\n            d.mask      = mask       [pyramidLevel];\n            d.zeroThreshold    = /*adjustedRMSE**/settings.thresholdsZero   [Math.min(settings.thresholdsZero   .length-1, pyramidLevel)];\n            d.outlierThreshold = /*adjustedRMSE**/settings.thresholdsOutlier[Math.min(settings.thresholdsOutlier.length-1, pyramidLevel)];\n            if (settings.thresholdsMulRMSE) {\n                d.zeroThreshold    *= RMSE; //adjustedRMSE;\n                d.outlierThreshold *= RMSE; //adjustedRMSE;\n            }\n            d.pyramidLevel = pyramidLevel;\n        }\n        transformer.transform(hessianGradientTransformerData, roi, tempParameters, null);\n\n//        double dstCount = hessianGradientTransformerData[0].dstCount;\n//        double dstCountZero = hessianGradientTransformerData[0].dstCountZero;\n//        double dstCountOutlier = hessianGradientTransformerData[0].dstCountOutlier;\n        for (int i = 0; i < n; i++) {\n            Data d = (Data)hessianGradientTransformerData[i];\n            gradient.put(i, gradient.get(i) - d.srcDstDot);\n            for (int j = 0; j < n; j++) {\n                hessian.put(i, j, hessian.get(i, j) + d.dstDstDot.get(j));\n            }\n        }\n//        prevOutlierRatio = dstCountOutlier/dstCount;\n//System.out.println(dstCountZero/dstCount + \" \" + dstCountOutlier/dstCount);\n\n        doRegularization(updateScale);\n    }\n\n    protected void doRegularization(final double[] scale) {\n        final double constraintError = parameters.getConstraintError();\n        final double stepSize = settings.stepSize;\n\n        // if we have a gamma or an alpha, compute the prior for regularization, but\n        // if prioParameters == null, our prior is zero motion, so no need to compute it\n        if ((settings.gammaTgamma != null || settings.alphaTikhonov != 0) &&\n                prior != null && priorParameters != null) {\n            for (int i = 0; i < n; i++) {\n                prior.put(i, parameters.get(i) - priorParameters.get(i));\n            }\n            cvMatMul(hessian, prior, prior);\n\n            // compute gradient\n            for (int i = 0; i < n; i++) {\n                gradient.put(i, gradient.get(i) + prior.get(i));\n            }\n        }\n//System.out.println(prior);\n\n        if (settings.constrained) {\n            // to get a well-conditionned matrix, compute what\n            // looks like an appropriate scale for the constraint\n            double constraintGradSum = 0;\n            for (double d : constraintGrad) {\n                constraintGradSum += d;\n            }\n            scale[n] = n*constraintGradSum;\n\n            for (int i = 0; i < n; i++) {\n                double c = constraintGrad[i]*scale[n];\n                hessian.put(i, n, c);\n                hessian.put(n, i, c);\n            }\n            gradient.put(n, -constraintError*scale[n]);\n        }\n\n        if (subspaceParameters != null && subspaceParameters.length > 0 &&\n                settings.alphaSubspace != 0.0) {\n            final int m = subspaceParameters.length;\n//            double[][] subspaceHessian  = new double[n+m][n+m];\n//            double[] subspaceGradient   = new double[n+m];\n\n            Arrays.fill(subspaceCorrelated, false);\n            tempParameters[0].set(parameters);\n            tempParameters[0].setSubspace(subspaceParameters);\n            Parallel.loop(0, n+m, tempSubspaceParameters.length, new Parallel.Looper() {\n            public void loop(int from, int to, int looperID) {\n//            int looperID = 0;\n//            for (int i = 0; i < n+m; i++) {\n            for (int i = from; i < to; i++) {\n                if (i < n) {\n                    Arrays.fill(subspaceJacobian[i], 0);\n                    subspaceJacobian[i][i] = scale[i];\n                } else {\n                    System.arraycopy(subspaceParameters, 0, tempSubspaceParameters[looperID], 0, m);\n                    tempSubspaceParameters[looperID][i-n] += stepSize;\n                    tempParameters[i-n+1].set(parameters);\n                    tempParameters[i-n+1].setSubspace(tempSubspaceParameters[looperID]);\n                    scale[i] = tempSubspaceParameters[looperID][i-n] - subspaceParameters[i-n];\n                    for (int j = 0; j < n; j++) {\n                        subspaceJacobian[i][j] = tempParameters[0].get(j) - tempParameters[i-n+1].get(j);\n                        subspaceCorrelated[j] |= subspaceJacobian[i][j] != 0; // this may not work in parallel...\n                    }\n                }\n            }}});\n\n            int subspaceCorrelatedCount = 0;\n//            double subspaceRMSE = 0;\n            for (int i = 0; i < n; i++) {\n                subspaceResidual[i] = parameters.get(i) - tempParameters[0].get(i);\n//                subspaceRMSE += subspaceResidual[i]*subspaceResidual[i];\n\n                if (subspaceCorrelated[i]) {\n                    subspaceCorrelatedCount++;\n                }\n            }\n//            subspaceRMSE = Math.sqrt(subspaceRMSE/n);\n//System.out.println((float)RMSE + \" \" + (float)subspaceRMSE);\n            final double K = settings.alphaSubspace*settings.alphaSubspace * RMSE*RMSE/\n                    subspaceCorrelatedCount;//(subspaceRMSE*subspaceRMSE);\n\n            Parallel.loop(0, n+m, new Parallel.Looper() {\n            public void loop(int from, int to, int looperID) {\n//            int looperID = 0;\n//            for (int i = 0; i < n+m; i++) {\n            for (int i = from; i < to; i++) {\n                if (i < n && !subspaceCorrelated[i]) {\n                    continue;\n                }\n\n                for (int j = i; j < n+m; j++) {\n                    if (j < n && !subspaceCorrelated[j]) {\n                        continue;\n                    }\n                    double h = 0;\n                    for (int k = 0; k < n; k++) {\n                        h += subspaceJacobian[i][k]*subspaceJacobian[j][k];\n                    }\n//                    subspaceHessian[i][j] = h;\n                    h = hessian.get(i, j) + K*h;\n                    hessian.put(i, j, h);\n                    hessian.put(j, i, h);\n                }\n\n                double g = 0;\n                for (int k = 0; k < n; k++) {\n                    g -= subspaceJacobian[i][k]*subspaceResidual[k];\n                }\n//                subspaceGradient[i] = g;\n                g = gradient.get(i) + K*g;\n                gradient.put(i, g);\n            }}});\n        }\n\n        // add Tikhonov regularization\n        int rows = hessian.rows(), cols = hessian.cols();\n        for (int i = 0; i < rows; i++) {\n            for (int j = 0; j < cols; j++) {\n                double h = hessian.get(i, j);\n                double g = 0;\n                if (settings.gammaTgamma != null && i < settings.gammaTgamma.rows() && j < settings.gammaTgamma.cols()) {\n                    g = settings.gammaTgamma.get(i, j);\n                }\n                double a = 0;\n                if (i == j && i < n) {\n                    a = settings.alphaTikhonov * settings.alphaTikhonov;\n                }\n                hessian.put(i, j, h + g + a);\n            }\n        }\n    }\n\n    protected void doRoi() {\n        transformer.transform(srcRoiPts, dstRoiPts, parameters, false);\n        double[] pts = dstRoiPts.get();\n        for (int i = 0; i < pts.length; i++) {\n            pts[i] /= (1<<pyramidLevel);\n        }\n        roi.x(0).y(0).width(mask[pyramidLevel].width()).height(mask[pyramidLevel].height());\n        // Add +3 all around because cvWarpPerspective() needs it for interpolation,\n        // and there seems to be something funny with memory alignment and\n        // ROIs, so let's align our ROI to a 16 byte boundary just in case..\n        JavaCV.boundingRect(pts, roi, 3, 3, 16, 1);\n//System.out.println(roi);\n\n        cvSetZero(mask[pyramidLevel]);\n        dstRoiPtsArray.put((byte)16, pts);\n        cvFillConvexPoly(mask[pyramidLevel], dstRoiPtsArray, 4, CvScalar.WHITE, 8, 16);\n    }\n\n    protected void doResidual() {\n        parameters.getConstraintError();\n//        cvSetZero(transformed[pyramidLevel]);\n//        cvSetZero(residual   [pyramidLevel]);\n        Data d = residualTransformerData[0];\n        d.srcImg    = template   [pyramidLevel];\n        d.subImg    = target     [pyramidLevel];\n        d.srcDotImg = null;\n        d.transImg  = transformed[pyramidLevel];\n        d.dstImg    = residual   [pyramidLevel];\n        d.mask      = mask       [pyramidLevel];\n//        d.zeroThreshold    = 0;\n//        d.outlierThreshold = 0;\n        d.zeroThreshold    = settings.thresholdsZero   [Math.min(settings.thresholdsZero   .length-1, pyramidLevel)];\n        d.outlierThreshold = settings.thresholdsOutlier[Math.min(settings.thresholdsOutlier.length-1, pyramidLevel)];\n        if (settings.thresholdsMulRMSE) {\n            d.zeroThreshold    *= RMSE;\n            d.outlierThreshold *= RMSE;\n        }\n        d.pyramidLevel = pyramidLevel;\n\n        transformer.transform(residualTransformerData, roi, parametersArray, null);\n\n        double dstDstDot = residualTransformerData[0].dstDstDot.get(0);\n        int dstCount = residualTransformerData[0].dstCount;\n        RMSE = dstCount < n ? Double.NaN : Math.sqrt(dstDstDot/dstCount);\n//        if (Double.isNaN(RMSE)) {\n//System.out.println(\"dstCount \" + dstCount + \" RMSE \" + RMSE + \" \" + pyramidLevel);\n//        }\n        residualUpdateNeeded = false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/GNImageAlignerCL.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport com.jogamp.opencl.CLImage2d;\nimport com.jogamp.opencl.CLImageFormat;\nimport com.jogamp.opencl.gl.CLGLContext;\nimport com.jogamp.opencl.gl.CLGLImage2d;\nimport com.jogamp.opengl.GL2;\nimport java.util.Arrays;\nimport org.bytedeco.javacv.ImageTransformer.Parameters;\nimport org.bytedeco.javacv.ImageTransformerCL.InputData;\nimport org.bytedeco.javacv.ImageTransformerCL.OutputData;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class GNImageAlignerCL extends GNImageAligner implements ImageAlignerCL {\n    public GNImageAlignerCL(ImageTransformerCL transformer, Parameters initialParameters,\n            CLImage2d template0, double[] roiPts, CLImage2d target0) {\n        this(transformer, initialParameters, template0, roiPts, target0, new GNImageAligner.Settings());\n    }\n    public GNImageAlignerCL(ImageTransformerCL transformer, Parameters initialParameters,\n            CLImage2d template0, double[] roiPts, CLImage2d target0, GNImageAligner.Settings settings) {\n        super(transformer, initialParameters);\n        setSettings(settings);\n        context = transformer.getContext();\n\n        final int minLevel = settings.pyramidLevelMin;\n        final int maxLevel = settings.pyramidLevelMax;\n\n        this.template    = new IplImage[maxLevel+1];\n        this.target      = new IplImage[maxLevel+1];\n        this.transformed = new IplImage[maxLevel+1];\n        this.residual    = new IplImage[maxLevel+1];\n        this.mask        = new IplImage[maxLevel+1];\n\n        this.templateCL    = new CLImage2d[maxLevel+1];\n        this.targetCL      = new CLImage2d[maxLevel+1];\n        this.transformedCL = new CLImage2d[maxLevel+1];\n        this.residualCL    = new CLImage2d[maxLevel+1];\n        this.maskCL        = new CLGLImage2d[maxLevel+1];\n        this.maskrb        = new int[maxLevel+1];\n        this.maskfb        = new int[maxLevel+1];\n        int w = template0 != null ? template0.width  : target0.width;\n        int h = template0 != null ? template0.height : target0.height;\n        CLGLContext c = context.getCLGLContext();\n//        GLContext glContext = c.getGLContext();\n//        glContext.makeCurrent();\n        GL2 gl = context.getGL2();\n        gl.glGenRenderbuffers(maxLevel+1, maskrb, 0);\n        gl.glGenFramebuffers(maxLevel+1, maskfb, 0);\n        CLImageFormat f = new CLImageFormat(CLImageFormat.ChannelOrder.RGBA, CLImageFormat.ChannelType.FLOAT);\n        for (int i = minLevel; i <= maxLevel; i++) {\n            templateCL   [i] = i == minLevel && template0 != null ? template0 : c.createImage2d(w, h, f);\n            targetCL     [i] = i == minLevel && target0   != null ? target0   : c.createImage2d(w, h, f);\n            transformedCL[i] = c.createImage2d(w, h, f);\n            residualCL   [i] = c.createImage2d(w, h, f);\n            gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, maskrb[i]);\n            gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, maskfb[i]);\n            gl.glRenderbufferStorage(GL2.GL_RENDERBUFFER, GL2.GL_LUMINANCE8, w, h);\n            gl.glFramebufferRenderbuffer(GL2.GL_FRAMEBUFFER, GL2.GL_COLOR_ATTACHMENT0, GL2.GL_RENDERBUFFER, maskrb[i]);\n            assert gl.glCheckFramebufferStatus(GL2.GL_FRAMEBUFFER) == GL2.GL_FRAMEBUFFER_COMPLETE;\n            maskCL[i] = c.createFromGLRenderbuffer(maskrb[i]);\n            System.out.println(maskCL[i] + \" \" + maskCL[i].getElementSize() + \" \" + maskCL[i].getFormat());\n            w /= 2;\n            h /= 2;\n        }\n//        glContext.release();\n\n        this.inputData = new InputData();\n        this.outputData = new OutputData(false);\n        this.templateChanged = new boolean[maxLevel+1];\n        Arrays.fill(templateChanged, true);\n\n        setConstrained(settings.constrained);\n        setTemplateImageCL(template0, roiPts);\n        setTargetImageCL(target0);\n    }\n    public void release() {\n        final int minLevel = settings.pyramidLevelMin;\n        final int maxLevel = settings.pyramidLevelMax;\n\n        if (templateCL != null && targetCL != null && transformedCL != null &&\n                residualCL != null && maskCL != null) {\n            for (int i = minLevel; i <= maxLevel; i++) {\n                if (i > minLevel) templateCL[i].release();\n                if (i > minLevel) targetCL  [i].release();\n                transformedCL[i].release();\n                residualCL   [i].release();\n                maskCL       [i].release();\n            }\n            templateCL = targetCL = transformedCL = residualCL = maskCL = null;\n        }\n\n        // NVIDIA drivers crash if we don't delete those before terminating\n        context.getGLContext().makeCurrent();\n        GL2 gl = context.getGL2();\n        if (maskfb != null) {\n            gl.glDeleteFramebuffers(maxLevel+1, maskfb, 0);\n            maskfb = null;\n        }\n        if (maskrb != null) {\n            gl.glDeleteRenderbuffers(maxLevel+1, maskrb, 0);\n            maskrb = null;\n        }\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    private final JavaCVCL context;\n    private CLImage2d[] templateCL, targetCL, transformedCL, residualCL;\n    private CLGLImage2d[] maskCL;\n    private int[] maskrb, maskfb;\n    private CLImage2d[] imagesCL = new CLImage2d[5];\n    private InputData inputData;\n    private OutputData outputData;\n    private boolean[] templateChanged;\n\n    @Override public IplImage getTemplateImage() {\n        return getTemplateImage(true);\n    }\n    public IplImage getTemplateImage(boolean blocking) {\n        if (templateChanged[pyramidLevel]) {\n            templateChanged[pyramidLevel] = false;\n            return template[pyramidLevel] = context.readImage(getTemplateImageCL(), template[pyramidLevel], blocking);\n        } else {\n            return template[pyramidLevel];\n        }\n    }\n    @Override public void setTemplateImage(IplImage template0, double[] roiPts) {\n        context.writeImage(templateCL[settings.pyramidLevelMin], template0, false);\n        setTemplateImageCL(templateCL[settings.pyramidLevelMin], roiPts);\n    }\n\n    @Override public IplImage getTargetImage() {\n        return getTargetImage(true);\n    }\n    public IplImage getTargetImage(boolean blocking) {\n        return target[pyramidLevel] = context.readImage(getTargetImageCL(), target[pyramidLevel], blocking);\n    }\n    @Override public void setTargetImage(IplImage target0) {\n        context.writeImage(targetCL[settings.pyramidLevelMin], target0, false);\n        setTargetImageCL(targetCL[settings.pyramidLevelMin]);\n    }\n\n    @Override public IplImage getTransformedImage() {\n        return getTransformedImage(true);\n    }\n    public IplImage getTransformedImage(boolean blocking) {\n        return transformed[pyramidLevel] = context.readImage(getTransformedImageCL(), transformed[pyramidLevel], blocking);\n    }\n\n    @Override public IplImage getResidualImage() {\n        return getResidualImage(true);\n    }\n    public IplImage getResidualImage(boolean blocking) {\n        return residual[pyramidLevel] = context.readImage(getResidualImageCL(), residual[pyramidLevel], blocking);\n    }\n\n    @Override public IplImage getMaskImage() {\n        return getMaskImage(true);\n    }\n    public IplImage getMaskImage(boolean blocking) {\n        context.acquireGLObject(maskCL[pyramidLevel]);\n        mask[pyramidLevel] = context.readImage(getMaskImageCL(), mask[pyramidLevel], blocking);\n        context.releaseGLObject(maskCL[pyramidLevel]);\n        return mask[pyramidLevel];\n    }\n\n    @Override public double getRMSE() {\n        if (residualUpdateNeeded) {\n            doRoi();\n            doResidual();\n        }\n        return RMSE;\n    }\n\n    @Override public int getPixelCount() {\n        if (residualUpdateNeeded) {\n            doRoi();\n            doResidual();\n        }\n        return outputData.dstCount;\n    }\n\n    @Override public int getOutlierCount() {\n        return outputData.dstCountOutlier;\n    }\n\n    @Override public CvRect getRoi() {\n        if (residualUpdateNeeded) {\n            doRoi();\n        }\n        return roi.x(inputData.roiX).y(inputData.roiY).\n                width(inputData.roiWidth).height(inputData.roiHeight);\n    }\n\n    @Override public IplImage[] getImages() {\n        return getImages(true);\n    }\n    public IplImage[] getImages(boolean blocking) {\n        images[0] = getTemplateImage(false);\n        images[1] = getTargetImage(false);\n        images[2] = getTransformedImage(false);\n        images[3] = getResidualImage(false);\n        images[4] = getMaskImage(blocking);\n        return images;\n    }\n\n    public CLImage2d getTemplateImageCL() {\n        return templateCL[pyramidLevel];\n    }\n    public void setTemplateImageCL(CLImage2d template0, double[] roiPts) {\n        final int minLevel = settings.pyramidLevelMin;\n        final int maxLevel = settings.pyramidLevelMax;\n\n        if (roiPts == null && template0 != null) {\n            int w = template0.width  << minLevel;\n            int h = template0.height << minLevel;\n            this.srcRoiPts.put(0.0, 0.0,  w, 0.0,  w, h,  0, h);\n        } else {\n            this.srcRoiPts.put(roiPts);\n        }\n\n        if (template0 == null) {\n            return;\n        }\n\n//        if (templateCL == null || templateCL.length != settings.pyramidLevels) {\n//            templateCL = new CLImage2d[settings.pyramidLevels];\n//        }\n        templateCL[minLevel] = template0;\n        for (int i = minLevel+1; i <= maxLevel; i++) {\n//            if (templateCL[i] == null) {\n//                int w = templateCL[i-1].width/2;\n//                int h = templateCL[i-1].height/2;\n//                CLImageFormat format = new CLImageFormat(CLImageFormat.ChannelOrder.RGBA, CLImageFormat.ChannelType.FLOAT);\n//                templateCL[i] = JavaCVCL.getCLContext().createImage2d(w, h, format);\n//            }\n            context.pyrDown(templateCL[i-1], templateCL[i]);\n        }\n        setPyramidLevel(maxLevel);\n        Arrays.fill(templateChanged, true);\n    }\n\n    public CLImage2d getTargetImageCL() {\n        return targetCL[pyramidLevel];\n    }\n    public void setTargetImageCL(CLImage2d target0) {\n        final int minLevel = settings.pyramidLevelMin;\n        final int maxLevel = settings.pyramidLevelMax;\n\n//        if (targetCL == null || targetCL.length != settings.pyramidLevels) {\n//            targetCL = new CLImage2d[settings.pyramidLevels];\n//        }\n        targetCL[minLevel] = target0;\n        for (int i = minLevel+1; i <= maxLevel; i++) {\n//            if (targetCL[i] == null) {\n//                int w = targetCL[i-1].width/2;\n//                int h = targetCL[i-1].height/2;\n//                CLImageFormat format = new CLImageFormat(CLImageFormat.ChannelOrder.RGBA, CLImageFormat.ChannelType.FLOAT);\n//                targetCL[i] = JavaCVCL.getCLContext().createImage2d(w, h, format);\n//            }\n            context.pyrDown(targetCL[i-1], targetCL[i]);\n        }\n        setPyramidLevel(maxLevel);\n    }\n\n    public CLImage2d getTransformedImageCL() {\n        if (residualUpdateNeeded) {\n            doRoi();\n            doResidual();\n        }\n        return transformedCL[pyramidLevel];\n    }\n    public CLImage2d getResidualImageCL() {\n        if (residualUpdateNeeded) {\n            doRoi();\n            doResidual();\n        }\n        return residualCL[pyramidLevel];\n    }\n    public CLImage2d getMaskImageCL() {\n        return maskCL[pyramidLevel];\n    }\n\n    public CLImage2d[] getImagesCL() {\n        imagesCL[0] = templateCL   [pyramidLevel];\n        imagesCL[1] = targetCL     [pyramidLevel];\n        imagesCL[2] = transformedCL[pyramidLevel];\n        imagesCL[3] = residualCL   [pyramidLevel];\n        imagesCL[4] = maskCL       [pyramidLevel];\n        return imagesCL;\n    }\n\n    @Override protected void doHessianGradient(final double[] scale) {\n        final double constraintError = parameters.getConstraintError();\n        final double stepSize = settings.stepSize;\n\n        cvSetZero(gradient);\n        cvSetZero(hessian);\n\n        Parallel.loop(0, n, new Parallel.Looper() {\n        public void loop(int from, int to, int looperID) {\n//        for (int i = 0; i < n; i++) {\n        for (int i = from; i < to; i++) {\n            tempParameters[i].set(parameters);\n            tempParameters[i].set(i, tempParameters[i].get(i) + /*(1<<pyramidLevel)**/stepSize);\n            scale[i] = tempParameters[i].get(i) - parameters.get(i);\n            constraintGrad[i] = tempParameters[i].getConstraintError() - constraintError;\n        }}});\n\n        inputData.zeroThreshold    = settings.thresholdsZero   [Math.min(settings.thresholdsZero   .length-1, pyramidLevel)];\n        inputData.outlierThreshold = settings.thresholdsOutlier[Math.min(settings.thresholdsOutlier.length-1, pyramidLevel)];\n        if (settings.thresholdsMulRMSE) {\n            inputData.zeroThreshold    *= RMSE;\n            inputData.outlierThreshold *= RMSE;\n        }\n        inputData.pyramidLevel = pyramidLevel;\n        context.acquireGLObject(maskCL[pyramidLevel]);\n        ((ImageTransformerCL)transformer).transform(templateCL[pyramidLevel], transformedCL[pyramidLevel],\n                residualCL[pyramidLevel], null, null, maskCL[pyramidLevel],\n                tempParameters, null, inputData, outputData);\n        context.releaseGLObject(maskCL[pyramidLevel]);\n\n        doRegularization(updateScale);\n\n        outputData.readBuffer(context);\n        for (int i = 0; i < n; i++) {\n            gradient.put(i, gradient.get(i) - outputData.srcDstDot.get(i));\n            for (int j = 0; j < n; j++) {\n                hessian.put(i, j, hessian.get(i, j) + outputData.dstDstDot.get(i*n + j));\n            }\n        }\n    }\n\n    @Override protected void doRoi() {\n        transformer.transform(srcRoiPts, dstRoiPts, parameters, false);\n        double[] pts = dstRoiPts.get();\n        for (int i = 0; i < pts.length; i++) {\n            pts[i] /= (1<<pyramidLevel);\n        }\n        roi.x(0).y(0).width(maskCL[pyramidLevel].width).height(maskCL[pyramidLevel].height);\n        // Add +3 all around because cvWarpPerspective() needs it for interpolation,\n        // and there seems to be something funny with memory alignment and\n        // ROIs, so let's align our ROI to a 16 byte boundary just in case..\n        JavaCV.boundingRect(pts, roi, 3, 3, 16, 1);\n//System.out.println(roi);\n        inputData.roiX = roi.x();\n        inputData.roiY = roi.y();\n        inputData.roiWidth  = roi.width();\n        inputData.roiHeight = roi.height();\n\n//        GLContext glContext = context.getGLContext();\n//        glContext.makeCurrent();\n        GL2 gl = context.getGL2();\n        gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, maskfb[pyramidLevel]);\n\n        gl.glMatrixMode(GL2.GL_PROJECTION);\n        gl.glLoadIdentity();\n        context.getGLU().gluOrtho2D(0.0f, maskCL[pyramidLevel].width, 0.0f, maskCL[pyramidLevel].height);\n\n        gl.glMatrixMode(GL2.GL_MODELVIEW);\n        gl.glLoadIdentity();\n        gl.glViewport(0, 0, maskCL[pyramidLevel].width, maskCL[pyramidLevel].height);\n\n        gl.glClearColor(0, 0, 0, 0);\n        gl.glClear(GL2.GL_COLOR_BUFFER_BIT);\n        gl.glColor4f(1, 1, 1, 1);\n        gl.glBegin(GL2.GL_POLYGON);\n// XXX: Remove \"extra\" pixels here...\n            gl.glVertex2d(pts[0],   pts[1]);\n            gl.glVertex2d(pts[2]+1, pts[3]);\n            gl.glVertex2d(pts[4]+1, pts[5]+1);\n            gl.glVertex2d(pts[6],   pts[7]+1);\n        gl.glEnd();\n//        gl.glFinish();\n//        glContext.release();\n    }\n\n    @Override protected void doResidual() {\n        parameters.getConstraintError();\n\n//        inputData.zeroThreshold = 0;\n//        inputData.outlierThreshold = 0;\n        inputData.zeroThreshold    = settings.thresholdsZero   [Math.min(settings.thresholdsZero   .length-1, pyramidLevel)];\n        inputData.outlierThreshold = settings.thresholdsOutlier[Math.min(settings.thresholdsOutlier.length-1, pyramidLevel)];\n        if (settings.thresholdsMulRMSE) {\n            inputData.zeroThreshold    *= RMSE;\n            inputData.outlierThreshold *= RMSE;\n        }\n        inputData.pyramidLevel = pyramidLevel;\n        context.acquireGLObject(maskCL[pyramidLevel]);\n        ((ImageTransformerCL)transformer).transform(templateCL[pyramidLevel], targetCL[pyramidLevel], null,\n                transformedCL[pyramidLevel], residualCL[pyramidLevel], maskCL[pyramidLevel],\n                parametersArray, null, inputData, outputData);\n        context.releaseGLObject(maskCL[pyramidLevel]);\n\n        outputData.readBuffer(context);\n        double dstDstDot = outputData.dstDstDot.get(0);\n        int dstCount = outputData.dstCount;\n        RMSE = dstCount < n ? Double.NaN : Math.sqrt(dstDstDot/dstCount);\n//        if (Double.isNaN(RMSE)) {\n//System.out.println(\"dstCount \" + outputData.dstCount + \" RMSE \" + RMSE + \" \" + pyramidLevel);\n//        }\n        residualUpdateNeeded = false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/GeometricCalibrator.java",
    "content": "/*\n * Copyright (C) 2009-2011 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.FloatBuffer;\nimport java.nio.IntBuffer;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport org.bytedeco.javacv.ProjectiveDevice.CalibrationSettings;\n\nimport org.bytedeco.opencv.opencv_calib3d.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_calib3d.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class GeometricCalibrator {\n    public GeometricCalibrator(Settings settings, MarkerDetector.Settings detectorSettings,\n            MarkedPlane markedPlane, ProjectiveDevice projectiveDevice) {\n        this.settings         = settings;\n        this.markerDetector   = new MarkerDetector(detectorSettings);\n        this.markedPlane      = markedPlane;\n        this.projectiveDevice = projectiveDevice;\n\n        cvSetIdentity(prevWarp);\n        cvSetIdentity(lastWarp);\n\n        if (markedPlane != null) {\n            int w = markedPlane.getImage().width();\n            int h = markedPlane.getImage().height();\n            warpSrcPts.put(0.0, 0.0,  w, 0.0,  w, h,  0.0, h);\n        }\n    }\n\n    public static class Settings extends BaseChildSettings {\n        double detectedBoardMin  = 0.5;\n        double patternSteadySize = 0.005;\n        double patternMovedSize  = 0.05;\n\n        public double getDetectedBoardMin() {\n            return detectedBoardMin;\n        }\n        public void setDetectedBoardMin(double detectedBoardMin) {\n            this.detectedBoardMin = detectedBoardMin;\n        }\n\n        public double getPatternSteadySize() {\n            return patternSteadySize;\n        }\n        public void setPatternSteadySize(double patternSteadySize) {\n            this.patternSteadySize = patternSteadySize;\n        }\n\n        public double getPatternMovedSize() {\n            return patternMovedSize;\n        }\n        public void setPatternMovedSize(double patternMovedSize) {\n            this.patternMovedSize = patternMovedSize;\n        }\n    }\n\n    private Settings settings;\n\n    MarkerDetector markerDetector;\n    private MarkedPlane markedPlane;\n    private ProjectiveDevice projectiveDevice;\n    private LinkedList<Marker[]> allObjectMarkers = new LinkedList<Marker[]>();\n    private LinkedList<Marker[]> allImageMarkers  = new LinkedList<Marker[]>();\n    private IplImage tempImage = null;\n    private Marker[] lastDetectedMarkers = null;\n    private CvMat warp       = CvMat.create(3, 3);\n    private CvMat prevWarp   = CvMat.create(3, 3);\n    private CvMat lastWarp   = CvMat.create(3, 3);\n    private CvMat warpSrcPts = CvMat.create(1, 4, CV_64F, 2);\n    private CvMat warpDstPts = CvMat.create(1, 4, CV_64F, 2);\n    private CvMat tempPts    = CvMat.create(1, 4, CV_64F, 2);\n\n    public MarkerDetector getMarkerDetector() {\n        return markerDetector;\n    }\n    public MarkedPlane getMarkedPlane() {\n        return markedPlane;\n    }\n    public ProjectiveDevice getProjectiveDevice() {\n        return projectiveDevice;\n    }\n\n    public LinkedList<Marker[]> getAllObjectMarkers() {\n        return allObjectMarkers;\n    }\n    public void setAllObjectMarkers(LinkedList<Marker[]> allObjectMarkers) {\n        this.allObjectMarkers = allObjectMarkers;\n    }\n\n    public LinkedList<Marker[]> getAllImageMarkers() {\n        return allImageMarkers;\n    }\n    public void setAllImageMarkers(LinkedList<Marker[]> allImageMarkers) {\n        this.allImageMarkers = allImageMarkers;\n    }\n\n    public Marker[] processImage(IplImage image) {\n        projectiveDevice.imageWidth = image.width();\n        projectiveDevice.imageHeight = image.height();\n\n        final boolean whiteMarkers = markedPlane.getForegroundColor().magnitude() >\n                                     markedPlane.getBackgroundColor().magnitude();\n        if (image.depth() > 8) {\n            if (tempImage == null ||\n                    tempImage.width()     != image.width()  ||\n                    tempImage.height()    != image.height()) {\n                tempImage = (IplImage)IplImage.create(image.width(), image.height(),\n                        IPL_DEPTH_8U, 1, image.origin());\n            }\n            cvConvertScale(image, tempImage, 1.0/(1<<8), 0);\n            lastDetectedMarkers = markerDetector.detect(tempImage, whiteMarkers);\n        } else {\n            lastDetectedMarkers = markerDetector.detect(image, whiteMarkers);\n        }\n\n        // First, check if we detected enough markers\n        if (lastDetectedMarkers.length < markedPlane.getMarkers().length*settings.detectedBoardMin) {\n            return null;\n        }\n\n        // then check by how much the corners of the calibration board moved\n        markedPlane.getTotalWarp(lastDetectedMarkers, warp);\n        cvPerspectiveTransform  (warpSrcPts, warpDstPts, warp);\n        cvPerspectiveTransform  (warpSrcPts, tempPts,    prevWarp);\n        double rmsePrev = cvNorm(warpDstPts, tempPts);\n        cvPerspectiveTransform  (warpSrcPts, tempPts,    lastWarp);\n        double rmseLast = cvNorm(warpDstPts, tempPts);\n//System.out.println(\"rmsePrev = \" + rmsePrev + \" rmseLast = \" + rmseLast);\n        // save warp for next iteration...\n        cvCopy(warp, prevWarp);\n\n        // send upstream our recommendation for addition or not of these markers...\n        int imageSize = (image.width()+image.height())/2;\n        if (rmsePrev < settings.patternSteadySize*imageSize &&\n                rmseLast > settings.patternMovedSize*imageSize) {\n            return lastDetectedMarkers;\n        } else {\n            return null;\n        }\n    }\n\n    public void drawMarkers(IplImage image) {\n        markerDetector.draw(image, lastDetectedMarkers);\n    }\n\n    public void addMarkers() {\n        addMarkers(markedPlane.getMarkers(), lastDetectedMarkers);\n    }\n    public void addMarkers(Marker[] imageMarkers) {\n        addMarkers(markedPlane.getMarkers(), imageMarkers);\n    }\n    public void addMarkers(Marker[] objectMarkers, Marker[] imageMarkers) {\n        // add only matching markers...\n        int maxLength = Math.min(objectMarkers.length, imageMarkers.length);\n        Marker[] om = new Marker[maxLength];\n        Marker[] im = new Marker[maxLength];\n        int i = 0;\n        for (Marker m1 : objectMarkers) {\n            for (Marker m2 : imageMarkers) {\n                if (m1.id == m2.id) {\n                    om[i] = m1;\n                    im[i] = m2;\n                    i++;\n                    break;\n                }\n            }\n        }\n        if (i < maxLength) {\n            om = Arrays.copyOf(om, i);\n            im = Arrays.copyOf(im, i);\n        }\n        allObjectMarkers.add(om);\n        allImageMarkers.add(im);\n\n        // we added the detected markers, so save last computed warp too...\n        cvCopy(prevWarp, lastWarp);\n    }\n\n    public int getImageCount() {\n        assert(allObjectMarkers.size() == allImageMarkers.size());\n        return allObjectMarkers.size();\n    }\n\n    private Point3fVectorVector getObjectPoints(CvMat points, CvMat counts) {\n        FloatBuffer pointsBuf = points.getFloatBuffer();\n        IntBuffer countsBuf = counts.getIntBuffer();\n        int n = counts.length();\n        Point3fVectorVector vectors = new Point3fVectorVector(n);\n        for (int i = 0; i < n; i++) {\n            int m = countsBuf.get();\n            Point3fVector vector = new Point3fVector(m);\n            for (int j = 0; j < m; j++) {\n                vector.put(j, new Point3f(pointsBuf.get(), pointsBuf.get(), pointsBuf.get()));\n            }\n            vectors.put(i, vector);\n        }\n        return vectors;\n    }\n\n    private Point2fVectorVector getImagePoints(CvMat points, CvMat counts) {\n        FloatBuffer pointsBuf = points.getFloatBuffer();\n        IntBuffer countsBuf = counts.getIntBuffer();\n        int n = counts.length();\n        Point2fVectorVector vectors = new Point2fVectorVector(n);\n        for (int i = 0; i < n; i++) {\n            int m = countsBuf.get();\n            Point2fVector vector = new Point2fVector(m);\n            for (int j = 0; j < m; j++) {\n                vector.put(j, new Point2f(pointsBuf.get(), pointsBuf.get()));\n            }\n            vectors.put(i, vector);\n        }\n        return vectors;\n    }\n\n    private CvMat[] getPoints(boolean useCenters) {\n        // fill up pointCounts, objectPoints and imagePoints, with data from\n        // srcMarkers and dstMarkers\n        assert(allObjectMarkers.size() == allImageMarkers.size());\n        Iterator<Marker[]> i1 = allObjectMarkers.iterator(),\n                           i2 = allImageMarkers.iterator();\n        CvMat pointCounts = CvMat.create(1, allImageMarkers.size(), CV_32S, 1);\n        IntBuffer pointCountsBuf = pointCounts.getIntBuffer();\n        int totalPointCount = 0;\n        while (i1.hasNext() && i2.hasNext()) {\n            Marker[] m1 = i1.next(),\n                     m2 = i2.next();\n            assert(m1.length == m2.length);\n            int n = m1.length*(useCenters ? 1 : 4);\n            pointCountsBuf.put(n);\n            totalPointCount += n;\n        }\n        i1 = allObjectMarkers.iterator();\n        i2 = allImageMarkers.iterator();\n        CvMat objectPoints = CvMat.create(1, totalPointCount, CV_32F, 3);\n        CvMat imagePoints  = CvMat.create(1, totalPointCount, CV_32F, 2);\n        FloatBuffer objectPointsBuf = objectPoints.getFloatBuffer();\n        FloatBuffer imagePointsBuf  = imagePoints.getFloatBuffer();\n        while (i1.hasNext() && i2.hasNext()) {\n            Marker[] m1 = i1.next(),\n                     m2 = i2.next();\n            for (int j = 0; j < m1.length; j++) {\n                if (useCenters) {\n                    double[] c1 = m1[j].getCenter();\n                    objectPointsBuf.put((float)c1[0]);\n                    objectPointsBuf.put((float)c1[1]);\n                    objectPointsBuf.put(0);\n\n                    double[] c2 = m2[j].getCenter();\n                    imagePointsBuf.put((float)c2[0]);\n                    imagePointsBuf.put((float)c2[1]);\n                } else { // use corners...\n                    for (int k = 0; k < 4; k++) {\n                        objectPointsBuf.put((float)m1[j].corners[2*k    ]);\n                        objectPointsBuf.put((float)m1[j].corners[2*k + 1]);\n                        objectPointsBuf.put(0);\n\n                        imagePointsBuf.put((float)m2[j].corners[2*k    ]);\n                        imagePointsBuf.put((float)m2[j].corners[2*k + 1]);\n                    }\n                }\n            }\n        }\n\n        return new CvMat[] { objectPoints, imagePoints, pointCounts };\n    }\n\n    public static double[] computeReprojectionError(CvMat object_points,\n            CvMat image_points, CvMat point_counts, CvMat camera_matrix,\n            CvMat dist_coeffs, CvMat rot_vects, CvMat trans_vects,\n            CvMat per_view_errors ) {\n        CvMat image_points2 = CvMat.create(image_points.rows(),\n            image_points.cols(), image_points.type());\n\n        int i, image_count = rot_vects.rows(), points_so_far = 0;\n        double total_err = 0, max_err = 0, err;\n\n        CvMat object_points_i = new CvMat(),\n              image_points_i  = new CvMat(),\n              image_points2_i = new CvMat();\n        IntBuffer point_counts_buf = point_counts.getIntBuffer();\n        CvMat rot_vect = new CvMat(), trans_vect = new CvMat();\n\n        for (i = 0; i < image_count; i++) {\n            object_points_i.reset();\n            image_points_i .reset();\n            image_points2_i.reset();\n            int point_count = point_counts_buf.get(i);\n\n            cvGetCols(object_points, object_points_i,\n                    points_so_far, points_so_far + point_count);\n            cvGetCols(image_points, image_points_i,\n                    points_so_far, points_so_far + point_count);\n            cvGetCols(image_points2, image_points2_i,\n                    points_so_far, points_so_far + point_count);\n            points_so_far += point_count;\n\n            cvGetRows(rot_vects,   rot_vect,   i, i+1, 1);\n            cvGetRows(trans_vects, trans_vect, i, i+1, 1);\n\n            projectPoints(cvarrToMat(object_points_i), cvarrToMat(rot_vect), cvarrToMat(trans_vect),\n                          cvarrToMat(camera_matrix), cvarrToMat(dist_coeffs), cvarrToMat(image_points2_i));\n            err = cvNorm(image_points_i, image_points2_i);\n            err *= err;\n            if (per_view_errors != null)\n                per_view_errors.put(i, Math.sqrt(err/point_count));\n            total_err += err;\n\n            for (int j = 0; j < point_count; j++) {\n                double x1 = image_points_i .get(0, j, 0);\n                double y1 = image_points_i .get(0, j, 1);\n                double x2 = image_points2_i.get(0, j, 0);\n                double y2 = image_points2_i.get(0, j, 1);\n                double dx = x1-x2;\n                double dy = y1-y2;\n                err = Math.sqrt(dx*dx + dy*dy);\n                if (err > max_err) {\n                    max_err = err;\n                }\n            }\n        }\n\n        return new double[] { Math.sqrt(total_err/points_so_far), max_err };\n    }\n\n    public double[] calibrate(boolean useCenters) {\n        ProjectiveDevice d = projectiveDevice;\n        CalibrationSettings dsettings = (CalibrationSettings)d.getSettings();\n\n        if (d.cameraMatrix == null) {\n            d.cameraMatrix = CvMat.create(3, 3);\n            cvSetZero(d.cameraMatrix);\n            if ((dsettings.flags & CV_CALIB_FIX_ASPECT_RATIO) != 0) {\n                d.cameraMatrix.put(0, dsettings.initAspectRatio);\n                d.cameraMatrix.put(4, 1.);\n            }\n        }\n        int kn = dsettings.isFixK3() ? 4 : 5;\n        if (dsettings.isRationalModel() && !dsettings.isFixK4() &&\n                !dsettings.isFixK4() && !dsettings.isFixK5()) {\n            kn = 8;\n        }\n        if (d.distortionCoeffs == null || d.distortionCoeffs.cols() != kn) {\n            d.distortionCoeffs = CvMat.create(1, kn);\n            cvSetZero(d.distortionCoeffs);\n        }\n\n        CvMat rotVects = new CvMat(), transVects = new CvMat();\n        d.extrParams = CvMat.create(allImageMarkers.size(), 6);\n        cvGetCols(d.extrParams, rotVects,   0, 3);\n        cvGetCols(d.extrParams, transVects, 3, 6);\n\n        CvMat[] points = getPoints(useCenters);\n        MatVector rvecs = new MatVector();\n        MatVector tvecs = new MatVector();\n        Mat distortionCoeffs = new Mat();\n        calibrateCamera(getObjectPoints(points[0], points[2]), getImagePoints(points[1], points[2]),\n                new Size(d.imageWidth, d.imageHeight),\n                cvarrToMat(d.cameraMatrix), distortionCoeffs,\n                rvecs, tvecs, dsettings.flags,\n                new TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 30, JavaCV.DBL_EPSILON));\n        int n = (int)rvecs.size();\n        CvMat row = new CvMat();\n        for (int i = 0; i < n; i++) {\n            cvTranspose(cvMat(rvecs.get(i)), cvGetRow(rotVects, row, i));\n            cvTranspose(cvMat(tvecs.get(i)), cvGetRow(transVects, row, i));\n        }\n        d.distortionCoeffs = cvMat(distortionCoeffs).clone();\n\n        if (cvCheckArr(d.cameraMatrix,     CV_CHECK_QUIET, 0, 0) != 0 &&\n            cvCheckArr(d.distortionCoeffs, CV_CHECK_QUIET, 0, 0) != 0 &&\n            cvCheckArr(d.extrParams,       CV_CHECK_QUIET, 0, 0) != 0) {\n\n            d.reprojErrs = CvMat.create(1, allImageMarkers.size());\n            double[] err = computeReprojectionError(points[0], points[1], points[2],\n                    d.cameraMatrix, d.distortionCoeffs, rotVects, transVects, d.reprojErrs);\n            d.avgReprojErr = err[0];\n            d.maxReprojErr = err[1];\n//            d.nominalDistance = d.getNominalDistance(markedPlane);\n            return err;\n        } else {\n            d.cameraMatrix = null;\n            d.avgReprojErr = -1;\n            d.maxReprojErr = -1;\n            return null;\n        }\n    }\n\n    public static double[] computeStereoError(CvMat imagePoints1, CvMat imagePoints2,\n            CvMat M1, CvMat D1, CvMat M2, CvMat D2, CvMat F) {\n        // CALIBRATION QUALITY CHECK\n        // because the output fundamental matrix implicitly\n        // includes all the output information,\n        // we can check the quality of calibration using the\n        // epipolar geometry constraint: m2^t*F*m1=0\n        int N = imagePoints1.cols();\n        CvMat L1 = CvMat.create(1, N, CV_32F, 3);\n        CvMat L2 = CvMat.create(1, N, CV_32F, 3);\n        //Always work in undistorted space\n        undistortPoints(cvarrToMat(imagePoints1), cvarrToMat(imagePoints1), cvarrToMat(M1), cvarrToMat(D1), null, cvarrToMat(M1));\n        undistortPoints(cvarrToMat(imagePoints2), cvarrToMat(imagePoints2), cvarrToMat(M2), cvarrToMat(D2), null, cvarrToMat(M2));\n        //imagePoints1.put(d1.undistort(imagePoints1.get()));\n        //imagePoints2.put(d2.undistort(imagePoints2.get()));\n        computeCorrespondEpilines(cvarrToMat(imagePoints1), 1, cvarrToMat(F), cvarrToMat(L1));\n        computeCorrespondEpilines(cvarrToMat(imagePoints2), 2, cvarrToMat(F), cvarrToMat(L2));\n        double avgErr = 0, maxErr = 0;\n        CvMat p1 = imagePoints1, p2 = imagePoints2;\n        for(int i = 0; i < N; i++ ) {\n            double e1 = p1.get(0, i, 0)*L2.get(0, i, 0) +\n                        p1.get(0, i, 1)*L2.get(0, i, 1) + L2.get(0, i, 2);\n            double e2 = p2.get(0, i, 0)*L1.get(0, i, 0) +\n                        p2.get(0, i, 1)*L1.get(0, i, 1) + L1.get(0, i, 2);\n            double err = e1*e1 + e2*e2;\n            avgErr += err;\n\n            err = Math.sqrt(err);\n            if (err > maxErr) {\n                maxErr = err;\n            }\n       }\n       return new double[] { Math.sqrt(avgErr/N), maxErr };\n    }\n\n    public double[] calibrateStereo(boolean useCenters, GeometricCalibrator peer) {\n        ProjectiveDevice d = projectiveDevice;\n        ProjectiveDevice dp = peer.projectiveDevice;\n        CalibrationSettings dsettings = (CalibrationSettings)d.getSettings();\n        CalibrationSettings dpsettings = (CalibrationSettings)dp.getSettings();\n\n        CvMat[] points1 = getPoints(useCenters);\n        CvMat[] points2 = peer.getPoints(useCenters);\n\n        // find points in common from points1 and points2\n        // since points1[0] and points2[0] might not be equal...\n        FloatBuffer objPts1 = points1[0].getFloatBuffer();\n        FloatBuffer imgPts1 = points1[1].getFloatBuffer();\n        IntBuffer imgCount1 = points1[2].getIntBuffer();\n        FloatBuffer objPts2 = points2[0].getFloatBuffer();\n        FloatBuffer imgPts2 = points2[1].getFloatBuffer();\n        IntBuffer imgCount2 = points2[2].getIntBuffer();\n        assert(imgCount1.capacity() == imgCount2.capacity());\n\n        CvMat objectPointsMat = CvMat.create(1, Math.min(objPts1.capacity(), objPts2.capacity()), CV_32F, 3);\n        CvMat imagePoints1Mat = CvMat.create(1, Math.min(imgPts1.capacity(), imgPts2.capacity()), CV_32F, 2);\n        CvMat imagePoints2Mat = CvMat.create(1, Math.min(imgPts1.capacity(), imgPts2.capacity()), CV_32F, 2);\n        CvMat pointCountsMat  = CvMat.create(1, imgCount1.capacity(), CV_32S, 1);\n        FloatBuffer objectPoints = objectPointsMat.getFloatBuffer();\n        FloatBuffer imagePoints1 = imagePoints1Mat.getFloatBuffer();\n        FloatBuffer imagePoints2 = imagePoints2Mat.getFloatBuffer();\n        IntBuffer   pointCounts  = pointCountsMat .getIntBuffer();\n\n        int end1 = 0, end2 = 0;\n        for (int i = 0; i < imgCount1.capacity(); i++) {\n            int start1 = end1;\n            int start2 = end2;\n            end1 = start1 + imgCount1.get(i);\n            end2 = start2 + imgCount2.get(i);\n\n            int count = 0;\n            for (int j = start1; j < end1; j++) {\n                float x1 = objPts1.get(j*3  );\n                float y1 = objPts1.get(j*3+1);\n                float z1 = objPts1.get(j*3+2);\n                for (int k = start2; k < end2; k++) {\n                    float x2 = objPts2.get(k*3  );\n                    float y2 = objPts2.get(k*3+1);\n                    float z2 = objPts2.get(k*3+2);\n                    if (x1 == x2 && y1 == y2 && z1 == z2) {\n                        objectPoints.put(x1);\n                        objectPoints.put(y1);\n                        objectPoints.put(z1);\n\n                        imagePoints1.put(imgPts1.get(j*2));\n                        imagePoints1.put(imgPts1.get(j*2+1));\n\n                        imagePoints2.put(imgPts2.get(k*2));\n                        imagePoints2.put(imgPts2.get(k*2+1));\n\n                        count++;\n                        break;\n                    }\n                }\n            }\n            if (count > 0) {\n                pointCounts.put(count);\n            }\n        }\n        objectPointsMat.cols(objectPoints.position()/3);\n        imagePoints1Mat.cols(imagePoints1.position()/2);\n        imagePoints2Mat.cols(imagePoints2.position()/2);\n        pointCountsMat .cols(pointCounts .position());\n\n\n        // place our ProjectiveDevice at the origin...\n        d.R = CvMat.create(3, 3); d.R.put(1.0, 0.0, 0.0,  0.0, 1.0, 0.0,  0.0, 0.0, 1.0);\n        d.T = CvMat.create(3, 1); d.T.put(0.0, 0.0, 0.0);\n        d.E = CvMat.create(3, 3); cvSetZero(d.E);\n        d.F = CvMat.create(3, 3); cvSetZero(d.F);\n\n        dp.R = CvMat.create(3, 3);\n        dp.T = CvMat.create(3, 1);\n        dp.E = CvMat.create(3, 3);\n        dp.F = CvMat.create(3, 3);\n\n        stereoCalibrate(getObjectPoints(objectPointsMat, pointCountsMat), getImagePoints(imagePoints1Mat, pointCountsMat), getImagePoints(imagePoints2Mat, pointCountsMat),\n                cvarrToMat(d.cameraMatrix), cvarrToMat(d.distortionCoeffs), cvarrToMat(dp.cameraMatrix), cvarrToMat(dp.distortionCoeffs),\n                new Size(d.imageWidth, d.imageHeight), cvarrToMat(dp.R), cvarrToMat(dp.T), cvarrToMat(dp.E), cvarrToMat(dp.F), dpsettings.flags,\n                new TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,100,1e-6));\n\n        // compute and return epipolar error...\n        d.avgEpipolarErr = 0.0;\n        d.maxEpipolarErr = 0.0;\n        double err[] = computeStereoError(imagePoints1Mat, imagePoints2Mat,\n                 d.cameraMatrix,  d.distortionCoeffs,\n                dp.cameraMatrix, dp.distortionCoeffs, dp.F);\n        dp.avgEpipolarErr = err[0];\n        dp.maxEpipolarErr = err[1];\n\n        return err;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/HandMouse.java",
    "content": "/*\n * Copyright (C) 2011-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.ByteBuffer;\nimport java.nio.FloatBuffer;\nimport java.nio.IntBuffer;\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.Loader;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class HandMouse {\n    public HandMouse() {\n        this(new Settings());\n    }\n    public HandMouse(Settings settings) {\n        setSettings(settings);\n    }\n\n    public static class Settings extends BaseChildSettings {\n        public Settings() { }\n        public Settings(Settings s) {\n            s.mopIterations   = mopIterations;\n            s.clickSteadySize = clickSteadySize;\n            s.clickSteadyTime = clickSteadyTime;\n            s.edgeAreaMin     = edgeAreaMin;\n            s.edgeAreaMax     = edgeAreaMax;\n            s.thresholdHigh   = thresholdHigh;\n            s.thresholdLow    = thresholdLow;\n            s.brightnessMin   = brightnessMin;\n            s.updateAlpha     = updateAlpha;\n        }\n\n        int    mopIterations   = 1;\n        double clickSteadySize = 0.05;\n        long   clickSteadyTime = 250;\n        double edgeAreaMin     = 0.001;\n        double edgeAreaMax     = 0.1;\n        double thresholdHigh   = 0.5;\n        double thresholdLow    = 0.25;\n        double brightnessMin   = 0.1;\n        double updateAlpha     = 0.5;\n\n        public int getMopIterations() {\n            return mopIterations;\n        }\n        public void setMopIterations(int mopIterations) {\n            this.mopIterations = mopIterations;\n        }\n\n        public double getClickSteadySize() {\n            return clickSteadySize;\n        }\n        public void setClickSteadySize(double clickSteadySize) {\n            this.clickSteadySize = clickSteadySize;\n        }\n\n        public long getClickSteadyTime() {\n            return clickSteadyTime;\n        }\n        public void setClickSteadyTime(long clickSteadyTime) {\n            this.clickSteadyTime = clickSteadyTime;\n        }\n\n        public double getEdgeAreaMin() {\n            return edgeAreaMin;\n        }\n        public void setEdgeAreaMin(double edgeAreaMin) {\n            this.edgeAreaMin = edgeAreaMin;\n        }\n\n        public double getEdgeAreaMax() {\n            return edgeAreaMax;\n        }\n        public void setEdgeAreaMax(double edgeAreaMax) {\n            this.edgeAreaMax = edgeAreaMax;\n        }\n\n        public double getThresholdHigh() {\n            return thresholdHigh;\n        }\n        public void setThresholdHigh(double thresholdHigh) {\n            this.thresholdHigh = thresholdHigh;\n        }\n\n        public double getThresholdLow() {\n            return thresholdLow;\n        }\n        public void setThresholdLow(double thresholdLow) {\n            this.thresholdLow = thresholdLow;\n        }\n\n        public double getBrightnessMin() {\n            return brightnessMin;\n        }\n        public void setBrightnessMin(double brightnessMin) {\n            this.brightnessMin = brightnessMin;\n        }\n\n        public double getUpdateAlpha() {\n            return updateAlpha;\n        }\n        public void setUpdateAlpha(double updateAlpha) {\n            this.updateAlpha = updateAlpha;\n        }\n    }\n\n    private Settings settings;\n    public Settings getSettings() {\n        return settings;\n    }\n    public void setSettings(Settings settings) {\n        this.settings = settings;\n    }\n\n    private IplImage relativeResidual = null, binaryImage = null;\n    private CvRect roi = null;\n    private CvMemStorage storage = CvMemStorage.create();\n    private int contourPointsSize = 0;\n    private IntPointer intPointer = new IntPointer(1);\n    private CvPoint contourPoints = null;\n    private IntBuffer contourPointsBuffer = null;\n    private CvMoments moments = new CvMoments();\n    private double edgeX = 0, edgeY = 0, centerX = 0, centerY = 0;\n    private double imageTipX = -1, tipX = -1, prevTipX = -1;\n    private double imageTipY = -1, tipY = -1, prevTipY = -1;\n    private long tipTime = 0, prevTipTime = 0;\n    private CvPoint pt1 = new CvPoint(), pt2 = new CvPoint();\n    private boolean imageUpdateNeeded = false;\n\n    public void reset() {\n        tipX = tipY = prevTipX = prevTipY = -1;\n    }\n\n    public void update(IplImage[] images, int pyramidLevel, CvRect roi, double[] roiPts) {\n        this.roi = roi;\n//        double RMSE = aligner.getRMSE()*((GNImageAligner)aligner).prevOutlierRatio;\n//        double threshold = RMSE*settings.threshold;\n//        double threshold2 = RMSE*settings.threshold2;//threshold*threshold;\n\n        IplImage target      = images[1];\n        IplImage transformed = images[2];\n        IplImage residual    = images[3];\n        IplImage mask        = images[4];\n        int width    = roi.width();\n        int height   = roi.height();\n        int channels = residual.nChannels();\n        relativeResidual = IplImage.createIfNotCompatible(relativeResidual, mask);\n        binaryImage      = IplImage.createIfNotCompatible(binaryImage,      mask);\n        cvResetImageROI(relativeResidual);\n        cvResetImageROI(binaryImage);\n\n        double brightnessMin = (channels > 3 ? 3 : channels)*settings.brightnessMin;\n        double contourEdgeAreaMax = (width+height)/2*width*height*settings.edgeAreaMax;\n        double contourEdgeAreaMin = (width+height)/2*width*height*settings.edgeAreaMin;\n        ByteBuffer maskBuf = mask.getByteBuffer();\n        FloatBuffer residualBuf = residual.getFloatBuffer();\n        FloatBuffer targetBuf = target.getFloatBuffer();\n        FloatBuffer transformedBuf = transformed.getFloatBuffer();\n        ByteBuffer relResBuf = relativeResidual.getByteBuffer();\n        while (maskBuf.hasRemaining() && residualBuf.hasRemaining() &&\n                targetBuf.hasRemaining() && transformedBuf.hasRemaining() &&\n                relResBuf.hasRemaining()) {\n            byte m = maskBuf.get();\n            if (m == 0) {\n                residualBuf.position(residualBuf.position() + channels);\n                targetBuf.position(targetBuf.position() + channels);\n                transformedBuf.position(transformedBuf.position() + channels);\n                relResBuf.put((byte)0);\n            } else {\n                double relativeNorm = 0;\n                double brightness = 0;\n                for (int z = 0; z < channels; z++) {\n                    float r = Math.abs(residualBuf.get());\n                    float c = targetBuf.get();\n                    float t = transformedBuf.get();\n                    if (z < 3) {\n                        float maxct = Math.max(c,t);\n                        brightness += maxct;\n                        relativeNorm = Math.max(r/maxct, relativeNorm);\n                    } // ignore alpha channel\n                }\n                if (brightness < brightnessMin) {\n                    relResBuf.put((byte)0);\n                } else {\n                    relResBuf.put((byte)Math.round(255 / settings.thresholdHigh *\n                            Math.min(relativeNorm, settings.thresholdHigh)));\n                }\n            }\n        }\n\n        JavaCV.hysteresisThreshold(relativeResidual, binaryImage,\n                255, 255*settings.thresholdLow/settings.thresholdHigh, 255);\n\n        int roiX = roi.x(), roiY = roi.y();\n        cvSetImageROI(binaryImage, roi);\n\n        if (settings.mopIterations > 0) {\n            cvMorphologyEx(binaryImage, binaryImage, null, null, CV_MOP_OPEN,  settings.mopIterations);\n            cvMorphologyEx(binaryImage, binaryImage, null, null, CV_MOP_CLOSE, settings.mopIterations);\n        }\n        CvSeq contour = new CvContour(null);\n        cvFindContours(binaryImage, storage, contour, Loader.sizeof(CvContour.class),\n                CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);\n        double largestContourEdgeArea = 0;\n        CvSeq largestContour = null;\n        while (contour != null && !contour.isNull()) {\n            contourPointsSize = contour.total();\n            if (contourPoints == null || contourPoints.capacity() < contourPointsSize) {\n                contourPoints = new CvPoint(contourPointsSize);\n                contourPointsBuffer = contourPoints.asByteBuffer().asIntBuffer();\n            }\n            cvCvtSeqToArray(contour, contourPoints.position(0));\n\n            double[] edgePts = new double[roiPts.length];\n            for (int i = 0; i < roiPts.length/2; i++) {\n                edgePts[2*i    ] = roiPts[2*i    ]/(1<<pyramidLevel) - roiX;\n                edgePts[2*i + 1] = roiPts[2*i + 1]/(1<<pyramidLevel) - roiY;\n            }\n\n            double m00 = 0, m10 = 0, m01 = 0;\n            for (int i = 0; i < contourPointsSize; i++) {\n                int x = contourPointsBuffer.get(2*i    ),\n                    y = contourPointsBuffer.get(2*i + 1);\n                for (int j = 0; j < roiPts.length/2; j++) {\n                    double x1 = edgePts[ 2*j                     ],\n                           y1 = edgePts[ 2*j + 1                 ],\n                           x2 = edgePts[(2*j + 2) % edgePts.length],\n                           y2 = edgePts[(2*j + 3) % edgePts.length];\n                    double dx = x2 - x1;\n                    double dy = y2 - y1;\n                    double d2 = dx*dx + dy*dy;\n                    double u = ((x - x1)*dx + (y - y1)*dy) / d2;\n\n                    double px = x1 + u*dx;\n                    double py = y1 + u*dy;\n\n                    dx = px - x;\n                    dy = py - y;\n                    d2 = dx*dx + dy*dy;\n                    if (d2 < 2) {\n                        m00 += 1;\n                        m10 += x;\n                        m01 += y;\n                        break;\n                    }\n                }\n            }\n            double contourEdgeArea = m00*Math.abs(cvContourArea(contour, CV_WHOLE_SEQ, 0));\n            if (contourEdgeArea > contourEdgeAreaMin && contourEdgeArea < contourEdgeAreaMax &&\n                    contourEdgeArea > largestContourEdgeArea) {\n                largestContourEdgeArea = contourEdgeArea;\n                largestContour = contour;\n\n                double inv_m00 = 1 / m00;\n                edgeX = m10 * inv_m00;\n                edgeY = m01 * inv_m00;\n            }\n            contour = contour.h_next();\n        }\n\n        if (isClick()) {\n            prevTipX = -1;\n            prevTipY = -1;\n            prevTipTime = 0;\n        } else if (!isSteady()) {\n            prevTipX = tipX;\n            prevTipY = tipY;\n            prevTipTime = System.currentTimeMillis();\n        }\n\n        if (largestContour == null) {\n            tipX = -1;\n            tipY = -1;\n            tipTime = 0;\n            imageUpdateNeeded = false;\n        } else {\n            cvMoments(largestContour, moments, 0);\n            double inv_m00 = 1 / moments.m00();\n            centerX = moments.m10() * inv_m00;\n            centerY = moments.m01() * inv_m00;\n\n            contourPointsSize = largestContour.total();\n            cvCvtSeqToArray(largestContour, contourPoints.position(0));\n\n            double tipDist2 = 0;\n            int tipIndex = 0;\n            for (int i = 0; i < contourPointsSize; i++) {\n                int x = contourPointsBuffer.get(2*i    ),\n                    y = contourPointsBuffer.get(2*i + 1);\n                double dx = centerX - edgeX;\n                double dy = centerY - edgeY;\n                double d2 = dx*dx + dy*dy;\n                double u = ((x - edgeX)*dx + (y - edgeY)*dy) / d2;\n\n                double px = edgeX + u*dx;\n                double py = edgeY + u*dy;\n\n                dx = px - edgeX;\n                dy = py - edgeY;\n                d2 = dx*dx + dy*dy;\n                if (d2 > tipDist2) {\n                    tipIndex = i;\n                    tipDist2 = d2;\n                }\n            }\n            double a = imageTipX < 0 || imageTipY < 0 ? 1.0 : settings.updateAlpha;\n            imageTipX = a*contourPointsBuffer.get(2*tipIndex    ) + (1-a)*imageTipX;\n            imageTipY = a*contourPointsBuffer.get(2*tipIndex + 1) + (1-a)*imageTipY;\n            tipX = (imageTipX+roiX)*(1<<pyramidLevel);\n            tipY = (imageTipY+roiY)*(1<<pyramidLevel);\n            tipTime = System.currentTimeMillis();\n            imageUpdateNeeded = true;\n        }\n\n        cvClearMemStorage(storage);\n    }\n\n    public IplImage getRelativeResidual() {\n        return relativeResidual;\n    }\n    public IplImage getResultImage() {\n        if (imageUpdateNeeded) {\n            cvSetZero(binaryImage);\n            cvFillPoly(binaryImage, contourPoints, intPointer.put(contourPointsSize), 1, CvScalar.WHITE, 8, 0);\n\n            pt1.put((byte)16, edgeX, edgeY);\n            cvCircle(binaryImage, pt1, 5<<16, CvScalar.GRAY, 2, 8, 16);\n\n            pt1.put((byte)16, centerX-5, centerY-5); pt2.put((byte)16, centerX+5, centerY+5);\n            cvRectangle(binaryImage, pt1, pt2, CvScalar.GRAY, 2, 8, 16);\n\n            pt1.put((byte)16, imageTipX-5, imageTipY-5); pt2.put((byte)16, imageTipX+5, imageTipY+5);\n            cvLine(binaryImage, pt1, pt2, CvScalar.GRAY, 2, 8, 16);\n            pt1.put((byte)16, imageTipX-5, imageTipY+5); pt2.put((byte)16, imageTipX+5, imageTipY-5);\n            cvLine(binaryImage, pt1, pt2, CvScalar.GRAY, 2, 8, 16);\n\n            cvResetImageROI(binaryImage);\n            imageUpdateNeeded = false;\n        }\n        return binaryImage;\n    }\n\n    public double getX() {\n        return tipX;\n    }\n    public double getY() {\n        return tipY;\n    }\n\n    public boolean isSteady() {\n        if (tipX >= 0 && tipY >= 0 && prevTipX >= 0 && prevTipY >= 0) {\n            double dx = tipX - prevTipX;\n            double dy = tipY - prevTipY;\n            int imageSize = (roi.width() + roi.height())/2;\n            double steadySize = settings.clickSteadySize*imageSize;\n            return dx*dx + dy*dy < steadySize*steadySize;\n        }\n        return false;\n    }\n    public boolean isClick() {\n        return isSteady() && tipTime - prevTipTime > settings.clickSteadyTime;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/IPCameraFrameGrabber.java",
    "content": "/*\n * Copyright (C) 2013 Greg Perry\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Loader;\n\nimport javax.imageio.ImageIO;\nimport java.awt.image.BufferedImage;\nimport java.io.ByteArrayInputStream;\nimport java.io.DataInputStream;\nimport java.io.EOFException;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.util.concurrent.TimeUnit;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\n\npublic class IPCameraFrameGrabber extends FrameGrabber {\n\n    /*\n     * excellent reference - http://www.jpegcameras.com/ foscam url\n     * http://host/videostream.cgi?user=username&pwd=password\n     * http://192.168.0.59:60/videostream.cgi?user=admin&pwd=password android ip\n     * cam http://192.168.0.57:8080/videofeed\n     */\n\n    private static Exception loadingException = null;\n\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.opencv.global.opencv_highgui.class);\n            } catch (Throwable t) {\n                throw loadingException = new Exception(\"Failed to load \" + IPCameraFrameGrabber.class, t);\n            }\n        }\n    }\n\n    private final FrameConverter converter = new OpenCVFrameConverter.ToMat();\n    private final URL url;\n    private final int connectionTimeout;\n    private final int readTimeout;\n    private DataInputStream input;\n    private byte[] pixelBuffer = new byte[1024];\n    private Mat decoded = null;\n\n    /**\n     * @param url          The URL to create the camera connection with.\n     * @param startTimeout How long should this wait on the connection while trying to {@link #start()} before\n     *                     timing out.\n     *                     If this value is less than zero it will be ignored.\n     *                     {@link URLConnection#setConnectTimeout(int)}\n     * @param grabTimeout  How long should grab wait while reading the connection before timing out.\n     *                     If this value is less than zero it will be ignored.\n     *                     {@link URLConnection#setReadTimeout(int)}\n     * @param timeUnit     The time unit to use for the connection and read timeout.\n     *                     If this value is null then the start timeout and grab timeout will be ignored.\n     */\n    public IPCameraFrameGrabber(URL url, int startTimeout, int grabTimeout, TimeUnit timeUnit) {\n        super(); // Always good practice to do this\n        if (url == null) {\n            throw new IllegalArgumentException(\"URL can not be null\");\n        }\n        this.url = url;\n        if (timeUnit != null) {\n            this.connectionTimeout = toIntExact(TimeUnit.MILLISECONDS.convert(startTimeout, timeUnit));\n            this.readTimeout = toIntExact(TimeUnit.MILLISECONDS.convert(grabTimeout, timeUnit));\n        } else {\n            this.connectionTimeout = -1;\n            this.readTimeout = -1;\n        }\n    }\n\n    public IPCameraFrameGrabber(String urlstr, int connectionTimeout, int readTimeout, TimeUnit timeUnit) throws MalformedURLException {\n        this(new URL(urlstr), connectionTimeout, readTimeout, timeUnit);\n    }\n\n    /**\n     * @param urlstr A string to be used to create the URL.\n     * @throws MalformedURLException if the urlstr is a malformed URL\n     * @deprecated By not setting the connection timeout and the read timeout if your network ever crashes\n     * then {@link #start()} or {@link #grab()} can hang for upwards of 45 to 60 seconds before failing.\n     * You should always explicitly set the connectionTimeout and readTimeout so that your application can\n     * respond appropriately to a loss or failure to connect.\n     */\n    @Deprecated\n    public IPCameraFrameGrabber(String urlstr) throws MalformedURLException {\n        this(new URL(urlstr), -1, -1, null);\n    }\n\n    @Override\n    public void start() throws Exception {\n        try {\n            /*\n             * We don't need to keep a reference to the connection\n             * after it is opened in the parent class.\n             * It never uses it outside of start.\n             */\n            final URLConnection connection = url.openConnection();\n            // If the class was initialized with timeout values then configure those\n            if (connectionTimeout >= 0) {\n                connection.setConnectTimeout(connectionTimeout);\n            }\n            if (readTimeout >= 0) {\n                connection.setReadTimeout(readTimeout);\n            }\n            input = new DataInputStream(connection.getInputStream());\n        } catch (IOException e) {\n            throw new Exception(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public void stop() throws Exception {\n        if (input != null) {\n            try {\n                input.close();\n            } catch (IOException e) {\n                throw new Exception(e.getMessage(), e);\n            } finally {\n                // Close may have failed but there's really nothing we can do about it at this point\n                input = null;\n                // Don't set the url to null, it may be needed to restart this object\n                releaseDecoded();\n            }\n        }\n    }\n\n    @Override\n    public void trigger() throws Exception {\n    }\n\n    @Override\n    public Frame grab() throws Exception {\n        try {\n            final byte[] b = readImage();\n            final Mat mat = new Mat(1, b.length, CV_8UC1, new BytePointer(b));\n            releaseDecoded();\n            return converter.convert(decoded = imdecode(mat, IMREAD_COLOR));\n        } catch (IOException e) {\n            throw new Exception(e.getMessage(), e);\n        }\n    }\n\n    public BufferedImage grabBufferedImage() throws IOException {\n        BufferedImage bi = ImageIO.read(new ByteArrayInputStream(readImage()));\n        return bi;\n    }\n\n    /**\n     * Ensures that if the decoded image is not null that it gets released and set to null.\n     * If the image was not set to null then trying to release a null pointer will cause  a\n     * segfault.\n     */\n    private void releaseDecoded() {\n        if (decoded != null) {\n            decoded.release();\n            decoded = null;\n        }\n    }\n\n    private byte[] readImage() throws IOException {\n        final StringBuffer sb = new StringBuffer();\n        int c;\n        // read http subheader\n        while ((c = input.read()) != -1) {\n            if (c > 0) {\n                sb.append((char) c);\n                if (c == 13) {\n                    sb.append((char) input.read());// '10'+\n                    c = input.read();\n                    sb.append((char) c);\n                    if (c == 13) {\n                        sb.append((char) input.read());// '10'\n                        break; // done with subheader\n                    }\n\n                }\n            }\n        }\n        // find embedded jpeg in stream\n        /*\n         * Some cameras return headers 'content-length' using different casing\n         * Eg. Axis cameras return 'Content-Length:' while TrendNet cameras return 'content-length:'\n         */\n        final String subheader = sb.toString().toLowerCase();\n        //log.debug(subheader);\n\n        // Yay! - server was nice and sent content length\n        int c0 = subheader.indexOf(\"content-length: \");\n        final int c1 = subheader.indexOf('\\r', c0);\n\n        if (c0 < 0) {\n            //log.info(\"no content length returning null\");\n            throw new EOFException(\"The camera stream ended unexpectedly\");\n        }\n\n        c0 += 16;\n        final int contentLength = Integer.parseInt(subheader.substring(c0, c1).trim());\n        //log.debug(\"Content-Length: \" + contentLength);\n\n        // adaptive size - careful - don't want a 2G jpeg\n        ensureBufferCapacity(contentLength);\n\n        input.readFully(pixelBuffer, 0, contentLength);\n        input.read();// \\r\n        input.read();// \\n\n        input.read();// \\r\n        input.read();// \\n\n\n        return pixelBuffer;\n    }\n\n    @Override\n    public void release() throws Exception {\n    }\n\n    /**\n     * Grow the pixel buffer if necessary.  Using this method instead of allocating a new buffer every time a frame\n     * is grabbed improves performance by reducing the frequency of garbage collections.  In a simple test, the\n     * original version of IPCameraFrameGrabber that allocated a 4096 element byte array for every read\n     * caused about 200MB of allocations within 13 seconds.  In this version, almost no additional heap space\n     * is typically allocated per frame.\n     */\n    private void ensureBufferCapacity(int desiredCapacity) {\n        int capacity = pixelBuffer.length;\n\n        while (capacity < desiredCapacity) {\n            capacity *= 2;\n        }\n\n        if (capacity > pixelBuffer.length) {\n            pixelBuffer = new byte[capacity];\n        }\n    }\n\n    /**\n     * Returns the value of the {@code long} argument;\n     * throwing an exception if the value overflows an {@code int}.\n     *\n     * @param value the long value\n     * @return the argument as an int\n     * @throws ArithmeticException if the {@code argument} overflows an int\n     * @see <a href=\"https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toIntExact-long-\">Java 8 Implementation</a>\n     */\n    private static int toIntExact(long value) {\n        if ((int) value != value) {\n            throw new ArithmeticException(\"integer overflow\");\n        }\n        return (int) value;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ImageAligner.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport org.bytedeco.javacv.ImageTransformer.Parameters;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic interface ImageAligner {\n\n    public class Settings extends BaseChildSettings implements Cloneable {\n        public Settings() { }\n        public Settings(Settings s) {\n            pyramidLevelMin   = s.pyramidLevelMin;\n            pyramidLevelMax   = s.pyramidLevelMax;\n            thresholdsZero    = s.thresholdsZero;\n            thresholdsOutlier = s.thresholdsOutlier;\n            thresholdsMulRMSE = s.thresholdsMulRMSE;\n        }\n\n        int pyramidLevelMin        = 0;\n        int pyramidLevelMax        = 4;\n        double[] thresholdsZero    = { 0.04, 0.03, 0.02, 0.01, 0 };\n        double[] thresholdsOutlier = { 0.2 };\n        boolean thresholdsMulRMSE  = false;\n\n        public int getPyramidLevelMin() {\n            return pyramidLevelMin;\n        }\n        public void setPyramidLevelMin(int pyramidLevelMin) {\n            this.pyramidLevelMin = pyramidLevelMin;\n        }\n\n        public int getPyramidLevelMax() {\n            return pyramidLevelMax;\n        }\n        public void setPyramidLevelMax(int pyramidLevelMax) {\n            this.pyramidLevelMax = pyramidLevelMax;\n        }\n\n        public double[] getThresholdsZero() {\n            return thresholdsZero;\n        }\n        public void setThresholdsZero(double[] thresholdsZero) {\n            this.thresholdsZero = thresholdsZero;\n        }\n\n        public double[] getThresholdsOutlier() {\n            return thresholdsOutlier;\n        }\n        public void setThresholdsOutlier(double[] thresholdsOutlier) {\n            this.thresholdsOutlier = thresholdsOutlier;\n        }\n\n        public boolean isThresholdsMulRMSE() {\n            return thresholdsMulRMSE;\n        }\n        public void setThresholdsMulRMSE(boolean thresholdsMulRMSE) {\n            this.thresholdsMulRMSE = thresholdsMulRMSE;\n        }\n\n        @Override public Settings clone() {\n            return new Settings(this);\n        }\n    }\n    Settings getSettings();\n    void setSettings(Settings settings);\n\n    IplImage getTemplateImage();\n    void setTemplateImage(IplImage template0, double[] roiPts);\n\n    IplImage getTargetImage();\n    void setTargetImage(IplImage target0);\n\n    int getPyramidLevel();\n    void setPyramidLevel(int pyramidLevel);\n\n    Parameters getParameters();\n    void setParameters(Parameters parameters);\n\n    double[] getTransformedRoiPts();\n    IplImage getTransformedImage();\n    IplImage getResidualImage();\n    IplImage getMaskImage();\n    double getRMSE();\n    CvRect getRoi();\n\n    IplImage[] getImages();\n\n    boolean iterate(double[] delta);\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ImageAlignerCL.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport com.jogamp.opencl.CLImage2d;\n\n/**\n *\n * @author Samuel Audet\n */\npublic interface ImageAlignerCL extends ImageAligner {\n\n    CLImage2d getTemplateImageCL();\n    void setTemplateImageCL(CLImage2d template0, double[] roiPts);\n\n    CLImage2d getTargetImageCL();\n    void setTargetImageCL(CLImage2d target0);\n\n    CLImage2d getTransformedImageCL();\n    CLImage2d getResidualImageCL();\n    CLImage2d getMaskImageCL();\n\n    CLImage2d[] getImagesCL();\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ImageTransformer.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.DoubleBuffer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic interface ImageTransformer {\n\n    public class Data {\n        public Data() { this(null, null, null, null, 0, 0, 0, null, null, 0); }\n        public Data(IplImage srcImg, IplImage subImg, IplImage srcDotImg, IplImage mask,\n                double zeroThreshold, double outlierThreshold, int pyramidLevel,\n                IplImage transImg, IplImage dstImg, int dstDstDotLength) {\n            this.srcImg    = srcImg;\n            this.subImg    = subImg;\n            this.srcDotImg = srcDotImg;\n            this.mask      = mask;\n            this.zeroThreshold    = zeroThreshold;\n            this.outlierThreshold = outlierThreshold;\n            this.pyramidLevel     = pyramidLevel;\n            this.transImg  = transImg;\n            this.dstImg    = dstImg;\n            this.dstDstDot = dstDstDotLength == 0 ? null : \n                    ByteBuffer.allocateDirect(dstDstDotLength*8).\n                            order(ByteOrder.nativeOrder()).asDoubleBuffer();\n        }\n\n        // input\n        public IplImage srcImg, subImg, srcDotImg, mask;\n        public double   zeroThreshold, outlierThreshold;\n        public int      pyramidLevel;\n\n        // output\n        public IplImage transImg, dstImg;\n        public int      dstCount, dstCountZero, dstCountOutlier;\n        public double   srcDstDot;\n        public DoubleBuffer dstDstDot;\n    }\n\n    public interface Parameters extends Cloneable {\n        int size();\n        double[] get();\n        double   get(int i);\n        void set(double ... p);\n        void set(int i, double p);\n        void set(Parameters p);\n        void reset(boolean asIdentity);\n        double getConstraintError();\n        void compose(Parameters p1, boolean inverse1, Parameters p2, boolean inverse2);\n        boolean preoptimize();\n        double[] getSubspace();\n        void setSubspace(double ... p);\n        Parameters clone();\n    }\n\n    Parameters createParameters();\n    void transform(Data[] data, CvRect roi, Parameters[] parameters, boolean[] inverses);\n    void transform(CvMat srcPts, CvMat dstPts, Parameters parameters, boolean inverse);\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ImageTransformerCL.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport com.jogamp.opencl.CLBuffer;\nimport com.jogamp.opencl.CLImage2d;\nimport java.nio.ByteBuffer;\nimport java.nio.FloatBuffer;\n\n/**\n *\n * @author Samuel Audet\n */\npublic interface ImageTransformerCL extends ImageTransformer {\n\n    public class InputData {\n        public InputData() { this(true); }\n        public InputData(boolean autoWrite) { this.autoWrite = autoWrite; }\n\n        public int pyramidLevel = 0;\n        public int roiX = 0, roiY = 0, roiWidth = 0, roiHeight = 0;\n        public double zeroThreshold = 0, outlierThreshold = 0;\n\n        CLBuffer<ByteBuffer> buffer = null;\n        boolean autoWrite = true;\n\n        CLBuffer<ByteBuffer> getBuffer(JavaCVCL context) {\n            int structSize = 4*4;\n            if (buffer == null || buffer.getCLSize() < structSize) {\n                if (buffer != null) buffer.release();\n                buffer = context.getCLContext().createByteBuffer(structSize, CLBuffer.Mem.READ_ONLY);\n            }\n            return buffer;\n        }\n\n        public CLBuffer<ByteBuffer> writeBuffer(JavaCVCL context) {\n            getBuffer(context);\n            ByteBuffer byteBuffer = (ByteBuffer)buffer.getBuffer().rewind();\n            byteBuffer.putInt(roiY).putInt(roiHeight).putFloat((float)zeroThreshold)\n                    .putFloat((float)outlierThreshold).rewind();\n            context.writeBuffer(buffer, false); // upload input data\n            return buffer;\n        }\n    }\n\n    public class OutputData {\n        public OutputData() { this(true); }\n        public OutputData(boolean autoRead) { this.autoRead = autoRead; }\n\n        public int dstCount = 0, dstCountZero = 0, dstCountOutlier = 0;\n        public FloatBuffer srcDstDot = null, dstDstDot = null;\n\n        CLBuffer<ByteBuffer> buffer = null;\n        boolean autoRead = true;\n\n        CLBuffer<ByteBuffer> getBuffer(JavaCVCL context, int dotSize, int reduceSize) {\n            int structSize = 4*(4 + dotSize + dotSize*dotSize);\n            if (buffer == null || buffer.getCLSize() < structSize*reduceSize) {\n                if (buffer != null) buffer.release();\n                buffer = context.getCLContext().createByteBuffer(structSize*reduceSize);\n                ByteBuffer byteBuffer = buffer.getBuffer();\n                byteBuffer.position(4*4);             srcDstDot = byteBuffer.asFloatBuffer();\n                byteBuffer.position(4*(4 + dotSize)); dstDstDot = byteBuffer.asFloatBuffer();\n                byteBuffer.rewind();\n            }\n            return buffer;\n        }\n\n        public CLBuffer<ByteBuffer> readBuffer(JavaCVCL context) {\n            //getBuffer(context, dotSize, reduceSize);\n            context.readBuffer(buffer, true); // read results back (blocking read)\n            ByteBuffer byteBuffer = buffer.getBuffer();\n            dstCount        = byteBuffer.getInt(4);\n            dstCountZero    = byteBuffer.getInt(8);\n            dstCountOutlier = byteBuffer.getInt(12);\n            return buffer;\n        }\n    }\n\n    JavaCVCL getContext();\n\n    void transform(CLImage2d srcImg, CLImage2d subImg, CLImage2d srcDotImg, CLImage2d transImg, CLImage2d dstImg,\n            CLImage2d mask, ImageTransformer.Parameters[] parameters, boolean[] inverses, InputData inputData, OutputData outputData);\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/Java2DFrameConverter.java",
    "content": "/*\n * Copyright (C) 2015-2019 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.awt.Graphics;\nimport java.awt.Rectangle;\nimport java.awt.Transparency;\nimport java.awt.color.ColorSpace;\nimport java.awt.image.BufferedImage;\nimport java.awt.image.ColorModel;\nimport java.awt.image.ComponentColorModel;\nimport java.awt.image.ComponentSampleModel;\nimport java.awt.image.DataBuffer;\nimport java.awt.image.DataBufferByte;\nimport java.awt.image.DataBufferDouble;\nimport java.awt.image.DataBufferFloat;\nimport java.awt.image.DataBufferInt;\nimport java.awt.image.DataBufferShort;\nimport java.awt.image.DataBufferUShort;\nimport java.awt.image.MultiPixelPackedSampleModel;\nimport java.awt.image.Raster;\nimport java.awt.image.SampleModel;\nimport java.awt.image.SinglePixelPackedSampleModel;\nimport java.awt.image.WritableRaster;\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.DoubleBuffer;\nimport java.nio.FloatBuffer;\nimport java.nio.IntBuffer;\nimport java.nio.ShortBuffer;\n\n/**\n * A utility class to copy data between {@link Frame} and {@link BufferedImage}.\n * Since {@link BufferedImage} does not support NIO buffers, we cannot share\n * allocated memory with {@link Frame}.\n *\n * @author Samuel Audet\n */\npublic class Java2DFrameConverter extends FrameConverter<BufferedImage> {\n\n    @Override public Frame convert(BufferedImage img) {\n        return getFrame(img);\n    }\n\n    @Override public BufferedImage convert(Frame frame) {\n        return getBufferedImage(frame);\n    }\n\n    /**\n     * @param source\n     * @return null if source is null\n     */\n    public static BufferedImage cloneBufferedImage(BufferedImage source) {\n        if (source == null) {\n            return null;\n        }\n        int type = source.getType();\n        if (type == BufferedImage.TYPE_CUSTOM) {\n            return new BufferedImage(\n                    source.getColorModel(),\n                    source.copyData(null),\n                    source.isAlphaPremultiplied(),\n                    null\n            );\n        } else {\n            BufferedImage copy = new BufferedImage(source.getWidth(), source.getHeight(), type);\n            Graphics g = copy.getGraphics();\n            g.drawImage(source, 0, 0, null);\n            g.dispose();\n            return copy;\n        }\n    }\n\n    public static final byte[]\n            gamma22    = new byte[256],\n            gamma22inv = new byte[256];\n    static {\n        for (int i = 0; i < 256; i++) {\n            gamma22[i]    = (byte)Math.round(Math.pow(i/255.0,   2.2)*255.0);\n            gamma22inv[i] = (byte)Math.round(Math.pow(i/255.0, 1/2.2)*255.0);\n        }\n    }\n    public static int decodeGamma22(int value) {\n        return gamma22[value & 0xFF] & 0xFF;\n    }\n    public static int encodeGamma22(int value) {\n        return gamma22inv[value & 0xFF] & 0xFF;\n    }\n    public static void flipCopyWithGamma(ByteBuffer srcBuf, int srcBufferIndex, int srcStep,\n                                         ByteBuffer dstBuf, int dstBufferIndex, int dstStep,\n                                         boolean signed, double gamma, boolean flip, int channels) {\n        assert srcBuf != dstBuf;\n        int w = Math.min(srcStep, dstStep);\n        int srcLine = srcBufferIndex, dstLine = dstBufferIndex;\n        byte[] buffer = new byte[channels];\n        while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) {\n            if (flip) {\n                srcBufferIndex = srcBuf.capacity() - srcLine - srcStep;\n            } else {\n                srcBufferIndex = srcLine;\n            }\n            dstBufferIndex = dstLine;\n            w = Math.min(Math.min(w, srcBuf.capacity() - srcBufferIndex), dstBuf.capacity() - dstBufferIndex);\n            if (signed) {\n                if (channels > 1) {\n                    for (int x = 0; x < w; x+=channels) {\n                        for (int z = 0; z < channels; z++) {\n                            int in = srcBuf.get(srcBufferIndex++);\n                            byte out;\n                            if (gamma == 1.0) {\n                                out = (byte)in;\n                            } else {\n                                out = (byte)Math.round(Math.pow((double)in/Byte.MAX_VALUE, gamma)*Byte.MAX_VALUE);\n                            }\n                            buffer[z] = out;\n                        }\n                        for (int z = channels-1; z >= 0; z--) {\n                            dstBuf.put(dstBufferIndex++, buffer[z]);\n                        }\n                    }\n                } else {\n                    for (int x = 0; x < w; x++) {\n                        int in = srcBuf.get(srcBufferIndex++);\n                        byte out;\n                        if (gamma == 1.0) {\n                            out = (byte)in;\n                        } else {\n                            out = (byte)Math.round(Math.pow((double)in/Byte.MAX_VALUE, gamma)*Byte.MAX_VALUE);\n                        }\n                        dstBuf.put(dstBufferIndex++, out);\n                    }\n                }\n            } else {\n                if (channels > 1) {\n                    for (int x = 0; x < w; x+=channels) {\n                        for (int z = 0; z < channels; z++) {\n                            byte out;\n                            int in = srcBuf.get(srcBufferIndex++) & 0xFF;\n                            if (gamma == 1.0) {\n                                out = (byte)in;\n                            } else if (gamma == 2.2) {\n                                out = gamma22[in];\n                            } else if (gamma == 1/2.2) {\n                                out = gamma22inv[in];\n                            } else {\n                                out = (byte)Math.round(Math.pow((double)in/0xFF, gamma)*0xFF);\n                            }\n                            buffer[z] = out;\n                        }\n                        for (int z = channels-1; z >= 0; z--) {\n                            dstBuf.put(dstBufferIndex++, buffer[z]);\n                        }\n                    }\n                } else {\n                    for (int x = 0; x < w; x++) {\n                        byte out;\n                        int in = srcBuf.get(srcBufferIndex++) & 0xFF;\n                        if (gamma == 1.0) {\n                            out = (byte)in;\n                        } else if (gamma == 2.2) {\n                            out = gamma22[in];\n                        } else if (gamma == 1/2.2) {\n                            out = gamma22inv[in];\n                        } else {\n                            out = (byte)Math.round(Math.pow((double)in/0xFF, gamma)*0xFF);\n                        }\n                        dstBuf.put(dstBufferIndex++, out);\n                    }\n                }\n            }\n            srcLine += srcStep;\n            dstLine += dstStep;\n        }\n    }\n    public static void flipCopyWithGamma(ShortBuffer srcBuf, int srcBufferIndex, int srcStep,\n                                         ShortBuffer dstBuf, int dstBufferIndex, int dstStep,\n                                         boolean signed, double gamma, boolean flip, int channels) {\n        assert srcBuf != dstBuf;\n        int w = Math.min(srcStep, dstStep);\n        int srcLine = srcBufferIndex, dstLine = dstBufferIndex;\n        short[] buffer = new short[channels];\n        while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) {\n            if (flip) {\n                srcBufferIndex = srcBuf.capacity() - srcLine - srcStep;\n            } else {\n                srcBufferIndex = srcLine;\n            }\n            dstBufferIndex = dstLine;\n            w = Math.min(Math.min(w, srcBuf.capacity() - srcBufferIndex), dstBuf.capacity() - dstBufferIndex);\n            if (signed) {\n                if (channels > 1) {\n                    for (int x = 0; x < w; x+=channels) {\n                        for (int z = 0; z < channels; z++) {\n                            int in = srcBuf.get(srcBufferIndex++);\n                            short out;\n                            if (gamma == 1.0) {\n                                out = (short)in;\n                            } else {\n                                out = (short)Math.round(Math.pow((double)in/Short.MAX_VALUE, gamma)*Short.MAX_VALUE);\n                            }\n                            buffer[z] = out;\n                        }\n                        for (int z = channels-1; z >= 0; z--) {\n                            dstBuf.put(dstBufferIndex++, buffer[z]);\n                        }\n                    }\n                } else {\n                    for (int x = 0; x < w; x++) {\n                        int in = srcBuf.get(srcBufferIndex++);\n                        short out;\n                        if (gamma == 1.0) {\n                            out = (short)in;\n                        } else {\n                            out = (short)Math.round(Math.pow((double)in/Short.MAX_VALUE, gamma)*Short.MAX_VALUE);\n                        }\n                        dstBuf.put(dstBufferIndex++, out);\n                    }\n                }\n            } else {\n                if (channels > 1) {\n                    for (int x = 0; x < w; x+=channels) {\n                        for (int z = 0; z < channels; z++) {\n                            int in = srcBuf.get(srcBufferIndex++);\n                            short out;\n                            if (gamma == 1.0) {\n                                out = (short)in;\n                            } else {\n                                out = (short)Math.round(Math.pow((double)in/0xFFFF, gamma)*0xFFFF);\n                            }\n                            buffer[z] = out;\n                        }\n                        for (int z = channels-1; z >= 0; z--) {\n                            dstBuf.put(dstBufferIndex++, buffer[z]);\n                        }\n                    }\n                } else {\n                    for (int x = 0; x < w; x++) {\n                        int in = srcBuf.get(srcBufferIndex++) & 0xFFFF;\n                        short out;\n                        if (gamma == 1.0) {\n                            out = (short)in;\n                        } else {\n                            out = (short)Math.round(Math.pow((double)in/0xFFFF, gamma)*0xFFFF);\n                        }\n                        dstBuf.put(dstBufferIndex++, out);\n                    }\n                }\n            }\n            srcLine += srcStep;\n            dstLine += dstStep;\n        }\n    }\n    public static void flipCopyWithGamma(IntBuffer srcBuf, int srcBufferIndex, int srcStep,\n                                         IntBuffer dstBuf, int dstBufferIndex, int dstStep,\n                                         double gamma, boolean flip, int channels) {\n        assert srcBuf != dstBuf;\n        int w = Math.min(srcStep, dstStep);\n        int srcLine = srcBufferIndex, dstLine = dstBufferIndex;\n        int[] buffer = new int[channels];\n        while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) {\n            if (flip) {\n                srcBufferIndex = srcBuf.capacity() - srcLine - srcStep;\n            } else {\n                srcBufferIndex = srcLine;\n            }\n            dstBufferIndex = dstLine;\n            w = Math.min(Math.min(w, srcBuf.capacity() - srcBufferIndex), dstBuf.capacity() - dstBufferIndex);\n            if (channels > 1) {\n                for (int x = 0; x < w; x+=channels) {\n                    for (int z = 0; z < channels; z++) {\n                        int in = srcBuf.get(srcBufferIndex++);\n                        int out;\n                        if (gamma == 1.0) {\n                            out = (int)in;\n                        } else {\n                            out = (int)Math.round(Math.pow((double)in/Integer.MAX_VALUE, gamma)*Integer.MAX_VALUE);\n                        }\n                        buffer[z] = out;\n                    }\n                    for (int z = channels-1; z >= 0; z--) {\n                        dstBuf.put(dstBufferIndex++, buffer[z]);\n                    }\n                }\n            } else {\n                for (int x = 0; x < w; x++) {\n                    int in = srcBuf.get(srcBufferIndex++);\n                    int out;\n                    if (gamma == 1.0) {\n                        out = in;\n                    } else {\n                        out = (int)Math.round(Math.pow((double)in/Integer.MAX_VALUE, gamma)*Integer.MAX_VALUE);\n                    }\n                    dstBuf.put(dstBufferIndex++, out);\n                }\n            }\n            srcLine += srcStep;\n            dstLine += dstStep;\n        }\n    }\n    public static void flipCopyWithGamma(FloatBuffer srcBuf, int srcBufferIndex, int srcStep,\n                                         FloatBuffer dstBuf, int dstBufferIndex, int dstStep,\n                                         double gamma, boolean flip, int channels) {\n        assert srcBuf != dstBuf;\n        int w = Math.min(srcStep, dstStep);\n        int srcLine = srcBufferIndex, dstLine = dstBufferIndex;\n        float[] buffer = new float[channels];\n        while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) {\n            if (flip) {\n                srcBufferIndex = srcBuf.capacity() - srcLine - srcStep;\n            } else {\n                srcBufferIndex = srcLine;\n            }\n            dstBufferIndex = dstLine;\n            w = Math.min(Math.min(w, srcBuf.capacity() - srcBufferIndex), dstBuf.capacity() - dstBufferIndex);\n            if (channels > 1) {\n                for (int x = 0; x < w; x+=channels) {\n                    for (int z = 0; z < channels; z++) {\n                        float in = srcBuf.get(srcBufferIndex++);\n                        float out;\n                        if (gamma == 1.0) {\n                            out = in;\n                        } else {\n                            out = (float)Math.pow(in, gamma);\n                        }\n                        buffer[z] = out;\n                    }\n                    for (int z = channels-1; z >= 0; z--) {\n                        dstBuf.put(dstBufferIndex++, buffer[z]);\n                    }\n                }\n            } else {\n                for (int x = 0; x < w; x++) {\n                    float in = srcBuf.get(srcBufferIndex++);\n                    float out;\n                    if (gamma == 1.0) {\n                        out = in;\n                    } else {\n                        out = (float)Math.pow(in, gamma);\n                    }\n                    dstBuf.put(dstBufferIndex++, out);\n                }\n            }\n            srcLine += srcStep;\n            dstLine += dstStep;\n        }\n    }\n    public static void flipCopyWithGamma(DoubleBuffer srcBuf, int srcBufferIndex, int srcStep,\n                                         DoubleBuffer dstBuf, int dstBufferIndex, int dstStep,\n                                         double gamma, boolean flip, int channels) {\n        assert srcBuf != dstBuf;\n        int w = Math.min(srcStep, dstStep);\n        int srcLine = srcBufferIndex, dstLine = dstBufferIndex;\n        double[] buffer = new double[channels];\n        while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) {\n            if (flip) {\n                srcBufferIndex = srcBuf.capacity() - srcLine - srcStep;\n            } else {\n                srcBufferIndex = srcLine;\n            }\n            dstBufferIndex = dstLine;\n            w = Math.min(Math.min(w, srcBuf.capacity() - srcBufferIndex), dstBuf.capacity() - dstBufferIndex);\n            if (channels > 1) {\n                for (int x = 0; x < w; x+=channels) {\n                    for (int z = 0; z < channels; z++) {\n                        double in = srcBuf.get(srcBufferIndex++);\n                        double out;\n                        if (gamma == 1.0) {\n                            out = in;\n                        } else {\n                            out = Math.pow(in, gamma);\n                        }\n                        buffer[z] = out;\n                    }\n                    for (int z = channels-1; z >= 0; z--) {\n                        dstBuf.put(dstBufferIndex++, buffer[z]);\n                    }\n                }\n            } else {\n                for (int x = 0; x < w; x++) {\n                    double in = srcBuf.get(srcBufferIndex++);\n                    double out;\n                    if (gamma == 1.0) {\n                        out = in;\n                    } else {\n                        out = Math.pow(in, gamma);\n                    }\n                    dstBuf.put(dstBufferIndex++, out);\n                }\n            }\n            srcLine += srcStep;\n            dstLine += dstStep;\n        }\n    }\n\n    public static void applyGamma(Frame frame, double gamma) {\n        applyGamma(frame.image[0], frame.imageDepth, frame.imageStride, gamma);\n    }\n    public static void applyGamma(Buffer buffer, int depth, int stride, double gamma) {\n        if (gamma == 1.0) {\n            return;\n        }\n        switch (depth) {\n            case Frame.DEPTH_UBYTE:\n                flipCopyWithGamma(((ByteBuffer)buffer).asReadOnlyBuffer(), 0, stride, (ByteBuffer)buffer, 0, stride, false, gamma, false, 0);\n                break;\n            case Frame.DEPTH_BYTE:\n                flipCopyWithGamma(((ByteBuffer)buffer).asReadOnlyBuffer(), 0, stride, (ByteBuffer)buffer, 0, stride, true, gamma, false, 0);\n                break;\n            case Frame.DEPTH_USHORT:\n                flipCopyWithGamma(((ShortBuffer)buffer).asReadOnlyBuffer(), 0, stride, (ShortBuffer)buffer, 0, stride, false, gamma, false, 0);\n                break;\n            case Frame.DEPTH_SHORT:\n                flipCopyWithGamma(((ShortBuffer)buffer).asReadOnlyBuffer(), 0, stride, (ShortBuffer)buffer, 0, stride, true, gamma, false, 0);\n                break;\n            case Frame.DEPTH_INT:\n                flipCopyWithGamma(((IntBuffer)buffer).asReadOnlyBuffer(), 0, stride, (IntBuffer)buffer, 0, stride, gamma, false, 0);\n                break;\n            case Frame.DEPTH_FLOAT:\n                flipCopyWithGamma(((FloatBuffer)buffer).asReadOnlyBuffer(), 0, stride, (FloatBuffer)buffer, 0, stride, gamma, false, 0);\n                break;\n            case Frame.DEPTH_DOUBLE:\n                flipCopyWithGamma(((DoubleBuffer)buffer).asReadOnlyBuffer(), 0, stride, (DoubleBuffer)buffer, 0, stride, gamma, false, 0);\n                break;\n            default:\n                assert false;\n        }\n    }\n\n    public static void copy(Frame frame, BufferedImage bufferedImage) {\n        copy(frame, bufferedImage, 1.0);\n    }\n    public static void copy(Frame frame, BufferedImage bufferedImage, double gamma) {\n        copy(frame, bufferedImage, gamma, false, null);\n    }\n    public static void copy(Frame frame, BufferedImage bufferedImage, double gamma, boolean flipChannels, Rectangle roi) {\n        Buffer in = frame.image[0];\n        int bufferIndex = roi == null ? 0 : roi.y*frame.imageStride + roi.x*frame.imageChannels;\n        SampleModel sm = bufferedImage.getSampleModel();\n        Raster r       = bufferedImage.getRaster();\n        DataBuffer out = r.getDataBuffer();\n        int x = -r.getSampleModelTranslateX();\n        int y = -r.getSampleModelTranslateY();\n        int step = sm.getWidth()*sm.getNumBands();\n        int channels = sm.getNumBands();\n        if (sm instanceof ComponentSampleModel) {\n            step = ((ComponentSampleModel)sm).getScanlineStride();\n            channels = ((ComponentSampleModel)sm).getPixelStride();\n        } else if (sm instanceof SinglePixelPackedSampleModel) {\n            step = ((SinglePixelPackedSampleModel)sm).getScanlineStride();\n            channels = 1;\n        } else if (sm instanceof MultiPixelPackedSampleModel) {\n            step = ((MultiPixelPackedSampleModel)sm).getScanlineStride();\n            channels = ((MultiPixelPackedSampleModel)sm).getPixelBitStride()/8; // ??\n        }\n        int start = y*step + x*channels;\n\n        if (out instanceof DataBufferByte) {\n            byte[] a = ((DataBufferByte)out).getData();\n            flipCopyWithGamma((ByteBuffer)in, bufferIndex, frame.imageStride, ByteBuffer.wrap(a), start, step, false, gamma, false, flipChannels ? channels : 0);\n        } else if (out instanceof DataBufferDouble) {\n            double[] a = ((DataBufferDouble)out).getData();\n            flipCopyWithGamma((DoubleBuffer)in, bufferIndex, frame.imageStride, DoubleBuffer.wrap(a), start, step, gamma, false, flipChannels ? channels : 0);\n        } else if (out instanceof DataBufferFloat) {\n            float[] a = ((DataBufferFloat)out).getData();\n            flipCopyWithGamma((FloatBuffer)in, bufferIndex, frame.imageStride, FloatBuffer.wrap(a), start, step, gamma, false, flipChannels ? channels : 0);\n        } else if (out instanceof DataBufferInt) {\n            int[] a = ((DataBufferInt)out).getData();\n            int stride = frame.imageStride;\n            if (in instanceof ByteBuffer) {\n                in = ((ByteBuffer)in).order(flipChannels ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN).asIntBuffer();\n                stride /= 4;\n            }\n            flipCopyWithGamma((IntBuffer)in, bufferIndex, stride, IntBuffer.wrap(a), start, step, gamma, false, flipChannels ? channels : 0);\n        } else if (out instanceof DataBufferShort) {\n            short[] a = ((DataBufferShort)out).getData();\n            flipCopyWithGamma((ShortBuffer)in, bufferIndex, frame.imageStride, ShortBuffer.wrap(a), start, step, true, gamma, false, flipChannels ? channels : 0);\n        } else if (out instanceof DataBufferUShort) {\n            short[] a = ((DataBufferUShort)out).getData();\n            flipCopyWithGamma((ShortBuffer)in, bufferIndex, frame.imageStride, ShortBuffer.wrap(a), start, step, false, gamma, false, flipChannels ? channels : 0);\n        } else {\n            assert false;\n        }\n    }\n\n    public static void copy(BufferedImage image, Frame frame) {\n        copy(image, frame, 1.0);\n    }\n    public static void copy(BufferedImage image, Frame frame, double gamma) {\n        copy(image, frame, gamma, false, null);\n    }\n    public static void copy(BufferedImage image, Frame frame, double gamma, boolean flipChannels, Rectangle roi) {\n        Buffer out = frame.image[0];\n        int bufferIndex = roi == null ? 0 : roi.y*frame.imageStride + roi.x*frame.imageChannels;\n        SampleModel sm = image.getSampleModel();\n        Raster r       = image.getRaster();\n        DataBuffer in  = r.getDataBuffer();\n        int x = -r.getSampleModelTranslateX();\n        int y = -r.getSampleModelTranslateY();\n        int step = sm.getWidth()*sm.getNumBands();\n        int channels = sm.getNumBands();\n        if (sm instanceof ComponentSampleModel) {\n            step = ((ComponentSampleModel)sm).getScanlineStride();\n            channels = ((ComponentSampleModel)sm).getPixelStride();\n        } else if (sm instanceof SinglePixelPackedSampleModel) {\n            step = ((SinglePixelPackedSampleModel)sm).getScanlineStride();\n            channels = 1;\n        } else if (sm instanceof MultiPixelPackedSampleModel) {\n            step = ((MultiPixelPackedSampleModel)sm).getScanlineStride();\n            channels = ((MultiPixelPackedSampleModel)sm).getPixelBitStride()/8; // ??\n        }\n        int start = y*step + x*channels;\n\n        if (in instanceof DataBufferByte) {\n            byte[] a = ((DataBufferByte)in).getData();\n            flipCopyWithGamma(ByteBuffer.wrap(a), start, step, (ByteBuffer)out, bufferIndex, frame.imageStride, false, gamma, false, flipChannels ? channels : 0);\n        } else if (in instanceof DataBufferDouble) {\n            double[] a = ((DataBufferDouble)in).getData();\n            flipCopyWithGamma(DoubleBuffer.wrap(a), start, step, (DoubleBuffer)out, bufferIndex, frame.imageStride, gamma, false, flipChannels ? channels : 0);\n        } else if (in instanceof DataBufferFloat) {\n            float[] a = ((DataBufferFloat)in).getData();\n            flipCopyWithGamma(FloatBuffer.wrap(a), start, step, (FloatBuffer)out, bufferIndex, frame.imageStride, gamma, false, flipChannels ? channels : 0);\n        } else if (in instanceof DataBufferInt) {\n            int[] a = ((DataBufferInt)in).getData();\n            int stride = frame.imageStride;\n            if (out instanceof ByteBuffer) {\n                out = ((ByteBuffer)out).order(flipChannels ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN).asIntBuffer();\n                stride /= 4;\n            }\n            flipCopyWithGamma(IntBuffer.wrap(a), start, step, (IntBuffer)out, bufferIndex, stride, gamma, false, flipChannels ? channels : 0);\n        } else if (in instanceof DataBufferShort) {\n            short[] a = ((DataBufferShort)in).getData();\n            flipCopyWithGamma(ShortBuffer.wrap(a), start, step, (ShortBuffer)out, bufferIndex, frame.imageStride, true, gamma, false, flipChannels ? channels : 0);\n        } else if (in instanceof DataBufferUShort) {\n            short[] a = ((DataBufferUShort)in).getData();\n            flipCopyWithGamma(ShortBuffer.wrap(a), start, step, (ShortBuffer)out, bufferIndex, frame.imageStride, false, gamma, false, flipChannels ? channels : 0);\n        } else {\n            assert false;\n        }\n    }\n\n    protected BufferedImage bufferedImage = null;\n    public static int getBufferedImageType(Frame frame) {\n        // precanned BufferedImage types are confusing... in practice though,\n        // they all use the sRGB color model when blitting:\n        //     http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5051418\n        // and we should use them because they are *A LOT* faster with Java 2D.\n        // workaround: do gamma correction ourselves (\"gamma\" parameter)\n        //             since we'll never use getRGB() and setRGB(), right?\n        int type = BufferedImage.TYPE_CUSTOM;\n        if (frame.imageChannels == 1) {\n            if (frame.imageDepth == Frame.DEPTH_UBYTE || frame.imageDepth == Frame.DEPTH_BYTE) {\n                type = BufferedImage.TYPE_BYTE_GRAY;\n            } else if (frame.imageDepth == Frame.DEPTH_USHORT) {\n                type = BufferedImage.TYPE_USHORT_GRAY;\n            }\n        } else if (frame.imageChannels == 3) {\n            if (frame.imageDepth == Frame.DEPTH_UBYTE || frame.imageDepth == Frame.DEPTH_BYTE) {\n                type = BufferedImage.TYPE_3BYTE_BGR;\n            }\n        } else if (frame.imageChannels == 4) {\n            // The channels end up reversed of what we need for OpenCL.\n            // We work around this in copyTo() and copyFrom() by\n            // inversing the channels to let us use RGBA in our IplImage.\n            if (frame.imageDepth == Frame.DEPTH_UBYTE || frame.imageDepth == Frame.DEPTH_BYTE) {\n                type = BufferedImage.TYPE_4BYTE_ABGR;\n            }\n        }\n        return type;\n    }\n    public BufferedImage getBufferedImage(Frame frame) {\n        return getBufferedImage(frame, 1.0);\n    }\n    public BufferedImage getBufferedImage(Frame frame, double gamma) {\n        return getBufferedImage(frame, gamma, false, null);\n    }\n    public BufferedImage getBufferedImage(Frame frame, double gamma, boolean flipChannels, ColorSpace cs) {\n        if (frame == null || frame.image == null) {\n            return null;\n        }\n        int type = getBufferedImageType(frame);\n\n        if (bufferedImage == null || bufferedImage.getWidth() != frame.imageWidth\n                || bufferedImage.getHeight() != frame.imageHeight || bufferedImage.getType() != type) {\n            bufferedImage = type == BufferedImage.TYPE_CUSTOM || cs != null ? null\n                    : new BufferedImage(frame.imageWidth, frame.imageHeight, type);\n        }\n\n        if (bufferedImage == null) {\n            boolean alpha = false;\n            int[] offsets = null;\n            if (frame.imageChannels == 1) {\n                alpha = false;\n                if (cs == null) {\n                    cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);\n                }\n                offsets = new int[] {0};\n            } else if (frame.imageChannels == 3) {\n                alpha = false;\n                if (cs == null) {\n                    cs = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);\n                }\n                // raster in \"BGR\" order like OpenCV..\n                offsets = new int[] {2, 1, 0};\n            } else if (frame.imageChannels == 4) {\n                alpha = true;\n                if (cs == null) {\n                    cs = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);\n                }\n                // raster in \"RGBA\" order for OpenCL.. alpha needs to be last\n                offsets = new int[] {0, 1, 2, 3};\n            } else {\n                assert false;\n            }\n\n            ColorModel cm = null;\n            WritableRaster wr = null;\n            if (frame.imageDepth == Frame.DEPTH_UBYTE || frame.imageDepth == Frame.DEPTH_BYTE) {\n                cm = new ComponentColorModel(cs, alpha,\n                        false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);\n                wr = Raster.createWritableRaster(new ComponentSampleModel(\n                        DataBuffer.TYPE_BYTE, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride,\n                        offsets), null);\n            } else if (frame.imageDepth == Frame.DEPTH_USHORT) {\n                cm = new ComponentColorModel(cs, alpha,\n                        false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);\n                wr = Raster.createWritableRaster(new ComponentSampleModel(\n                        DataBuffer.TYPE_USHORT, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride,\n                        offsets), null);\n            } else if (frame.imageDepth == Frame.DEPTH_SHORT) {\n                cm = new ComponentColorModel(cs, alpha,\n                        false, Transparency.OPAQUE, DataBuffer.TYPE_SHORT);\n                wr = Raster.createWritableRaster(new ComponentSampleModel(\n                        DataBuffer.TYPE_SHORT, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride,\n                        offsets), null);\n            } else if (frame.imageDepth == Frame.DEPTH_INT) {\n                cm = new ComponentColorModel(cs, alpha,\n                        false, Transparency.OPAQUE, DataBuffer.TYPE_INT);\n                wr = Raster.createWritableRaster(new ComponentSampleModel(\n                        DataBuffer.TYPE_INT, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride,\n                        offsets), null);\n            } else if (frame.imageDepth == Frame.DEPTH_FLOAT) {\n                cm = new ComponentColorModel(cs, alpha,\n                        false, Transparency.OPAQUE, DataBuffer.TYPE_FLOAT);\n                wr = Raster.createWritableRaster(new ComponentSampleModel(\n                        DataBuffer.TYPE_FLOAT, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride,\n                        offsets), null);\n            } else if (frame.imageDepth == Frame.DEPTH_DOUBLE) {\n                cm = new ComponentColorModel(cs, alpha,\n                        false, Transparency.OPAQUE, DataBuffer.TYPE_DOUBLE);\n                wr = Raster.createWritableRaster(new ComponentSampleModel(\n                        DataBuffer.TYPE_DOUBLE, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride,\n                        offsets), null);\n            } else {\n                assert false;\n            }\n\n            bufferedImage = new BufferedImage(cm, wr, false, null);\n        }\n\n        if (bufferedImage != null) {\n            copy(frame, bufferedImage, gamma, flipChannels, null);\n        }\n\n        return bufferedImage;\n    }\n\n    /**\n     * Returns a Frame based on a BufferedImage.\n     */\n    public Frame getFrame(BufferedImage image) {\n        return getFrame(image, 1.0);\n    }\n    /**\n     * Returns a Frame based on a BufferedImage, and given gamma.\n     */\n    public Frame getFrame(BufferedImage image, double gamma) {\n        return getFrame(image, gamma, false);\n    }\n    /**\n     * Returns a Frame based on a BufferedImage, given gamma, and inverted channels flag.\n     */\n    public Frame getFrame(BufferedImage image, double gamma, boolean flipChannels) {\n        if (image == null) {\n            return null;\n        }\n        SampleModel sm = image.getSampleModel();\n        int depth = 0, numChannels = sm.getNumBands();\n        switch (image.getType()) {\n            case BufferedImage.TYPE_INT_RGB:\n            case BufferedImage.TYPE_INT_ARGB:\n            case BufferedImage.TYPE_INT_ARGB_PRE:\n            case BufferedImage.TYPE_INT_BGR:\n                depth = Frame.DEPTH_UBYTE;\n                numChannels = 4;\n                break;\n        }\n        if (depth == 0 || numChannels == 0) {\n            switch (sm.getDataType()) {\n                case DataBuffer.TYPE_BYTE:   depth = Frame.DEPTH_UBYTE;  break;\n                case DataBuffer.TYPE_USHORT: depth = Frame.DEPTH_USHORT; break;\n                case DataBuffer.TYPE_SHORT:  depth = Frame.DEPTH_SHORT;  break;\n                case DataBuffer.TYPE_INT:    depth = Frame.DEPTH_INT;    break;\n                case DataBuffer.TYPE_FLOAT:  depth = Frame.DEPTH_FLOAT;  break;\n                case DataBuffer.TYPE_DOUBLE: depth = Frame.DEPTH_DOUBLE; break;\n                default: assert false;\n            }\n        }\n        if (frame == null || frame.imageWidth != image.getWidth() || frame.imageHeight != image.getHeight()\n                || frame.imageDepth != depth || frame.imageChannels != numChannels) {\n            if (frame != null) {\n                frame.close();\n            }\n            frame = new Frame(image.getWidth(), image.getHeight(), depth, numChannels);\n        }\n        copy(image, frame, gamma, flipChannels, null);\n        return frame;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/Java2DFrameUtils.java",
    "content": "package org.bytedeco.javacv;\n\nimport java.awt.image.BufferedImage;\nimport java.awt.image.ColorModel;\nimport java.awt.image.WritableRaster;\n\nimport org.bytedeco.javacv.Frame;\nimport org.bytedeco.javacv.Java2DFrameConverter;\nimport org.bytedeco.javacv.OpenCVFrameConverter;\nimport org.bytedeco.opencv.opencv_core.IplImage;\nimport org.bytedeco.opencv.opencv_core.Mat;\n\n/**\n * Convenience class for performing various conversions between Mat, IplImage,\n * BufferedImage and Frame objects. Methods are synchronized because the\n * underlying JavaCV converters aren't safe for concurrent access.\n *\n * All created Frame, Mat, IplImages and BufferedImages are cloned internally\n * after creation so that their memory locations remain valid after the\n * converters which created them are closed or garbage collected. This is safer\n * for the caller, but may be slower.\n *\n * If performance is critical, use the *FrameConverter classes directly, after\n * reading about the image validity constraints (eg, images data is only valid\n * until next call to the converter).\n *\n * @see <a href=\"https://groups.google.com/forum/#!topic/javacv/sSgY9e-IDRA\">Java2DFrameConverter crashes JVM</a>\n * @see FrameConverter\n *\n * @author Sam West, Joel Wong, Sep 2016.\n *\n */\npublic class Java2DFrameUtils {\n\n    private static OpenCVFrameConverter.ToIplImage  iplConv = new OpenCVFrameConverter.ToIplImage();\n    private static OpenCVFrameConverter.ToMat       matConv = new OpenCVFrameConverter.ToMat();\n    private static Java2DFrameConverter             biConv  = new Java2DFrameConverter();\n\n    /**\n     * Clones (deep copies the data) of a {@link BufferedImage}. Necessary when\n     * converting to BufferedImages from JavaCV types to avoid re-using the same\n     * memory locations.\n     *\n     * @param source\n     * @return\n     */\n    public static BufferedImage deepCopy(BufferedImage source) {\n        return Java2DFrameConverter.cloneBufferedImage(source);\n    }\n\n    public synchronized static BufferedImage toBufferedImage(IplImage src) {\n        try (Frame f = iplConv.convert(src).clone()) {\n            return deepCopy(biConv.getBufferedImage(f));\n        }\n    }\n\n    public synchronized static BufferedImage toBufferedImage(Mat src) {\n        try (Frame f = matConv.convert(src).clone()) {\n            return deepCopy(biConv.getBufferedImage(f));\n        }\n    }\n\n    public synchronized static BufferedImage toBufferedImage(Frame src) {\n        try (Frame f = src.clone()) {\n            return deepCopy(biConv.getBufferedImage(f));\n        }\n    }\n\n    public synchronized static IplImage toIplImage(Mat src){\n        return iplConv.convertToIplImage(matConv.convert(src)).clone();\n    }\n\n    public synchronized static IplImage toIplImage(Frame src){\n        return iplConv.convertToIplImage(src).clone();\n    }\n\n    public synchronized static IplImage toIplImage(BufferedImage src){\n        return iplConv.convertToIplImage(biConv.convert(src)).clone();\n    }\n\n    public synchronized static Mat toMat(IplImage src){\n        return matConv.convertToMat(iplConv.convert(src).clone());\n    }\n\n    public synchronized static Mat toMat(Frame src){\n        return matConv.convertToMat(src).clone();\n    }\n\n    public synchronized static Mat toMat(BufferedImage src){\n        return matConv.convertToMat(biConv.convert(src)).clone();\n    }\n\n    public synchronized static Frame toFrame(IplImage src){\n        return iplConv.convert(src).clone();\n    }\n\n    public synchronized static Frame toFrame(Mat src){\n        return matConv.convert(src).clone();\n    }\n\n    public synchronized static Frame toFrame(BufferedImage src){\n        return biConv.convert(src).clone();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/JavaCV.java",
    "content": "/*\n * Copyright (C) 2009-2016 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.ByteBuffer;\nimport java.nio.DoubleBuffer;\nimport java.nio.FloatBuffer;\nimport java.nio.IntBuffer;\nimport java.nio.ShortBuffer;\nimport java.util.Arrays;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.global.opencv_core;\nimport org.bytedeco.opencv.global.opencv_imgproc;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class JavaCV {\n\n    public static final double\n            SQRT2 = 1.41421356237309504880,\n            FLT_EPSILON = 1.19209290e-7F,\n            DBL_EPSILON = 2.2204460492503131e-16;\n\n    /** returns the distance^2 between the line (x1, y1) (x2, y2) and the point (x3, y3) */\n    public static double distanceToLine(double x1, double y1, double x2, double y2, double x3, double y3) {\n        double dx = x2 - x1;\n        double dy = y2 - y1;\n        double d2 = dx*dx + dy*dy;\n        double u = ((x3 - x1)*dx + (y3 - y1)*dy) / d2;\n\n        double x = x1 + u*dx;\n        double y = y1 + u*dy;\n\n        dx = x - x3;\n        dy = y - y3;\n        return dx*dx + dy*dy;\n    }\n\n    /**\n     * returns the largest rectangle of given aspect ratio and angle,\n     * bounded by the contour and sharing the same centroid\n     */\n    private static ThreadLocal<CvMoments> moments = CvMoments.createThreadLocal();\n    public static CvBox2D boundedRect(CvMat contour, CvBox2D box) {\n        int contourLength = contour.length();\n        CvMoments m = moments.get();\n        cvMoments(contour, m, 0);\n        double inv_m00 = 1 / m.m00();\n        double centerX = m.m10() * inv_m00;\n        double centerY = m.m01() * inv_m00;\n\n        float[] pts = new float[8];\n        CvPoint2D32f center = box.center();\n        CvSize2D32f size = box.size();\n        center.put(centerX, centerY);\n        cvBoxPoints(box, pts);\n\n        float scale = Float.POSITIVE_INFINITY;\n        for (int i = 0; i < 4; i++) {\n            double x1 = centerX,  y1 = centerY,\n                   x2 = pts[2*i], y2 = pts[2*i + 1];\n            for (int j = 0; j < contourLength; j++) {\n                int k = (j + 1) % contourLength;\n                double x3 = contour.get(2*j), y3 = contour.get(2*j + 1),\n                       x4 = contour.get(2*k), y4 = contour.get(2*k + 1);\n                double d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);\n                double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3))/d,\n                       ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3))/d;\n                if (ub >= 0 && ub <= 1 && ua >= 0 && ua < scale) {\n                    scale = (float)ua;\n                }\n            }\n        }\n        size.width(scale*size.width()).height(scale*size.height());\n        return box;\n    }\n\n    /**\n     * Similar to cvBoundingRect(), but can also pad the output with some extra\n     * pixels, useful to use as ROI for operations with interpolation. Further\n     * aligns the region to specified boundaries, for easier vectorization and\n     * subsampling, and also uses on input the rect argument as a maximum boundary.\n     */\n    public static CvRect boundingRect(double[] contour, CvRect rect,\n            int padX, int padY, int alignX, int alignY) {\n        double minX = contour[0];\n        double minY = contour[1];\n        double maxX = contour[0];\n        double maxY = contour[1];\n        for (int i = 1; i < contour.length/2; i++) {\n            double x = contour[2*i  ];\n            double y = contour[2*i+1];\n            minX = Math.min(minX, x);\n            minY = Math.min(minY, y);\n            maxX = Math.max(maxX, x);\n            maxY = Math.max(maxY, y);\n        }\n        int x = (int)Math.floor(Math.max(rect.x(), minX-padX)/alignX)*alignX;\n        int y = (int)Math.floor(Math.max(rect.y(), minY-padY)/alignY)*alignY;\n        int width  = (int)Math.ceil(Math.min(rect.width(),  maxX+padX)/alignX)*alignX - x;\n        int height = (int)Math.ceil(Math.min(rect.height(), maxY+padY)/alignY)*alignY - y;\n\n        return rect.x(x).y(y).width(Math.max(0, width)).height(Math.max(0, height));\n    }\n\n    private static ThreadLocal<CvMat>\n            A8x8 = CvMat.createThreadLocal(8, 8),\n            b8x1 = CvMat.createThreadLocal(8, 1),\n            x8x1 = CvMat.createThreadLocal(8, 1);\n    /**\n     * this is basically cvGetPerspectiveTransform() using CV_LU instead of\n     * CV_SVD, because the latter gives inaccurate results...\n     * Consider using {@link opencv_imgproc#getPerspectiveTransform} instead.\n     */\n    public static CvMat getPerspectiveTransform(double[] src, double[] dst, CvMat map_matrix) {\n        // creating and releasing matrices via NIO here in this function\n        // can easily become a bottleneck, so we use ThreadLocal references\n        CvMat A = A8x8.get();\n        CvMat b = b8x1.get();\n        CvMat x = x8x1.get();\n\n        for(int i = 0; i < 4; ++i ) {\n            A.put(i*8+0, src[i*2]);   A.put((i+4)*8+3, src[i*2]);\n            A.put(i*8+1, src[i*2+1]); A.put((i+4)*8+4, src[i*2+1]);\n            A.put(i*8+2, 1);          A.put((i+4)*8+5, 1);\n            A.put(i*8+3, 0);          A.put(i*8+4, 0); A.put(i*8+5, 0);\n            A.put((i+4)*8+0, 0);  A.put((i+4)*8+1, 0); A.put((i+4)*8+2, 0);\n\n            A.put(i*8+6,     -src[i*2]  *dst[i*2]);\n            A.put(i*8+7,     -src[i*2+1]*dst[i*2]);\n            A.put((i+4)*8+6, -src[i*2]  *dst[i*2+1]);\n            A.put((i+4)*8+7, -src[i*2+1]*dst[i*2+1]);\n\n            b.put(i,   dst[i*2]);\n            b.put(i+4, dst[i*2+1]);\n        }\n        cvSolve(A, b, x, CV_LU);\n        map_matrix.put(x.get());\n        map_matrix.put(8, 1);\n\n        return map_matrix;\n    }\n\n    /** Consider using {@link opencv_core#perspectiveTransform} instead. */\n    public static void perspectiveTransform(double[] src, double[] dst, CvMat map_matrix) {\n        double[] mat = map_matrix.get();\n        for (int j = 0; j < src.length; j += 2) {\n            double x = src[j], y = src[j + 1];\n            double w = x*mat[6] + y*mat[7] + mat[8];\n\n            if (Math.abs(w) > FLT_EPSILON) {\n                w = 1.0/w;\n                dst[j] = (x*mat[0] + y*mat[1] + mat[2])*w;\n                dst[j+1] = (x*mat[3] + y*mat[4] + mat[5])*w;\n            } else {\n                dst[j] = dst[j+1] = 0;\n            }\n        }\n    }\n\n    private static ThreadLocal<CvMat>\n            A3x3 = CvMat.createThreadLocal(3, 3), b3x1 = CvMat.createThreadLocal(3, 1);\n    public static CvMat getPlaneParameters(double[] src, double[] dst,\n            CvMat invSrcK, CvMat dstK, CvMat R, CvMat t, CvMat n) {\n        CvMat A = A3x3.get(), b = b3x1.get();\n\n        double[] x = new double[6], y = new double[6];\n        perspectiveTransform(src, x, invSrcK);\n        cvInvert(dstK, A);\n        perspectiveTransform(dst, y, A);\n\n        for (int i = 0; i < 3; i++) {\n            A.put(i, 0, (t.get(2)*y[i*2] - t.get(0))*x[i*2  ]);\n            A.put(i, 1, (t.get(2)*y[i*2] - t.get(0))*x[i*2+1]);\n            A.put(i, 2,  t.get(2)*y[i*2] - t.get(0));\n\n            b.put(i, (R.get(2, 0)*x[i*2] + R.get(2, 1)*x[i*2+1] + R.get(2, 2))*y[i*2] -\n                     (R.get(0, 0)*x[i*2] + R.get(0, 1)*x[i*2+1] + R.get(0, 2)));\n        }\n        cvSolve(A, b, n, CV_LU);\n\n        return n;\n    }\n\n    private static ThreadLocal<CvMat>\n            n3x1 = CvMat.createThreadLocal(3, 1);\n    public static CvMat getPerspectiveTransform(double[] src, double[] dst, \n            CvMat invSrcK, CvMat dstK, CvMat R, CvMat t, CvMat H) {\n        CvMat n = n3x1.get();\n        getPlaneParameters(src, dst, invSrcK, dstK, R, t, n);\n\n        // H = R - t*n^T\n        cvGEMM(t, n, -1,  R, 1,  H, CV_GEMM_B_T);\n        // H = dstK * H * srcK^-1\n        cvMatMul(dstK, H, H);\n        cvMatMul(H, invSrcK, H);\n\n        return H;\n    }\n\n    private static ThreadLocal<CvMat>\n            H3x3 = CvMat.createThreadLocal(3, 3);\n    public static void perspectiveTransform(double[] src, double[] dst,\n            CvMat invSrcK, CvMat dstK, CvMat R, CvMat t, CvMat n, boolean invert) {\n        CvMat H = H3x3.get();\n\n        // H = R - t*n^T\n        cvGEMM(t, n, -1,  R, 1,  H, CV_GEMM_B_T);\n\n        // H = dstK * H * srcK^-1\n        cvMatMul(dstK, H, H);\n        cvMatMul(H, invSrcK, H);\n        if (invert) {\n            cvInvert(H, H);\n        }\n        perspectiveTransform(src, dst, H);\n    }\n\n    private static ThreadLocal<CvMat>\n            M3x2 = CvMat.createThreadLocal(3, 2), S2x2 = CvMat.createThreadLocal(2, 2),\n            U3x2 = CvMat.createThreadLocal(3, 2), V2x2 = CvMat.createThreadLocal(2, 2);\n    /**\n     * Algorithms for Plane-Based Pose Estimation, Peter Sturm\n     * This assumes plane parameters n == z axis.\n     */\n    public static void HtoRt(CvMat H, CvMat R, CvMat t) {\n        CvMat M = M3x2.get(), S = S2x2.get(),\n              U = U3x2.get(), V = V2x2.get();\n        M.put(H.get(0), H.get(1),\n              H.get(3), H.get(4),\n              H.get(6), H.get(7));\n        cvSVD(M, S, U, V, CV_SVD_V_T);\n\n        double lambda = S.get(3);\n        t.put(H.get(2)/lambda, H.get(5)/lambda, H.get(8)/lambda);\n\n        cvMatMul(U, V, M);\n        R.put(M.get(0), M.get(1), M.get(2)*M.get(5) - M.get(3)*M.get(4),\n              M.get(2), M.get(3), M.get(1)*M.get(4) - M.get(0)*M.get(5),\n              M.get(4), M.get(5), M.get(0)*M.get(3) - M.get(1)*M.get(2));\n    }\n\n    private static ThreadLocal<CvMat>\n            R13x3 = CvMat.createThreadLocal(3, 3), R23x3 = CvMat.createThreadLocal(3, 3),\n            t13x1 = CvMat.createThreadLocal(3, 1), t23x1 = CvMat.createThreadLocal(3, 1),\n            n13x1 = CvMat.createThreadLocal(3, 1), n23x1 = CvMat.createThreadLocal(3, 1),\n            H13x3 = CvMat.createThreadLocal(3, 3), H23x3 = CvMat.createThreadLocal(3, 3);\n    public static double HnToRt(CvMat H, CvMat n, CvMat R, CvMat t) {\n        CvMat S = S3x3.get(), U = U3x3.get(), V = V3x3.get();\n        cvSVD(H, S, U, V, 0);\n\n        CvMat R1 = R13x3.get(),  R2 = R23x3.get(),\n              t1 = t13x1.get(),  t2 = t23x1.get(),\n              n1 = n13x1.get(),  n2 = n23x1.get(),\n              H1 = H13x3.get(),  H2 = H23x3.get();\n        double zeta = homogToRt(S, U, V, R1, t1, n1, R2, t2, n2);\n\n        // H = (R^-1 * H)/s2\n        cvGEMM(R1, H, 1/S.get(4),  null, 0,  H1, CV_GEMM_A_T);\n        cvGEMM(R2, H, 1/S.get(4),  null, 0,  H2, CV_GEMM_A_T);\n\n        // H = H - I\n        H1.put(0, H1.get(0)-1); H1.put(4, H1.get(4)-1); H1.put(8, H1.get(8)-1);\n        H2.put(0, H2.get(0)-1); H2.put(4, H2.get(4)-1); H2.put(8, H2.get(8)-1);\n\n        // Now H should ~= -tn^T, so extract \"average\" t\n        double d   =    Math.abs   (n.get(0)) + Math.abs   (n.get(1)) + Math.abs   (n.get(2));\n        double s[] = { -Math.signum(n.get(0)), -Math.signum(n.get(1)), -Math.signum(n.get(2)) };\n        t1.put(0.0, 0.0, 0.0);\n        t2.put(0.0, 0.0, 0.0);\n        for (int i = 0; i < 3; i++) {\n            t1.put(0, t1.get(0) + s[i]*H1.get(i)  /d);\n            t1.put(1, t1.get(1) + s[i]*H1.get(i+3)/d);\n            t1.put(2, t1.get(2) + s[i]*H1.get(i+6)/d);\n\n            t2.put(0, t2.get(0) + s[i]*H2.get(i)  /d);\n            t2.put(1, t2.get(1) + s[i]*H2.get(i+3)/d);\n            t2.put(2, t2.get(2) + s[i]*H2.get(i+6)/d);\n        }\n\n        // H = H + tn^T\n        cvGEMM(t1, n, 1,  H1, 1,  H1, CV_GEMM_B_T);\n        cvGEMM(t2, n, 1,  H2, 1,  H2, CV_GEMM_B_T);\n\n        // take what's left as the error of the model,\n        // this either indicates inaccurate camera matrix K or normal vector n\n        double err1 = cvNorm(H1);\n        double err2 = cvNorm(H2);\n\n        double err;\n        if (err1 < err2) {\n            if (R != null) {\n                R.put(R1);\n            }\n            if (t != null) {\n                t.put(t1);\n            }\n            err = err1;\n        } else {\n            if (R != null) {\n                R.put(R2);\n            }\n            if (t != null) {\n                t.put(t2);\n            }\n            err = err2;\n        }\n\n        return err;\n    }\n\n    private static ThreadLocal<CvMat>\n            S3x3 = CvMat.createThreadLocal(3, 3),\n            U3x3 = CvMat.createThreadLocal(3, 3),\n            V3x3 = CvMat.createThreadLocal(3, 3);\n    /**\n     * Ported to Java/OpenCV from\n     * Bill Triggs. Autocalibration from Planar Scenes. In 5th European Conference\n     * on Computer Vision (ECCV ’98), volume I, pages 89–105. Springer-Verlag, 1998.\n     */\n    public static double homogToRt(CvMat H,\n            CvMat R1, CvMat t1, CvMat n1,\n            CvMat R2, CvMat t2, CvMat n2) {\n        CvMat S = S3x3.get(), U = U3x3.get(), V = V3x3.get();\n        cvSVD(H, S, U, V, 0);\n        double zeta = homogToRt(S, U, V, R1, t1, n1, R2, t2, n2);\n        return zeta;\n    }\n    public static double homogToRt(CvMat S, CvMat U, CvMat V,\n            CvMat R1, CvMat t1, CvMat n1,\n            CvMat R2, CvMat t2, CvMat n2) {\n        double s1 = S.get(0)/S.get(4);\n        double s3 = S.get(8)/S.get(4);\n        double zeta = s1-s3;\n        double a1 = Math.sqrt(1 - s3*s3);\n        double b1 = Math.sqrt(s1*s1 - 1);\n        double[] ab = unitize(a1, b1);\n        double[] cd = unitize(1+s1*s3, a1*b1);\n        double[] ef = unitize(-ab[1]/s1, -ab[0]/s3);\n\n        R1.put(cd[0],0,cd[1], 0,1,0, -cd[1],0,cd[0]);\n        cvGEMM(U , R1, 1,  null, 0,  R1, 0);\n        cvGEMM(R1, V,  1,  null, 0,  R1, CV_GEMM_B_T);\n\n        R2.put(cd[0],0,-cd[1], 0,1,0, cd[1],0,cd[0]);\n        cvGEMM(U , R2, 1,  null, 0,  R2, 0);\n        cvGEMM(R2, V,  1,  null, 0,  R2, CV_GEMM_B_T);\n\n        double[] v1 = { V.get(0), V.get(3), V.get(6) };\n        double[] v3 = { V.get(2), V.get(5), V.get(8) };\n        double sign1 = 1, sign2 = 1;\n        for (int i = 2; i >= 0; i--) {\n            n1.put(i, sign1*(ab[1]*v1[i] - ab[0]*v3[i]));\n            n2.put(i, sign2*(ab[1]*v1[i] + ab[0]*v3[i]));\n            t1.put(i, sign1*(ef[0]*v1[i] + ef[1]*v3[i]));\n            t2.put(i, sign2*(ef[0]*v1[i] - ef[1]*v3[i]));\n            if (i == 2) {\n                if (n1.get(2) < 0) {\n                    n1.put(2, -n1.get(2));\n                    t1.put(2, -t1.get(2));\n                    sign1 = -1;\n                }\n                if (n2.get(2) < 0) {\n                    n2.put(2, -n2.get(2));\n                    t2.put(2, -t2.get(2));\n                    sign2 = -1;\n                }\n            }\n        }\n\n        return zeta;\n    }\n\n    public static double[] unitize(double a, double b) {\n        double norm = Math.sqrt(a*a + b*b);\n        if (norm > FLT_EPSILON) {\n            a = a / norm;\n            b = b / norm;\n        }\n        return new double[] { a, b };\n    }\n\n    /** more sophisticated than cvAdaptiveThreshold() */\n    public static void adaptiveThreshold(IplImage srcImage, final IplImage sumImage,\n            final IplImage sqSumImage, final IplImage dstImage, final boolean invert,\n            final int windowMax, final int windowMin, final double varMultiplier, final double k) {\n        final int w = srcImage.width();\n        final int h = srcImage.height();\n        final int srcChannels = srcImage.nChannels();\n        final int srcDepth = srcImage.depth();\n        final int dstDepth = dstImage.depth();\n\n        if (srcChannels > 1 && dstDepth == IPL_DEPTH_8U) {\n            cvCvtColor(srcImage, dstImage, srcChannels == 4 ? CV_RGBA2GRAY : CV_BGR2GRAY);\n            srcImage = dstImage;\n        }\n\n        final ByteBuffer srcBuf = srcImage.getByteBuffer();\n        final ByteBuffer dstBuf = dstImage.getByteBuffer();\n        final DoubleBuffer sumBuf = sumImage.getDoubleBuffer();\n        final DoubleBuffer sqSumBuf = sqSumImage.getDoubleBuffer();\n        final int srcStep = srcImage.widthStep();\n        final int dstStep = dstImage.widthStep();\n        final int sumStep = sumImage.widthStep();\n        final int sqSumStep = sqSumImage.widthStep();\n\n        // compute integral images\n        cvIntegral(srcImage, sumImage, sqSumImage, null);\n\n        // try to detect a reasonable maximum and minimum intensity\n        // for thresholds instead of simply 0 and 255...\n        double totalMean = sumBuf.get((h-1)*sumStep/8 + (w-1)) -\n                           sumBuf.get((h-1)*sumStep/8) -\n                           sumBuf.get(w-1) + sumBuf.get(0);\n        totalMean /= w*h;\n        double totalSqMean = sqSumBuf.get((h-1)*sqSumStep/8 + (w-1)) -\n                             sqSumBuf.get((h-1)*sqSumStep/8) -\n                             sqSumBuf.get(w-1) + sqSumBuf.get(0);\n        totalSqMean /= w*h;\n        double totalVar = totalSqMean - totalMean*totalMean;\n//double totalDev = Math.sqrt(totalVar);\n//System.out.println(totalDev);\n        final double targetVar = totalVar*varMultiplier;\n\n        //for (int y = 0; y < h; y++) {\n        Parallel.loop(0, h, new Parallel.Looper() {\n        public void loop(int from, int to, int looperID) {\n            for (int y = from; y < to; y++) {\n                for (int x = 0; x < w; x++) {\n                    double var = 0, mean = 0, sqMean = 0;\n                    int upperLimit = windowMax;\n                    int lowerLimit = windowMin;\n                    int window = upperLimit; // start with windowMax\n                    while (upperLimit - lowerLimit > 2) {\n                        int x1 = Math.max(x-window/2, 0);\n                        int x2 = Math.min(x+window/2+1, w);\n\n                        int y1 = Math.max(y-window/2, 0);\n                        int y2 = Math.min(y+window/2+1, h);\n\n                        mean = sumBuf.get(y2*sumStep/8 + x2) -\n                               sumBuf.get(y2*sumStep/8 + x1) -\n                               sumBuf.get(y1*sumStep/8 + x2) +\n                               sumBuf.get(y1*sumStep/8 + x1);\n                        mean /= window*window;\n                        sqMean = sqSumBuf.get(y2*sqSumStep/8 + x2) -\n                                           sqSumBuf.get(y2*sqSumStep/8 + x1) -\n                                           sqSumBuf.get(y1*sqSumStep/8 + x2) +\n                                           sqSumBuf.get(y1*sqSumStep/8 + x1);\n                        sqMean /= window*window;\n                        var = sqMean - mean*mean;\n\n                        // if we're at maximum window size, but variance is\n                        // too low anyway, let's break out immediately\n                        if (window == upperLimit && var < targetVar) {\n                            break;\n                        }\n\n                        // otherwise, start binary search\n                        if (var > targetVar) {\n                            upperLimit = window;\n                        } else {\n                            lowerLimit = window;\n                        }\n\n                        window = lowerLimit   + (upperLimit-lowerLimit)/2;\n                        window = (window/2)*2 + 1;\n                    }\n\n                    double value = 0;\n                    if (srcDepth == IPL_DEPTH_8U) {\n                        value = srcBuf.get(y*srcStep       +   x) & 0xFF;\n                    } else if (srcDepth == IPL_DEPTH_32F) {\n                        value = srcBuf.getFloat(y*srcStep  + 4*x);\n                    } else if (srcDepth == IPL_DEPTH_64F) {\n                        value = srcBuf.getDouble(y*srcStep + 8*x);\n                    } else {\n                        // cvIntegral() does not support other image types,\n                        // so we should not be able to get here.\n                        assert false;\n                    }\n                    if (invert) {\n                        //double threshold = 255 - (255 - mean) * (1 + 0.1*(Math.sqrt(var)/128 - 1));\n                        double threshold = 255 - (255 - mean) * k;\n                        dstBuf.put(y*dstStep + x, (value < threshold ? (byte)0xFF : (byte)0x00));\n                    } else {\n                        //double threshold = mean * (1 + k*(Math.sqrt(var)/128 - 1));\n                        double threshold = mean * k;\n                        dstBuf.put(y*dstStep + x, (value > threshold ? (byte)0xFF : (byte)0x00));\n                    }\n                }\n            }\n        }});\n    }\n\n    /** similar to hysteresis thresholding as used by the Canny edge detector */\n    public static void hysteresisThreshold(IplImage srcImage, IplImage dstImage,\n            double highThresh, double lowThresh, double maxValue) {\n        int highThreshold = (int)Math.round(highThresh);\n        int lowThreshold  = (int)Math.round(lowThresh);\n        byte lowValue  = 0;\n        byte medValue  = (byte)Math.round(maxValue/2);\n        byte highValue = (byte)Math.round(maxValue);\n\n        int height = srcImage.height();\n        int width  = srcImage.width();\n\n        ByteBuffer srcData = srcImage.getByteBuffer();\n        ByteBuffer dstData = dstImage.getByteBuffer();\n        int srcStep = srcImage.widthStep();\n        int dstStep = dstImage.widthStep();\n        int srcIndex = 0;\n        int dstIndex = 0;\n\n        //\n        // first pass forward\n        //\n\n        // first line\n        int i = 0;\n        int in = srcData.get(srcIndex+i)&0xFF;\n        if (in >= highThreshold) {\n            dstData.put(dstIndex+i, highValue);\n        } else if (in < lowThreshold) {\n            dstData.put(dstIndex+i, lowValue);\n        } else {\n            dstData.put(dstIndex+i, medValue);\n        }\n\n        for (i = 1; i < width-1; i++) {\n            in = srcData.get(srcIndex+i)&0xFF;\n            if (in >= highThreshold) {\n                dstData.put(dstIndex+i, highValue);\n            } else if (in < lowThreshold) {\n                dstData.put(dstIndex+i, lowValue);\n            } else {\n                byte prev = dstData.get(dstIndex+i-1);\n                if (prev == highValue) {\n                    dstData.put(dstIndex+i, highValue);\n                } else {\n                    dstData.put(dstIndex+i, medValue);\n                }\n            }\n        }\n\n        i = width-1;\n        in = srcData.get(srcIndex+i)&0xFF;\n        if (in >= highThreshold) {\n            dstData.put(dstIndex+i, highValue);\n        } else if (in < lowThreshold) {\n            dstData.put(dstIndex+i, lowValue);\n        } else {\n            byte prev = dstData.get(dstIndex+i-1);\n            if (prev == highValue) {\n                dstData.put(dstIndex+i, highValue);\n            } else {\n                dstData.put(dstIndex+i, medValue);\n            }\n        }\n\n        height--;\n\n        // other lines\n        while (height-- > 0) {\n            srcIndex += srcStep;\n            dstIndex += dstStep;\n\n            // first column\n            i = 0;\n            in = srcData.get(srcIndex+i)&0xFF;\n            if (in >= highThreshold) {\n                dstData.put(dstIndex+i, highValue);\n            } else if (in < lowThreshold) {\n                dstData.put(dstIndex+i, lowValue);\n            } else {\n                byte prev1 = dstData.get(dstIndex+i-dstStep);\n                byte prev2 = dstData.get(dstIndex+i-dstStep+1);\n                if (prev1 == highValue || prev2 == highValue) {\n                    dstData.put(dstIndex+i, highValue);\n                } else {\n                    dstData.put(dstIndex+i, medValue);\n                }\n            }\n\n            // other columns\n            for (i = 1; i < width-1; i++) {\n                in = srcData.get(srcIndex+i)&0xFF;\n                if (in >= highThreshold) {\n                    dstData.put(dstIndex+i, highValue);\n                } else if (in < lowThreshold) {\n                    dstData.put(dstIndex+i, lowValue);\n                } else {\n                    byte prev1 = dstData.get(dstIndex+i-1);\n                    byte prev2 = dstData.get(dstIndex+i-dstStep-1);\n                    byte prev3 = dstData.get(dstIndex+i-dstStep);\n                    byte prev4 = dstData.get(dstIndex+i-dstStep+1);\n\n                    if (prev1 == highValue || prev2 == highValue ||\n                        prev3 == highValue || prev4 == highValue) {\n                        dstData.put(dstIndex+i, highValue);\n                    } else {\n                        dstData.put(dstIndex+i, medValue);\n                    }\n                }\n            }\n\n            // last column\n            i = width-1;\n            in = srcData.get(srcIndex+i)&0xFF;\n            if (in >= highThreshold) {\n                dstData.put(dstIndex+i, highValue);\n            } else if (in < lowThreshold) {\n                dstData.put(dstIndex+i, lowValue);\n            } else {\n                byte prev1 = dstData.get(dstIndex+i-1);\n                byte prev2 = dstData.get(dstIndex+i-dstStep-1);\n                byte prev3 = dstData.get(dstIndex+i-dstStep);\n\n                if (prev1 == highValue || prev2 == highValue ||\n                    prev3 == highValue) {\n                    dstData.put(dstIndex+i, highValue);\n                } else {\n                    dstData.put(dstIndex+i, medValue);\n                }\n            }\n        }\n\n        height = srcImage.height();\n        width  = srcImage.width();\n        dstIndex = (height-1)*dstStep;\n\n        //\n        // second pass backward\n        //\n\n        // first (actually last) line\n        i = width-1;\n        if (dstData.get(dstIndex+i) == medValue) {\n            dstData.put(dstIndex+i, lowValue);\n        }\n\n        for (i = width-2; i > 0 ; i--) {\n            if (dstData.get(dstIndex+i) == medValue) {\n                if (dstData.get(dstIndex+i+1) == highValue) {\n                    dstData.put(dstIndex+i, highValue);\n                } else {\n                    dstData.put(dstIndex+i, lowValue);\n                }\n            }\n        }\n\n        i = 0;\n        if (dstData.get(dstIndex+i) == medValue) {\n            if (dstData.get(dstIndex+i+1) == highValue) {\n                dstData.put(dstIndex+i, highValue);\n            } else {\n                dstData.put(dstIndex+i, lowValue);\n            }\n        }\n\n        height--;\n\n        // other lines\n        while (height-- > 0) {\n            dstIndex -= dstStep;\n\n            // first column\n            i = width-1;\n            if (dstData.get(dstIndex+i) == medValue) {\n                if (dstData.get(dstIndex+i+dstStep)   == highValue ||\n                    dstData.get(dstIndex+i+dstStep-1) == highValue) {\n                    dstData.put(dstIndex+i, highValue);\n                } else {\n                    dstData.put(dstIndex+i, lowValue);\n                }\n            }\n\n            // other columns\n            for (i = width-2; i > 0 ; i--) {\n                if (dstData.get(dstIndex+i) == medValue) {\n                    if (dstData.get(dstIndex+i+1)         == highValue ||\n                        dstData.get(dstIndex+i+dstStep+1) == highValue ||\n                        dstData.get(dstIndex+i+dstStep)   == highValue ||\n                        dstData.get(dstIndex+i+dstStep-1) == highValue) {\n                        dstData.put(dstIndex+i, highValue);\n                    } else {\n                        dstData.put(dstIndex+i, lowValue);\n                    }\n                }\n            }\n\n            // last column\n            i = 0;\n            if (dstData.get(dstIndex+i) == medValue) {\n                if (dstData.get(dstIndex+i+1)         == highValue ||\n                    dstData.get(dstIndex+i+dstStep+1) == highValue ||\n                    dstData.get(dstIndex+i+dstStep)   == highValue) {\n                    dstData.put(dstIndex+i, highValue);\n                } else {\n                    dstData.put(dstIndex+i, lowValue);\n                }\n            }\n        }\n    }\n\n    /** Clamps image intensities between min and max. */\n    public static void clamp(IplImage src, IplImage dst, double min, double max) {\n        switch (src.depth()) {\n            case IPL_DEPTH_8U: {\n                ByteBuffer sb = src.getByteBuffer();\n                ByteBuffer db = dst.getByteBuffer();\n                for (int i = 0; i < sb.capacity(); i++) {\n                    db.put(i, (byte)Math.max(Math.min(sb.get(i) & 0xFF,max),min));\n                }\n                break;\n            }\n            case IPL_DEPTH_16U: {\n                ShortBuffer sb = src.getShortBuffer();\n                ShortBuffer db = dst.getShortBuffer();\n                for (int i = 0; i < sb.capacity(); i++) {\n                    db.put(i, (short)Math.max(Math.min(sb.get(i) & 0xFFFF,max),min));\n                }\n                break;\n            }\n            case IPL_DEPTH_32F: {\n                FloatBuffer sb = src.getFloatBuffer();\n                FloatBuffer db = dst.getFloatBuffer();\n                for (int i = 0; i < sb.capacity(); i++) {\n                    db.put(i, (float)Math.max(Math.min(sb.get(i),max),min));\n                }\n                break;\n            }\n            case IPL_DEPTH_8S: {\n                ByteBuffer sb = src.getByteBuffer();\n                ByteBuffer db = dst.getByteBuffer();\n                for (int i = 0; i < sb.capacity(); i++) {\n                    db.put(i, (byte)Math.max(Math.min(sb.get(i),max),min));\n                }\n                break;\n            }\n            case IPL_DEPTH_16S: {\n                ShortBuffer sb = src.getShortBuffer();\n                ShortBuffer db = dst.getShortBuffer();\n                for (int i = 0; i < sb.capacity(); i++) {\n                    db.put(i, (short)Math.max(Math.min(sb.get(i),max),min));\n                }\n                break;\n            }\n            case IPL_DEPTH_32S: {\n                IntBuffer sb = src.getIntBuffer();\n                IntBuffer db = dst.getIntBuffer();\n                for (int i = 0; i < sb.capacity(); i++) {\n                    db.put(i, (int)Math.max(Math.min(sb.get(i),max),min));\n                }\n                break;\n            }\n            case IPL_DEPTH_64F: {\n                DoubleBuffer sb = src.getDoubleBuffer();\n                DoubleBuffer db = dst.getDoubleBuffer();\n                for (int i = 0; i < sb.capacity(); i++) {\n                    db.put(i, Math.max(Math.min(sb.get(i),max),min));\n                }\n                break;\n            }\n            default: assert(false);\n        }\n    }\n\n    /** vector norm 2 */\n    public static double norm(double[] v) {\n        return norm(v, 2.0);\n    }\n    /** vector norm p */\n    public static double norm(double[] v, double p) {\n        double norm = 0;\n        if (p == 1.0) {\n            for (double e : v) {\n                norm += Math.abs(e);\n            }\n        } else if (p == 2.0) {\n            for (double e : v) {\n                norm += e*e;\n            }\n            norm = Math.sqrt(norm);\n        } else if (p == Double.POSITIVE_INFINITY) {\n            for (double e : v) {\n                e = Math.abs(e);\n                if (e > norm) {\n                    norm = e;\n                }\n            }\n        } else if (p == Double.NEGATIVE_INFINITY) {\n            norm = Double.MAX_VALUE;\n            for (double e : v) {\n                e = Math.abs(e);\n                if (e < norm) {\n                    norm = e;\n                }\n            }\n        } else {\n            for (double e : v) {\n                norm += Math.pow(Math.abs(e), p);\n            }\n            norm = Math.pow(norm, 1/p);\n        }\n        return norm;\n    }\n\n    /** induced norm 2 */\n    public static double norm(CvMat A) {\n        return norm(A, 2.0);\n    }\n    /** induced norm p */\n    public static double norm(CvMat A, double p) {\n        return norm(A, p, null);\n    }\n    /** induced norm p */\n    public static double norm(CvMat A, double p, CvMat W) {\n        double norm = -1;\n\n        if (p == 1.0) {\n            int cols = A.cols(), rows = A.rows();\n            for (int j = 0; j < cols; j++) {\n                double n = 0;\n                for (int i = 0; i < rows; i++) {\n                    n += Math.abs(A.get(i, j));\n                }\n                norm = Math.max(n, norm);\n            }\n        } else if (p == 2.0) {\n            int size = Math.min(A.rows(), A.cols());\n            if (W == null || W.rows() != size || W.cols() != 1) {\n                W = CvMat.create(size, 1);\n            }\n            cvSVD(A, W, null, null, 0);\n            norm = W.get(0); // largest singular value\n        } else if (p == Double.POSITIVE_INFINITY) {\n            int rows = A.rows(), cols = A.cols();\n            for (int i = 0; i < rows; i++) {\n                double n = 0;\n                for (int j = 0; j < cols; j++) {\n                    n += Math.abs(A.get(i, j));\n                }\n                norm = Math.max(n, norm);\n            }\n        } else {\n            assert(false);\n        }\n        return norm;\n    }\n\n    public static double cond(CvMat A) {\n        return cond(A, 2.0);\n    }\n    public static double cond(CvMat A, double p) {\n        return cond(A, p, null);\n    }\n    public static double cond(CvMat A, double p, CvMat W) {\n        double cond = -1;\n\n        if (p == 2.0) {\n            int size = Math.min(A.rows(), A.cols());\n            if (W == null || W.rows() != size || W.cols() != 1) {\n                W = CvMat.create(size, 1);\n            }\n            cvSVD(A, W, null, null, 0);\n            cond = W.get(0)/W.get(W.length()-1); // largest/smallest singular value\n        } else {\n            // should put something faster here if we're really serious\n            // about using something other than the 2-norm\n            int rows = A.rows(), cols = A.cols();\n            if (W == null || W.rows() != rows || W.cols() != cols) {\n                W = CvMat.create(rows, cols);\n            }\n            CvMat Ainv = W;\n            cvInvert(A, Ainv);\n            cond = norm(A, p)*norm(Ainv, p);\n        }\n        return cond;\n    }\n\n    public static double median(double[] doubles) {\n        double[] sorted = doubles.clone();\n        Arrays.sort(sorted);\n        if (doubles.length%2 == 0) {\n            return (sorted[doubles.length/2 - 1] + sorted[doubles.length/2])/2;\n        } else {\n            return sorted[doubles.length/2];\n        }\n    }\n    public static <T extends Object> T median(T[] objects) {\n        T[] sorted = objects.clone();\n        Arrays.sort(sorted);\n        return sorted[sorted.length/2];\n    }\n\n    public static void fractalTriangleWave(double[] line, int i, int j, double a) {\n        fractalTriangleWave(line, i, j, a, -1);\n    }\n    public static void fractalTriangleWave(double[] line, int i, int j, double a, int roughness) {\n        int m = (j-i)/2+i;\n        if (i == j || i == m) {\n            return;\n        }\n        line[m] = (line[i]+line[j])/2 + a;\n        if (roughness > 0 && line.length > roughness*(j-i)) {\n            fractalTriangleWave(line, i, m, 0, roughness);\n            fractalTriangleWave(line, m, j, 0, roughness);\n        } else {\n            fractalTriangleWave(line, i, m,  a/SQRT2, roughness);\n            fractalTriangleWave(line, m, j, -a/SQRT2, roughness);\n        }\n    }\n\n    public static void fractalTriangleWave(IplImage image, CvMat H) {\n        fractalTriangleWave(image, H, -1);\n    }\n    public static void fractalTriangleWave(IplImage image, CvMat H, int roughness) {\n        assert (image.depth() == IPL_DEPTH_32F);\n        double[] line = new double[image.width()];\n        fractalTriangleWave(line, 0,             line.length/2,  1, roughness);\n        fractalTriangleWave(line, line.length/2, line.length-1, -1, roughness);\n\n        double[] minMax = { Double.MAX_VALUE, Double.MIN_VALUE };\n        int height   = image.height();\n        int width    = image.width();\n        int channels = image.nChannels();\n        int step     = image.widthStep();\n        int start = 0;\n        if (image.roi() != null) {\n            height = image.roi().height();\n            width  = image.roi().width();\n            start  = image.roi().yOffset()*step/4 + image.roi().xOffset()*channels;\n        }\n        FloatBuffer fb = image.getFloatBuffer(start);\n        double[] h = H == null ? null : H.get();\n        for (int y = 0; y < height; y++) {\n            for (int x = 0; x < width; x++) {\n                for (int z = 0; z < channels; z++) {\n                    double sum = 0.0;\n                    if (h == null) {\n                        sum += line[x];\n                    } else {\n                        double x2 = (h[0]*x + h[1]*y + h[2])/(h[6]*x + h[7]*y + h[8]);\n                        while (x2 < 0) {\n                            x2 += line.length;\n                        }\n                        int xi2   = (int)x2;\n                        double xn = x2 - xi2;\n                        sum += line[ xi2   %line.length]*(1-xn) +\n                               line[(xi2+1)%line.length]*xn;\n                    }\n                    minMax[0] = Math.min(minMax[0], sum);\n                    minMax[1] = Math.max(minMax[1], sum);\n                    fb.put(y*step/4 + x*channels + z, (float)sum);\n                }\n            }\n        }\n\n        cvConvertScale(image, image, 1/(minMax[1]-minMax[0]),\n                -minMax[0]/(minMax[1]-minMax[0]));\n    }\n\n    public static void main(String[] args) {\n        String version = JavaCV.class.getPackage().getImplementationVersion();\n        if (version == null) {\n            version = \"unknown\";\n        }\n        System.out.println(\n            \"JavaCV version \" + version + \"\\n\" +\n            \"Copyright (C) 2009-2018 Samuel Audet <samuel.audet@gmail.com>\\n\" +\n            \"Project site: https://github.com/bytedeco/javacv\");\n        System.exit(0);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/JavaCVCL.java",
    "content": "/*\n * Copyright (C) 2011-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport com.jogamp.opencl.CLCommandQueue;\nimport com.jogamp.opencl.CLBuffer;\nimport com.jogamp.opencl.CLContext;\nimport com.jogamp.opencl.CLDevice;\nimport com.jogamp.opencl.CLEventList;\nimport com.jogamp.opencl.CLImage2d;\nimport com.jogamp.opencl.CLImageFormat;\nimport com.jogamp.opencl.CLImageFormat.ChannelOrder;\nimport com.jogamp.opencl.CLImageFormat.ChannelType;\nimport com.jogamp.opencl.CLKernel;\nimport com.jogamp.opencl.CLMemory;\nimport com.jogamp.opencl.CLObject;\nimport com.jogamp.opencl.CLPlatform;\nimport com.jogamp.opencl.CLProgram;\nimport com.jogamp.opencl.CLProgram.CompilerOptions;\nimport com.jogamp.opencl.gl.CLGLContext;\nimport com.jogamp.opencl.gl.CLGLImage2d;\nimport com.jogamp.opencl.gl.CLGLObject;\nimport com.jogamp.opengl.GL;\nimport com.jogamp.opengl.GL2;\nimport com.jogamp.opengl.GLCapabilities;\nimport com.jogamp.opengl.GLCapabilitiesImmutable;\nimport com.jogamp.opengl.GLContext;\nimport com.jogamp.opengl.GLDrawableFactory;\nimport com.jogamp.opengl.GLException;\nimport com.jogamp.opengl.GLProfile;\nimport com.jogamp.opengl.glu.GLU;\nimport java.io.InputStream;\nimport java.io.IOException;\nimport java.io.SequenceInputStream;\nimport java.nio.ByteBuffer;\nimport java.util.Vector;\nimport java.util.logging.Logger;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.BytePointer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n *\n * To make NVIDIA drivers happy, we may need to limit the maximum\n * heap memory size of Java to 1 GB, by defining something like\n *      export _JAVA_OPTIONS=-Xmx1G\n *\n */\npublic class JavaCVCL {\n    public JavaCVCL(CLContext context) {\n        this(context, context.getDevices()[0]);\n    }\n    public JavaCVCL(CLContext context, CLDevice device) {\n//        this.pbuffer = null;\n        this.context = context;\n        this.glu = context instanceof CLGLContext ? new GLU() : null;\n        this.commandQueue = device.createCommandQueue(/*Mode.PROFILING_MODE*/);\n        CLKernel[] kernels = buildKernels(fastCompilerOptions, \"JavaCV.cl\", \"pyrDown\", \"remap\", \"remapBayer\");\n        this.pyrDownKernel    = kernels[0];\n        this.remapKernel      = kernels[1];\n        this.remapBayerKernel = kernels[2];\n    }\n\n    public static GLCapabilities getDefaultGLCapabilities(GLProfile profile) {\n        GLCapabilities caps = new GLCapabilities(profile != null ? profile : GLProfile.getDefault());\n        // Without line below, there is an error on Windows.\n        caps.setDoubleBuffered(false);\n        return caps;\n    }\n\n    public JavaCVCL() {\n        this(false);\n    }\n    public JavaCVCL(boolean createPbuffer) {\n        this(createPbuffer ? getDefaultGLCapabilities(null) : null, null, null);\n    }\n    public JavaCVCL(GLContext shareWith) {\n        this(getDefaultGLCapabilities(shareWith == null ? null :\n            shareWith.getGLDrawable().getGLProfile()), shareWith, null);\n    }\n    public JavaCVCL(GLCapabilitiesImmutable caps, GLContext shareWith, CLDevice device) {\n//        GLPbuffer pbuffer = null;\n//        if (caps != null) {\n//            GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile());\n//            if (factory.canCreateGLPbuffer(null, caps.getGLProfile())) {\n//                try {\n//                    // makes a new buffer\n//                    pbuffer = factory.createGLPbuffer(null, caps, null, 32, 32, shareWith);\n//                    // required for drawing to the buffer\n//                    pbuffer.createContext(shareWith).makeCurrent();\n//                } catch (GLException e) {\n//                    logger.warning(\"Could not create PBuffer: \" + e);\n//                }\n//            } else {\n//                logger.warning(\"OpenGL implementation does not support PBuffers.\");\n//            }\n//        }\n//        this.pbuffer = pbuffer;\n\n        GLContext glContext = GLContext.getCurrent();\n        if (device == null && glContext != null) {\n            // woohoo! we have a GLContext\n\n            // find gl compatible device\n            CLDevice[] devices = CLPlatform.getDefault().listCLDevices();\n            for (CLDevice d : devices) {\n                if(d.isGLMemorySharingSupported()) {\n                    device = d;\n                    break;\n                }\n            }\n//            if(null==device) {\n//                throw new RuntimeException(\"couldn't find any CL/GL memory sharing devices ..\");\n//            }\n        }\n        if (glContext != null && device != null) {\n            // create OpenCL context before creating any OpenGL objects\n            // you want to share with OpenCL (AMD driver requirement)\n            context = CLGLContext.create(glContext, device);\n            glu = GLU.createGLU();\n        } else if (device != null) {\n            context = CLContext.create(device);\n            glu = null;\n        } else {\n            // find a CL implementation\n            //CLPlatform platform = CLPlatform.getDefault(/*type(CPU)*/);\n            context = CLContext.create(/*platform.getMaxFlopsDevice()*/);\n            device = context.getDevices()[0];\n            glu = null;\n        }\n\n        // creade a command queue with benchmarking flag set\n        commandQueue = device.createCommandQueue(/*Mode.PROFILING_MODE*/);\n\n        CLKernel[] kernels = buildKernels(fastCompilerOptions, \"JavaCV.cl\", \"pyrDown\", \"remap\", \"remapBayer\");\n        this.pyrDownKernel    = kernels[0];\n        this.remapKernel      = kernels[1];\n        this.remapBayerKernel = kernels[2];\n    }\n\n    public void release() {\n        if (!context.isReleased()) {\n            context.release();\n//            if (pbuffer != null) {\n//                pbuffer.getContext().makeCurrent();\n//                pbuffer.getContext().release();\n//                pbuffer.getContext().destroy();\n//                pbuffer.destroy();\n//            }\n        }\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    public static final String fastCompilerOptions = // \"-cl-nv-verbose \" +\n            CompilerOptions.FAST_RELAXED_MATH + \" \" + CompilerOptions.ENABLE_MAD;\n\n    private static final Logger logger = Logger.getLogger(JavaCVCL.class.getName());\n\n//    private final GLPbuffer pbuffer;\n    private final CLContext context;\n    private final CLCommandQueue commandQueue;\n    private final GLU glu;\n    private final CLKernel pyrDownKernel, remapKernel, remapBayerKernel;\n\n    public CLContext getCLContext() {\n        return context;\n    }\n\n    public CLCommandQueue getCLCommandQueue() {\n        return commandQueue;\n    }\n\n    public CLGLContext getCLGLContext() {\n        return context instanceof CLGLContext ? (CLGLContext)context : null;\n    }\n\n    public GLContext getGLContext() {\n        return context instanceof CLGLContext ? ((CLGLContext)context).getGLContext() : null;\n    }\n\n    public GL getGL() {\n        GLContext glContext = getGLContext();\n        return glContext != null ? glContext.getGL() : null;\n    }\n\n    public GL2 getGL2() {\n        GL gl = getGL();\n        return gl != null ? gl.getGL2() : null;\n    }\n\n    public GLU getGLU() {\n        return glu;\n    }\n\n    public CLKernel buildKernel(String resourceNames, String kernelName) {\n        return buildKernels(fastCompilerOptions, Loader.getCallerClass(2), resourceNames, kernelName)[0];\n    }\n    public CLKernel buildKernel(String compilerOptions, String resourceNames, String kernelName) {\n        return buildKernels(compilerOptions, Loader.getCallerClass(2), resourceNames, kernelName)[0];\n    }\n\n    public CLKernel[] buildKernels(String compilerOptions, String resourceNames, String ... kernelNames) {\n        return buildKernels(compilerOptions, Loader.getCallerClass(2), resourceNames, kernelNames);\n    }\n    public CLKernel[] buildKernels(String compilerOptions, Class resourceClass, String resourceNames, String ... kernelNames) {\n        try {\n            //load and compile program for the chosen device\n            InputStream s;\n            String[] a = resourceNames.split(\":\");\n            if (a.length == 1) {\n                s = resourceClass.getResourceAsStream(a[0]);\n            } else {\n                Vector<InputStream> vs = new Vector<InputStream>(a.length);\n                for (String name : a) {\n                    vs.addElement(resourceClass.getResourceAsStream(name));\n                }\n                s = new SequenceInputStream(vs.elements());\n            }\n            CLProgram program = context.createProgram(s);\n//System.out.println(\"Building \" + resourceNames + \"...\");\n            program.build(compilerOptions);\n//System.out.println(program.getBuildLog());\n            assert program.isExecutable();\n\n            // create kernel and set function parameters\n            CLKernel[] kernels = new CLKernel[kernelNames.length];\n            for (int i = 0; i < kernelNames.length; i++) {\n                kernels[i] = program.createCLKernel(kernelNames[i]);\n            }\n            return kernels;\n        } catch(IOException ex) {\n            throw (Error)new LinkageError(ex.toString()).initCause(ex);\n        }\n    }\n\n    public CLImage2d createCLImageFrom(IplImage image, CLImage2d.Mem ... flags) {\n        int width = image.width();\n        int height = image.height();\n        int pitch = image.widthStep();\n        ByteBuffer buffer = image.getByteBuffer();\n        ChannelOrder order = null;\n        ChannelType type = null;\n        int size = 0;\n        switch (image.depth()) {\n            case IPL_DEPTH_8S:  type = ChannelType.SNORM_INT8;   size = 1; break;\n            case IPL_DEPTH_8U:  type = ChannelType.UNORM_INT8;   size = 1; break;\n            case IPL_DEPTH_16S: type = ChannelType.SNORM_INT16;  size = 2; break;\n            case IPL_DEPTH_16U: type = ChannelType.UNORM_INT16;  size = 2; break;\n            case IPL_DEPTH_32S: type = ChannelType.SIGNED_INT32; size = 4; break;\n            case IPL_DEPTH_32F: type = ChannelType.FLOAT;        size = 4; break;\n            default: assert false;\n        }\n        switch (image.nChannels()) {\n            case 1: order = ChannelOrder.LUMINANCE;       break;\n            case 2: order = ChannelOrder.RG;   size *= 2; break;\n            case 3: order = ChannelOrder.RGB;  size *= 3; break;\n            case 4: order = ChannelOrder.RGBA; size *= 4; break;\n            default: assert false;\n        }\n        // NVIDIA drivers do not like it when width != pitch/size\n        if (width != pitch/size) {\n            width = pitch/size;\n        }\n        CLImageFormat format = new CLImageFormat(order, type);\n        return context.createImage2d(buffer, width, height, /*pitch,*/ format, flags);\n    }\n\n    public CLGLImage2d createCLGLImageFrom(IplImage image, CLImage2d.Mem ... flags) {\n        GL2 gl = getGL2();\n        if (gl == null) {\n            return null;\n        }\n\n        int width = image.width();\n        int height = image.height();\n        int pitch = image.widthStep();\n        //ByteBuffer buffer = image.getByteBuffer();\n        int format = 0;\n        int size = 0;\n        switch (image.nChannels()) {\n            case 1:\n                switch (image.depth()) {\n                    case IPL_DEPTH_8S:  format = GL2.GL_LUMINANCE8_SNORM;  size = 1; break;\n                    case IPL_DEPTH_8U:  format = GL2.GL_LUMINANCE8;        size = 1; break;\n                    case IPL_DEPTH_16S: format = GL2.GL_LUMINANCE16_SNORM; size = 2; break;\n                    case IPL_DEPTH_16U: format = GL2.GL_LUMINANCE16;       size = 2; break;\n                    case IPL_DEPTH_32S: format = GL2.GL_LUMINANCE32I;      size = 4; break;\n                    case IPL_DEPTH_32F: format = GL2.GL_LUMINANCE32F;      size = 4; break;\n                    default: assert false;\n                }\n                break;\n            case 2:\n                switch (image.depth()) {\n                    case IPL_DEPTH_8S:  format = GL2.GL_RG8_SNORM;  size = 2; break;\n                    case IPL_DEPTH_8U:  format = GL2.GL_RG8;        size = 2; break;\n                    case IPL_DEPTH_16S: format = GL2.GL_RG16_SNORM; size = 4; break;\n                    case IPL_DEPTH_16U: format = GL2.GL_RG16;       size = 4; break;\n                    case IPL_DEPTH_32S: format = GL2.GL_RG32I;      size = 8; break;\n                    case IPL_DEPTH_32F: format = GL2.GL_RG32F;      size = 8; break;\n                    default: assert false;\n                }\n                break;\n            case 3:\n                switch (image.depth()) {\n                    case IPL_DEPTH_8S:  format = GL2.GL_RGB8_SNORM;  size = 3; break;\n                    case IPL_DEPTH_8U:  format = GL2.GL_RGB8;        size = 3; break;\n                    case IPL_DEPTH_16S: format = GL2.GL_RGB16_SNORM; size = 6; break;\n                    case IPL_DEPTH_16U: format = GL2.GL_RGB16;       size = 6; break;\n                    case IPL_DEPTH_32S: format = GL2.GL_RGB32I;      size = 12; break;\n                    case IPL_DEPTH_32F: format = GL2.GL_RGB32F;      size = 12; break;\n                    default: assert false;\n                }\n                break;\n            case 4:\n                switch (image.depth()) {\n                    case IPL_DEPTH_8S:  format = GL2.GL_RGBA8_SNORM;  size = 4; break;\n                    case IPL_DEPTH_8U:  format = GL2.GL_RGBA8;        size = 4; break;\n                    case IPL_DEPTH_16S: format = GL2.GL_RGBA16_SNORM; size = 8; break;\n                    case IPL_DEPTH_16U: format = GL2.GL_RGBA16;       size = 8; break;\n                    case IPL_DEPTH_32S: format = GL2.GL_RGBA32I;      size = 16; break;\n                    case IPL_DEPTH_32F: format = GL2.GL_RGBA32F;      size = 16; break;\n                    default: assert false;\n                }\n                break;\n            default: assert false;\n        }\n        // NVIDIA drivers do not like it when width != pitch/size\n        if (width != pitch/size) {\n            width = pitch/size;\n        }\n        int[] renderBuffer = new int[1];\n        gl.glGenRenderbuffers(1, renderBuffer, 0);\n        gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, renderBuffer[0]);\n        gl.glRenderbufferStorage(GL2.GL_RENDERBUFFER, format, width, height);\n        return getCLGLContext().createFromGLRenderbuffer(renderBuffer[0], flags);\n    }\n\n    public void releaseCLGLImage(CLGLImage2d image) {\n        image.release();\n        getGL2().glDeleteRenderbuffers(1, new int[] { image.getGLObjectID() }, 0);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public CLBuffer createPinnedBuffer(int size) {\n        // as per NVIDIA's OpenCL Best Practices Guide\n        CLBuffer pinnedBuffer = context.createBuffer(size, CLMemory.Mem.ALLOCATE_BUFFER);\n        ByteBuffer byteBuffer = commandQueue.putMapBuffer(pinnedBuffer, CLMemory.Map.READ_WRITE, true);\n        pinnedBuffer.use(byteBuffer);\n        return pinnedBuffer;\n    }\n\n    class PinnedIplImage extends IplImage {\n        PinnedIplImage(int width, int height, int depth, int channels) {\n            super(cvCreateImageHeader(new CvSize().width(width).height(height), depth, channels));\n            pinnedBuffer = createPinnedBuffer(imageSize());\n            imageData(new BytePointer(getByteBuffer()));\n        }\n\n        final CLBuffer pinnedBuffer;\n\n        public CLBuffer getCLBuffer() {\n            return pinnedBuffer;\n        }\n\n        @Override public ByteBuffer getByteBuffer() {\n            return (ByteBuffer)pinnedBuffer.getBuffer();\n        }\n\n        @Override public void release() {\n            commandQueue.putUnmapMemory(pinnedBuffer, getByteBuffer());\n            pinnedBuffer.release();\n            cvReleaseImageHeader(this);\n        }\n    }\n\n    public IplImage createPinnedIplImage(int width, int height, int depth, int channels) {\n        return new PinnedIplImage(width, height, depth, channels);\n    }\n\n    public IplImage createIplImageFrom(CLImage2d image) {\n        int width = image.width;\n        int height = image.height;\n        CLImageFormat format = image.getFormat();\n        ChannelOrder order = format.getImageChannelOrder();\n        ChannelType type = format.getImageChannelDataType();\n        int depth = 0, channels = 0;\n        switch (order) {\n            case R:\n            case A:\n            case INTENSITY:\n            case LUMINANCE:\n                channels = 1;\n                break;\n            case Rx:\n            case RG:\n            case RA:\n                channels = 2;\n                break;\n            case RGx:\n            case RGB:\n                channels = 3;\n                break;\n            case RGBx:\n            case RGBA:\n            case ARGB:\n            case BGRA:\n                channels = 4;\n                break;\n            default: assert false;\n        }\n        switch (type) {\n            case SIGNED_INT8:\n            case SNORM_INT8:    depth = IPL_DEPTH_8S;  break;\n            case UNSIGNED_INT8:\n            case UNORM_INT8:    depth = IPL_DEPTH_8U;  break;\n            case SIGNED_INT16:\n            case SNORM_INT16:   depth = IPL_DEPTH_16S; break;\n            case UNSIGNED_INT16:\n            case UNORM_INT16:   depth = IPL_DEPTH_16U; break;\n            case UNSIGNED_INT32:\n            case SIGNED_INT32:  depth = IPL_DEPTH_32S; break;\n            case FLOAT:         depth = IPL_DEPTH_32F; break;\n            case HALF_FLOAT:\n            case UNORM_SHORT_565:\n            case UNORM_SHORT_555:\n            case UNORM_INT_101010:\n            default: assert false;\n        }\n        return IplImage.create(width, height, depth, channels);\n        //return createPinnedIplImage(width, height, depth, channels);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public IplImage readImage(CLImage2d clImg, IplImage iplImage, boolean blocking) {\n        if (iplImage == null) {\n            iplImage = createIplImageFrom(clImg);\n        }\n        int x = 0, y = 0;\n        int width = clImg.width;\n        int height = clImg.height;\n        int pitch = iplImage.widthStep();\n        ByteBuffer buffer = iplImage.getByteBuffer();\n        IplROI roi = iplImage.roi();\n        if (roi != null) {\n            x = roi.xOffset();\n            y = roi.yOffset();\n            width = roi.width();\n            height = roi.height();\n            int pixelSize = iplImage.nChannels()*((iplImage.depth()&~IPL_DEPTH_SIGN)/8);\n            buffer = iplImage.getByteBuffer(y*pitch + x*pixelSize);\n        }\n        clImg.use(buffer);\n        commandQueue.putReadImage(clImg, pitch, x, y, width, height, blocking);\n        return iplImage;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public CLImage2d writeImage(CLImage2d clImg, IplImage iplImage, boolean blocking) {\n        if (clImg == null) {\n            clImg = createCLImageFrom(iplImage);\n        }\n        int x = 0, y = 0;\n        int width = iplImage.width();\n        int height = iplImage.height();\n        int pitch = iplImage.widthStep();\n        ByteBuffer buffer = iplImage.getByteBuffer();\n        IplROI roi = iplImage.roi();\n        if (roi != null) {\n            x = roi.xOffset();\n            y = roi.yOffset();\n            width = roi.width();\n            height = roi.height();\n            int pixelSize = iplImage.nChannels()*((iplImage.depth()&~IPL_DEPTH_SIGN)/8);\n            buffer = iplImage.getByteBuffer(y*pitch + x*pixelSize);\n        }\n        clImg.use(buffer);\n        commandQueue.putWriteImage(clImg, pitch, x, y, width, height, blocking);\n        return clImg;\n    }\n\n    public void acquireGLObject(CLObject object) {\n        if (object instanceof CLGLObject) {\n            commandQueue.putAcquireGLObject((CLGLObject)object);\n        }\n    }\n    public void releaseGLObject(CLObject object) {\n        if (object instanceof CLGLObject) {\n            commandQueue.putReleaseGLObject((CLGLObject)object);\n        }\n    }\n\n    public void readBuffer(CLBuffer<?> buffer, boolean blocking) {\n        commandQueue.putReadBuffer(buffer, blocking);\n    }\n    public void writeBuffer(CLBuffer<?> buffer, boolean blocking) {\n        commandQueue.putWriteBuffer(buffer, blocking);\n    }\n\n    public void executeKernel(CLKernel kernel,\n            long globalWorkOffsetX, long globalWorkSizeX, long localWorkSizeX) {\n        commandQueue.put1DRangeKernel(kernel,\n                globalWorkOffsetX, globalWorkSizeX, localWorkSizeX);\n    }\n    public void executeKernel(CLKernel kernel,\n            long globalWorkOffsetX, long globalWorkSizeX, long localWorkSizeX, CLEventList events) {\n        commandQueue.put1DRangeKernel(kernel,\n                globalWorkOffsetX, globalWorkSizeX, localWorkSizeX, events);\n    }\n    public void executeKernel(CLKernel kernel,\n            long globalWorkOffsetX, long globalWorkSizeX, long localWorkSizeX,\n            CLEventList condition, CLEventList events) {\n        commandQueue.put1DRangeKernel(kernel,\n                globalWorkOffsetX, globalWorkSizeX, localWorkSizeX, condition, events);\n    }\n\n    public void executeKernel(CLKernel kernel,\n            long globalWorkOffsetX, long globalWorkOffsetY,\n            long globalWorkSizeX, long globalWorkSizeY,\n            long localWorkSizeX, long localWorkSizeY) {\n        commandQueue.put2DRangeKernel(kernel,\n                globalWorkOffsetX, globalWorkOffsetY,\n                globalWorkSizeX, globalWorkSizeY,\n                localWorkSizeX, localWorkSizeY);\n    }\n    public void executeKernel(CLKernel kernel,\n            long globalWorkOffsetX, long globalWorkOffsetY,\n            long globalWorkSizeX, long globalWorkSizeY,\n            long localWorkSizeX, long localWorkSizeY, CLEventList events) {\n        commandQueue.put2DRangeKernel(kernel,\n                globalWorkOffsetX, globalWorkOffsetY,\n                globalWorkSizeX, globalWorkSizeY,\n                localWorkSizeX, localWorkSizeY, events);\n    }\n    public void executeKernel(CLKernel kernel,\n            long globalWorkOffsetX, long globalWorkOffsetY,\n            long globalWorkSizeX, long globalWorkSizeY,\n            long localWorkSizeX, long localWorkSizeY,\n            CLEventList condition, CLEventList events) {\n        commandQueue.put2DRangeKernel(kernel,\n                globalWorkOffsetX, globalWorkOffsetY,\n                globalWorkSizeX, globalWorkSizeY,\n                localWorkSizeX, localWorkSizeY, condition, events);\n    }\n\n    public void executeKernel(CLKernel kernel,\n            long globalWorkOffsetX, long globalWorkOffsetY, long globalWorkOffsetZ,\n            long globalWorkSizeX, long globalWorkSizeY, long globalWorkSizeZ,\n            long localWorkSizeX, long localWorkSizeY, long localWorkSizeZ) {\n        commandQueue.put3DRangeKernel(kernel,\n                globalWorkOffsetX, globalWorkOffsetY, globalWorkOffsetZ,\n                globalWorkSizeX, globalWorkSizeY, globalWorkSizeZ,\n                localWorkSizeX, localWorkSizeY, localWorkSizeZ);\n    }\n    public void executeKernel(CLKernel kernel,\n            long globalWorkOffsetX, long globalWorkOffsetY, long globalWorkOffsetZ,\n            long globalWorkSizeX, long globalWorkSizeY, long globalWorkSizeZ,\n            long localWorkSizeX, long localWorkSizeY, long localWorkSizeZ, CLEventList events) {\n        commandQueue.put3DRangeKernel(kernel,\n                globalWorkOffsetX, globalWorkOffsetY, globalWorkOffsetZ,\n                globalWorkSizeX, globalWorkSizeY, globalWorkSizeZ,\n                localWorkSizeX, localWorkSizeY, localWorkSizeZ, events);\n    }\n    public void executeKernel(CLKernel kernel,\n            long globalWorkOffsetX, long globalWorkOffsetY, long globalWorkOffsetZ,\n            long globalWorkSizeX, long globalWorkSizeY, long globalWorkSizeZ,\n            long localWorkSizeX, long localWorkSizeY, long localWorkSizeZ,\n            CLEventList condition, CLEventList events) {\n        commandQueue.put3DRangeKernel(kernel,\n                globalWorkOffsetX, globalWorkOffsetY, globalWorkOffsetZ,\n                globalWorkSizeX, globalWorkSizeY, globalWorkSizeZ,\n                localWorkSizeX, localWorkSizeY, localWorkSizeZ, condition, events);\n    }\n\n    public void finish() {\n        commandQueue.finish();\n    }\n    public void flush() {\n        commandQueue.flush();\n    }\n\n    public static int alignCeil(int x, int n) {\n        return (x + n-1)/n*n;\n    }\n    public static int alignFloor(int x, int n) {\n        return x/n*n;\n    }\n\n    public void pyrDown(CLImage2d srcImg, CLImage2d dstImg) {\n        CLEventList list = null;//new CLEventList(1);\n\n        pyrDownKernel.putArg(srcImg).putArg(dstImg).rewind();\n        executeKernel(pyrDownKernel, 0, 0, alignCeil(dstImg.width, 2), alignCeil(dstImg.height, 64), 2, 64, list); // execute program\n\n//        finish();\n//        CLEvent event = list.getEvent(0);\n//        System.out.println(\"pyrDown: \" + (event.getProfilingInfo(CLEvent.ProfilingCommand.END) -\n//                                          event.getProfilingInfo(CLEvent.ProfilingCommand.START))/1000000.0);\n    }\n\n    public void remap(CLImage2d srcImg, CLImage2d dstImg, CLImage2d mapxImg, CLImage2d mapyImg) {\n        remap(srcImg, dstImg, mapxImg, mapyImg, -1L);\n    }\n    public void remap(CLImage2d srcImg, CLImage2d dstImg, CLImage2d mapxImg, CLImage2d mapyImg, long sensorPattern) {\n        CLEventList list = null;//new CLEventList(1);\n\n        CLKernel kernel;\n        if (sensorPattern != -1L) {\n            kernel = remapBayerKernel.putArg(srcImg).putArg(dstImg).putArg(mapxImg).putArg(mapyImg).putArg(sensorPattern).rewind();\n        } else {\n            kernel = remapKernel.putArg(srcImg).putArg(dstImg).putArg(mapxImg).putArg(mapyImg).rewind();\n        }\n        executeKernel(kernel, 0, 0, alignCeil(dstImg.width, 2), alignCeil(dstImg.height, 64), 2, 64, list); // execute program\n\n//        finish();\n//        CLEvent event = list.getEvent(0);\n//        System.out.println(\"remap: \" + (event.getProfilingInfo(CLEvent.ProfilingCommand.END) -\n//                                        event.getProfilingInfo(CLEvent.ProfilingCommand.START))/1000000.0);\n    }\n\n    public static void main(String[] args) {\n        JavaCVCL context = new JavaCVCL();\n\n        CLImageFormat[] formats = context.getCLContext().getSupportedImage2dFormats();\n        for (CLImageFormat f : formats) {\n            System.out.println(f);\n        }\n\n        CameraDevice camera = new CameraDevice(\"Camera\");\n        camera.imageWidth = 1280;\n        camera.imageHeight = 960;\n        camera.cameraMatrix = CvMat.create(3, 3);\n        double f = camera.imageWidth*2.5;\n        camera.cameraMatrix.put(f,   0.0, camera.imageWidth /2,\n                                0.0, f,   camera.imageHeight/2,\n                                0.0, 0.0,    1);\n        camera.R = CvMat.create(3, 3);\n        cvSetIdentity(camera.R);\n        camera.T = CvMat.create(3, 1);\n        cvSetZero(camera.T);\n        camera.distortionCoeffs = CvMat.create(1, 4);\n        cvSetZero(camera.distortionCoeffs);\n        camera.distortionCoeffs.put(0.2);\n        camera.colorMixingMatrix = CvMat.create(3, 3);\n        cvSetIdentity(camera.colorMixingMatrix);\n\n        IplImage srcImg = cvLoadImageRGBA(args[0]);\n        //IplImage dstImg = srcImg.clone();\n        IplImage downDst = IplImage.create(srcImg.width()/2, srcImg.height()/2, IPL_DEPTH_8U /*IPL_DEPTH_32F*/, 4);\n        camera.setFixedPointMaps(false);\n        camera.setMapsPyramidLevel(1);\n        IplImage mapxImg = camera.getUndistortMap1();\n        IplImage mapyImg = camera.getUndistortMap2();\n        long start = System.nanoTime();\n        cvRemap(srcImg, downDst, mapxImg, mapyImg, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, CvScalar.ZERO);\n        System.out.println(\"cvRemap: \" + (System.nanoTime()-start)/1000000.0);\n        cvSaveImage(\"/tmp/opencv.png\", downDst);\n\n        CLImage2d src = context.createCLImageFrom(srcImg);\n//        CLImage2d dst = context.createCLImageFrom(dstImg);\n        CLImage2d dst = context.createCLImageFrom(downDst);\n\n        CLImage2d mapx = context.createCLImageFrom(mapxImg);\n        CLImage2d mapy = context.createCLImageFrom(mapyImg);\n        context.writeImage(src, srcImg, false);\n        context.writeImage(mapx, mapxImg, false);\n        context.writeImage(mapy, mapyImg, false);\n\n        //context.pyrDown(src, dst);\n        context.remap(src, dst, mapx, mapy);\n        context.readImage(dst, downDst, true);\n        //cvConvertScale(downDst, downDst, 255, 0);\n        cvSaveImage(\"/tmp/javacvcl.png\", downDst);\n\n        context.release();\n        System.exit(0);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/JavaCvErrorCallback.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.awt.Component;\nimport java.awt.EventQueue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport javax.swing.JOptionPane;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Pointer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class JavaCvErrorCallback extends CvErrorCallback {\n\n    static JavaCvErrorCallback instance;\n\n    public JavaCvErrorCallback() {\n        this(false);\n    }\n    public JavaCvErrorCallback(boolean showDialog) {\n        this(showDialog, null);\n    }\n    public JavaCvErrorCallback(boolean showDialog, Component parent) {\n        this(showDialog, parent, 0);\n    }\n    public JavaCvErrorCallback(boolean showDialog, Component parent, int rc) {\n        instance = this;\n        this.parent = parent;\n        this.showDialog = showDialog;\n        this.rc = rc;\n    }\n\n    private long lastErrorTime = 0;\n    private Component parent;\n    private boolean showDialog;\n    private int rc;\n\n    @Override public int call(int status, BytePointer func_name, BytePointer err_msg,\n            BytePointer file_name, int line, Pointer userdata) {\n        final String title = \"OpenCV Error\";\n        final String message = cvErrorStr(status) +\n                \" (\" + err_msg.getString() + \")\\nin function \" +\n                func_name.getString() + \", \" + file_name.getString() + \"(\" + line + \")\";\n        Logger.getLogger(JavaCvErrorCallback.class.getName()).log(Level.SEVERE,\n                title + \": \" + message, new java.lang.Exception(\"Strack trace\"));\n        if (showDialog) {\n            // Show no more than 1 dialog per second since we cannot stop OpenCV\n            // from processing and throwing more errors. Maybe in the future\n            // when JavaCPP allows us to throw Exceptions across...\n            if (System.currentTimeMillis() - lastErrorTime > 1000) {\n                EventQueue.invokeLater(new Runnable() {\n                    public void run() {\n                        JOptionPane.showMessageDialog(parent, message,\n                                title, JOptionPane.ERROR_MESSAGE);\n                    }\n                });\n            }\n            lastErrorTime = System.currentTimeMillis();\n        }\n        return rc; // 0 = please don't terminate\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/JavaFXFrameConverter.java",
    "content": "/*\n * Copyright (C) 2018 Samuel Audet, Johan Vos\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.bytedeco.javacv;\n\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport java.nio.IntBuffer;\nimport javafx.scene.image.Image;\nimport javafx.scene.image.PixelFormat;\nimport javafx.scene.image.PixelReader;\nimport javafx.scene.image.WritableImage;\nimport javafx.scene.image.WritablePixelFormat;\nimport javafx.scene.paint.Color;\n\n/**\n *\n * Convert Frames into JavaFX images and vice versa\n * @author johan\n */\npublic class JavaFXFrameConverter extends FrameConverter<Image> {\n\n    @Override\n    public Frame convert(Image f) {\n        throw new UnsupportedOperationException(\"conversion from Image to Frame not supported yet.\");\n    }\n\n    @Override\n    public Image convert(Frame frame) {\n        int iw = frame.imageWidth;\n        int ih = frame.imageHeight;\n        PixelReader pr = new FramePixelReader(frame);\n        WritableImage answer = new WritableImage(pr, iw, ih);\n        return answer;\n    }\n\n    class FramePixelReader implements PixelReader {\n\n        Frame frame;\n\n        FramePixelReader(Frame f) {\n            this.frame = f;\n        }\n\n        @Override\n        public PixelFormat getPixelFormat() {\n            throw new UnsupportedOperationException(\"getPixelFormat not supported yet.\");\n        }\n\n        @Override\n        public int getArgb(int x, int y) {\n            throw new UnsupportedOperationException(\"getArgb not supported yet.\");\n        }\n\n        @Override\n        public Color getColor(int x, int y) {\n            throw new UnsupportedOperationException(\"getColor not supported yet.\");\n        }\n\n        @Override\n        public <T extends Buffer> void getPixels(int x, int y, int w, int h, WritablePixelFormat<T> pixelformat, T buffer, int scanlineStride) {\n            int fss = frame.imageStride;\n            if (frame.imageChannels != 3) {\n                throw new UnsupportedOperationException(\"We only support frames with imageChannels = 3 (BGR)\");\n            }\n            if (buffer instanceof ByteBuffer) {\n                ByteBuffer bb = (ByteBuffer) buffer;\n                ByteBuffer b = (ByteBuffer) frame.image[0];\n                for (int i = y; i < y + h; i++) {\n                    for (int j = x; j < x + w; j++) {\n                        int base = 3 * j;\n                        bb.put(b.get(fss * i + base));\n                        bb.put(b.get(fss * i + base + 1));\n                        bb.put(b.get(fss * i + base + 2));\n                        bb.put((byte) 255);\n                    }\n                }\n\n            } else throw new UnsupportedOperationException (\"We only support bytebuffers at the moment\");\n        }\n\n        @Override\n        public void getPixels(int x, int y, int w, int h, WritablePixelFormat<ByteBuffer> pixelformat, byte[] buffer, int offset, int scanlineStride) {\n            throw new UnsupportedOperationException(\"getPixels<ByteBuffer> Not supported yet.\");\n        }\n\n        @Override\n        public void getPixels(int x, int y, int w, int h, WritablePixelFormat<IntBuffer> pixelformat, int[] buffer, int offset, int scanlineStride) {\n            throw new UnsupportedOperationException(\"getPixels<IntBuffer>Not supported yet.\");\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/LeptonicaFrameConverter.java",
    "content": "/*\n * Copyright (C) 2018-2021 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.Pointer;\n\nimport org.bytedeco.leptonica.*;\nimport static org.bytedeco.leptonica.global.leptonica.*;\n\n/**\n * A utility class to map data between {@link Frame} and {@link PIX},\n * which is the image data structure used by Tesseract.\n * Currently supports only plain PIX images, not FPIX or DPIX ones.\n *\n * @author Samuel Audet\n */\npublic class LeptonicaFrameConverter extends FrameConverter<PIX> {\n    static { Loader.load(org.bytedeco.leptonica.global.leptonica.class); }\n\n    PIX pix;\n    BytePointer frameData, pixData;\n    ByteBuffer frameBuffer, pixBuffer;\n\n    static boolean isEqual(Frame frame, PIX pix) {\n        return pix != null && frame != null && frame.image != null && frame.image.length > 0\n                && frame.imageWidth == pix.w() && frame.imageHeight == pix.h()\n                && frame.imageChannels == pix.d() / 8 && frame.imageDepth == Frame.DEPTH_UBYTE\n                && (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)\n                    || new Pointer(frame.image[0]).address() == pix.data().address())\n                && frame.imageStride * Math.abs(frame.imageDepth) / 8 == pix.wpl() * 4;\n    }\n\n    public PIX convert(Frame frame) {\n        if (frame == null || frame.image == null) {\n            return null;\n        } else if (frame.opaque instanceof PIX) {\n            return (PIX)frame.opaque;\n        } else if (!isEqual(frame, pix)) {\n            Pointer data;\n            if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {\n                if (pixData == null || pixData.capacity() < frame.imageHeight * frame.imageStride) {\n                    if (pixData != null) {\n                        pixData.releaseReference();\n                    }\n                    pixData = new BytePointer(frame.imageHeight * frame.imageStride).retainReference();\n                }\n                data = pixData;\n                pixBuffer = data.asByteBuffer().order(ByteOrder.BIG_ENDIAN);\n            } else {\n                data = new Pointer(frame.image[0].position(0));\n            }\n            if (pix != null) {\n                pix.releaseReference();\n            }\n            pix = PIX.create(frame.imageWidth, frame.imageHeight, frame.imageChannels * 8, data)\n                     .wpl(frame.imageStride / 4 * Math.abs(frame.imageDepth) / 8).retainReference();\n        }\n\n        if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {\n            ((ByteBuffer)pixBuffer.position(0)).asIntBuffer()\n                    .put(((ByteBuffer)frame.image[0].position(0)).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer());\n        }\n        return pix;\n    }\n\n    public Frame convert(PIX pix) {\n        if (pix == null) {\n            return null;\n        }\n\n        PIX tempPix = null;\n        if (pix.colormap() != null) {\n            tempPix = pix = pixRemoveColormap(pix, REMOVE_CMAP_TO_FULL_COLOR);\n        } else if (pix.d() < 8) {\n            switch (pix.d()) {\n                case 1:\n                    tempPix = pix = pixConvert1To8(null, pix, (byte)0, (byte)255);\n                    break;\n                case 2:\n                    tempPix = pix = pixConvert2To8(pix, (byte)0, (byte)85, (byte)170, (byte)255, 0);\n                    break;\n                case 4:\n                    tempPix = pix = pixConvert4To8(pix, 0);\n                    break;\n                default:\n                    assert false;\n            }\n        }\n\n        if (!isEqual(frame, pix)) {\n            frame = new Frame();\n            frame.imageWidth = pix.w();\n            frame.imageHeight = pix.h();\n            frame.imageDepth = Frame.DEPTH_UBYTE;\n            frame.imageChannels = pix.d() / 8;\n            frame.imageStride = pix.wpl() * 4;\n            if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {\n                if (frameData == null || frameData.capacity() < frame.imageHeight * frame.imageStride) {\n                    if (frameData != null) {\n                        frameData.releaseReference();\n                    }\n                    frameData = new BytePointer(frame.imageHeight * frame.imageStride).retainReference();\n                }\n                frameBuffer = frameData.asByteBuffer().order(ByteOrder.LITTLE_ENDIAN);\n                frame.opaque = frameData;\n                frame.image = new Buffer[] { frameBuffer };\n            } else {\n                if (tempPix != null) {\n                    if (this.pix != null) {\n                        this.pix.releaseReference();\n                    }\n                    this.pix = pix = pix.clone();\n                }\n                frame.opaque = pix;\n                frame.image = new Buffer[] { pix.createBuffer() };\n            }\n        }\n\n        if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {\n            ((ByteBuffer)frameBuffer.position(0)).asIntBuffer()\n                    .put(pix.createBuffer().order(ByteOrder.BIG_ENDIAN).asIntBuffer());\n        }\n\n        if (tempPix != null) {\n            pixDestroy(tempPix);\n        }\n        return frame;\n    }\n\n    @Override public void close() {\n        super.close();\n        if (pix != null) {\n            pix.releaseReference();\n            pix = null;\n        }\n        if (pixData != null) {\n            pixData.releaseReference();\n            pixData = null;\n        }\n        if (frameData != null) {\n            frameData.releaseReference();\n            frameData = null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/LibgdxFrameConverter.java",
    "content": "/*\n * Copyright (C) 2025 shan-luan\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport com.badlogic.gdx.graphics.Pixmap;\n\nimport java.nio.ByteBuffer;\n\n/**\n * A utility class for converting between {@link Pixmap} and {@link Frame}.\n * The alpha channel is not converted and memory cannot be shared.\n *\n * @author shan-luan\n */\npublic class LibgdxFrameConverter extends FrameConverter<Pixmap> {\n    /**\n     * Converts a {@link Pixmap} to a {@link Frame}.\n     *\n     * @param pixmap the Pixmap to convert,RGBA8888 format\n     * @return the converted Frame\n     */\n    @Override\n    public Frame convert(Pixmap pixmap) {\n        if (pixmap == null) return null;\n\n        Frame frame = new Frame(pixmap.getWidth(), pixmap.getHeight(), Frame.DEPTH_UBYTE, 3, pixmap.getWidth() * 3);\n\n        ByteBuffer pixmapBuffer = pixmap.getPixels().duplicate();\n        ByteBuffer frameBuffer = (ByteBuffer) frame.image[0];\n\n        int numPixels = pixmap.getWidth() * pixmap.getHeight();\n        for (int i = 0; i < numPixels; i++) {\n            byte r = pixmapBuffer.get();\n            byte g = pixmapBuffer.get();\n            byte b = pixmapBuffer.get();\n            pixmapBuffer.position(pixmapBuffer.position() + 1);\n\n            frameBuffer.put(b);\n            frameBuffer.put(g);\n            frameBuffer.put(r);\n        }\n\n        frameBuffer.flip();\n        return frame;\n    }\n\n    /**\n     * Converts a {@link Frame} to a {@link Pixmap}.\n     *\n     * @param frame the Frame to convert\n     * @return the converted Pixmap, RGBA8888 format\n     */\n    @Override\n    public Pixmap convert(Frame frame) {\n        if (frame == null || frame.image[0] == null) return null;\n        Pixmap pixmap = new Pixmap(frame.imageWidth, frame.imageHeight, Pixmap.Format.RGBA8888);\n        ByteBuffer frameBuffer = ((ByteBuffer) frame.image[0]).duplicate();\n        ByteBuffer pixmapBuffer = pixmap.getPixels();\n        pixmapBuffer.position(0);\n        frameBuffer.rewind();\n\n        int numPixels = frame.imageWidth * frame.imageHeight;\n        for (int i = 0; i < numPixels; i++) {\n            byte b = frameBuffer.get();\n            byte g = frameBuffer.get();\n            byte r = frameBuffer.get();\n\n            pixmapBuffer.put(r);\n            pixmapBuffer.put(g);\n            pixmapBuffer.put(b);\n            pixmapBuffer.put((byte) -1);// alpha always set to 255\n        }\n\n        pixmapBuffer.flip();\n        return pixmap;\n    }\n\n    /**\n     * Converts a {@link Frame} to a {@link Pixmap}.\n     * Available only when the format of the Frame is {@link org.bytedeco.ffmpeg.global.avutil\n     * #AV_PIX_FMT_RGBA}.\n     * Faster than {@link #convert(Frame)}\n     *\n     * @param frame the Frame to convert\n     * @return the converted Pixmap, RGBA8888 format\n     */\n    public Pixmap fastConvert(Frame frame) {\n        if (frame == null || frame.image[0] == null) return null;\n\n        Pixmap pixmap = new Pixmap(frame.imageWidth, frame.imageHeight, Pixmap.Format.RGBA8888);\n        ByteBuffer frameBuffer = ((ByteBuffer) frame.image[0]).duplicate();\n        ByteBuffer pixmapBuffer = pixmap.getPixels();\n        pixmapBuffer.position(0);\n        frameBuffer.rewind();\n        pixmapBuffer.put(frameBuffer);\n        pixmapBuffer.flip();\n        return pixmap;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/MarkedPlane.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport org.bytedeco.opencv.opencv_calib3d.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_calib3d.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class MarkedPlane {\n\n    public MarkedPlane(int width, int height, Marker[] planeMarkers, double superScale) {\n        this(width, height, planeMarkers, false, CvScalar.BLACK, CvScalar.WHITE, superScale);\n    }\n    public MarkedPlane(int width, int height, Marker[] markers,\n            boolean initPrewarp, CvScalar foregroundColor, CvScalar backgroundColor, double superScale) {\n        this.markers = markers;\n        this.foregroundColor = foregroundColor;\n        this.backgroundColor = backgroundColor;\n\n//        this.srcPts    = CvMat.create(planeMarkers.length*4, 2);\n//        this.dstPts    = CvMat.create(planeMarkers.length*4, 2);\n\n        this.prewarp = null;\n//        this.totalWarp = CvMat.create(3, 3);\n//        this.tempWarp = CvMat.create(3, 3);\n\n        if (initPrewarp) {\n            prewarp = CvMat.create(3, 3);\n            double minx = Double.MAX_VALUE, miny = Double.MAX_VALUE,\n                   maxx = Double.MIN_VALUE, maxy = Double.MIN_VALUE;\n            for (Marker m : markers) {\n                double[] c = m.corners;\n                minx = Math.min(Math.min(Math.min(Math.min(minx, c[0]), c[2]), c[4]), c[6]);\n                miny = Math.min(Math.min(Math.min(Math.min(miny, c[1]), c[3]), c[5]), c[7]);\n                maxx = Math.max(Math.max(Math.max(Math.max(maxx, c[0]), c[2]), c[4]), c[6]);\n                maxy = Math.max(Math.max(Math.max(Math.max(maxy, c[1]), c[3]), c[5]), c[7]);\n            }\n            double aspect = (maxx-minx)/(maxy-miny);\n            if (aspect > (double)width/height) {\n                double h = (double)width/aspect;\n//                srcPtsBuf.position(0); srcPtsBuf.put(new double[] { minx, miny, maxx, miny, maxx, maxy, minx, maxy });\n//                dstPtsBuf.position(0); dstPtsBuf.put(new double[] { 0, height-h, width, height-h, width, height, 0, height });\n//                srcPts.height = dstPts.height = 4;\n//                cvFindHomography(srcPts, dstPts, preWarp);\n                JavaCV.getPerspectiveTransform(\n                        new double[] { minx, miny, maxx, miny, maxx, maxy, minx, maxy },\n                        new double[] { 0, height-h, width, height-h, width, height, 0, height }, prewarp);\n            } else {\n                double w = height*aspect;\n//                srcPtsBuf.position(0); srcPtsBuf.put(new double[] { minx, miny, maxx, miny, maxx, maxy, minx, maxy });\n//                dstPtsBuf.position(0); dstPtsBuf.put(new double[] { 0, 0, w, 0, w, height, 0, height });\n//                srcPts.height = dstPts.height = 4;\n//                cvFindHomography(srcPts, dstPts, preWarp);\n                JavaCV.getPerspectiveTransform(\n                        new double[] { minx, miny, maxx, miny, maxx, maxy, minx, maxy },\n                        new double[] { 0, 0, w, 0, w, height, 0, height }, prewarp);\n            }\n        }\n\n        if (width > 0 && height > 0) {\n            planeImage = IplImage.create(width, height, IPL_DEPTH_8U, 1);\n            if (superScale == 1.0) {\n                superPlaneImage = null;\n            } else {\n                superPlaneImage = IplImage.create((int)Math.ceil(width*superScale),\n                        (int)Math.ceil(height*superScale), IPL_DEPTH_8U, 1);\n            }\n            setPrewarp(prewarp);\n        }\n\n        localSrcPts = CvMat.createThreadLocal(markers.length*4, 2);\n        localDstPts = CvMat.createThreadLocal(markers.length*4, 2);\n    }\n\n    private Marker[] markers = null;\n//    private CvPoint tempPts = new CvPoint(4);\n//    private CvMat srcPts, dstPts;\n    private CvMat prewarp;//, totalWarp, tempWarp;\n\n    private IplImage planeImage = null, superPlaneImage = null;\n    private CvScalar foregroundColor, backgroundColor;\n\n    private ThreadLocal<CvMat> localSrcPts, localDstPts;\n\n    public CvScalar getForegroundColor() {\n        return foregroundColor;\n    }\n    public void setForegroundColor(CvScalar foregroundColor) {\n        this.foregroundColor = foregroundColor;\n        setPrewarp(prewarp);\n    }\n\n    public CvScalar getBackgroundColor() {\n        return backgroundColor;\n    }\n    public void setBackgroundColor(CvScalar backgroundColor) {\n        this.backgroundColor = backgroundColor;\n        setPrewarp(prewarp);\n    }\n\n    public Marker[] getMarkers() {\n        return markers;\n    }\n    public void setColors(CvScalar foregroundColor, CvScalar backgroundColor) {\n        this.foregroundColor = foregroundColor;\n        this.backgroundColor = backgroundColor;\n        setPrewarp(prewarp);\n    }\n\n    public CvMat getPrewarp() {\n        return prewarp;\n    }\n    public void setPrewarp(CvMat prewarp) {\n        this.prewarp = prewarp;\n        if (superPlaneImage == null) {\n            cvSet(planeImage, backgroundColor);\n        } else {\n            cvSet(superPlaneImage, backgroundColor);\n        }\n        for (int i = 0; i < markers.length; i++) {\n            if (superPlaneImage == null) {\n                markers[i].draw(planeImage, foregroundColor, 1.0, prewarp);\n            } else {\n                markers[i].draw(superPlaneImage, foregroundColor, (double)\n                        superPlaneImage.width()/planeImage.width(), prewarp);\n            }\n        }\n        if (superPlaneImage != null) {\n            cvResize(superPlaneImage, planeImage, CV_INTER_AREA);\n        }\n        //cvSaveImage(\"planeImage.png\", planeImage);\n    }\n\n    public IplImage getImage() {\n        return planeImage;\n    }\n    public int getWidth() {\n        return planeImage.width();\n    }\n    public int getHeight() {\n        return planeImage.height();\n    }\n\n    public double getTotalWarp(Marker[] imagedMarkers, CvMat totalWarp) {\n        return getTotalWarp(imagedMarkers, totalWarp, false);\n    }\n    private static ThreadLocal<CvMat>\n            tempWarp3x3 = CvMat.createThreadLocal(3, 3);\n    public double getTotalWarp(Marker[] imagedMarkers, CvMat totalWarp, boolean useCenters) {\n        double rmse = Double.POSITIVE_INFINITY;\n        int pointsPerMarker = useCenters ? 1 : 4;\n\n        CvMat srcPts = localSrcPts.get(); srcPts.rows(markers.length*pointsPerMarker);\n        CvMat dstPts = localDstPts.get(); dstPts.rows(markers.length*pointsPerMarker);\n\n        int numPoints = 0;\n        for (Marker m1 : markers) {\n            for (Marker m2 : imagedMarkers) {\n                if (m1.id == m2.id) {\n                    if (useCenters) {\n                        srcPts.put(numPoints*2, m1.getCenter());\n                        dstPts.put(numPoints*2, m2.getCenter());\n                    } else {\n                        srcPts.put(numPoints*2, m1.corners);\n                        dstPts.put(numPoints*2, m2.corners);\n                    }\n                    numPoints += pointsPerMarker;\n                    break;\n                }\n            }\n        }\n\n        if (numPoints > 4 || (srcPts.rows() == 4 && numPoints == 4)) {\n            // compute homography ... should we use a robust method?\n            srcPts.rows(numPoints); dstPts.rows(numPoints);\n            if (numPoints == 4) {\n                JavaCV.getPerspectiveTransform(srcPts.get(), dstPts.get(), totalWarp);\n            } else {\n                cvCopy(cvMat(findHomography(cvarrToMat(srcPts), cvarrToMat(dstPts))), totalWarp);\n            }\n\n            // compute transformed source<->dest RMSE\n            srcPts.cols(1); srcPts.type(CV_64F, 2);\n            dstPts.cols(1); dstPts.type(CV_64F, 2);\n            cvPerspectiveTransform(srcPts, srcPts, totalWarp);\n            srcPts.cols(2); srcPts.type(CV_64F, 1);\n            dstPts.cols(2); dstPts.type(CV_64F, 1);\n\n            rmse = 0;\n            for (int i = 0; i < numPoints; i++) {\n                double dx = dstPts.get(i*2  )-srcPts.get(i*2  );\n                double dy = dstPts.get(i*2+1)-srcPts.get(i*2+1);\n                rmse += dx*dx+dy*dy;\n            }\n            rmse = Math.sqrt(rmse/numPoints);\n//            System.out.println(rmse);\n\n            if (prewarp != null) {\n                // remove pre-warp from total warp\n                CvMat tempWarp = tempWarp3x3.get();\n                cvInvert(prewarp, tempWarp);\n                cvMatMul(totalWarp, tempWarp, totalWarp);\n            }\n//            System.out.println(\"totalWarp:\\n\" + totalWarp);\n        }\n        return rmse;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/Marker.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\n\nimport org.bytedeco.artoolkitplus.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.artoolkitplus.global.ARToolKitPlus.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class Marker implements Cloneable {\n    public Marker(int id, double[] corners, double confidence) {\n        this.id = id;\n        this.corners = corners;\n        this.confidence = confidence;\n    }\n    public Marker(int id, double ... corners) {\n        this(id, corners, 1.0);\n    }\n    @Override public Marker clone() {\n        return new Marker(id, corners.clone(), confidence);\n    }\n    public int id;\n    public double[] corners;\n    public double confidence;\n\n    @Override public int hashCode() {\n        int hash = 7;\n        hash = 37 * hash + this.id;\n        hash = 37 * hash + (this.corners != null ? this.corners.hashCode() : 0);\n        return hash;\n    }\n    @Override public boolean equals(Object o) {\n        if (o instanceof Marker) {\n            Marker m = (Marker)o;\n            return m.id == id && Arrays.equals(m.corners, corners);\n        }\n        return false;\n    }\n\n    public double[] getCenter() {\n        double x = 0, y = 0;\nif (true) {\n// the centroid is not what we want as it does not remain at\n// the same physical point under projective transformations..\n// But it has the advantage of averaging noise better, and does\n// give better results\n        for (int i = 0; i < 4; i++) {\n            x += corners[2*i  ];\n            y += corners[2*i+1];\n        }\n        x /= 4;\n        y /= 4;\n} else {\n        double x1 = corners[0]; double y1 = corners[1];\n        double x2 = corners[4]; double y2 = corners[5];\n        double x3 = corners[2]; double y3 = corners[3];\n        double x4 = corners[6]; double y4 = corners[7];\n\n        double u = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3))/\n                   ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));\n        x = x1 + u*(x2-x1);\n        y = y1 + u*(y2-y1);\n}\n        return new double[] { x, y };\n    }\n\n    public IplImage getImage() {\n        return getImage(id);\n    }\n\n    private static IplImage imageCache[] = new IplImage[4096];\n    public static IplImage getImage(int id) {\n        if (imageCache[id] == null) {\n            imageCache[id] = IplImage.create(8, 8, IPL_DEPTH_8U, 1);\n            createImagePatternBCH(id, imageCache[id].getByteBuffer());\n        }\n        return imageCache[id];\n    }\n\n    private static final double[] src = { 0, 0, 8, 0, 8, 8, 0, 8 };\n    public void draw(IplImage image) {\n        draw(image, CvScalar.BLACK, 1, null);\n    }\n    public void draw(IplImage image, CvScalar color, double scale, CvMat prewarp) {\n        draw(image, color, scale, scale, prewarp);\n    }\n    private static ThreadLocal<CvMat>\n            H3x3      = CvMat.createThreadLocal(3, 3),\n            srcPts4x1 = CvMat.createThreadLocal(4, 1, CV_64F, 2),\n            dstPts4x1 = CvMat.createThreadLocal(4, 1, CV_64F, 2);\n    public void draw(IplImage image, CvScalar color, double scaleX, double scaleY, CvMat prewarp) {\n        CvMat H = H3x3.get();\n        JavaCV.getPerspectiveTransform(src, corners, H);\n        if (prewarp != null) {\n            cvGEMM(prewarp, H, 1, null, 0, H, 0);\n        }\n        IplImage  marker = getImage();\n        ByteBuffer  mbuf = marker.getByteBuffer();\n        CvMat     srcPts = srcPts4x1.get();\n        CvMat     dstPts = dstPts4x1.get();\n        CvPoint  tempPts = new CvPoint(4);\n\n        int h = marker.height();\n        int w = marker.width();\n        for (int y = 0; y < h; y++) {\n            for (int x = 0; x < w; x++) {\n                if (mbuf.get(y*w + x) == 0) {\n                    srcPts.put((double)x, y,  x+1, y,  x+1, y+1,  x, y+1);\n                    //System.out.println(\"srcPts\" + srcPts);\n                    cvPerspectiveTransform(srcPts, dstPts, H);\n                    //System.out.println(\"dstPts\" + dstPts);\n\n                    double centerx = 0, centery = 0;\n                    for (int i = 0; i < 4; i++) {\n                      centerx += dstPts.get(i*2  );\n                      centery += dstPts.get(i*2+1);\n                    }\n                    centerx /= 4;\n                    centery /= 4;\n                    for (int i = 0; i < 4; i++) {\n                        double a = dstPts.get(i*2  );\n                        double b = dstPts.get(i*2+1);\n                        double dx = centerx - a;\n                        double dy = centery - b;\n                        dx = dx < 0 ? -1 : 0;\n                        dy = dy < 0 ? -1 : 0;\n                        tempPts.position(i).x((int)Math.round((a*scaleX + dx) * (1<<16)));\n                        tempPts.position(i).y((int)Math.round((b*scaleY + dy) * (1<<16)));\n                    }\n                    cvFillConvexPoly(image, tempPts.position(0), 4, color, 8/*CV_AA*/, 16);\n                }\n            }\n        }\n    }\n\n    public static class ArraySettings extends BaseChildSettings {\n        int rows = 8, columns = 12;\n        double sizeX = 200, sizeY = 200, spacingX = 300, spacingY = 300;\n        boolean checkered = true;\n\n        public int getRows() {\n            return rows;\n        }\n        public void setRows(int rows) {\n            firePropertyChange(\"rows\", this.rows, this.rows = rows);\n        }\n\n        public int getColumns() {\n            return columns;\n        }\n        public void setColumns(int columns) {\n            firePropertyChange(\"columns\", this.columns, this.columns = columns);\n        }\n\n        public double getSizeX() {\n            return sizeX;\n        }\n        public void setSizeX(double sizeX) {\n            firePropertyChange(\"sizeX\", this.sizeX, this.sizeX = sizeX);\n        }\n        public double getSizeY() {\n            return sizeY;\n        }\n        public void setSizeY(double sizeY) {\n            firePropertyChange(\"sizeY\", this.sizeY, this.sizeY = sizeY);\n        }\n\n        public double getSpacingX() {\n            return spacingX;\n        }\n        public void setSpacingX(double spacingX) {\n            firePropertyChange(\"spacingX\", this.spacingX, this.spacingX = spacingX);\n        }\n        public double getSpacingY() {\n            return spacingY;\n        }\n        public void setSpacingY(double spacingY) {\n            firePropertyChange(\"spacingY\", this.spacingY, this.spacingY = spacingY);\n        }\n\n        public boolean isCheckered() {\n            return checkered;\n        }\n        public void setCheckered(boolean checkered) {\n            firePropertyChange(\"checkered\", this.checkered, this.checkered = checkered);\n        }\n    }\n    public static Marker[][] createArray(ArraySettings settings) {\n        return createArray(settings, 0, 0);\n    }\n    public static Marker[][] createArray(ArraySettings settings, double marginx, double marginy) {\n        Marker[] markers = new Marker[settings.rows*settings.columns];\n        int id = 0;\n        for (int y = 0; y < settings.rows; y++) {\n            for (int x = 0; x < settings.columns; x++) {\n                double sx =   settings.sizeX/2;\n                double sy =   settings.sizeY/2;\n                double cx = x*settings.spacingX + sx + marginx;\n                double cy = y*settings.spacingY + sy + marginy;\n                markers[id] = new Marker(id, new double[] {\n                    cx-sx, cy-sy,  cx+sx, cy-sy,  cx+sx, cy+sy,  cx-sx, cy+sy }, 1);\n                id++;\n            }\n        }\n        if (!settings.checkered) {\n            return new Marker[][] { markers };\n        } else {\n            Marker[] markers1 = new Marker[markers.length/2];\n            Marker[] markers2 = new Marker[markers.length/2];\n            for (int i = 0; i < markers.length; i++) {\n                int x = i%settings.columns;\n                int y = i/settings.columns;\n                if (x%2==0 ^ y%2==0) {\n                    markers2[i/2] = markers[i];\n                } else {\n                    markers1[i/2] = markers[i];\n                }\n            }\n            return new Marker[][] { markers2, markers1 };\n        }\n    }\n    public static Marker[][] createArray(int rows, int columns, double sizeX, double sizeY,\n            double spacingX, double spacingY, boolean checkered, double marginx, double marginy) {\n        ArraySettings s = new ArraySettings();\n        s.rows      = rows;      s.columns  = columns;\n        s.sizeX     = sizeX;     s.sizeY    = sizeY;\n        s.spacingX  = spacingX;  s.spacingY = spacingY;\n        s.checkered = checkered;\n        return createArray(s, marginx, marginy);\n    }\n\n    public static void applyWarp(Marker[] markers, CvMat warp) {\n        CvMat pts = srcPts4x1.get();\n\n        for (Marker m : markers) {\n            cvPerspectiveTransform(pts.put(m.corners), pts, warp);\n            pts.get(m.corners);\n        }\n    }\n\n    @Override public String toString() {\n        String s = \"[\" + id + \": \" +\n                \"(\" + corners[0] + \", \" + corners[1] + \") \" +\n                \"(\" + corners[2] + \", \" + corners[3] + \") \" +\n                \"(\" + corners[4] + \", \" + corners[5] + \") \" +\n                \"(\" + corners[6] + \", \" + corners[7] + \")]\";\n        return s;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/MarkerDetector.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.util.Arrays;\nimport org.bytedeco.javacpp.IntPointer;\n\nimport org.bytedeco.artoolkitplus.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.artoolkitplus.global.ARToolKitPlus.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class MarkerDetector {\n    public MarkerDetector(Settings settings) {\n        setSettings(settings);\n    }\n    public MarkerDetector() {\n        this(new Settings());\n    }\n\n    // the k's will depend strongly on the ratio between ambient light\n    // (including minimum projector intensity) and the intensity level\n    // used for the projector markers... this is because we use binary\n    // thresholding while we actually have three levels..\n    public static class Settings extends BaseChildSettings {\n        int thresholdWindowMin = 5;\n        int thresholdWindowMax = 63;\n        double thresholdVarMultiplier = 1.0;\n        double thresholdKBlackMarkers = 0.6;\n        double thresholdKWhiteMarkers = 1.0;\n        int subPixelWindow = 11;\n\n        public int getThresholdWindowMin() {\n            return thresholdWindowMin;\n        }\n        public void setThresholdWindowMin(int thresholdWindowMin) {\n            this.thresholdWindowMin = thresholdWindowMin;\n        }\n\n        public int getThresholdWindowMax() {\n            return thresholdWindowMax;\n        }\n        public void setThresholdWindowMax(int thresholdWindowMax) {\n            this.thresholdWindowMax = thresholdWindowMax;\n        }\n\n        public double getThresholdVarMultiplier() {\n            return thresholdVarMultiplier;\n        }\n        public void setThresholdVarMultiplier(double thresholdVarMultiplier) {\n            this.thresholdVarMultiplier = thresholdVarMultiplier;\n        }\n\n        public double getThresholdKBlackMarkers() {\n            return thresholdKBlackMarkers;\n        }\n        public void setThresholdKBlackMarkers(double thresholdKBlackMarkers) {\n            this.thresholdKBlackMarkers = thresholdKBlackMarkers;\n        }\n\n        public double getThresholdKWhiteMarkers() {\n            return thresholdKWhiteMarkers;\n        }\n        public void setThresholdKWhiteMarkers(double thresholdKWhiteMarkers) {\n            this.thresholdKWhiteMarkers = thresholdKWhiteMarkers;\n        }\n\n        public int getSubPixelWindow() {\n            return subPixelWindow;\n        }\n        public void setSubPixelWindow(int subPixelWindow) {\n            this.subPixelWindow = subPixelWindow;\n        }\n    }\n\n    private Settings settings;\n    public Settings getSettings() {\n        return settings;\n    }\n    public void setSettings(Settings settings) {\n        this.settings = settings;\n        this.subPixelSize = cvSize(settings.subPixelWindow/2, settings.subPixelWindow/2);\n        this.subPixelZeroZone = cvSize(-1,-1);\n        this.subPixelTermCriteria = cvTermCriteria(CV_TERMCRIT_EPS, 100, 0.001);\n    }\n\n    private MultiTracker tracker = null;\n    private IntPointer markerNum = new IntPointer(1);\n    private int width = 0, height = 0, depth = 0, channels = 0;\n    private IplImage tempImage, tempImage2, sumImage, sqSumImage, thresholdedImage;\n    private CvMat points = CvMat.create(1, 4, CV_32F, 2);\n    private CvPoint2D32f corners = new CvPoint2D32f(4);\n    private CvMemStorage memory = CvMemStorage.create();\n    private CvSize subPixelSize = null, subPixelZeroZone = null;\n    private CvTermCriteria subPixelTermCriteria = null;\n\n    private CvFont font = cvFont(1, 1);\n    private CvSize textSize = new CvSize();\n\n    public IplImage getThresholdedImage() {\n        return thresholdedImage;\n    }\n\n    private void init(IplImage image) {\n        if (tracker != null && image.width() == width && image.height() == height &&\n                image.depth() == depth && image.nChannels() == channels) {\n            return;\n        }\n\n        width    = image.width();\n        height   = image.height();\n        depth    = image.depth();\n        channels = image.nChannels();\n\n        if (depth != IPL_DEPTH_8U || channels > 1) {\n            tempImage    = IplImage.create(width, height, IPL_DEPTH_8U, 1);\n        }\n        if (depth != IPL_DEPTH_8U && channels > 1) {\n            tempImage2   = IplImage.create(width, height, IPL_DEPTH_8U, 3);\n        }\n        sumImage         = IplImage.create(width+1, height+1, IPL_DEPTH_64F, 1);\n        sqSumImage       = IplImage.create(width+1, height+1, IPL_DEPTH_64F, 1);\n        thresholdedImage = IplImage.create(width,   height,   IPL_DEPTH_8U,  1);\n\n        tracker = new MultiTracker(thresholdedImage.widthStep(), thresholdedImage.height());\n\n//        if (depth != IPL_DEPTH_8U) {\n//            throw new Exception(\"Unsupported format: IplImage must have depth == IPL_DEPTH_8U.\");\n//        }\n        int pixfmt = PIXEL_FORMAT_LUM;\n//        switch (nChannels) {\n//            case 4: pixfmt = PIXEL_FORMAT_BGRA; break;\n//            case 3: pixfmt = PIXEL_FORMAT_BGR;  break;\n//            case 1: pixfmt = PIXEL_FORMAT_LUM;  break;\n//            default:\n//                throw new Exception(\"Unsupported format: No support for IplImage with \" + channels + \" channels.\");\n//        }\n        tracker.setPixelFormat(pixfmt);\n//        if(!tracker.init(\"data/LogitechPro4000.dat\",\n//                   \"data/markerboard_480-499.cfg\", 1.0f, 1000.0f, null)) {\n//            throw new Exception(\"ERROR: init() failed.\");\n//        }\n        tracker.setBorderWidth(0.125f);\n//        tracker.setThreshold(128);\n//        tracker.activateAutoThreshold(true);\n//        tracker.setNumAutoThresholdRetries(10);\n        tracker.setUndistortionMode(UNDIST_NONE);\n//        tracker.setPoseEstimator(POSE_ESTIMATOR_RPP);\n        tracker.setMarkerMode(MARKER_ID_BCH);\n        tracker.setImageProcessingMode(IMAGE_FULL_RES);\n    }\n\n    public Marker[] detect(IplImage image, boolean whiteMarkers) {\n        init(image);\n\n        if (depth != IPL_DEPTH_8U && channels > 1) {\n            cvConvertScale(image, tempImage2, 255/image.highValue(), 0);\n            cvCvtColor(tempImage2, tempImage, channels > 3 ? CV_RGBA2GRAY : CV_BGR2GRAY);\n            image = tempImage;\n        } else if (depth != IPL_DEPTH_8U) {\n            cvConvertScale(image, tempImage, 255/image.highValue(), 0);\n            image = tempImage;\n        } else if (channels > 1) {\n            cvCvtColor(image, tempImage, channels > 3 ? CV_RGBA2GRAY : CV_BGR2GRAY);\n            image = tempImage;\n        }\n//long time1 = System.currentTimeMillis();\n        JavaCV.adaptiveThreshold(image, sumImage, sqSumImage, thresholdedImage, whiteMarkers,\n                settings.thresholdWindowMax, settings.thresholdWindowMin, settings.thresholdVarMultiplier,\n                whiteMarkers ? settings.thresholdKWhiteMarkers : settings.thresholdKBlackMarkers);\n//CanvasFrame.global.showImage(thresholded, 0.5);\n//CanvasFrame.global.waitKey();\n//long time2 = System.currentTimeMillis();\n\n        int n = 0;\n        ARMarkerInfo markers = new ARMarkerInfo(null);\n        tracker.arDetectMarkerLite(thresholdedImage.imageData(), 128 /*tracker.getThreshold()*/, markers, markerNum);\n//long time3 = System.currentTimeMillis();\n        Marker[] markers2 = new Marker[markerNum.get(0)];\n        for (int i = 0; i < markers2.length && !markers.isNull(); i++) {\n            markers.position(i);\n            int id = markers.id();\n            if (id < 0) {\n                // no detected ID...\n                continue;\n            }\n            int dir = markers.dir();\n            float confidence = markers.cf();\n            float[] vertex = new float[8];\n            markers.vertex().get(vertex);\n\n            int w = settings.subPixelWindow/2+1;\n            if (vertex[0]-w < 0 || vertex[0]+w >= width || vertex[1]-w < 0 || vertex[1]+w >= height ||\n                vertex[2]-w < 0 || vertex[2]+w >= width || vertex[3]-w < 0 || vertex[3]+w >= height ||\n                vertex[4]-w < 0 || vertex[4]+w >= width || vertex[5]-w < 0 || vertex[5]+w >= height ||\n                vertex[6]-w < 0 || vertex[6]+w >= width || vertex[7]-w < 0 || vertex[7]+w >= height) {\n                // too tight for cvFindCornerSubPix...\n                    continue;\n            }\n\n            points.getFloatBuffer().put(vertex);\n            CvBox2D box = cvMinAreaRect2(points, memory);\n            float bw = box.size().width();\n            float bh = box.size().height();\n            cvClearMemStorage(memory);\n            if (bw <= 0 || bh <= 0 || bw/bh < 0.1 || bw/bh > 10) {\n                // marker is too \"flat\" to have been IDed correctly...\n                continue;\n            }\n\n            for (int j = 0; j < 4; j++) {\n                corners.position(j).put(vertex[2*j], vertex[2*j+1]);\n            }\n\nif (false) {\n            // move the search window a bit (max 1/4 of the window) toward the center...\n            // this allows us to cram more markers closer to one another\n            double cx = 0, cy = 0;\n            for (int j = 0; j < 4; j++) {\n                corners.position(j);\n                cx += corners.x();\n                cy += corners.y();\n            }\n            cx /= 4;\n            cy /= 4;\n            for (int j = 0; j < 4; j++) {\n                corners.position(j);\n                float x = corners.x();\n                float y = corners.y();\n                double dx = cx - x;\n                double dy = cy - y;\n                corners.x(x + (float)Math.signum(dx)*(settings.subPixelWindow/4));\n                corners.y(y + (float)Math.signum(dy)*(settings.subPixelWindow/4));\n            }\n}\n            cvFindCornerSubPix(image, corners.position(0), 4, subPixelSize, subPixelZeroZone, subPixelTermCriteria);\n            double[] d = { corners.position((4-dir)%4).x(), corners.position((4-dir)%4).y(),\n                           corners.position((5-dir)%4).x(), corners.position((5-dir)%4).y(),\n                           corners.position((6-dir)%4).x(), corners.position((6-dir)%4).y(),\n                           corners.position((7-dir)%4).x(), corners.position((7-dir)%4).y() };\n\n            markers2[n++] = new Marker(id, d, confidence);\n        }\n//long time4 = System.currentTimeMillis();\n//System.out.println(\"thresholdTime = \" + (time2-time1) + \"  detectTime = \" + (time3-time2) + \"  subPixTime = \" + (time4-time3));\n\n        //cvCvtColor(thresholdedImage, image, CV_GRAY2BGR);\n        //cvCopy(thresholdedImage, image, null);\n\n        return Arrays.copyOf(markers2, n);\n    }\n\n    public void draw(IplImage image, Marker[] markers) {\n        for (Marker m : markers) {\n            int cx = 0, cy = 0;\n            int[] pts = new int[8];\n            for (int i = 0; i < 4; i++) {\n                int x = (int)Math.round(m.corners[i*2  ] * (1<<16));\n                int y = (int)Math.round(m.corners[i*2+1] * (1<<16));\n                pts[2*i    ] = x;\n                pts[2*i + 1] = y;\n                cx += x;\n                cy += y;\n\n// draw little colored squares in corners to confirm that the corners\n// are returned in the right order...\n//                CvPoint pt2a = cvPoint(pts[i].x+200000, pts[i].y+200000);\n//                cvRectangle(image, pts, pt2a,\n//                        i == 0? CV_RGB(maxIntensity, 0, 0) :\n//                            i == 1? CV_RGB(0, maxIntensity, 0) :\n//                                i == 2? CV_RGB(0, 0, maxIntensity) :\n//                                    CV_RGB(maxIntensity, maxIntensity, maxIntensity),\n//                        CV_FILLED, CV_AA, 16);\n            }\n            cx /= 4;\n            cy /= 4;\n\n            cvPolyLine(image, pts, new int[] { pts.length/2 }, 1, 1, CV_RGB(0, 0, image.highValue()), 1, CV_AA, 16);\n\n            String text = Integer.toString(m.id);\n            int[] baseline = new int[1];\n            cvGetTextSize(text, font, textSize, baseline);\n\n            int[] pt1 = { cx - (textSize.width() *3/2 << 16)/2,\n                          cy + (textSize.height()*3/2 << 16)/2 };\n            int[] pt2 = { cx + (textSize.width() *3/2 << 16)/2,\n                          cy - (textSize.height()*3/2 << 16)/2 };\n            cvRectangle(image, pt1, pt2, CV_RGB(0, image.highValue(), 0), CV_FILLED, CV_AA, 16);\n\n            int[] pt = { (int)Math.round((double)cx/(1<<16) - textSize.width()/2),\n                         (int)Math.round((double)cy/(1<<16) + textSize.height()/2) + 1 };\n            cvPutText(image, text, pt, font, CvScalar.BLACK);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ObjectFinder.java",
    "content": "/*\n * Copyright (C) 2009-2015 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n *\n * Adapted from the find_obj.cpp sample in the source package of OpenCV 2.3.1:\n *\n * A Demo to OpenCV Implementation of SURF\n * Further Information Refer to \"SURF: Speed-Up Robust Feature\"\n * Author: Liu Liu\n * liuliu.1987+opencv@gmail.com\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.ByteBuffer;\nimport java.nio.FloatBuffer;\nimport java.nio.IntBuffer;\nimport java.util.ArrayList;\nimport java.util.logging.Logger;\n\nimport org.bytedeco.opencv.opencv_calib3d.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_features2d.*;\nimport org.bytedeco.opencv.opencv_flann.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_calib3d.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_features2d.*;\nimport static org.bytedeco.opencv.global.opencv_flann.*;\nimport static org.bytedeco.opencv.global.opencv_imgcodecs.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n *\n * ObjectFinder does not work out-of-the-box under Android, because it lacks the standard\n * java.beans.beancontext package. We can work around it by doing the following *BEFORE*\n * following the instructions in the README.md file:\n *\n * 1. Remove BaseChildSettings.class and BaseSettings.class from javacv.jar\n * 2. Follow the instructions in the README.md file\n * 3. In your project, define empty classes BaseChildSettings and BaseSettings under the org.bytedeco.javacv package name\n */\npublic class ObjectFinder {\n    public ObjectFinder(IplImage objectImage) {\n        settings = new Settings();\n        settings.objectImage = objectImage;\n        setSettings(settings);\n    }\n    public ObjectFinder(Settings settings) {\n        setSettings(settings);\n    }\n\n    public static class Settings extends BaseChildSettings {\n        IplImage objectImage = null;\n        AKAZE detector = AKAZE.create();\n        double distanceThreshold = 0.75;\n        int matchesMin = 4;\n        double ransacReprojThreshold = 1.0;\n        boolean useFLANN = false;\n\n        public IplImage getObjectImage() {\n            return objectImage;\n        }\n        public void setObjectImage(IplImage objectImage) {\n            this.objectImage = objectImage;\n        }\n\n        public int getDescriptorType() {\n            return detector.getDescriptorType();\n        }\n        public void setDescriptorType(int dtype) {\n            detector.setDescriptorType(dtype);\n        }\n\n        public int getDescriptorSize() {\n            return detector.getDescriptorSize();\n        }\n        public void setDescriptorSize(int dsize) {\n            detector.setDescriptorSize(dsize);\n        }\n\n        public int getDescriptorChannels() {\n            return detector.getDescriptorChannels();\n        }\n        public void setDescriptorChannels(int dch) {\n            detector.setDescriptorChannels(dch);\n        }\n\n        public double getThreshold() {\n            return detector.getThreshold();\n        }\n        public void setThreshold(double threshold) {\n            detector.setThreshold(threshold);\n        }\n\n        public int getNOctaves() {\n            return detector.getNOctaves();\n        }\n        public void setNOctaves(int nOctaves) {\n            detector.setNOctaves(nOctaves);\n        }\n\n        public int getNOctaveLayers() {\n            return detector.getNOctaveLayers();\n        }\n        public void setNOctaveLayers(int nOctaveLayers) {\n            detector.setNOctaveLayers(nOctaveLayers);\n        }\n\n        public double getDistanceThreshold() {\n            return distanceThreshold;\n        }\n        public void setDistanceThreshold(double distanceThreshold) {\n            this.distanceThreshold = distanceThreshold;\n        }\n\n        public int getMatchesMin() {\n            return matchesMin;\n        }\n        public void setMatchesMin(int matchesMin) {\n            this.matchesMin = matchesMin;\n        }\n\n        public double getRansacReprojThreshold() {\n            return ransacReprojThreshold;\n        }\n        public void setRansacReprojThreshold(double ransacReprojThreshold) {\n            this.ransacReprojThreshold = ransacReprojThreshold;\n        }\n\n        public boolean isUseFLANN() {\n            return useFLANN;\n        }\n        public void setUseFLANN(boolean useFLANN) {\n            this.useFLANN = useFLANN;\n        }\n    }\n\n    Settings settings;\n    public Settings getSettings() {\n        return settings;\n    }\n    public void setSettings(Settings settings) {\n        this.settings = settings;\n\n        objectKeypoints = new KeyPointVector();\n        objectDescriptors = new Mat();\n        settings.detector.detectAndCompute(cvarrToMat(settings.objectImage),\n                new Mat(), objectKeypoints, objectDescriptors, false);\n\n        int total = (int)objectKeypoints.size();\n        if (settings.useFLANN) {\n            indicesMat = new Mat(total, 2, CV_32SC1);\n            distsMat   = new Mat(total, 2, CV_32FC1);\n            flannIndex = new Index();\n            indexParams = new LshIndexParams(12, 20, 2); // using LSH Hamming distance\n            searchParams = new SearchParams(64, 0, true); // maximum number of leafs checked\n            searchParams.deallocate(false); // for some reason FLANN seems to do it for us\n        }\n        pt1  = new Mat(total, 1, CV_32FC2);\n        pt2  = new Mat(total, 1, CV_32FC2);\n        mask = new Mat(total, 1, CV_8UC1);\n        H    = new Mat(3, 3, CV_64FC1);\n        ptpairs = new ArrayList<Integer>(2*objectDescriptors.rows());\n        logger.info(total + \" object descriptors\");\n    }\n\n    static final Logger logger = Logger.getLogger(ObjectFinder.class.getName());\n\n    KeyPointVector objectKeypoints = null, imageKeypoints = null;\n    Mat objectDescriptors = null, imageDescriptors = null;\n    Mat indicesMat, distsMat;\n    Index flannIndex = null;\n    IndexParams indexParams = null;\n    SearchParams searchParams = null;\n    Mat pt1 = null, pt2 = null, mask = null, H = null;\n    ArrayList<Integer> ptpairs = null;\n\n    public double[] find(IplImage image) {\n        if (objectDescriptors.rows() < settings.getMatchesMin()) {\n            return null;\n        }\n        imageKeypoints = new KeyPointVector();\n        imageDescriptors = new Mat();\n        settings.detector.detectAndCompute(cvarrToMat(image),\n                new Mat(), imageKeypoints, imageDescriptors, false);\n        if (imageDescriptors.rows() < settings.getMatchesMin()) {\n            return null;\n        }\n\n        int total = (int)imageKeypoints.size();\n        logger.info(total + \" image descriptors\");\n\n        int w = settings.objectImage.width();\n        int h = settings.objectImage.height();\n        double[] srcCorners = {0, 0,  w, 0,  w, h,  0, h};\n        double[] dstCorners = locatePlanarObject(objectKeypoints, objectDescriptors,\n                imageKeypoints, imageDescriptors, srcCorners);\n        return dstCorners;\n    }\n\n    static final int[] bits = new int[256];\n    static {\n        for (int i = 0; i < bits.length; i++) {\n            for (int j = i; j != 0; j >>= 1) {\n                bits[i] += j & 0x1;\n            }\n        }\n    }\n\n    int compareDescriptors(ByteBuffer d1, ByteBuffer d2, int best) {\n        int totalCost = 0;\n        assert d1.limit() - d1.position() == d2.limit() - d2.position();\n        while (d1.position() < d1.limit()) {\n            totalCost += bits[(d1.get() ^ d2.get()) & 0xFF];\n            if (totalCost > best)\n                break;\n        }\n        return totalCost;\n    }\n\n    int naiveNearestNeighbor(ByteBuffer vec, ByteBuffer modelDescriptors) {\n        int neighbor = -1;\n        int d, dist1 = Integer.MAX_VALUE, dist2 = Integer.MAX_VALUE;\n        int size = vec.limit() - vec.position();\n\n        for (int i = 0; i * size < modelDescriptors.capacity(); i++) {\n            ByteBuffer mvec = (ByteBuffer)modelDescriptors.position(i * size).limit((i + 1) * size);\n            d = compareDescriptors((ByteBuffer)vec.reset(), mvec, dist2);\n            if (d < dist1) {\n                dist2 = dist1;\n                dist1 = d;\n                neighbor = i;\n            } else if (d < dist2) {\n                dist2 = d;\n            }\n        }\n        if (dist1 < settings.distanceThreshold*dist2)\n            return neighbor;\n        return -1;\n    }\n\n    void findPairs(Mat objectDescriptors, Mat imageDescriptors) {\n        int size = imageDescriptors.cols();\n        ByteBuffer objectBuf = objectDescriptors.createBuffer();\n        ByteBuffer imageBuf = imageDescriptors.createBuffer();\n\n        for (int i = 0; i * size < objectBuf.capacity(); i++) {\n            ByteBuffer descriptor = (ByteBuffer)objectBuf.position(i * size).limit((i + 1) * size).mark();\n            int nearestNeighbor = naiveNearestNeighbor(descriptor, imageBuf);\n            if (nearestNeighbor >= 0) {\n                ptpairs.add(i);\n                ptpairs.add(nearestNeighbor);\n            }\n        }\n    }\n\n    void flannFindPairs(Mat objectDescriptors, Mat imageDescriptors) {\n        int length = objectDescriptors.rows();\n\n        // find nearest neighbors using FLANN\n        flannIndex.build(imageDescriptors, indexParams, FLANN_DIST_HAMMING);\n        flannIndex.knnSearch(objectDescriptors, indicesMat, distsMat, 2, searchParams);\n\n        IntBuffer indicesBuf = indicesMat.createBuffer();\n        IntBuffer distsBuf = distsMat.createBuffer();\n        for (int i = 0; i < length; i++) {\n            if (distsBuf.get(2*i) < settings.distanceThreshold*distsBuf.get(2*i+1)) {\n                ptpairs.add(i);\n                ptpairs.add(indicesBuf.get(2*i));\n            }\n        }\n    }\n\n    /** a rough implementation for object location */\n    double[] locatePlanarObject(KeyPointVector objectKeypoints, Mat objectDescriptors,\n            KeyPointVector imageKeypoints, Mat imageDescriptors, double[] srcCorners) {\n        ptpairs.clear();\n        if (settings.useFLANN) {\n            flannFindPairs(objectDescriptors, imageDescriptors);\n        } else {\n            findPairs(objectDescriptors, imageDescriptors);\n        }\n        int n = ptpairs.size()/2;\n        logger.info(n + \" matching pairs found\");\n        if (n < settings.matchesMin) {\n            return null;\n        }\n\n        pt1 .resize(n);\n        pt2 .resize(n);\n        mask.resize(n);\n        FloatBuffer pt1Idx = pt1.createBuffer();\n        FloatBuffer pt2Idx = pt2.createBuffer();\n        for (int i = 0; i < n; i++) {\n            Point2f p1 = objectKeypoints.get(ptpairs.get(2*i)).pt();\n            pt1Idx.put(2*i, p1.x()); pt1Idx.put(2*i+1, p1.y());\n            Point2f p2 = imageKeypoints.get(ptpairs.get(2*i+1)).pt();\n            pt2Idx.put(2*i, p2.x()); pt2Idx.put(2*i+1, p2.y());\n        }\n\n        H = findHomography(pt1, pt2, CV_RANSAC, settings.ransacReprojThreshold, mask, 2000, 0.995);\n        if (H.empty() || countNonZero(mask) < settings.matchesMin) {\n            return null;\n        }\n\n        double[] h = (double[])H.createIndexer(false).array();\n        double[] dstCorners = new double[srcCorners.length];\n        for(int i = 0; i < srcCorners.length/2; i++) {\n            double x = srcCorners[2*i], y = srcCorners[2*i + 1];\n            double Z = 1/(h[6]*x + h[7]*y + h[8]);\n            double X = (h[0]*x + h[1]*y + h[2])*Z;\n            double Y = (h[3]*x + h[4]*y + h[5])*Z;\n            dstCorners[2*i    ] = X;\n            dstCorners[2*i + 1] = Y;\n        }\n        return dstCorners;\n    }\n\n    public static void main(String[] args) throws Exception {\n//        Logger.getLogger(\"org.bytedeco.javacv\").setLevel(Level.OFF);\n\n        String objectFilename = args.length == 2 ? args[0] : \"/usr/local/share/OpenCV/samples/c/box.png\";\n        String sceneFilename  = args.length == 2 ? args[1] : \"/usr/local/share/OpenCV/samples/c/box_in_scene.png\";\n\n        IplImage object = cvLoadImage(objectFilename, IMREAD_GRAYSCALE);\n        IplImage image  = cvLoadImage(sceneFilename,  IMREAD_GRAYSCALE);\n        if (object == null || image == null) {\n            System.err.println(\"Can not load \" + objectFilename + \" and/or \" + sceneFilename);\n            System.exit(-1);\n        }\n\n        IplImage objectColor = IplImage.create(object.width(), object.height(), 8, 3);\n        cvCvtColor(object, objectColor, CV_GRAY2BGR);\n\n        IplImage correspond = IplImage.create(image.width(), object.height()+ image.height(), 8, 1);\n        cvSetImageROI(correspond, cvRect(0, 0, object.width(), object.height()));\n        cvCopy(object, correspond);\n        cvSetImageROI(correspond, cvRect(0, object.height(), correspond.width(), correspond.height()));\n        cvCopy(image, correspond);\n        cvResetImageROI(correspond);\n\n        ObjectFinder.Settings settings = new ObjectFinder.Settings();\n        settings.objectImage = object;\n        settings.useFLANN = true;\n        settings.ransacReprojThreshold = 5;\n        ObjectFinder finder = new ObjectFinder(settings);\n\n        long start = System.currentTimeMillis();\n        double[] dst_corners = finder.find(image);\n        System.out.println(\"Finding time = \" + (System.currentTimeMillis() - start) + \" ms\");\n\n        if (dst_corners !=  null) {\n            for (int i = 0; i < 4; i++) {\n                int j = (i+1)%4;\n                int x1 = (int)Math.round(dst_corners[2*i    ]);\n                int y1 = (int)Math.round(dst_corners[2*i + 1]);\n                int x2 = (int)Math.round(dst_corners[2*j    ]);\n                int y2 = (int)Math.round(dst_corners[2*j + 1]);\n                line(cvarrToMat(correspond), new Point(x1, y1 + object.height()),\n                        new Point(x2, y2 + object.height()),\n                        Scalar.WHITE, 1, 8, 0);\n            }\n        }\n\n        for (int i = 0; i < finder.ptpairs.size(); i += 2) {\n            Point2f pt1 = finder.objectKeypoints.get(finder.ptpairs.get(i)).pt();\n            Point2f pt2 = finder.imageKeypoints.get(finder.ptpairs.get(i + 1)).pt();\n            line(cvarrToMat(correspond), new Point(Math.round(pt1.x()), Math.round(pt1.y())),\n                    new Point(Math.round(pt2.x()), Math.round(pt2.y() + object.height())),\n                    Scalar.WHITE, 1, 8, 0);\n        }\n\n        CanvasFrame objectFrame = new CanvasFrame(\"Object\");\n        CanvasFrame correspondFrame = new CanvasFrame(\"Object Correspond\");\n        OpenCVFrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n\n        correspondFrame.showImage(converter.convert(correspond));\n        for (int i = 0; i < finder.objectKeypoints.size(); i++) {\n            KeyPoint r = finder.objectKeypoints.get(i);\n            Point center = new Point(Math.round(r.pt().x()), Math.round(r.pt().y()));\n            int radius = Math.round(r.size() / 2);\n            circle(cvarrToMat(objectColor), center, radius, Scalar.RED, 1, 8, 0);\n        }\n        objectFrame.showImage(converter.convert(objectColor));\n\n        objectFrame.waitKey();\n\n        objectFrame.dispose();\n        correspondFrame.dispose();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/OpenCVFrameConverter.java",
    "content": "/*\n * Copyright (C) 2015-2021 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.Pointer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n * A utility class to map data between {@link Frame} and {@link IplImage} or {@link Mat}.\n * Since this is an abstract class, one must choose between two concrete classes:\n * {@link ToIplImage} or {@link ToMat}. {@link ToOrgOpenCvCoreMat} is also available to\n * do the same with {@link org.opencv.core.Mat} from the official Java API of OpenCV.\n *\n * @author Samuel Audet\n */\npublic abstract class OpenCVFrameConverter<F> extends FrameConverter<F> {\n    static { Loader.load(org.bytedeco.opencv.global.opencv_core.class); }\n\n    IplImage img;\n    Mat mat;\n    org.opencv.core.Mat orgOpenCvCoreMat;\n\n    public static class ToIplImage extends OpenCVFrameConverter<IplImage> {\n        @Override public Frame convert(IplImage img) { return super.convert(img); }\n        @Override public IplImage convert(Frame frame) { return convertToIplImage(frame); }\n    }\n\n    public static class ToMat extends OpenCVFrameConverter<Mat> {\n        @Override public Frame convert(Mat mat) { return super.convert(mat); }\n        @Override public Mat convert(Frame frame) { return convertToMat(frame); }\n    }\n\n    public static class ToOrgOpenCvCoreMat extends OpenCVFrameConverter<org.opencv.core.Mat> {\n        @Override public Frame convert(org.opencv.core.Mat mat) { return super.convert(mat); }\n        @Override public org.opencv.core.Mat convert(Frame frame) { return convertToOrgOpenCvCoreMat(frame); }\n    }\n\n    public static int getFrameDepth(int depth) {\n        switch (depth) {\n            case IPL_DEPTH_8U:  case CV_8U:  return Frame.DEPTH_UBYTE;\n            case IPL_DEPTH_8S:  case CV_8S:  return Frame.DEPTH_BYTE;\n            case IPL_DEPTH_16U: case CV_16U: return Frame.DEPTH_USHORT;\n            case IPL_DEPTH_16S: case CV_16S: return Frame.DEPTH_SHORT;\n            case IPL_DEPTH_32F: case CV_32F: return Frame.DEPTH_FLOAT;\n            case IPL_DEPTH_32S: case CV_32S: return Frame.DEPTH_INT;\n            case IPL_DEPTH_64F: case CV_64F: return Frame.DEPTH_DOUBLE;\n            default: return -1;\n        }\n    }\n\n    public static int getIplImageDepth(int depth) {\n        switch (depth) {\n            case Frame.DEPTH_UBYTE:  return IPL_DEPTH_8U;\n            case Frame.DEPTH_BYTE:   return IPL_DEPTH_8S;\n            case Frame.DEPTH_USHORT: return IPL_DEPTH_16U;\n            case Frame.DEPTH_SHORT:  return IPL_DEPTH_16S;\n            case Frame.DEPTH_FLOAT:  return IPL_DEPTH_32F;\n            case Frame.DEPTH_INT:    return IPL_DEPTH_32S;\n            case Frame.DEPTH_DOUBLE: return IPL_DEPTH_64F;\n            default:  return -1;\n        }\n    }\n    static boolean isEqual(Frame frame, IplImage img) {\n        return img != null && frame != null && frame.image != null && frame.image.length > 0\n                && frame.imageWidth == img.width() && frame.imageHeight == img.height()\n                && frame.imageChannels == img.nChannels() && getIplImageDepth(frame.imageDepth) == img.depth()\n                && new Pointer(frame.image[0].position(0)).address() == img.imageData().address()\n                && frame.imageStride * Math.abs(frame.imageDepth) / 8 == img.widthStep();\n    }\n    public IplImage convertToIplImage(Frame frame) {\n        if (frame == null || frame.image == null) {\n            return null;\n        } else if (frame.opaque instanceof IplImage) {\n            return (IplImage)frame.opaque;\n        } else if (!isEqual(frame, img)) {\n            int depth = getIplImageDepth(frame.imageDepth);\n            if (img != null) {\n                img.releaseReference();\n            }\n            img = depth < 0 ? null : (IplImage)IplImage.create(frame.imageWidth, frame.imageHeight, depth, frame.imageChannels, new Pointer(frame.image[0].position(0)))\n                    .widthStep(frame.imageStride * Math.abs(frame.imageDepth) / 8)\n                    .imageSize(frame.image[0].capacity() * Math.abs(frame.imageDepth) / 8).retainReference();\n        }\n        return img;\n    }\n    public Frame convert(IplImage img) {\n        if (img == null) {\n            return null;\n        } else if (!isEqual(frame, img)) {\n            frame = new Frame();\n            frame.imageWidth = img.width();\n            frame.imageHeight = img.height();\n            frame.imageDepth = getFrameDepth(img.depth());\n            frame.imageChannels = img.nChannels();\n            frame.imageStride = img.widthStep() * 8 / Math.abs(frame.imageDepth);\n            frame.image = new Buffer[] { img.createBuffer() };\n        }\n        frame.opaque = img;\n        return frame;\n    }\n\n    public static int getMatDepth(int depth) {\n        switch (depth) {\n            case Frame.DEPTH_UBYTE:  return CV_8U;\n            case Frame.DEPTH_BYTE:   return CV_8S;\n            case Frame.DEPTH_USHORT: return CV_16U;\n            case Frame.DEPTH_SHORT:  return CV_16S;\n            case Frame.DEPTH_FLOAT:  return CV_32F;\n            case Frame.DEPTH_INT:    return CV_32S;\n            case Frame.DEPTH_DOUBLE: return CV_64F;\n            default:  return -1;\n        }\n    }\n    static boolean isEqual(Frame frame, Mat mat) {\n        return mat != null && frame != null && frame.image != null && frame.image.length > 0\n                && frame.imageWidth == mat.cols() && frame.imageHeight == mat.rows()\n                && frame.imageChannels == mat.channels() && getMatDepth(frame.imageDepth) == mat.depth()\n                && new Pointer(frame.image[0].position(0)).address() == mat.data().address()\n                && frame.imageStride * Math.abs(frame.imageDepth) / 8 == (int)mat.step();\n    }\n    public Mat convertToMat(Frame frame) {\n        if (frame == null || frame.image == null) {\n            return null;\n        } else if (frame.opaque instanceof Mat) {\n            return (Mat)frame.opaque;\n        } else if (!isEqual(frame, mat)) {\n            int depth = getMatDepth(frame.imageDepth);\n            if (mat != null) {\n                mat.releaseReference();\n            }\n            mat = depth < 0 ? null : (Mat)new Mat(frame.imageHeight, frame.imageWidth, CV_MAKETYPE(depth, frame.imageChannels),\n                    new Pointer(frame.image[0].position(0)), frame.imageStride * Math.abs(frame.imageDepth) / 8).retainReference();\n        }\n        return mat;\n    }\n    public Frame convert(Mat mat) {\n        if (mat == null) {\n            return null;\n        } else if (!isEqual(frame, mat)) {\n            frame = new Frame();\n            frame.imageWidth = mat.cols();\n            frame.imageHeight = mat.rows();\n            frame.imageDepth = getFrameDepth(mat.depth());\n            frame.imageChannels = mat.channels();\n            frame.imageStride = (int)mat.step() * 8 / Math.abs(frame.imageDepth);\n            frame.image = new Buffer[] { mat.createBuffer() };\n        }\n        frame.opaque = mat;\n        return frame;\n    }\n\n    static boolean isEqual(Frame frame, org.opencv.core.Mat mat) {\n        return mat != null && frame != null && frame.image != null && frame.image.length > 0\n                && frame.imageWidth == mat.cols() && frame.imageHeight == mat.rows()\n                && frame.imageChannels == mat.channels() && getMatDepth(frame.imageDepth) == mat.depth()\n                && new Pointer(frame.image[0].position(0)).address() == mat.dataAddr();\n    }\n    public org.opencv.core.Mat convertToOrgOpenCvCoreMat(Frame frame) {\n        if (frame == null || frame.image == null) {\n            return null;\n        } else if (frame.opaque instanceof org.opencv.core.Mat) {\n            return (org.opencv.core.Mat)frame.opaque;\n        } else if (!isEqual(frame, mat)) {\n            int depth = getMatDepth(frame.imageDepth);\n            orgOpenCvCoreMat = depth < 0 ? null : new org.opencv.core.Mat(frame.imageHeight, frame.imageWidth,\n                    CV_MAKETYPE(depth, frame.imageChannels), new BytePointer(new Pointer(frame.image[0].position(0)))\n                            .capacity(frame.image[0].capacity() * Math.abs(frame.imageDepth) / 8).asByteBuffer(),\n                    frame.imageStride * Math.abs(frame.imageDepth) / 8);\n        }\n        return orgOpenCvCoreMat;\n    }\n    public Frame convert(final org.opencv.core.Mat mat) {\n        if (mat == null) {\n            return null;\n        } else if (!isEqual(frame, mat)) {\n            frame = new Frame();\n            frame.imageWidth = mat.cols();\n            frame.imageHeight = mat.rows();\n            frame.imageDepth = getFrameDepth(mat.depth());\n            frame.imageChannels = mat.channels();\n            frame.imageStride = (int)mat.step1();\n            ByteBuffer byteBuffer = new BytePointer() { { address = mat.dataAddr(); } }.capacity(mat.rows() * mat.step1() * mat.elemSize1()).asByteBuffer();\n            switch (mat.depth()) {\n                case CV_8U:\n                case CV_8S:\n                    frame.image = new Buffer[] { byteBuffer };\n                    break;\n                case CV_16U:\n                case CV_16S:\n                    frame.image = new Buffer[] { byteBuffer.asShortBuffer() };\n                    break;\n                case CV_32F:\n                    frame.image = new Buffer[] { byteBuffer.asFloatBuffer() };\n                    break;\n                case CV_32S:\n                    frame.image = new Buffer[] { byteBuffer.asIntBuffer() };\n                    break;\n                case CV_64F:\n                    frame.image = new Buffer[] { byteBuffer.asDoubleBuffer() };\n                    break;\n                default:\n                    frame.image = null;\n                    break;\n            }\n        }\n        frame.opaque = mat;\n        return frame;\n    }\n\n    @Override public void close() {\n        super.close();\n        if (img != null) {\n            img.releaseReference();\n            img = null;\n        }\n        if (mat != null) {\n            mat.releaseReference();\n            mat = null;\n        }\n        if (orgOpenCvCoreMat != null) {\n            orgOpenCvCoreMat.release();\n            orgOpenCvCoreMat = null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/OpenCVFrameGrabber.java",
    "content": "/*\n * Copyright (C) 2009-2019 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport org.bytedeco.javacpp.Loader;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.opencv.opencv_videoio.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_videoio.*;\n\n/**\n *\n * @author Samuel Audet\n * @author Lloyd (github.com/lloydmeta)\n */\npublic class OpenCVFrameGrabber extends FrameGrabber {\n    public static String[] getDeviceDescriptions() throws Exception {\n        tryLoad();\n        throw new UnsupportedOperationException(\"Device enumeration not support by OpenCV.\");\n    }\n\n    public static OpenCVFrameGrabber createDefault(File deviceFile)   throws Exception { return new OpenCVFrameGrabber(deviceFile); }\n    public static OpenCVFrameGrabber createDefault(String devicePath) throws Exception { return new OpenCVFrameGrabber(devicePath); }\n    public static OpenCVFrameGrabber createDefault(int deviceNumber)  throws Exception { return new OpenCVFrameGrabber(deviceNumber); }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.opencv.global.opencv_highgui.class);\n            } catch (Throwable t) {\n                throw loadingException = new Exception(\"Failed to load \" + OpenCVFrameGrabber.class, t);\n            }\n        }\n    }\n\n    public OpenCVFrameGrabber(int deviceNumber) {\n        this.deviceNumber = deviceNumber;\n    }\n    public OpenCVFrameGrabber(File file) {\n        this(file.getAbsolutePath());\n    }\n    public OpenCVFrameGrabber(File file, int apiPreference) {\n      this(file.getAbsolutePath(), apiPreference);\n    }\n    public OpenCVFrameGrabber(String filename) {\n        this.filename = filename;\n    }\n    public OpenCVFrameGrabber(String filename, int apiPreference) {\n      this.filename = filename;\n      this.apiPreference = apiPreference;\n    }\n\n    public void release() throws Exception {\n        stop();\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    private int deviceNumber = 0;\n    private String filename = null;\n    private int apiPreference = 0;\n    private VideoCapture capture = null;\n    private Mat returnMatrix = null;\n    private final OpenCVFrameConverter converter = new OpenCVFrameConverter.ToMat();\n    private final Mat mat = new Mat();\n\n    @Override public double getGamma() {\n        // default to a gamma of 2.2 for cheap Webcams, DV cameras, etc.\n        if (gamma == 0.0) {\n            return 2.2;\n        } else {\n            return gamma;\n        }\n    }\n\n    @Override public String getFormat() {\n        if (capture == null) {\n            return super.getFormat();\n        } else {\n            int fourcc = (int)capture.get(CAP_PROP_FOURCC);\n            return \"\" + (char)( fourcc        & 0xFF) +\n                        (char)((fourcc >>  8) & 0xFF) +\n                        (char)((fourcc >> 16) & 0xFF) +\n                        (char)((fourcc >> 24) & 0xFF);\n        }\n    }\n\n    @Override public int getImageWidth() {\n        if (returnMatrix != null) {\n            return returnMatrix.cols();\n        } else {\n            return capture == null ? super.getImageWidth() : (int)capture.get(CAP_PROP_FRAME_WIDTH);\n        }\n    }\n\n    @Override public int getImageHeight() {\n        if (returnMatrix != null) {\n            return returnMatrix.rows();\n        } else {\n            return capture == null ? super.getImageHeight() : (int)capture.get(CAP_PROP_FRAME_HEIGHT);\n        }\n    }\n\n    @Override public int getPixelFormat() {\n        return capture == null ? super.getPixelFormat() : (int)capture.get(CAP_PROP_CONVERT_RGB);\n    }\n\n    @Override public double getFrameRate() {\n        return capture == null ? super.getFrameRate() : (int)capture.get(CAP_PROP_FPS);\n    }\n\n    @Override public void setImageMode(ImageMode imageMode) {\n        if (imageMode != this.imageMode) {\n            returnMatrix = null;\n        }\n        super.setImageMode(imageMode);\n    }\n\n    @Override public int getFrameNumber() {\n        return capture == null ? super.getFrameNumber() :\n                (int)capture.get(CAP_PROP_POS_FRAMES);\n    }\n    @Override public void setFrameNumber(int frameNumber) throws Exception {\n        if (capture == null) {\n            super.setFrameNumber(frameNumber);\n        } else {\n            if (!capture.set(CAP_PROP_POS_FRAMES, frameNumber)) {\n                throw new Exception(\"set() Error: Could not set CAP_PROP_POS_FRAMES to \" + frameNumber + \".\");\n            }\n        }\n    }\n\n    @Override public long getTimestamp() {\n        return capture == null ? super.getTimestamp() :\n                Math.round(capture.get(CAP_PROP_POS_MSEC)*1000);\n    }\n    @Override public void setTimestamp(long timestamp) throws Exception {\n        if (capture == null) {\n            super.setTimestamp(timestamp);\n        } else {\n            if (!capture.set(CAP_PROP_POS_MSEC, timestamp/1000.0)) {\n                throw new Exception(\"set() Error: Could not set CAP_PROP_POS_MSEC to \" + timestamp/1000.0 + \".\");\n            }\n        }\n    }\n\n    @Override public int getLengthInFrames() {\n        return capture == null ? super.getLengthInFrames() :\n                (int)capture.get(CAP_PROP_FRAME_COUNT);\n    }\n    @Override public long getLengthInTime() {\n        return Math.round(getLengthInFrames() * 1000000L / getFrameRate());\n    }\n\n    public double getOption(int propId) {\n        if (capture != null) {\n            return capture.get(propId);\n        }\n        return Double.parseDouble(options.get(Integer.toString(propId)));\n    }\n    \n    /**\n     *\n     * @param propId Property ID, look at opencv_videoio for possible values\n     * @param value\n     */\n    public void setOption(int propId, double value) {\n        options.put(Integer.toString(propId), Double.toString(value));\n        if (capture != null) {\n            capture.set(propId, value);\n        }\n    }\n\n    public void start() throws Exception {\n        if (filename != null && filename.length() > 0) {\n            if (apiPreference > 0) {\n                capture = new VideoCapture(filename, apiPreference);\n            } else {\n                capture = new VideoCapture(filename);\n            }\n        } else {\n            capture = new VideoCapture(deviceNumber);\n        }\n\n        if (format != null && format.length() >= 4) {\n            format = format.toUpperCase();\n            byte cc0 = (byte)format.charAt(0);\n            byte cc1 = (byte)format.charAt(1);\n            byte cc2 = (byte)format.charAt(2);\n            byte cc3 = (byte)format.charAt(3);\n            capture.set(CAP_PROP_FOURCC, VideoWriter.fourcc(cc0, cc1, cc2, cc3));\n        }\n\n        if (imageWidth > 0) {\n            if (!capture.set(CAP_PROP_FRAME_WIDTH, imageWidth)) {\n                capture.set(CAP_PROP_FRAME_WIDTH, imageWidth);\n            }\n        }\n        if (imageHeight > 0) {\n            if (!capture.set(CAP_PROP_FRAME_HEIGHT, imageHeight)) {\n                capture.set(CAP_PROP_FRAME_HEIGHT, imageHeight);\n            }\n        }\n        if (frameRate > 0) {\n            capture.set(CAP_PROP_FPS, frameRate);\n        }\n        if (bpp > 0) {\n            capture.set(CAP_PROP_FORMAT, bpp); // ??\n        }\n        if (imageMode == ImageMode.RAW) {\n            capture.set(CAP_PROP_CONVERT_RGB, 0);\n        }\n\n        for (Entry<String, String> e : options.entrySet()) {\n            capture.set(Integer.parseInt(e.getKey()), Double.parseDouble(e.getValue()));\n        }\n\n        Mat mat = new Mat();\n\n        try {\n            // Before retrieve() starts returning something else then null\n            // QTKit sometimes requires some \"warm-up\" time for some reason...\n            // The first frame on Linux is sometimes null as well,\n            // so it's probably a good idea to run this for all platforms... ?\n            int count = 0;\n            while (count++ < 100 && !capture.read(mat)) {\n                Thread.sleep(100);\n            }\n        } catch (InterruptedException ex) {\n            // reset interrupt to be nice\n            Thread.currentThread().interrupt();\n        }\n        if (!capture.read(mat)) {\n            throw new Exception(\"read() Error: Could not read frame in start().\");\n        }\n\n        if (!triggerMode) {\n            if (!capture.grab()) {\n                throw new Exception(\"grab() Error: Could not grab frame. (Has start() been called?)\");\n            }\n        }\n    }\n\n    public void stop() throws Exception {\n        if (capture != null) {\n            capture.release();\n            capture = null;\n        }\n    }\n\n    public void trigger() throws Exception {\n        Mat mat = new Mat();\n        for (int i = 0; i < numBuffers+1; i++) {\n            capture.read(mat);\n        }\n        if (!capture.grab()) {\n            throw new Exception(\"grab() Error: Could not grab frame. (Has start() been called?)\");\n        }\n    }\n\n    public Frame grab() throws Exception {\n        if (!capture.retrieve(mat)) {\n            throw new Exception(\"retrieve() Error: Could not retrieve frame. (Has start() been called?)\");\n        }\n        if (!triggerMode) {\n            if (!capture.grab()) {\n                throw new Exception(\"grab() Error: Could not grab frame. (Has start() been called?)\");\n            }\n        }\n\n        if (imageMode == ImageMode.GRAY && mat.channels() > 1) {\n            if (returnMatrix == null) {\n                returnMatrix = new Mat(mat.rows(), mat.cols(), mat.depth(), 1);\n            }\n\n            cvtColor(mat, returnMatrix, CV_BGR2GRAY);\n        } else if (imageMode == ImageMode.COLOR && mat.channels() == 1) {\n            if (returnMatrix == null) {\n                returnMatrix = new Mat(mat.rows(), mat.cols(), mat.depth(), 3);\n            }\n            cvtColor(mat, returnMatrix, CV_GRAY2BGR);\n        } else {\n            returnMatrix = mat;\n        }\n        return converter.convert(returnMatrix);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/OpenCVFrameRecorder.java",
    "content": "/*\n * Copyright (C) 2009-2019 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport org.bytedeco.javacpp.Loader;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_videoio.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_videoio.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class OpenCVFrameRecorder extends FrameRecorder {\n    public static OpenCVFrameRecorder createDefault(File f, int w, int h)   throws Exception { return new OpenCVFrameRecorder(f, w, h); }\n    public static OpenCVFrameRecorder createDefault(String f, int w, int h) throws Exception { return new OpenCVFrameRecorder(f, w, h); }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.opencv.global.opencv_highgui.class);\n            } catch (Throwable t) {\n                throw loadingException = new Exception(\"Failed to load \" + OpenCVFrameRecorder.class, t);\n            }\n        }\n    }\n\n    public OpenCVFrameRecorder(File file, int imageWidth, int imageHeight) {\n        this(file.getAbsolutePath(), imageWidth, imageHeight);\n    }\n    public OpenCVFrameRecorder(String filename, int imageWidth, int imageHeight) {\n        this.filename    = filename;\n        this.imageWidth  = imageWidth;\n        this.imageHeight = imageHeight;\n\n        this.pixelFormat = 1;\n//        this.videoCodec  = windows ? CV_FOURCC_PROMPT : CV_FOURCC_DEFAULT;\n        this.videoCodec  = windows ? -1 : VideoWriter.fourcc((byte)'I', (byte)'Y', (byte)'U', (byte)'V');\n        this.frameRate   = 30;\n    }\n    public void release() throws Exception {\n        if (writer != null) {\n            writer.release();\n            writer = null;\n        }\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    private static final boolean windows = Loader.getPlatform().startsWith(\"windows\");\n    private String filename;\n    private VideoWriter writer = null;\n    private OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();\n\n    public double getOption(int propId) {\n        if (writer != null) {\n            return writer.get(propId);\n        }\n        return Double.parseDouble(options.get(Integer.toString(propId)));\n    }\n    \n    /**\n     *\n     * @param propId Property ID, look at opencv_videoio for possible values\n     * @param value\n     */\n    public void setOption(int propId, double value) {\n        options.put(Integer.toString(propId), Double.toString(value));\n        if (writer != null) {\n            writer.set(propId, value);\n        }\n    }\n\n    public void start() throws Exception {\n        writer = new VideoWriter(filename, fourCCCodec(), frameRate, new Size(imageWidth, imageHeight), isColour());\n\n        for (Entry<String, String> e : options.entrySet()) {\n            writer.set(Integer.parseInt(e.getKey()), Double.parseDouble(e.getValue()));\n        }\n    }\n\n    /**\n     * Pixel format is an int and maps to colour if != 0, greyscale otherwise.\n     */\n    private boolean isColour() {\n        return pixelFormat != 0;\n    }\n\n    /**\n     * VideoCodec in JavaCV jargon is the same as FourCC code in OpenCV speak\n     */\n    private int fourCCCodec() {\n        return videoCodec;\n    }\n\n    public void flush() throws Exception {\n    }\n\n    public void stop() throws Exception {\n        release();\n    }\n\n    public void record(Frame frame) throws Exception {\n        Mat mat = converter.convert(frame);\n        if (writer != null) {\n            writer.write(mat);\n        } else {\n            throw new Exception(\"Cannot record: There is no writer (Has start() been called?)\");\n        }\n        frame.keyFrame = true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/OpenKinect2FrameGrabber.java",
    "content": "/*\n * Copyright (C) 2014 Jeremy Laviole, Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.nio.ByteOrder;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.Pointer;\n\nimport org.bytedeco.libfreenect2.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.libfreenect2.global.freenect2.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Jeremy Laviole\n */\npublic class OpenKinect2FrameGrabber extends FrameGrabber {\n\n    public static String[] getDeviceDescriptions() throws FrameGrabber.Exception {\n        tryLoad();\n        String[] desc = new String[freenect2Context.enumerateDevices()];\n        for (int i = 0; i < desc.length; i++) {\n            desc[i] = freenect2Context.getDeviceSerialNumber(i).getString();\n        }\n        return desc;\n    }\n\n    public static int DEFAULT_DEPTH_WIDTH = 640;\n    public static int DEFAULT_DEPTH_HEIGHT = 480;\n    public static int DEFAULT_COLOR_WIDTH = 640;\n    public static int DEFAULT_COLOR_HEIGHT = 480;\n\n    private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;\n    private int depthImageWidth = DEFAULT_DEPTH_WIDTH;\n    private int depthImageHeight = DEFAULT_DEPTH_HEIGHT;\n    private int depthFrameRate = 60;\n\n    private int IRImageWidth = DEFAULT_DEPTH_WIDTH;\n    private int IRImageHeight = DEFAULT_DEPTH_HEIGHT;\n    private int IRFrameRate = 60;\n\n    private SyncMultiFrameListener frameListener;\n\n    public ByteOrder getByteOrder() {\n        return byteOrder;\n    }\n\n    public void setByteOrder(ByteOrder byteOrder) {\n        this.byteOrder = byteOrder;\n    }\n\n    public static OpenKinect2FrameGrabber createDefault(int deviceNumber) throws FrameGrabber.Exception {\n        return new OpenKinect2FrameGrabber(deviceNumber);\n    }\n\n    public static OpenKinect2FrameGrabber createDefault(File deviceFile) throws Exception {\n        throw new Exception(OpenKinect2FrameGrabber.class + \" does not support File devices.\");\n    }\n\n    public static OpenKinect2FrameGrabber createDefault(String devicePath) throws Exception {\n        throw new Exception(OpenKinect2FrameGrabber.class + \" does not support path.\");\n    }\n\n    private static FrameGrabber.Exception loadingException = null;\n    private static Freenect2 freenect2Context = null;\n\n    public static void tryLoad() throws FrameGrabber.Exception {\n        if (loadingException != null) {\n            loadingException.printStackTrace();\n            throw loadingException;\n        } else {\n            try {\n                if (freenect2Context != null) {\n                    return;\n                }\n                Loader.load(org.bytedeco.libfreenect2.global.freenect2.class);\n\n                // Context is shared accross cameras.\n                freenect2Context = new Freenect2();\n            } catch (Throwable t) {\n                throw loadingException = new FrameGrabber.Exception(\"Failed to load \" + OpenKinect2FrameGrabber.class, t);\n            }\n        }\n    }\n\n    private boolean colorEnabled = false;\n    private boolean depthEnabled = false;\n    private boolean IREnabled = false;\n\n    private int deviceNumber = 0;\n    private String serial = null;\n    private Freenect2Device device = null;\n\n    private int frameTypes = 0;\n\n    public OpenKinect2FrameGrabber(int deviceNumber) {\n        this.deviceNumber = deviceNumber;\n    }\n\n    public void enableColorStream() {\n        if (!colorEnabled) {\n            frameTypes |= org.bytedeco.libfreenect2.Frame.Color;\n            colorEnabled = true;\n        }\n    }\n\n    public void enableDepthStream() {\n        if (!depthEnabled) {\n            frameTypes |= org.bytedeco.libfreenect2.Frame.Depth;\n            depthEnabled = true;\n        }\n    }\n\n    public void enableIRStream() {\n        if (!IREnabled) {\n            frameTypes |= org.bytedeco.libfreenect2.Frame.Ir;\n            IREnabled = true;\n        }\n    }\n\n    public void release() throws FrameGrabber.Exception {\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    @Override\n    public void start() throws FrameGrabber.Exception {\n        startDevice(null);\n    }\n\n    public void startDevice(PacketPipeline pipeline) throws FrameGrabber.Exception {\n        if (freenect2Context == null) {\n            try {\n                OpenKinect2FrameGrabber.tryLoad();\n            } catch (Exception e) {\n                System.out.println(\"Exception in the TryLoad !\" + e);\n                e.printStackTrace();\n            }\n        }\n        if (freenect2Context == null) {\n            throw new Exception(\"FATAL error: OpenKinect2 camera: driver could not load.\");\n        }\n        if (freenect2Context.enumerateDevices() == 0) {\n            throw new Exception(\"FATAL error: OpenKinect2: no device connected!\");\n        }\n        device = null;\n\n//        pipeline = new CpuPacketPipeline();\n//        pipeline = new libfreenect2::OpenGLPacketPipeline();\n//        pipeline = new libfreenect2::OpenCLPacketPipeline(deviceId);\n//        pipeline = new libfreenect2::CudaPacketPipeline(deviceId);\n        serial = freenect2Context.getDeviceSerialNumber(deviceNumber).getString();\n        device = (pipeline != null) ? freenect2Context.openDevice(serial, pipeline) : freenect2Context.openDevice(serial);\n\n        frameListener = new SyncMultiFrameListener(frameTypes);\n\n        if (colorEnabled) {\n            device.setColorFrameListener(frameListener);\n        }\n        if (depthEnabled || IREnabled) {\n            device.setIrAndDepthFrameListener(frameListener);\n        }\n        rawVideoImage = IplImage.createHeader(1920, 1080, IPL_DEPTH_8U, 4);\n        device.start();\n\n        System.out.println(\"OpenKinect2 device started.\");\n        System.out.println(\"Serial: \" + device.getSerialNumber().getString());\n        System.out.println(\"Firmware: \" + device.getFirmwareVersion().getString());\n    }\n\n    /**\n     *\n     * @throws Exception\n     */\n    @Override\n    public void stop() throws FrameGrabber.Exception {\n        device.stop();\n        frameNumber = 0;\n    }\n\n//    private Pointer rawVideoImageData;\n    private IplImage rawVideoImage = null;\n    private IplImage videoImageRGBA = null;\n    private boolean hasFirstGoodColorImage = false;\n\n    private BytePointer videoBuffer = null;\n\n    protected void grabVideo() {\n        int iplDepth = IPL_DEPTH_8U;\n        org.bytedeco.libfreenect2.Frame rgb = frames.get(org.bytedeco.libfreenect2.Frame.Color);\n        int channels = (int) rgb.bytes_per_pixel();\n        int deviceWidth = (int) rgb.width();\n        int deviceHeight = (int) rgb.height();\n\n        BytePointer rawVideoImageData = rgb.data();\n        if (rawVideoImage == null) {\n            rawVideoImage = IplImage.createHeader(deviceWidth, deviceHeight, iplDepth, channels);\n        }\n\n        cvSetData(rawVideoImage, rawVideoImageData, deviceWidth * channels * iplDepth / 8);\n\n        if (videoImageRGBA == null) {\n            videoImageRGBA = rawVideoImage.clone();\n        }\n        cvCvtColor(rawVideoImage, videoImageRGBA, COLOR_BGRA2RGBA);\n    }\n\n    private IplImage rawIRImage = null;\n\n    protected void grabIR() {\n        /**\n         * 512x424 float. Range is [0.0, 65535.0].\n         */\n\n        org.bytedeco.libfreenect2.Frame IRImage = frames.get(org.bytedeco.libfreenect2.Frame.Ir);\n\n        int channels = 1;\n        int iplDepth = IPL_DEPTH_32F;\n        int bpp = (int) IRImage.bytes_per_pixel();\n        int deviceWidth = (int) IRImage.width();\n        int deviceHeight = (int) IRImage.height();\n\n        Pointer rawIRData = IRImage.data();\n        if (rawIRImage == null) {\n            rawIRImage = IplImage.createHeader(deviceWidth, deviceHeight, iplDepth, channels);\n        }\n\n        cvSetData(rawIRImage, rawIRData, deviceWidth * channels * iplDepth / 8);\n\n    }\n\n    private IplImage rawDepthImage = null;\n\n    protected void grabDepth() {\n\n        /**\n         * 512x424 float also ?.\n         */\n        org.bytedeco.libfreenect2.Frame depthImage = frames.get(org.bytedeco.libfreenect2.Frame.Depth);\n        int channels = 1;\n        int iplDepth = IPL_DEPTH_32F;\n        int bpp = (int) depthImage.bytes_per_pixel();\n        int deviceWidth = (int) depthImage.width();\n        int deviceHeight = (int) depthImage.height();\n\n        Pointer rawDepthData = depthImage.data();\n        if (rawDepthImage == null) {\n            rawDepthImage = IplImage.createHeader(deviceWidth, deviceHeight, iplDepth, channels);\n        }\n        cvSetData(rawDepthImage, rawDepthData, deviceWidth * channels * iplDepth / 8);\n    }\n\n    private FrameMap frames = new FrameMap();\n\n    /**\n     *\n     * @return null grabs all images, get them with grabColor, grabDepth, and\n     * grabIR instead.\n     * @throws org.bytedeco.javacv.FrameGrabber.Exception\n     */\n    public Frame grab() throws Exception {\n\n        if (!frameListener.waitForNewFrame(frames, 10 * 1000)) // 10 seconds\n        {\n            System.out.println(\"Openkinect2: timeout!\");\n            // TODO: throw exception\n        }\n        frameNumber++;\n        if (colorEnabled) {\n            grabVideo();\n        }\n        if (IREnabled) {\n            grabIR();\n        }\n        if (depthEnabled) {\n            grabDepth();\n        }\n\n        frameListener.release(frames);\n        return null;\n    }\n\n    public IplImage getVideoImage() {\n        return videoImageRGBA;\n//            return rawVideoImage;\n    }\n\n    public IplImage getIRImage() {\n        return rawIRImage;\n    }\n\n    public IplImage getDepthImage() {\n        return rawDepthImage;\n    }\n\n    @Override\n    public void trigger() throws Exception {\n//        device.wait_for_frames();\n    }\n\n    public int getDepthImageWidth() {\n        return depthImageWidth;\n    }\n\n    public void setDepthImageWidth(int depthImageWidth) {\n        this.depthImageWidth = depthImageWidth;\n    }\n\n    public int getDepthImageHeight() {\n        return depthImageHeight;\n    }\n\n    public void setDepthImageHeight(int depthImageHeight) {\n        this.depthImageHeight = depthImageHeight;\n    }\n\n    public int getIRImageWidth() {\n        return IRImageWidth;\n    }\n\n    public void setIRImageWidth(int IRImageWidth) {\n        this.IRImageWidth = IRImageWidth;\n    }\n\n    public int getIRImageHeight() {\n        return IRImageHeight;\n    }\n\n    public void setIRImageHeight(int IRImageHeight) {\n        this.IRImageHeight = IRImageHeight;\n    }\n\n    public int getDepthFrameRate() {\n        return depthFrameRate;\n    }\n\n    public void setDepthFrameRate(int frameRate) {\n        this.depthFrameRate = frameRate;\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/OpenKinectFrameGrabber.java",
    "content": "/*\n * Copyright (C) 2011-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.ShortBuffer;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Pointer;\nimport org.bytedeco.javacpp.Loader;\n\nimport org.bytedeco.libfreenect.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.libfreenect.global.freenect.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class OpenKinectFrameGrabber extends FrameGrabber {\n    public static String[] getDeviceDescriptions() throws Exception {\n        tryLoad();\n\n        freenect_context ctx = new freenect_context(null);\n        int err = freenect_init(ctx, null);\n        if (err < 0) {\n            throw new Exception(\"freenect_init() Error \" + err + \": Failed to init context.\");\n        }\n\n        int count = freenect_num_devices(ctx);\n        if (count < 0) {\n            throw new Exception(\"freenect_num_devices() Error \" + err + \": Failed to get number of devices.\");\n        }\n        String[] descriptions = new String[count];\n        for (int i = 0; i < descriptions.length; i++) {\n            descriptions[i] = \"Kinect #\" + i;\n        }\n\n        err = freenect_shutdown(ctx);\n        if (err < 0) {\n            throw new Exception(\"freenect_shutdown() Error \" + err + \": Failed to shutdown context.\");\n        }\n\n        return descriptions;\n    }\n\n    public static OpenKinectFrameGrabber createDefault(File deviceFile)   throws Exception { throw new Exception(OpenKinectFrameGrabber.class + \" does not support device files.\"); }\n    public static OpenKinectFrameGrabber createDefault(String devicePath) throws Exception { throw new Exception(OpenKinectFrameGrabber.class + \" does not support device paths.\"); }\n    public static OpenKinectFrameGrabber createDefault(int deviceNumber)  throws Exception { return new OpenKinectFrameGrabber(deviceNumber); }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.libfreenect.global.freenect.class);\n            } catch (Throwable t) {\n                throw loadingException = new Exception(\"Failed to load \" + OpenKinectFrameGrabber.class, t);\n            }\n        }\n    }\n\n    public OpenKinectFrameGrabber(int deviceNumber) {\n        this.deviceNumber = deviceNumber;\n    }\n    public void release() throws Exception {\n        stop();\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    private int deviceNumber = 0;\n    private boolean depth = false; // default to \"video\"\n    private BytePointer rawDepthImageData = new BytePointer((Pointer)null),\n                        rawVideoImageData = new BytePointer((Pointer)null),\n                        rawIRImageData = new BytePointer((Pointer)null);\n    private IplImage rawDepthImage = null, rawVideoImage = null, rawIRImage = null, returnImage = null;\n    private FrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n    private int[] timestamp = { 0 };\n    private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;\n    private int depthFormat = -1;\n    private int videoFormat = -1;\n\n    public ByteOrder getByteOrder() {\n        return byteOrder;\n    }\n    public void setByteOrder(ByteOrder byteOrder) {\n        this.byteOrder = byteOrder;\n    }\n\n    public int getDepthFormat() {\n        return depthFormat;\n    }\n    public void setDepthFormat(int depthFormat) {\n        this.depthFormat = depthFormat;\n    }\n\n    public int getVideoFormat() {\n        return videoFormat;\n    }\n    public void setVideoFormat(int videoFormat) {\n        this.videoFormat = videoFormat;\n    }\n\n    @Override public double getGamma() {\n        // I guess a default gamma of 2.2 is reasonable...\n        if (gamma == 0.0) {\n            return 2.2;\n        } else {\n            return gamma;\n        }\n    }\n\n    @Override public void setImageMode(ImageMode imageMode) {\n        if (imageMode != this.imageMode) {\n            returnImage = null;\n        }\n        super.setImageMode(imageMode);\n    }\n\n    public void start() throws Exception {\n        depth = \"depth\".equalsIgnoreCase(format);\n    }\n\n    public void stop() throws Exception {\n        freenect_sync_stop();\n    }\n\n    public void trigger() throws Exception {\n        for (int i = 0; i < numBuffers+1; i++) {\n            if (depth) {\n                int fmt = depthFormat < 0 ? bpp : depthFormat; // default bpp == 0 == FREENECT_DEPTH_11BIT\n                int err = freenect_sync_get_depth(rawDepthImageData, timestamp, deviceNumber, fmt);\n                if (err != 0) {\n                    throw new Exception(\"freenect_sync_get_depth() Error \" + err + \": Failed to get depth synchronously.\");\n                }\n            } else {\n                int fmt = videoFormat < 0 ? bpp : videoFormat; // default bpp == 0 == FREENECT_VIDEO_RGB\n                int err = freenect_sync_get_video(rawVideoImageData, timestamp, deviceNumber, fmt);\n                if (err != 0) {\n                    throw new Exception(\"freenect_sync_get_video() Error \" + err + \": Failed to get video synchronously.\");\n                }\n            }\n        }\n    }\n\n    public IplImage grabDepth() throws Exception {\n        int fmt = depthFormat < 0 ? bpp : depthFormat; // default bpp == 0 == FREENECT_DEPTH_11BIT\n        int iplDepth = IPL_DEPTH_16U, channels = 1;\n        switch (fmt) {\n            case FREENECT_DEPTH_11BIT:\n            case FREENECT_DEPTH_REGISTERED:\n            case FREENECT_DEPTH_MM:\n            case FREENECT_DEPTH_10BIT: iplDepth = IPL_DEPTH_16U; channels = 1; break;\n            case FREENECT_DEPTH_11BIT_PACKED:\n            case FREENECT_DEPTH_10BIT_PACKED:\n            default: assert false;\n        }\n\n        int err = freenect_sync_get_depth(rawDepthImageData, timestamp, deviceNumber, fmt);\n        if (err != 0) {\n            throw new Exception(\"freenect_sync_get_depth() Error \" + err + \": Failed to get depth synchronously.\");\n        }\n\n        int w = 640, h = 480; // how to get the resolution ??\n        if (rawDepthImage == null || rawDepthImage.width() != w || rawDepthImage.height() != h) {\n            rawDepthImage = IplImage.createHeader(w, h, iplDepth, channels);\n        }\n        cvSetData(rawDepthImage, rawDepthImageData, w*channels*iplDepth/8);\n\n        if (iplDepth > 8 && !ByteOrder.nativeOrder().equals(byteOrder)) {\n            // ack, the camera's endianness doesn't correspond to our machine ...\n            // swap bytes of 16-bit images\n            ByteBuffer  bb  = rawDepthImage.getByteBuffer();\n            ShortBuffer in  = bb.order(ByteOrder.BIG_ENDIAN   ).asShortBuffer();\n            ShortBuffer out = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();\n            out.put(in);\n        }\n\n        super.timestamp = timestamp[0];\n        return rawDepthImage;\n    }\n\n    public IplImage grabVideo() throws Exception {\n        int fmt = videoFormat < 0 ? bpp : videoFormat; // default bpp == 0 == FREENECT_VIDEO_RGB\n        int iplDepth = IPL_DEPTH_8U, channels = 3;\n        switch (fmt) {\n            case FREENECT_VIDEO_RGB:      iplDepth = IPL_DEPTH_8U; channels = 3; break;\n            case FREENECT_VIDEO_BAYER:\n            case FREENECT_VIDEO_IR_8BIT:  iplDepth = IPL_DEPTH_8U; channels = 1; break;\n            case FREENECT_VIDEO_IR_10BIT: iplDepth = IPL_DEPTH_16U; channels = 1; break;\n            case FREENECT_VIDEO_YUV_RGB:  iplDepth = IPL_DEPTH_8U; channels = 3; break;\n            case FREENECT_VIDEO_YUV_RAW:  iplDepth = IPL_DEPTH_8U; channels = 2; break;\n            case FREENECT_VIDEO_IR_10BIT_PACKED:\n            default: assert false;\n        }\n\n        int err = freenect_sync_get_video(rawVideoImageData, timestamp, deviceNumber, fmt);\n        if (err != 0) {\n            throw new Exception(\"freenect_sync_get_video() Error \" + err + \": Failed to get video synchronously.\");\n        }\n\n        int w = 640, h = 480; // how to get the resolution ??\n        if (rawVideoImage == null || rawVideoImage.width() != w || rawVideoImage.height() != h) {\n            rawVideoImage = IplImage.createHeader(w, h, iplDepth, channels);\n        }\n        cvSetData(rawVideoImage, rawVideoImageData, w*channels*iplDepth/8);\n\n        if (iplDepth > 8 && !ByteOrder.nativeOrder().equals(byteOrder)) {\n            // ack, the camera's endianness doesn't correspond to our machine ...\n            // swap bytes of 16-bit images\n            ByteBuffer  bb  = rawVideoImage.getByteBuffer();\n            ShortBuffer in  = bb.order(ByteOrder.BIG_ENDIAN   ).asShortBuffer();\n            ShortBuffer out = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();\n            out.put(in);\n        }\n\n        if (channels == 3) {\n            cvCvtColor(rawVideoImage, rawVideoImage, CV_RGB2BGR);\n        }\n        super.timestamp = timestamp[0];\n        return rawVideoImage;\n    }\n    \n    public IplImage grabIR() throws Exception {\n        int iplDepth = IPL_DEPTH_8U, channels = 1;\n\n        int err = freenect_sync_get_video(rawIRImageData, timestamp, deviceNumber, FREENECT_VIDEO_IR_8BIT);\n        if (err != 0) {\n            throw new Exception(\"freenect_sync_get_video() Error \" + err + \": Failed to get video synchronously.\");\n        }\n\n        int w = 640, h = 480; // how to get the resolution ??\n        if (rawIRImage == null || rawIRImage.width() != w || rawIRImage.height() != h) {\n            rawIRImage = IplImage.createHeader(w, h, iplDepth, channels);\n        }\n        cvSetData(rawIRImage, rawIRImageData, w*channels*iplDepth/8);\n\n        super.timestamp = timestamp[0];\n        return rawIRImage;\n    }\n\n    public Frame grab() throws Exception {\n        IplImage image = depth ? grabDepth() :  grabVideo();\n        int w = image.width();\n        int h = image.height();\n        int iplDepth = image.depth();\n        int channels = image.nChannels();\n\n        if (imageMode == ImageMode.COLOR && channels == 1) {\n            if (returnImage == null) {\n                returnImage = IplImage.create(w, h, iplDepth, 3);\n            }\n            cvCvtColor(image, returnImage, CV_GRAY2BGR);\n            return converter.convert(returnImage);\n        } else if (imageMode == ImageMode.GRAY && channels == 3) {\n            if (returnImage == null) {\n                returnImage = IplImage.create(w, h, iplDepth, 1);\n            }\n            cvCvtColor(image, returnImage, CV_BGR2GRAY);\n            return converter.convert(returnImage);\n        } else {\n            return converter.convert(image);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/PS3EyeFrameGrabber.java",
    "content": "/*\n * Copyright (C) 2011-2012 Jiri Masa, Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport cl.eye.CLCamera;\nimport java.io.File;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/** Minimal Sony PS3 Eye camera grabber implementation.\n * \n *  It allows grabbing of frames at higher speed than OpenCVFrameGrabber or VideoInputFrameGrabber.\n *  Underlying implementation of last two grabbers is limited to 30 FPS. PS3 allows grabbing\n *  at maximum speed of 75 FPS in VGA and 187 FPS in QVGA modes.\n *  \n *  This code was developed and tested with CLEyeMulticam.dll, version 1.2.0.1008. The dll library\n *  is part of Code Laboratories CL-Eye Platform SDK and is distributed as part of CLEyeMulticam\n *  Redistributable Dynamic Link Library. For license, download and installation see http://www.codelaboratories.com.\n *    \n *  The grab() method returns an internal instance of IplImage image with fresh camera frame. This returned image\n *  have to be considered \"read only\" and the caller needs to create it's own copy or clone that image.\n *  Calling of release() method for this image shall be avoided.\n *  Based on used resolution the image is in format either 640x480 or 320x240, IPL_DEPTH_8U, 4 channel (color) or 1 channel (gray).\n *  timestamp is set to actual value of System.nanoTime()/1000 obtained after return from the CL driver.\n *  \n *  Typical use case scenario:\n *     create new instance of PS3MiniGrabber\n *     set camera parameters\n *     start() grabber \n *     wait at least 2 frames\n *     grab() in loop\n *     stop() grabber\n *     release() internal resources\n *     \n * Note:\n * This code depends on the cl.eye.CLCamera class from Code Laboratories CL-Eye\n * Platform SDK. It is suggested to download SDK and edit the sample file\n * ....\\cl\\eye\\CLCamera.java. A few references to processing.core.PApplet class\n * shall be removed and the file recompiled. The tailored file is not included\n * here namely because of unclear licence.\n * \n *  @author jmasa, jmasa@cmail.cz\n *\n */\npublic class PS3EyeFrameGrabber extends FrameGrabber {\n    public static String[] getDeviceDescriptions() throws Exception {\n        tryLoad();\n        String[] descriptions = new String[CLCamera.cameraCount()];\n        for (int i = 0; i < descriptions.length; i++) {\n            descriptions[i] = CLCamera.cameraUUID(i);\n        }\n        return descriptions;\n    }\n\n    public static PS3EyeFrameGrabber createDefault(File deviceFile)   throws Exception { throw new Exception(PS3EyeFrameGrabber.class + \" does not support device files.\"); }\n    public static PS3EyeFrameGrabber createDefault(String devicePath) throws Exception { throw new Exception(PS3EyeFrameGrabber.class + \" does not support device paths.\"); }\n    public static PS3EyeFrameGrabber createDefault(int deviceNumber)  throws Exception { return new PS3EyeFrameGrabber(deviceNumber); }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                CLCamera.IsLibraryLoaded();\n            } catch (Throwable t) {\n                throw loadingException = new Exception(\"Failed to load \" + PS3EyeFrameGrabber.class, t);\n            }\n        }\n    }\n\n    CLCamera camera;\n    int cameraIndex = 0;\n    int[]  ps3_frame = null;             // buffer for PS3 camera frame data\n    byte[] ipl_frame = null;             // buffer for RGB-3ch, not allocated unless grab_RGB3() is called \n\n    IplImage image_4ch = null;\n    IplImage image_1ch = null;\n    FrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n\n    String stat;                  // status of PS3 camera handling - mostly for debugging\n    String uuid;                  // assigned camera unique key\n\n    // variables for trigger() implementation\n    //\n    protected enum Triggered {NO_TRIGGER, HAS_FRAME, NO_FRAME};\n    protected Triggered triggered = Triggered.NO_TRIGGER;\n\n\n    /** Default grabber, camera idx = 0, color mode, VGA resolution, 60 FPS frame rate.\n     *   \n     */\n    public PS3EyeFrameGrabber() throws Exception {\n        this(0);\n    }\n\n    /** Color mode, VGA resolution, 60 FPS frame rate.\n     *   @param cameraIndex system wide camera index\n     */\n    public PS3EyeFrameGrabber(int cameraIndex) throws Exception {\n        this(cameraIndex, 640, 480, 60);\n    }\n\n    public PS3EyeFrameGrabber(int cameraIndex, int imageWidth, int imageHeight, int framerate) throws Exception {\n        this(cameraIndex, 640, 480, 60, null);\n    }\n\n    /** Creates grabber, the caller can control basic image and grabbing parameters.\n     * \n     * @param cameraIndex - zero based index of used camera (OS system wide)\n     * @param imageWidth  - width of image \n     * @param imageHeight - height of image\n     * @param framerate   - frame rate - see CLCamera for allowed frame rates based on resolution\n     * @param applet      - PApplet object required by CLCamera\n     * @throws Exception  - if parameters don't follow CLCamera definition or camera is not created\n     */\n    public PS3EyeFrameGrabber(int cameraIndex, int imageWidth, int imageHeight, int framerate, Object applet) throws Exception {\n        camera = null;\n\n        if (! CLCamera.IsLibraryLoaded()) {\n            throw new Exception(\"CLEye multicam dll not loaded\");\n        }\n\n        this.camera = new CLCamera();\n        this.cameraIndex = cameraIndex;\n\n        stat = \"created\";\n        uuid = CLCamera.cameraUUID(cameraIndex);\n\n        if (((imageWidth == 640) && (imageHeight == 480)) || \n            ((imageWidth == 320) && (imageHeight == 240))) {\n            setImageWidth(imageWidth);\n            setImageHeight(imageHeight);\n        }\n        else throw new Exception(\"Only 640x480 or 320x240 images supported\");\n\n        setImageMode(ImageMode.COLOR);\n        setFrameRate((double) framerate);  \n        setTimeout(1 + 1000/framerate);\n        setBitsPerPixel(8);\n        setTriggerMode(false);\n        setNumBuffers(4);\n    }\n\n    /** \n     * @return system wide number of installed/detected Sony PS3 Eye cameras\n     */\n    public static int getCameraCount() {\n        return CLCamera.cameraCount();\n    }\n\n    /** Ask the driver for all installed PS3 cameras. Resulting array is sorted in order of camera index.\n     *  Its size is defined by CLCamera.cameraCount().\n     * \n     * @return array of camera unique uuids or null if there is no PS3 camera\n     */\n    public static String[] listPS3Cameras() {\n        int no = getCameraCount();\n        String[] uuids;\n        if (no > 0) {\n            uuids = new String[no];\n            for (--no; no >=0; no--) { uuids[no] = CLCamera.cameraUUID(no); }\n            return uuids;\n        }\n        return null;\n    }\n\n\n    /** Make IplImage form raw int[] frame data\n     *  Note: NO array size checks!!\n     * \n     * @param frame int[] image frame data \n     * @return internal IplImage set to frame\n     */\n    public IplImage makeImage(int[] frame) {\n        image_4ch.getIntBuffer().put(ps3_frame);\n        return image_4ch;\n    }\n\n\n    /** Grab one frame and return it as int[] (in the internal camera format RGBA).\n     *  Note: use makeImage() to create RGBA, 4-ch image\n     * @return frame as int[] without any processing or null if frame is not available \n     */\n    public int[] grab_raw() {\n        if (camera.getCameraFrame(ps3_frame, timeout)) {\n            return ps3_frame;\n        }\n        else return null;\n    }\n\n    public void trigger() throws Exception {\n        for (int i = 0; i < numBuffers+1; i++) {\n            grab_raw();\n        }\n\n        if ((ps3_frame = grab_raw()) != null) {\n            triggered = Triggered.HAS_FRAME;\n            timestamp = System.nanoTime()/1000;\n        }\n        else\n            triggered = Triggered.NO_FRAME;\n    }\n\n\n    /** Grab and convert one frame, default timeout is (1 + 1000/framerate) [milliseconds].\n     *  Every successful call returns an internal (preallocated) 640x480 or 320x240, IPL_DEPTH_8U, 4-channel image.\n     *  The caller shall consider it \"read only\" and make a copy/clone of it before further processing.\n     *  \n     *  The call might block for timeout [milliseconds].\n     * @return the image or null if there is no new image\n     */\n     public IplImage grab_RGB4() {\n\n        if (camera.getCameraFrame(ps3_frame, timeout)) {\n            timestamp = System.nanoTime()/1000;\n            image_4ch.getIntBuffer().put(ps3_frame);\n            return image_4ch;\n        }\n        else return null;\n    }\n\n    /** Grab one frame;\n     *  the caller have to make a copy of returned image before processing.\n     *  \n     *  It will throw null pointer exception if not started before grabbing.\n     *  @return \"read-only\" RGB, 4-channel or GRAY/1-channel image, it throws exception if no image is available\n     */\n    @Override\n    public Frame grab() throws Exception {\n        IplImage img = null;\n        switch (triggered) {\n            case NO_TRIGGER:\n                img = grab_RGB4();\n                break;\n            case HAS_FRAME:\n                triggered = Triggered.NO_TRIGGER;\n                img = makeImage(ps3_frame);\n                break;\n            case NO_FRAME:     \n                triggered = Triggered.NO_TRIGGER;\n                return null;\n            default:  // just schizophrenia - for future enhancement\n                throw new Exception(\"Int. error - unknown triggering state\");\n        }\n        if ((img != null) && (imageMode == ImageMode.GRAY)) {\n                cvCvtColor(img, image_1ch, CV_RGB2GRAY);\n                img = image_1ch;\n        }\n        return converter.convert(img);\n    }\n\n\n    /**\n     * Start camera first (before grabbing).\n     */\n    public void start() throws Exception {\n        boolean b;\n\n        if (ps3_frame == null) {\n            ps3_frame = new int[ imageWidth * imageHeight ];\n            image_4ch = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_8U, 4);\n            image_1ch = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_8U, 1);\n        }\n   \n        b = camera.createCamera(\n                 cameraIndex,\n                (imageMode == ImageMode.GRAY) ? CLCamera.CLEYE_MONO_PROCESSED : CLCamera.CLEYE_COLOR_PROCESSED,\n                (imageWidth == 320 && imageHeight == 240) ? CLCamera.CLEYE_QVGA : CLCamera.CLEYE_VGA,\n                (int)frameRate);\n        \n        if (!b) throw new Exception(\"Low level createCamera() failed\");\n        \n        b = camera.startCamera();\n        if (!b) throw new Exception(\"Camera start() failed\");\n        stat = \"started\";\n    }\n\n\n    /**\n     * Stop camera. It can be re-started if needed.\n     */\n    public void stop() throws Exception {\n        boolean b = camera.stopCamera();\n        if (b) stat = \"stopped\";\n        else throw new Exception(\"Camera stop() failed\");\n    }\n\n\n    /** Release resources:\n     *   - CL driver internal resources binded with camera HW\n     *   - internal IplImage\n     *  After calling this function, this mini-grabber object instance can not be used anymore.\n     */\n    public void release() {\n        if (camera != null) {\n            camera.dispose();\n            camera = null;\n        }\n\n        if (image_4ch != null) {\n            image_4ch.release();\n            image_4ch = null;\n        }\n\n        if (image_1ch != null) {\n            image_1ch.release();\n            image_1ch = null;\n        }\n\n        if (ipl_frame != null) ipl_frame = null;\n        if (ps3_frame != null) ps3_frame = null;\n\n        stat = \"released\";\n    }\n\n    /** Release internal resources, the same as calling release()\n     */\n    public void dispose() {\n        release();\n    }\n\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n\n    /** Return internal CLCamera object, mainly to set camera parameters,\n     *  changing camera parameters must be done on stopped camera and before start() is called.\n     *  See CL SDK - setCameraParameter(int param, int val) function.\n     *  \n     * @return internal CLCamera instance\n     */\n    public CLCamera getCamera() { return camera; }\n\n    public String getUUID() { return uuid; }    \n\n    /**\n     * @return status and camera parameters of the grabber\n     */\n    @Override public String toString() {\n        return \"UUID=\"+uuid + \"; status=\" + stat + \"; timeout=\" + timeout\n            + \"; \"\n            + ((camera != null) ? camera.toString() : \"<no camera>\")\n            ;\n    }\n\n\n    /** Just for testing - loads the CL CLEyeMulticam.dll file, invokes driver\n     *  and lists available cameras. \n     *   \n     * @param argv - argv is not used\n     */\n    public static void main(String[] argv) {\n        String[] uuids = listPS3Cameras();\n        for (int i = 0; i < uuids.length; i++)\n            System.out.println(i+\": \"+uuids[i]);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/Parallel.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class Parallel {\n    private static final ExecutorService threadPool = Executors.newCachedThreadPool();\n    public static final String NUM_THREADS = \"org.bytedeco.javacv.numthreads\";\n\n    public static int getNumThreads() {\n        try {\n            String s = System.getProperty(NUM_THREADS);\n            if (s != null) {\n                return Integer.valueOf(s);\n            }\n        } catch (NumberFormatException e) {\n            throw new RuntimeException(e);\n        }\n        return getNumCores();\n    }\n    public static void setNumThreads(int numThreads) {\n        System.setProperty(NUM_THREADS, Integer.toString(numThreads));\n    }\n\n    public static int getNumCores() {\n        return Runtime.getRuntime().availableProcessors();\n    }\n\n    public static void run(Runnable ... runnables) {\n        if (runnables.length == 1) {\n            runnables[0].run();\n            return;\n        }\n\n        Future[] futures = new Future[runnables.length];\n        for (int i = 0; i < runnables.length; i++) {\n            futures[i] = threadPool.submit(runnables[i]);\n        }\n\n        Throwable error = null;\n        try {\n            for (Future f : futures) {\n                if (!f.isDone()) {\n                    f.get();\n                }\n            }\n        } catch (Throwable t) {\n            error = t;\n        }\n\n        if (error != null) {\n            for (Future f : futures) {\n                f.cancel(true);\n            }\n            throw new RuntimeException(error);\n        }\n    }\n\n    public interface Looper {\n        void loop(int from, int to, int looperID);\n    }\n\n    public static void loop(int from, int to, final Looper looper) {\n        loop(from, to, getNumThreads(), looper);\n    }\n    public static void loop(int from, int to, int numThreads, final Looper looper) {\n        int numLoopers = Math.min(to-from, numThreads > 0 ? numThreads : getNumCores());\n        Runnable[] runnables = new Runnable[numLoopers];\n        for (int i = 0; i < numLoopers; i++) {\n            final int subFrom = (to-from)*i/numLoopers + from;\n            final int subTo = (to-from)*(i+1)/numLoopers + from;\n            final int looperID = i;\n            runnables[i] = new Runnable() {\n                public void run() {\n                    looper.loop(subFrom, subTo, looperID);\n                }\n            };\n        }\n        run(runnables);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProCamColorCalibrator.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.awt.Color;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProCamColorCalibrator {\n\n    public ProCamColorCalibrator(Settings settings, MarkerDetector.Settings detectorSettings,\n            MarkedPlane boardPlane, CameraDevice camera, ProjectorDevice projector) {\n        this.settings = settings;\n        this.markerDetector = new MarkerDetector(detectorSettings);\n        this.boardPlane = boardPlane;\n        this.camera = camera;\n        this.projector = projector;\n\n        Marker[] boardMarkers = boardPlane.getMarkers();\n        boardSrcPts = CvMat.create(4 + boardMarkers.length*4, 1, CV_64F, 2);\n        boardDstPts = CvMat.create(4 + boardMarkers.length*4, 1, CV_64F, 2);\n        boardSrcPts.put(0.0,                   0.0,\n                        boardPlane.getWidth(), 0.0,\n                        boardPlane.getWidth(), boardPlane.getHeight(),\n                        0.0,                   boardPlane.getHeight());\n        for (int i = 0; i < boardMarkers.length; i++) {\n            boardSrcPts.put(8 + i*8, boardMarkers[i].corners);\n        }\n        projSrcPts = CvMat.create(4, 1, CV_64F, 2);\n        projDstPts = CvMat.create(4, 1, CV_64F, 2);\n        projSrcPts.put(0.0,               0.0,\n                       projector.imageWidth-1, 0.0,\n                       projector.imageWidth-1, projector.imageHeight-1,\n                       0.0,                    projector.imageHeight-1);\n        camKinv = CvMat.create(3, 3);\n//        CvMat projKinv = CvMat.create(3, 3);\n        cvInvert(camera.cameraMatrix, camKinv);\n//        cvInvert(projector.cameraMatrix, projKinv);\n    }\n\n    public static class Settings extends BaseChildSettings {\n        int samplesPerChannel = 4;\n        double trimmingFraction = 0.01;\n        double detectedBoardMin = 0.5;\n\n        public int getSamplesPerChannel() {\n            return samplesPerChannel;\n        }\n        public void setSamplesPerChannel(int samplesPerChannel) {\n            this.samplesPerChannel = samplesPerChannel;\n        }\n\n//        public double getTrimmingFraction() {\n//            return trimmingFraction;\n//        }\n//        public void setTrimmingFraction(double trimmingFraction) {\n//            this.trimmingFraction = trimmingFraction;\n//        }\n\n        public double getDetectedBoardMin() {\n            return detectedBoardMin;\n        }\n        public void setDetectedBoardMin(double detectedBoardMin) {\n            this.detectedBoardMin = detectedBoardMin;\n        }\n    }\n\n    private Settings settings;\n\n    private MarkerDetector markerDetector = null;\n    private MarkedPlane boardPlane = null;\n    private CameraDevice camera = null;\n    private ProjectorDevice projector = null;\n    private Color[] projectorColors = null, cameraColors = null;\n    private int counter = 0;\n\n    private CvMat boardSrcPts, boardDstPts;\n    private CvMat projSrcPts, projDstPts;\n    private CvMat camKinv;\n    private IplImage mask, mask2, undistImage;\n\n    public int getColorCount() {\n        return counter;\n    }\n\n    public Color[] getProjectorColors() {\n        double invgamma = 1/projector.getSettings().getResponseGamma();\n        int s = settings.samplesPerChannel;\n        if (projectorColors == null) {\n            projectorColors = new Color[s*s*s];\n            cameraColors    = new Color[s*s*s];\n            for (int i = 0; i < projectorColors.length; i++) {\n                 int j = i/s;\n                 int k = j/s;\n                double r = Math.pow((double)(i%s)/(s-1), invgamma);\n                double g = Math.pow((double)(j%s)/(s-1), invgamma);\n                double b = Math.pow((double)(k%s)/(s-1), invgamma);\n                projectorColors[i] = new Color((float)r, (float)g, (float)b);\n            }\n        }\n        return projectorColors;\n    }\n    public Color getProjectorColor() {\n        return getProjectorColors()[counter];\n    }\n\n    public Color[] getCameraColors() {\n        return cameraColors;\n    }\n    public Color getCameraColor() {\n        return getCameraColors()[counter];\n    }\n\n    public void addCameraColor() {\n        counter++;\n    }\n    public void addCameraColor(Color color) {\n        cameraColors[counter++] = color;\n    }\n\n    public IplImage getMaskImage() {\n        return mask;\n    }\n    public IplImage getUndistortedCameraImage() {\n        return undistImage;\n    }\n\n//    public CvScalar getProjectorColor() {\n//        if (counter == 0) {\n//            return CvScalar.CV_RGB(0,0,0);\n//        } else if (counter == 1) {\n//            return CvScalar.CV_RGB(255,255,255);\n//        }\n//        // smallest (power of 2) number of channel that can accomodate \"counter\" number of samples\n//        int level = (int)Math.ceil(Math.log(Math.cbrt(counter+1))/Math.log(2));\n//        int samplesPerChannel = (int)Math.pow(2, level)+1;\n//        int prevSamplesPerChannel = (int)Math.pow(2, level-1)+1;\n//        int ignoreAmount = prevSamplesPerChannel*prevSamplesPerChannel*prevSamplesPerChannel;\n//        int samplesToJump = counter - Math.max(ignoreAmount, 2);\n//\n//        int n = 0;\n//        for (int i = 0; ; i++) {\n//            int j = i/samplesPerChannel;\n//            int k = j/samplesPerChannel;\n//\n//            int ri = (i%samplesPerChannel);\n//            int gi = (j%samplesPerChannel);\n//            int bi = (k%samplesPerChannel);\n//\n//            // only count odd samples, that are \"in between\"\n//            // samples we have already returned previously\n//            if (ri%2 != 0 || gi%2 != 0 || bi%2 != 0) {\n//                n++;\n//            }\n//\n//            if (n > samplesToJump) {\n//                int r = ri*255/(samplesPerChannel-1);\n//                int g = gi*255/(samplesPerChannel-1);\n//                int b = bi*255/(samplesPerChannel-1);\n//                return CvScalar.CV_RGB(r,g,b);\n//            }\n//        }\n//\n//    }\n\n    private static ThreadLocal<CvMat>\n            H3x3 = CvMat.createThreadLocal(3, 3),\n            R3x3 = CvMat.createThreadLocal(3, 3),\n            t3x1 = CvMat.createThreadLocal(3, 1),\n            n3x1 = CvMat.createThreadLocal(3, 1),\n            z3x1 = CvMat.createThreadLocal(3, 1);\n    public boolean processCameraImage(IplImage cameraImage) {\n        if (undistImage == null ||\n                undistImage.width()  != cameraImage.width()  ||\n                undistImage.height() != cameraImage.height() ||\n                undistImage.depth()  != cameraImage.depth()) {\n            undistImage = cameraImage.clone();\n        }\n\n        if (mask == null || mask2 == null ||\n                mask.width()  != cameraImage.width()  || mask2.width()  != cameraImage.width() ||\n                mask.height() != cameraImage.height() || mask2.height() != cameraImage.width()) {\n            mask = IplImage.create(cameraImage.width(), cameraImage.height(),\n                    IPL_DEPTH_8U, 1, cameraImage.origin());\n            mask2 = IplImage.create(cameraImage.width(), cameraImage.height(),\n                    IPL_DEPTH_8U, 1, cameraImage.origin());\n        }\n\n        CvMat H = H3x3.get();\n        CvMat R = R3x3.get();\n        CvMat t = t3x1.get();\n        CvMat n = n3x1.get();\n        CvMat z = z3x1.get();\n        z.put(0.0, 0.0, 1.0);\n\n        // detect the markers in the camera image, to\n        // 1. find the expected attenuation due to geometry\n        // 2. use only regions we know are \"white\" on the board\n        camera.undistort(cameraImage, undistImage);\n        Marker[] detectedBoardMarkers = markerDetector.detect(undistImage, false);\n        if (detectedBoardMarkers.length >= boardPlane.getMarkers().length*settings.detectedBoardMin) {\n            // use detected markers in the camera image, to\n            // 1. find the expected attenuation due to geometry\n            // 2. use only regions we know are \"white\" on the board\n            boardPlane.getTotalWarp(detectedBoardMarkers, H);\n            cvPerspectiveTransform(boardSrcPts, boardDstPts, H);\n            double[] boardPts = boardDstPts.get();\n\n            // Extract R and t from the board homography, using it as our\n            // \"first camera\", so we need to use z as the normal of the plane...\n            cvMatMul(camKinv, H, R);\n            double error = JavaCV.HnToRt(R, z, R, t);\n//            System.out.println(error);\n\n            // find the plane equation of the board in the camera's frame\n            // (normal vector n and distance d) and get the back-projection\n            // matrix of the camera\n            cvMatMul(R, z, n);\n            double d = cvDotProduct(t, z);\n\n            // find the homography from the camera to the projector\n            // H =  K_p (R - T*n^T/d)) K_c^-1\n            cvGEMM  (projector.T, n, -1/d,      projector.R, 1,  H, CV_GEMM_B_T);\n            cvMatMul(projector.cameraMatrix, H,                  H);\n            cvMatMul(H, camKinv,                                 H);\n            cvConvertScale(H, H, 1/H.get(8), 0);\n//            System.out.println(H);\n\n            // find the homography from the projector to the camera\n            cvInvert(H, H);\n            cvConvertScale(H, H, 1/H.get(8), 0);\n\n            // reproject projector edges into the camera image\n            cvPerspectiveTransform(projSrcPts, projDstPts, H);\n            double[] projPts = projDstPts.get();\n\n            // create mask containing only blank regions of the board\n            // intersected with regions coverable by the projector\n            cvSetZero(mask);\n            double cx = 0, cy = 0;\n            for (int j = 0; j < 4; j++) {\n                cx += boardPts[j*2    ];\n                cy += boardPts[j*2 + 1];\n            }\n            cx/=4; cy/=4;\n            for (int j = 0; j < 4; j++) {\n                boardPts[j*2    ] -= (boardPts[j*2    ] - cx)*settings.trimmingFraction;\n                boardPts[j*2 + 1] -= (boardPts[j*2 + 1] - cy)*settings.trimmingFraction;\n            }\n            cvFillConvexPoly(mask, new CvPoint(4).put((byte)16, boardPts, 0, 8),\n                    4, CvScalar.WHITE, 8, 16);\n\n            for (int j = 0; j < (boardPts.length-8)/8; j++) {\n                cvFillConvexPoly(mask, new CvPoint(4).put((byte)16, boardPts, 8 + j*8, 8),\n                        4, CvScalar.BLACK, 8, 16);\n            }\n\n            cvSetZero(mask2);\n            cx = 0; cy = 0;\n            for (int j = 0; j < 4; j++) {\n                cx += projPts[j*2    ];\n                cy += projPts[j*2 + 1];\n            }\n            cx/=4; cy/=4;\n            for (int j = 0; j < 4; j++) {\n                projPts[j*2    ] -= (projPts[j*2    ] - cx)*settings.trimmingFraction;\n                projPts[j*2 + 1] -= (projPts[j*2 + 1] - cy)*settings.trimmingFraction;\n            }\n            cvFillConvexPoly(mask2, new CvPoint(4).put((byte)16, projPts, 0, 8),\n                    4, CvScalar.WHITE, 8, 16);\n\n            cvAnd(mask, mask2, mask, null);\n            cvErode(mask, mask, null, 1);\n\n//cvSaveImage(\"masked\" + i + \".png\", cameraImages[i]);\n//try {\n//    Thread.sleep(1000);\n//} catch (InterruptedException ex) { }\n\n            // take the average as the camera response, and also\n            // compensate for attenuation caused by the geometry\n            CvScalar c = cvAvg(undistImage, mask);\n            int[] o = camera.getRGBColorOrder();\n            double s = cameraImage.highValue();\n            cameraColors[counter] = new Color((float)(c.val(o[0])/s),\n                    (float)(c.val(o[1])/s), (float)(c.val(o[2])/s));\n\n            return true;\n        }\n\n        return false;\n    }\n\n    public double calibrate() {\n        Color[] cc = getCameraColors();\n        Color[] pc = getProjectorColors();\n        assert (counter == pc.length);\n\n        ColorCalibrator calibrator = new ColorCalibrator(projector);\n        projector.avgColorErr = calibrator.calibrate(cc, pc);\n        camera.colorMixingMatrix = CvMat.create(3, 3);\n        camera.additiveLight     = CvMat.create(3, 1);\n        cvSetIdentity(camera.colorMixingMatrix);\n        cvSetZero    (camera.additiveLight);\n        counter = 0;\n        return projector.avgColorErr;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProCamGeometricCalibrator.java",
    "content": "/*\n * Copyright (C) 2009-2011 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.LinkedList;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProCamGeometricCalibrator {\n    public ProCamGeometricCalibrator(Settings settings, MarkerDetector.Settings detectorSettings,\n            MarkedPlane boardPlane, MarkedPlane projectorPlane,\n            ProjectiveDevice camera, ProjectiveDevice projector) {\n        this(settings, detectorSettings, boardPlane, projectorPlane, \n                new GeometricCalibrator[] { \n                new GeometricCalibrator(settings, detectorSettings, boardPlane, camera)},\n                new GeometricCalibrator(settings, detectorSettings, projectorPlane, projector));\n    }\n    @SuppressWarnings(\"unchecked\")\n    public ProCamGeometricCalibrator(Settings settings, MarkerDetector.Settings detectorSettings,\n            MarkedPlane boardPlane, MarkedPlane projectorPlane,\n            GeometricCalibrator[] cameraCalibrators, GeometricCalibrator projectorCalibrator) {\n        this.settings = settings;\n        this.detectorSettings = detectorSettings;\n        this.boardPlane = boardPlane;\n        this.projectorPlane = projectorPlane;\n\n        this.cameraCalibrators = cameraCalibrators;\n        int n = cameraCalibrators.length;\n        markerDetectors = new MarkerDetector[n];\n        // \"unchecked\" warning here\n        allImagedBoardMarkers = new LinkedList[n];\n        grayscaleImage = new IplImage[n];\n        tempImage1 = new IplImage[n];\n        tempImage2 = new IplImage[n];\n        lastDetectedMarkers1 = new Marker[n][];\n        lastDetectedMarkers2 = new Marker[n][];\n        rmseBoardWarp = new double[n];\n        rmseProjWarp = new double[n];\n        boardWarp = new CvMat[n];\n        projWarp = new CvMat[n];\n        prevBoardWarp = new CvMat[n];\n        lastBoardWarp = new CvMat[n];\n        tempPts1 = new CvMat[n];\n        tempPts2 = new CvMat[n];\n        for (int i = 0; i < n; i++) {\n            markerDetectors[i] = new MarkerDetector(detectorSettings);\n            allImagedBoardMarkers[i] = new LinkedList<Marker[]>();\n            grayscaleImage[i] = null;\n            tempImage1[i] = null;\n            tempImage2[i] = null;\n            lastDetectedMarkers1[i] = null;\n            lastDetectedMarkers2[i] = null;\n            rmseBoardWarp[i] = Double.POSITIVE_INFINITY;\n            rmseProjWarp[i] = Double.POSITIVE_INFINITY;\n            boardWarp[i] = CvMat.create(3, 3);\n            projWarp[i] = CvMat.create(3, 3);\n            prevBoardWarp[i] = CvMat.create(3, 3);\n            lastBoardWarp[i] = CvMat.create(3, 3);\n            cvSetIdentity(prevBoardWarp[i]);\n            cvSetIdentity(lastBoardWarp[i]);\n            tempPts1[i] = CvMat.create(1, 4, CV_64F, 2);\n            tempPts2[i] = CvMat.create(1, 4, CV_64F, 2);\n        }\n        this.projectorCalibrator = projectorCalibrator;\n\n        this.boardWarpSrcPts = CvMat.create(1, 4, CV_64F, 2);\n        if (boardPlane != null) {\n            int w = boardPlane.getImage().width();\n            int h = boardPlane.getImage().height();\n            boardWarpSrcPts.put(0.0, 0.0,  w, 0.0,  w, h,  0.0, h);\n        }\n        if (projectorPlane != null) {\n            int w = projectorPlane.getImage().width();\n            int h = projectorPlane.getImage().height();\n            projectorCalibrator.getProjectiveDevice().imageWidth = w;\n            projectorCalibrator.getProjectiveDevice().imageHeight = h;\n        }\n    }\n\n    private final int\n            MSB_IMAGE_SHIFT = 8,\n            LSB_IMAGE_SHIFT = 7;\n\n    public static class Settings extends GeometricCalibrator.Settings {\n        double detectedProjectorMin = 0.5;\n        boolean useOnlyIntersection = true;\n        double prewarpUpdateErrorMax = 0.01;\n\n        public double getDetectedProjectorMin() {\n            return detectedProjectorMin;\n        }\n        public void setDetectedProjectorMin(double detectedProjectorMin) {\n            this.detectedProjectorMin = detectedProjectorMin;\n        }\n\n        public boolean isUseOnlyIntersection() {\n            return useOnlyIntersection;\n        }\n        public void setUseOnlyIntersection(boolean useOnlyIntersection) {\n            this.useOnlyIntersection = useOnlyIntersection;\n        }\n\n        public double getPrewarpUpdateErrorMax() {\n            return prewarpUpdateErrorMax;\n        }\n        public void setPrewarpUpdateErrorMax(double prewarpUpdateErrorMax) {\n            this.prewarpUpdateErrorMax = prewarpUpdateErrorMax;\n        }\n    }\n\n    private Settings settings;\n    private MarkerDetector.Settings detectorSettings;\n\n    // (possibly) multiple camera stuff in arrays\n    private GeometricCalibrator[] cameraCalibrators;\n    private MarkerDetector[] markerDetectors;\n    // keep our own list of markers for the camera, since cameraCalibrators\n    // might be used outside ProCamGeometricCalibrator as well...\n    LinkedList<Marker[]>[] allImagedBoardMarkers;\n    private IplImage[] grayscaleImage, tempImage1, tempImage2;\n    private Marker[][] lastDetectedMarkers1, lastDetectedMarkers2;\n    private double[] rmseBoardWarp, rmseProjWarp;\n    private CvMat[] boardWarp, projWarp;\n    private CvMat[] prevBoardWarp, lastBoardWarp;\n    private CvMat[] tempPts1, tempPts2;\n\n    // single board and projector stuff\n    private boolean updatePrewarp = false;\n    private final MarkedPlane boardPlane, projectorPlane;\n    private final GeometricCalibrator projectorCalibrator;\n    private final CvMat boardWarpSrcPts;\n\n    public MarkedPlane getBoardPlane() {\n        return boardPlane;\n    }\n    public MarkedPlane getProjectorPlane() {\n        return projectorPlane;\n    }\n    public GeometricCalibrator[] getCameraCalibrators() {\n        return cameraCalibrators;\n    }\n    public GeometricCalibrator getProjectorCalibrator() {\n        return projectorCalibrator;\n    }\n    public int getImageCount() {\n        int n = projectorCalibrator.getImageCount()/cameraCalibrators.length;\n        for (GeometricCalibrator c : cameraCalibrators) {\n            assert(c.getImageCount() == n);\n        }\n        return n;\n    }\n\n    public Marker[][] processCameraImage(IplImage cameraImage) {\n        return processCameraImage(cameraImage, 0);\n    }\n    public Marker[][] processCameraImage(IplImage cameraImage, final int cameraNumber) {\n        cameraCalibrators[cameraNumber].getProjectiveDevice().imageWidth = cameraImage.width();\n        cameraCalibrators[cameraNumber].getProjectiveDevice().imageHeight = cameraImage.height();\n\n        if (cameraImage.nChannels() > 1) {\n            if (grayscaleImage[cameraNumber] == null ||\n                    grayscaleImage[cameraNumber].width()  != cameraImage.width()  ||\n                    grayscaleImage[cameraNumber].height() != cameraImage.height() ||\n                    grayscaleImage[cameraNumber].depth()  != cameraImage.depth()) {\n                grayscaleImage[cameraNumber] = IplImage.create(cameraImage.width(),\n                        cameraImage.height(), cameraImage.depth(), 1, cameraImage.origin());\n            }\n            cvCvtColor(cameraImage, grayscaleImage[cameraNumber], CV_BGR2GRAY);\n        } else {\n            grayscaleImage[cameraNumber] = cameraImage;\n        }\n\n        final boolean boardWhiteMarkers = boardPlane.getForegroundColor().magnitude() >\n                                          boardPlane.getBackgroundColor().magnitude();\n        final boolean projWhiteMarkers = projectorPlane.getForegroundColor().magnitude() >\n                                         projectorPlane.getBackgroundColor().magnitude();\n        if (grayscaleImage[cameraNumber].depth() > 8) {\n            if (tempImage1[cameraNumber] == null ||\n                    tempImage1[cameraNumber].width()  != grayscaleImage[cameraNumber].width()  ||\n                    tempImage1[cameraNumber].height() != grayscaleImage[cameraNumber].height()) {\n                tempImage1[cameraNumber] = IplImage.create(grayscaleImage[cameraNumber].width(),\n                        grayscaleImage[cameraNumber].height(), IPL_DEPTH_8U, 1,\n                        grayscaleImage[cameraNumber].origin());\n                tempImage2[cameraNumber] = IplImage.create(grayscaleImage[cameraNumber].width(),\n                        grayscaleImage[cameraNumber].height(), IPL_DEPTH_8U, 1,\n                        grayscaleImage[cameraNumber].origin());\n            }\n            Parallel.run(new Runnable() { public void run() {\n                cvConvertScale(grayscaleImage[cameraNumber],\n                        tempImage1[cameraNumber], 1.0/(1<<LSB_IMAGE_SHIFT), 0);\n                lastDetectedMarkers1[cameraNumber] = cameraCalibrators[cameraNumber].\n                        markerDetector.detect(tempImage1[cameraNumber], boardWhiteMarkers);\n            }}, new Runnable() { public void run() {\n                cvConvertScale(grayscaleImage[cameraNumber],\n                        tempImage2[cameraNumber], 1.0/(1<<MSB_IMAGE_SHIFT), 0);\n                lastDetectedMarkers2[cameraNumber] = \n                        markerDetectors[cameraNumber].detect(tempImage2[cameraNumber], projWhiteMarkers);\n            }});\n        } else {\n            Parallel.run(new Runnable() { public void run() {\n                lastDetectedMarkers1[cameraNumber] = cameraCalibrators[cameraNumber].\n                        markerDetector.detect(grayscaleImage[cameraNumber], boardWhiteMarkers);\n            }}, new Runnable() { public void run() {\n                lastDetectedMarkers2[cameraNumber] = \n                        markerDetectors[cameraNumber].detect(grayscaleImage[cameraNumber], projWhiteMarkers);\n            }});\n        }\n\n        return processMarkers(cameraNumber) ? \n            new Marker[][] { lastDetectedMarkers1[cameraNumber],\n                             lastDetectedMarkers2[cameraNumber] } : null;\n    }\n\n    public void drawMarkers(IplImage image) {\n        drawMarkers(image, 0);\n    }\n    public void drawMarkers(IplImage image, int cameraNumber) {\n        cameraCalibrators[cameraNumber].\n                markerDetector.draw(image, lastDetectedMarkers1[cameraNumber]);\n        projectorCalibrator.\n                markerDetector.draw(image, lastDetectedMarkers2[cameraNumber]);\n    }\n\n    public boolean processMarkers() {\n        return processMarkers(0);\n    }\n    public boolean processMarkers(int cameraNumber) {\n        return processMarkers(lastDetectedMarkers1[cameraNumber],\n                lastDetectedMarkers2[cameraNumber], cameraNumber);\n    }\n    public boolean processMarkers(Marker[] imagedBoardMarkers, Marker[] imagedProjectorMarkers) {\n        return processMarkers(imagedBoardMarkers, imagedProjectorMarkers, 0);\n    }\n    public boolean processMarkers(Marker[] imagedBoardMarkers, \n            Marker[] imagedProjectorMarkers, int cameraNumber) {\n        rmseBoardWarp[cameraNumber] = boardPlane    .getTotalWarp(imagedBoardMarkers,     boardWarp[cameraNumber]);\n        rmseProjWarp [cameraNumber] = projectorPlane.getTotalWarp(imagedProjectorMarkers, projWarp[cameraNumber]);\n\n        int imageSize = (cameraCalibrators[cameraNumber].getProjectiveDevice().imageWidth+\n                         cameraCalibrators[cameraNumber].getProjectiveDevice().imageHeight)/2;\n        if (rmseBoardWarp[cameraNumber] <= settings.prewarpUpdateErrorMax*imageSize &&\n                rmseProjWarp[cameraNumber] <= settings.prewarpUpdateErrorMax*imageSize) {\n            updatePrewarp = true;\n        } else {\n            // not detected accurately enough..\n            return false;\n        }\n\n        // First, check if we detected enough markers...\n        if (imagedBoardMarkers    .length < boardPlane    .getMarkers().length*settings.detectedBoardMin ||\n            imagedProjectorMarkers.length < projectorPlane.getMarkers().length*settings.detectedProjectorMin) {\n                return false;\n        }\n\n        // then check by how much the corners of the calibration board moved\n        cvPerspectiveTransform  (boardWarpSrcPts,        tempPts1[cameraNumber], boardWarp[cameraNumber]);\n        cvPerspectiveTransform  (boardWarpSrcPts,        tempPts2[cameraNumber], prevBoardWarp[cameraNumber]);\n        double rmsePrev = cvNorm(tempPts1[cameraNumber], tempPts2[cameraNumber]);\n        cvPerspectiveTransform  (boardWarpSrcPts,        tempPts2[cameraNumber], lastBoardWarp[cameraNumber]);\n        double rmseLast = cvNorm(tempPts1[cameraNumber], tempPts2[cameraNumber]);\n\n//System.out.println(\"rmsePrev = \" + rmsePrev + \" rmseLast = \" + rmseLast + \" cameraNumber = \" + cameraNumber);\n        // save boardWarp for next iteration...\n        cvCopy(boardWarp[cameraNumber], prevBoardWarp[cameraNumber]);\n\n        // send upstream our recommendation for addition or not of these markers...\n        if (rmsePrev < settings.patternSteadySize*imageSize &&\n                rmseLast > settings.patternMovedSize*imageSize) {\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    public void addMarkers() throws InterruptedException {\n        addMarkers(0);\n    }\n    public void addMarkers(int cameraNumber) throws InterruptedException {\n        addMarkers(lastDetectedMarkers1[cameraNumber], lastDetectedMarkers2[cameraNumber], cameraNumber);\n    }\n    public void addMarkers(Marker[] imagedBoardMarkers, Marker[] imagedProjectorMarkers) throws InterruptedException {\n        addMarkers(imagedBoardMarkers, imagedProjectorMarkers, 0);\n    }\n    private static ThreadLocal<CvMat>\n            tempWarp3x3 = CvMat.createThreadLocal(3, 3);\n    public void addMarkers(Marker[] imagedBoardMarkers, \n            Marker[] imagedProjectorMarkers, int cameraNumber) throws InterruptedException {\n        CvMat tempWarp = tempWarp3x3.get();\n\n        if (!settings.useOnlyIntersection) {\n            cameraCalibrators[cameraNumber].addMarkers(boardPlane.getMarkers(),\n                    imagedBoardMarkers);\n            allImagedBoardMarkers[cameraNumber].add(imagedBoardMarkers);\n        } else {\n            // deep cloning... warp board markers in projector plane\n            Marker[] inProjectorBoardMarkers = new Marker[imagedBoardMarkers.length];\n            for (int i = 0; i < inProjectorBoardMarkers.length; i++) {\n                inProjectorBoardMarkers[i] = imagedBoardMarkers[i].clone();\n            }\n            cvInvert(projWarp[cameraNumber], tempWarp);\n            Marker.applyWarp(inProjectorBoardMarkers, tempWarp);\n\n            // only add markers that are within the projector plane as well\n            int w = projectorPlane.getImage().width();\n            int h = projectorPlane.getImage().height();\n            Marker[] boardMarkersToAdd = new Marker[imagedBoardMarkers.length];\n            int totalToAdd = 0;\n            for (int i = 0; i < inProjectorBoardMarkers.length; i++) {\n                double[] c = inProjectorBoardMarkers[i].corners;\n                boolean outside = false;\n                for (int j = 0; j < 4; j++) {\n                    int margin = detectorSettings.subPixelWindow/2;\n                    if (c[2*j  ] < margin || c[2*j  ] >= w-margin ||\n                        c[2*j+1] < margin || c[2*j+1] >= h-margin) {\n                            outside = true;\n                            break;\n                    }\n                }\n                if (!outside) {\n                    boardMarkersToAdd[totalToAdd++] = imagedBoardMarkers[i];\n                }\n            }\n            Marker[] a = Arrays.copyOf(boardMarkersToAdd, totalToAdd);\n            cameraCalibrators[cameraNumber].addMarkers(boardPlane.getMarkers(), a);\n            allImagedBoardMarkers[cameraNumber].add(a);\n        }\n\n        // deep cloning...\n        Marker[] prewrappedProjMarkers = new Marker[projectorPlane.getMarkers().length];\n        for (int i = 0; i < prewrappedProjMarkers.length; i++) {\n            prewrappedProjMarkers[i] = projectorPlane.getMarkers()[i].clone();\n        }\n        // prewarp points for the projectorCalibrator\n        Marker.applyWarp(prewrappedProjMarkers, projectorPlane.getPrewarp());\n        synchronized (projectorCalibrator) {\n            // wait our turn to add markers orderly in the projector calibrator...\n            while (projectorCalibrator.getImageCount()%cameraCalibrators.length < cameraNumber) {\n                projectorCalibrator.wait();\n            }\n            projectorCalibrator.addMarkers(imagedProjectorMarkers, prewrappedProjMarkers);\n            projectorCalibrator.notify();\n        }\n\n        // we added the detected markers, so save last computed warp too...\n        cvCopy(boardWarp[cameraNumber], lastBoardWarp[cameraNumber]);\n    }\n\n    public IplImage getProjectorImage() {\n        if (updatePrewarp) {\n            // find which camera has the smallest RMSE\n            double minRmse = Double.MAX_VALUE;\n            int minCameraNumber = 0;\n            for (int i = 0; i < cameraCalibrators.length; i++) {\n                double rmse = rmseBoardWarp[i]+rmseProjWarp[i];\n                if (rmse < minRmse) {\n                    minRmse = rmse;\n                    minCameraNumber = i;\n                }\n            }\n\n            // and use it to update the prewarp...\n            CvMat prewarp = projectorPlane.getPrewarp();\n            cvInvert(          projWarp[minCameraNumber], prewarp);\n            cvMatMul(prewarp, boardWarp[minCameraNumber], prewarp);\n            projectorPlane.setPrewarp(prewarp);\n        }\n\n        return projectorPlane.getImage();\n    }\n\n    public double[] calibrate(boolean useCenters, boolean calibrateCameras) {\n        return calibrate(useCenters, calibrateCameras);\n    }\n    @SuppressWarnings(\"unchecked\")\n    public double[] calibrate(boolean useCenters, boolean calibrateCameras, int cameraAtOrigin) {\n        GeometricCalibrator calibratorAtOrigin = cameraCalibrators[cameraAtOrigin];\n\n        // calibrate camera if not already calibrated...\n        if (calibrateCameras) {\n            for (int cameraNumber = 0; cameraNumber < cameraCalibrators.length; cameraNumber++) {\n                cameraCalibrators[cameraNumber].calibrate(useCenters);\n                if (cameraCalibrators[cameraNumber] != calibratorAtOrigin) {\n                    calibratorAtOrigin.calibrateStereo(useCenters, cameraCalibrators[cameraNumber]);\n                }\n            }\n        }\n\n        // remove distortion from corners of imaged markers for projector calibration\n        // (in the case of the projector, markers imaged by the cameras, that is\n        // those affected by their distortions, are the \"object\" markers, but\n        // we need to remove this distortion, something we can do now that we\n        // have calibrated the cameras...)\n        LinkedList<Marker[]> allDistortedProjectorMarkers = projectorCalibrator.getAllObjectMarkers(),\n                             distortedProjectorMarkersAtOrigin = new LinkedList<Marker[]>(),\n                             allUndistortedProjectorMarkers = new LinkedList<Marker[]>(),\n                             undistortedProjectorMarkersAtOrigin = new LinkedList<Marker[]>();\n        Iterator<Marker[]> ip = allDistortedProjectorMarkers.iterator();\n        // \"unchecked\" warning here\n        Iterator<Marker[]>[] ib = new Iterator[cameraCalibrators.length];\n        for (int cameraNumber = 0; cameraNumber < cameraCalibrators.length; cameraNumber++) {\n            ib[cameraNumber] = allImagedBoardMarkers[cameraNumber].iterator();\n        }\n\n        // iterate over all the saved markers in the right order...\n        // eew, this is getting ugly...\n        while (ip.hasNext()) {\n            for (int cameraNumber = 0; cameraNumber < cameraCalibrators.length; cameraNumber++) {\n                double maxError = settings.prewarpUpdateErrorMax *\n                        (cameraCalibrators[cameraNumber].getProjectiveDevice().imageWidth+\n                         cameraCalibrators[cameraNumber].getProjectiveDevice().imageHeight)/2;\n\n                Marker[] distortedBoardMarkers = ib[cameraNumber].next(),\n                         distortedProjectorMarkers = ip.next(),\n                         undistortedBoardMarkers = new Marker[distortedBoardMarkers.length],\n                         undistortedProjectorMarkers = new Marker[distortedProjectorMarkers.length];\n\n                // remove radial distortion from all points imaged by the camera\n                for (int i = 0; i < distortedBoardMarkers.length; i++) {\n                    Marker m = undistortedBoardMarkers[i] = distortedBoardMarkers[i].clone();\n                    m.corners = cameraCalibrators[cameraNumber].getProjectiveDevice().undistort(m.corners);\n                }\n                for (int i = 0; i < distortedProjectorMarkers.length; i++) {\n                    Marker m = undistortedProjectorMarkers[i] = distortedProjectorMarkers[i].clone();\n                    m.corners = cameraCalibrators[cameraNumber].getProjectiveDevice().undistort(m.corners);\n                }\n\n                // remove linear distortion/warping of camera imaged markers from\n                // the projector, to get their physical location on the board\n                if (boardPlane.getTotalWarp(undistortedBoardMarkers, boardWarp[cameraNumber]) > maxError) {\n                    assert(false);\n                }\n                cvInvert(boardWarp[cameraNumber], boardWarp[cameraNumber]);\n                Marker.applyWarp(undistortedProjectorMarkers, boardWarp[cameraNumber]);\n\n                // tadam, we not have undistorted \"object\" corners for the projector..\n                allUndistortedProjectorMarkers.add(undistortedProjectorMarkers);\n                if (cameraCalibrators[cameraNumber] == calibratorAtOrigin) {\n                    undistortedProjectorMarkersAtOrigin.add(undistortedProjectorMarkers);\n                    distortedProjectorMarkersAtOrigin.add(distortedProjectorMarkers);\n                } else {\n                    undistortedProjectorMarkersAtOrigin.add(new Marker[0]);\n                    distortedProjectorMarkersAtOrigin.add(new Marker[0]);\n                }\n            }\n        }\n\n        // calibrate projector\n        projectorCalibrator.setAllObjectMarkers(allUndistortedProjectorMarkers);\n        double[] reprojErr = projectorCalibrator.calibrate(useCenters);\n//        projectorCalibrator.getProjectiveDevice().nominalDistance =\n//                projectorCalibrator.getProjectiveDevice().getNominalDistance(boardPlane);\n\n        // calibrate as a stereo pair (find rotation and translation)\n        // let's use the projector markers only...\n        LinkedList<Marker[]> om = calibratorAtOrigin.getAllObjectMarkers(),\n                             im = calibratorAtOrigin.getAllImageMarkers();\n        calibratorAtOrigin.setAllObjectMarkers(undistortedProjectorMarkersAtOrigin);\n        calibratorAtOrigin.setAllImageMarkers(distortedProjectorMarkersAtOrigin);\n        double[] epipolarErr = calibratorAtOrigin.calibrateStereo(useCenters, projectorCalibrator);\n\n        // reset everything as it was before we started, so we get the same\n        // result if called a second time..\n        projectorCalibrator.setAllObjectMarkers(allDistortedProjectorMarkers);\n        calibratorAtOrigin.setAllObjectMarkers(om);\n        calibratorAtOrigin.setAllImageMarkers(im);\n\n        return new double[] { reprojErr[0], reprojErr[1], epipolarErr[0], epipolarErr[1] };\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProCamTransformer.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport org.bytedeco.opencv.opencv_calib3d.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.javacv.cvkernels.*;\nimport static org.bytedeco.opencv.global.opencv_calib3d.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProCamTransformer implements ImageTransformer {\n    public ProCamTransformer(double[] referencePoints,\n            CameraDevice camera, ProjectorDevice projector) {\n        this(referencePoints, camera, projector, null);\n    }\n    public ProCamTransformer(double[] referencePoints,\n            CameraDevice camera, ProjectorDevice projector, CvMat n) {\n        this.camera = camera;\n        this.projector = projector;\n\n        if (referencePoints != null) {\n            this.surfaceTransformer = new ProjectiveColorTransformer(\n                    camera.cameraMatrix, camera.cameraMatrix, null, null, n,\n                    referencePoints, null, null, 3, 0);\n        }\n        double[] referencePoints1 = { 0, 0,  camera.imageWidth/2, camera.imageHeight,  camera.imageWidth, 0 };\n        double[] referencePoints2 = { 0, 0,  projector.imageWidth/2, projector.imageHeight,  projector.imageWidth, 0 };\n        if (n != null) {\n            invCameraMatrix = CvMat.create(3, 3);\n            cvInvert(camera.cameraMatrix, invCameraMatrix);\n            JavaCV.perspectiveTransform(referencePoints2, referencePoints1,\n                    invCameraMatrix, projector.cameraMatrix, projector.R, projector.T, n, true);\n        }\n        this.projectorTransformer = new ProjectiveColorTransformer(\n                camera.cameraMatrix, projector.cameraMatrix, projector.R, projector.T, null,\n                referencePoints1, referencePoints2, projector.colorMixingMatrix,\n                /*surfaceTransformer == null ? 3 : */1, 3);\n//        CvMat n2 = createParameters().getN();\n\n        if (referencePoints != null && n != null) {\n            frontoParallelH = camera.getFrontoParallelH(referencePoints, n, CvMat.create(3, 3));\n            invFrontoParallelH = frontoParallelH.clone();\n            cvInvert(frontoParallelH, invFrontoParallelH);\n        }\n    }\n\n    protected CameraDevice camera = null;\n    protected ProjectorDevice projector = null;\n    protected ProjectiveColorTransformer surfaceTransformer = null;\n    protected ProjectiveColorTransformer projectorTransformer = null;\n    protected IplImage[] projectorImage = null, surfaceImage = null;\n    protected CvScalar fillColor = cvScalar(0.0, 0.0, 0.0, 1.0);\n    protected CvRect roi = new CvRect();\n    protected CvMat frontoParallelH = null, invFrontoParallelH = null;\n    protected CvMat invCameraMatrix = null;\n\n    protected KernelData kernelData = null;\n    protected CvMat[] H1 = null;\n    protected CvMat[] H2 = null;\n    protected CvMat[] X = null;\n\n    public int getNumGains() {\n        return projectorTransformer.getNumGains();\n    }\n    public int getNumBiases() {\n        return projectorTransformer.getNumBiases();\n    }\n\n    public CvScalar getFillColor() {\n        return fillColor;\n    }\n    public void setFillColor(CvScalar fillColor) {\n        this.fillColor = fillColor;\n    }\n\n    public ProjectiveColorTransformer getSurfaceTransformer() {\n        return surfaceTransformer;\n    }\n    public ProjectiveColorTransformer getProjectorTransformer() {\n        return projectorTransformer;\n    }\n\n    public IplImage getProjectorImage(int pyramidLevel) {\n        return projectorImage[pyramidLevel];\n    }\n    public void setProjectorImage(IplImage projectorImage0, int minLevel, int maxLevel) {\n        setProjectorImage(projectorImage0, minLevel, maxLevel, true);\n    }\n    public void setProjectorImage(IplImage projectorImage0, int minLevel, int maxLevel, boolean convertToFloat) {\n        if (projectorImage == null || projectorImage.length != maxLevel+1) {\n            projectorImage = new IplImage[maxLevel+1];\n        }\n\n        if (projectorImage0.depth() == IPL_DEPTH_32F || !convertToFloat) {\n            projectorImage[minLevel] = projectorImage0;\n        } else {\n            if (projectorImage[minLevel] == null) {\n                projectorImage[minLevel] = IplImage.create(projectorImage0.width(), projectorImage0.height(),\n                        IPL_DEPTH_32F, projectorImage0.nChannels(), projectorImage0.origin());\n            }\n            IplROI ir = projectorImage0.roi();\n            if (ir != null) {\n                int align = 1<<(maxLevel+1);\n                roi.x(Math.max(0, (int)Math.floor((double)ir.xOffset()/align)*align));\n                roi.y(Math.max(0, (int)Math.floor((double)ir.yOffset()/align)*align));\n                roi.width (Math.min(projectorImage0.width(),  (int)Math.ceil((double)ir.width() /align)*align));\n                roi.height(Math.min(projectorImage0.height(), (int)Math.ceil((double)ir.height()/align)*align));\n                cvSetImageROI(projectorImage0, roi);\n                cvSetImageROI(projectorImage[minLevel], roi);\n            } else {\n                cvResetImageROI(projectorImage0);\n                cvResetImageROI(projectorImage[minLevel]);\n            }\n            cvConvertScale(projectorImage0, projectorImage[minLevel], 1.0/255.0, 0);\n        }\n\n//        CvScalar.ByValue average = cvAvg(projectorImage[0], null);\n//        cvSubS(projectorImage[0], average, projectorImage[0], null);\n\n        for (int i = minLevel+1; i <= maxLevel; i++) {\n            int w = projectorImage[i-1].width()/2;\n            int h = projectorImage[i-1].height()/2;\n            int d = projectorImage[i-1].depth();\n            int c = projectorImage[i-1].nChannels();\n            int o = projectorImage[i-1].origin();\n            if (projectorImage[i] == null) {\n                projectorImage[i] = IplImage.create(w, h, d, c, o);\n            }\n\n            IplROI ir = projectorImage[i-1].roi();\n            if (ir != null) {\n                roi.x(ir.xOffset()/2); roi.width (ir.width() /2);\n                roi.y(ir.yOffset()/2); roi.height(ir.height()/2);\n                cvSetImageROI(projectorImage[i], roi);\n            } else {\n                cvResetImageROI(projectorImage[i]);\n            }\n            cvPyrDown(projectorImage[i-1], projectorImage[i], CV_GAUSSIAN_5x5);\n            cvResetImageROI(projectorImage[i-1]);\n        }\n    }\n    public IplImage getSurfaceImage(int pyramidLevel) {\n        return surfaceImage[pyramidLevel];\n    }\n    public void setSurfaceImage(IplImage surfaceImage0, int pyramidLevels) {\n        if (surfaceImage == null || surfaceImage.length != pyramidLevels) {\n            surfaceImage = new IplImage[pyramidLevels];\n        }\n        surfaceImage[0] = surfaceImage0;\n        cvResetImageROI(surfaceImage0);\n        for (int i = 1; i < pyramidLevels; i++) {\n            int w = surfaceImage[i-1].width()/2;\n            int h = surfaceImage[i-1].height()/2;\n            int d = surfaceImage[i-1].depth();\n            int c = surfaceImage[i-1].nChannels();\n            int o = surfaceImage[i-1].origin();\n            if (surfaceImage[i] == null) {\n                surfaceImage[i] = IplImage.create(w, h, d, c, o);\n            } else {\n                cvResetImageROI(surfaceImage[i]);\n            }\n            cvPyrDown(surfaceImage[i-1], surfaceImage[i], CV_GAUSSIAN_5x5);\n        }\n    }\n\n    protected void prepareTransforms(CvMat H1, CvMat H2, CvMat X, int pyramidLevel, Parameters p) {\n        ProjectiveColorTransformer.Parameters cameraParameters    = p.getSurfaceParameters();\n        ProjectiveColorTransformer.Parameters projectorParameters = p.getProjectorParameters();\n\n        if (surfaceTransformer != null) {\n            cvInvert(cameraParameters.getH(), H1);\n        }\n        cvInvert(projectorParameters.getH(), H2);\n\n        // adjust the scale of the transformation based on the pyramid level\n        if (pyramidLevel > 0) {\n            int scale = 1<<pyramidLevel;\n            if (surfaceTransformer != null) {\n                H1.put(2, H1.get(2)/scale);\n                H1.put(5, H1.get(5)/scale);\n                H1.put(6, H1.get(6)*scale);\n                H1.put(7, H1.get(7)*scale);\n            }\n\n            H2.put(2, H2.get(2)/scale);\n            H2.put(5, H2.get(5)/scale);\n            H2.put(6, H2.get(6)*scale);\n            H2.put(7, H2.get(7)*scale);\n        }\n\n        double[] x = projector.colorMixingMatrix.get();\n        double[] a = projectorParameters.getColorParameters();\n        double a2 = a[0];\n        X.put(a2*x[0], a2*x[1], a2*x[2], a[1],\n              a2*x[3], a2*x[4], a2*x[5], a[2],\n              a2*x[6], a2*x[7], a2*x[8], a[3],\n              0, 0, 0, 1);\n    }\n\n    public void transform(final IplImage srcImage, final IplImage dstImage, final CvRect roi,\n            final int pyramidLevel, final ImageTransformer.Parameters parameters, final boolean inverse) {\n        if (inverse) {\n            throw new UnsupportedOperationException(\"Inverse transform not supported.\");\n        }\n        final Parameters p = ((Parameters)parameters);\n        final ProjectiveTransformer.Parameters cameraParameters    = p.getSurfaceParameters();\n        final ProjectiveTransformer.Parameters projectorParameters = p.getProjectorParameters();\n\n        if (p.tempImage == null || p.tempImage.length <= pyramidLevel) {\n            p.tempImage = new IplImage[pyramidLevel+1];\n        }\n        p.tempImage[pyramidLevel] = IplImage.createIfNotCompatible(p.tempImage[pyramidLevel], dstImage);\n        if (roi == null) {\n            cvResetImageROI(p.tempImage[pyramidLevel]);\n        } else {\n            cvSetImageROI(p.tempImage[pyramidLevel], roi);\n        }\n\n//        Parallel.run(new Runnable() { public void run() {\n            // warp the template image\n        if (surfaceTransformer != null) {\n            surfaceTransformer.transform(srcImage, p.tempImage[pyramidLevel], roi, pyramidLevel, cameraParameters, false);\n        }\n//        }}, new Runnable() { public void run() {\n            // warp the projector image\n            projectorTransformer.transform(projectorImage[pyramidLevel], dstImage, roi, pyramidLevel, projectorParameters, false);\n//        }});\n\n        // multiply projector image with template image\n        if (surfaceTransformer != null) {\n            cvMul(dstImage, p.tempImage[pyramidLevel], dstImage, 1/dstImage.highValue());\n        } else {\n            cvCopy(p.tempImage[pyramidLevel], dstImage);\n        }\n    }\n\n    public void transform(CvMat srcPts, CvMat dstPts, ImageTransformer.Parameters parameters, boolean inverse) {\n        if (surfaceTransformer != null) {\n            surfaceTransformer.transform(srcPts, dstPts, ((Parameters)parameters).surfaceParameters, inverse);\n        } else if (dstPts != srcPts) {\n            dstPts.put(srcPts);\n        }\n    }\n\n    public void transform(Data[] data, CvRect roi, ImageTransformer.Parameters[] parameters, boolean[] inverses) {\n        assert data.length == parameters.length;\n        if (kernelData == null || kernelData.capacity() < data.length) {\n            kernelData = new KernelData(data.length);\n        }\n        if ((H1 == null || H1.length < data.length) && surfaceTransformer != null) {\n            H1 = new CvMat[data.length];\n            for (int i = 0; i < H1.length; i++) {\n                H1[i] = CvMat.create(3, 3);\n            }\n        }\n        if (H2 == null || H2.length < data.length) {\n            H2 = new CvMat[data.length];\n            for (int i = 0; i < H2.length; i++) {\n                H2[i] = CvMat.create(3, 3);\n            }\n        }\n        if (X == null || X.length < data.length) {\n            X = new CvMat[data.length];\n            for (int i = 0; i < X.length; i++) {\n                X[i] = CvMat.create(4, 4);\n            }\n        }\n\n        for (int i = 0; i < data.length; i++) {\n            kernelData.position(i);\n\n            kernelData.srcImg(projectorImage[data[i].pyramidLevel]);\n            kernelData.srcImg2(surfaceTransformer == null ? null : data[i].srcImg);\n            kernelData.subImg(data[i].subImg);\n            kernelData.srcDotImg(data[i].srcDotImg);\n            kernelData.mask(data[i].mask);\n            kernelData.zeroThreshold(data[i].zeroThreshold);\n            kernelData.outlierThreshold(data[i].outlierThreshold);\n\n            if (inverses != null && inverses[i]) {\n                throw new UnsupportedOperationException(\"Inverse transform not supported.\");\n            }\n            prepareTransforms(surfaceTransformer == null ? null : H1[i],\n                    H2[i], X[i], data[i].pyramidLevel, (Parameters)parameters[i]);\n\n            kernelData.H1(H2[i]);\n            kernelData.H2(surfaceTransformer == null ? null : H1[i]);\n            kernelData.X (X [i]);\n\n            kernelData.transImg(data[i].transImg);\n            kernelData.dstImg(data[i].dstImg);\n            kernelData.dstDstDot(data[i].dstDstDot);\n        }\n\n        long fullCapacity = kernelData.capacity();\n        kernelData.capacity(data.length);\n        multiWarpColorTransform(kernelData, roi, getFillColor());\n        kernelData.capacity(fullCapacity);\n\n        for (int i = 0; i < data.length; i++) {\n            kernelData.position(i);\n            data[i].dstCount        = kernelData.dstCount();\n            data[i].dstCountZero    = kernelData.dstCountZero();\n            data[i].dstCountOutlier = kernelData.dstCountOutlier();\n            data[i].srcDstDot       = kernelData.srcDstDot();\n        }\n\n//        if (data[0].dstCountZero > 0) {\n//            System.err.println(data[0].dstCountZero + \" out of \" + data[0].dstCount\n//                    + \" are zero = \" + 100*data[0].dstCountZero/data[0].dstCount + \"%\");\n//        }\n    }\n\n    public Parameters createParameters() {\n        return new Parameters();\n    }\n\n    public class Parameters implements ImageTransformer.Parameters {\n        protected Parameters() {\n            reset(false);\n        }\n        protected Parameters(ProjectiveColorTransformer.Parameters surfaceParameters,\n                             ProjectiveColorTransformer.Parameters projectorParameters) {\n            reset(surfaceParameters, projectorParameters);\n        }\n\n        private ProjectiveColorTransformer.Parameters surfaceParameters = null;\n        private ProjectiveColorTransformer.Parameters projectorParameters = null;\n        private IplImage[] tempImage = null;\n        private CvMat H = CvMat.create(3, 3), R = CvMat.create(3, 3),\n                      n = CvMat.create(3, 1), t = CvMat.create(3, 1);\n\n        public ProjectiveColorTransformer.Parameters getSurfaceParameters() {\n            return surfaceParameters;\n        }\n        public ProjectiveColorTransformer.Parameters getProjectorParameters() {\n            return projectorParameters;\n        }\n\n        private int getSizeForSurface() {\n            return surfaceTransformer == null ? 0 : surfaceParameters.size() -\n                   surfaceTransformer.getNumGains() - surfaceTransformer.getNumBiases();\n        }\n        private int getSizeForProjector() {\n            return projectorParameters.size();\n        }\n        public int size() {\n            return getSizeForSurface() + getSizeForProjector();\n        }\n        public double[] get() {\n            double[] p = new double[size()];\n            for (int i = 0; i < p.length; i++) {\n                p[i] = get(i);\n            }\n            return p;\n        }\n        public double get(int i) {\n            if (i < getSizeForSurface()) {\n                return surfaceParameters.get(i);\n            } else {\n                return projectorParameters.get(i-getSizeForSurface());\n            }\n        }\n        public void set(double ... p) {\n            for (int i = 0; i < p.length; i++) {\n                set(i, p[i]);\n            }\n        }\n        public void set(int i, double p) {\n            if (i < getSizeForSurface()) {\n                surfaceParameters.set(i, p);\n            } else {\n                projectorParameters.set(i-getSizeForSurface(), p);\n            }\n        }\n        public void set(ImageTransformer.Parameters p) {\n            Parameters pcp = (Parameters)p;\n            if (surfaceTransformer != null) {\n                surfaceParameters.set(pcp.getSurfaceParameters());\n                surfaceParameters.resetColor(false);\n            }\n            projectorParameters.set(pcp.getProjectorParameters());\n        }\n        public void reset(boolean asIdentity) {\n            reset(null, null);\n        }\n        public void reset(ProjectiveColorTransformer.Parameters surfaceParameters,\n                          ProjectiveColorTransformer.Parameters projectorParameters) {\n            if (surfaceParameters == null && surfaceTransformer != null) {\n                surfaceParameters = surfaceTransformer.createParameters();\n            }\n            if (projectorParameters == null) {\n                projectorParameters = projectorTransformer.createParameters();\n            }\n            this.surfaceParameters   = surfaceParameters;\n            this.projectorParameters = projectorParameters;\n\n            setSubspace(getSubspace());\n        }\n//        public boolean addDelta(int i) {\n//            return addDelta(i, 1);\n//        }\n//        public boolean addDelta(int i, double scale) {\n//            // gradient varies linearly with intensity, so\n//            // the increment value is not very important, but\n//            // referenceCameraImage is good only for the value 1,\n//            // so let's use that\n//            if (i < getSizeForSurface()) {\n//                surfaceParameters.addDelta(i, scale);\n//                projectorParameters.setUpdateNeeded(true);\n//            } else {\n//                projectorParameters.addDelta(i-getSizeForSurface(), scale);\n//            }\n//\n//            return false;\n//        }\n        public double getConstraintError() {\n            double error = surfaceTransformer == null ? 0 : surfaceParameters.getConstraintError();\n            projectorParameters.update();\n            return error;\n        }\n        public void compose(ImageTransformer.Parameters p1, boolean inverse1,\n                ImageTransformer.Parameters p2, boolean inverse2) {\n            throw new UnsupportedOperationException(\"Compose operation not supported.\");\n        }\n\n        public boolean preoptimize() {\n            double[] p = setSubspaceInternal(getSubspaceInternal());\n            if (p != null) {\n                set(8, p[8]);\n                set(9, p[9]);\n                set(10, p[10]);\n                return true;\n            }\n            return false;\n        }\n        public void setSubspace(double ... p) {\n            double[] dst = setSubspaceInternal(p);\n            if (dst != null) {\n                set(dst);\n            }\n        }\n        public double[] getSubspace() {\n            return getSubspaceInternal();\n        }\n\n        private double[] setSubspaceInternal(double ... p) {\n            if (invFrontoParallelH == null) {\n                return null;\n            }\n            double[] dst = new double[8+3];\n            t.put(p[0], p[1], p[2]);\n            Rodrigues(cvarrToMat(t), cvarrToMat(R), null);\n            t.put(p[3], p[4], p[5]);\n\n            // compute new H\n            H.put(R.get(0), R.get(1), t.get(0),\n                  R.get(3), R.get(4), t.get(1),\n                  R.get(6), R.get(7), t.get(2));\n            cvMatMul(H, invFrontoParallelH, H);\n            cvMatMul(surfaceTransformer.getK2(), H,    H);\n            cvMatMul(H, surfaceTransformer.getInvK1(), H);\n\n            // compute new n, rotation from the z-axis\n            cvGEMM(R, t, 1,  null, 0,  t, CV_GEMM_A_T);\n            double scale = 1/t.get(2);\n            n.put(0.0, 0.0, 1.0);\n            cvGEMM(R, n, scale, null, 0, n, 0);\n\n            // compute and set new three points\n            double[] src = projectorTransformer.getReferencePoints2();\n            JavaCV.perspectiveTransform(src, dst,\n                    projectorTransformer.getInvK1(),projectorTransformer.getK2(),\n                    projectorTransformer.getR(), projectorTransformer.getT(), n, true);\n            dst[8]  = dst[0];\n            dst[9]  = dst[2];\n            dst[10] = dst[4];\n\n            // compute and set new four points\n            JavaCV.perspectiveTransform(surfaceTransformer.getReferencePoints1(), dst, H);\n\n            return dst;\n        }\n\n        private double[] getSubspaceInternal() {\n            if (frontoParallelH == null) {\n                return null;\n            }\n            cvMatMul(surfaceTransformer.getK1(),    frontoParallelH, H);\n            cvMatMul(surfaceParameters .getH(),     H, H);\n            cvMatMul(surfaceTransformer.getInvK2(), H, H);\n\n            JavaCV.HtoRt(H, R, t);\n            Rodrigues(cvarrToMat(R), cvarrToMat(n), null);\n            double[] p = { n.get(0), n.get(1), n.get(2),\n                           t.get(0), t.get(1), t.get(2) };\n            return p;\n        }\n\n        public CvMat getN() {\n            double[] src = projectorTransformer.getReferencePoints2();\n            double[] dst = projectorTransformer.getReferencePoints1().clone();\n            dst[0] = projectorParameters.get(0);\n            dst[2] = projectorParameters.get(1);\n            dst[4] = projectorParameters.get(2);\n\n            // get plane parameters n, but since we model the target to be\n            // the camera, we have to inverse everything before calling\n            // getPlaneParameters() and reframe the n it returns\n            cvTranspose(projectorTransformer.getR(), R);\n            cvGEMM(R, projectorTransformer.getT(), -1, null, 0, t, 0);\n            JavaCV.getPlaneParameters(src, dst, projectorTransformer.getInvK2(),\n                    projectorTransformer.getK1(), R, t, n);\n            double d = 1 + cvDotProduct(n, projectorTransformer.getT());\n            cvGEMM(R, n, 1/d,  null, 0,  n, 0);\n\n            return n;\n        }\n\n        public CvMat getN0() {\n            n = getN();\n            if (surfaceTransformer == null) {\n                return n;\n            }\n\n            // remove projective effect of the current n,\n            // leaving only the effect of n0\n            camera.getFrontoParallelH(surfaceParameters.get(), n, R);\n            cvInvert(surfaceParameters.getH(), H);\n            cvMatMul(H, surfaceTransformer.getK2(), H);\n            cvMatMul(H, R, H);\n            cvMatMul(surfaceTransformer.getInvK1(), H, H);\n\n            JavaCV.HtoRt(H, R, t);\n\n            // compute n0, as a rotation from the z-axis\n            cvGEMM(R, t, 1,  null, 0,  t, CV_GEMM_A_T);\n            double scale = 1/t.get(2);\n            n.put(0.0, 0.0, 1.0);\n            cvGEMM(R, n, scale, null, 0, n, 0);\n\n            return n;\n        }\n\n        @Override public Parameters clone() {\n            Parameters p = new Parameters();\n            p.surfaceParameters   = surfaceParameters == null ? null : surfaceParameters.clone();\n            p.projectorParameters = projectorParameters.clone();\n            return p;\n        }\n\n        @Override public String toString() {\n            if (surfaceParameters != null) {\n                return surfaceParameters.toString() + projectorParameters.toString();\n            } else {\n                return projectorParameters.toString();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProCamTransformerCL.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport com.jogamp.common.os.Platform;\nimport com.jogamp.opencl.CLBuffer;\nimport com.jogamp.opencl.CLEventList;\nimport com.jogamp.opencl.CLImage2d;\nimport com.jogamp.opencl.CLImageFormat;\nimport com.jogamp.opencl.CLKernel;\nimport com.jogamp.opencl.CLMemory;\nimport java.nio.FloatBuffer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProCamTransformerCL extends ProCamTransformer implements ImageTransformerCL {\n    public ProCamTransformerCL(JavaCVCL context, double[] referencePoints,\n            CameraDevice camera, ProjectorDevice projector) {\n        this(context, referencePoints, camera, projector, null);\n    }\n    public ProCamTransformerCL(JavaCVCL context, double[] referencePoints,\n            CameraDevice camera, ProjectorDevice projector, CvMat n) {\n        super(referencePoints, camera, projector, n);\n        final int dotSize = createParameters().size();\n        this.context  = context;\n        this.nullSize = Platform.is32Bit() ? 4 : 8;\n        this.H1Buffer = surfaceTransformer == null ? null :\n                        context.getCLContext().createFloatBuffer(dotSize*9,  CLBuffer.Mem.READ_ONLY);\n        this.H2Buffer = context.getCLContext().createFloatBuffer(dotSize*9,  CLBuffer.Mem.READ_ONLY);\n        this.XBuffer  = context.getCLContext().createFloatBuffer(dotSize*16, CLBuffer.Mem.READ_ONLY);\n        if (getClass() == ProCamTransformerCL.class) {\n            CLKernel[] kernels = context.buildKernels(\n                    JavaCVCL.fastCompilerOptions + \" -cl-nv-maxrregcount=32 -DDOT_SIZE=\" + dotSize,\n                    //JavaCVCL.fastCompilerOptions + \" -DDOT_SIZE=\" + dotSize,\n                    \"ImageTransformer.cl:ProCamTransformer.cl\",\n                    \"transformOne\", \"transformSub\", \"transformDot\", \"reduceOutputData\");\n            oneKernel    = kernels[0];\n            subKernel    = kernels[1];\n            dotKernel    = kernels[2];\n            reduceKernel = kernels[3];\n        }\n    }\n\n    private static final ThreadLocal<CvMat>\n            H13x3 = CvMat.createThreadLocal(3, 3),\n            H23x3 = CvMat.createThreadLocal(3, 3),\n            X4x4  = CvMat.createThreadLocal(4, 4);\n\n    protected final JavaCVCL context;\n    protected final int nullSize;\n    protected final CLBuffer<FloatBuffer> H1Buffer, H2Buffer, XBuffer;\n    protected CLImage2d[] projectorImageCL = null, surfaceImageCL = null;\n    private CLKernel oneKernel, subKernel, dotKernel, reduceKernel;\n\n    public JavaCVCL getContext() {\n        return context;\n    }\n\n    public ProjectiveColorTransformerCL getSurfaceTransformerCL() {\n        return (ProjectiveColorTransformerCL)surfaceTransformer;\n    }\n    public ProjectiveColorTransformerCL getProjectorTransformerCL() {\n        return (ProjectiveColorTransformerCL)projectorTransformer;\n    }\n\n    public CLImage2d getProjectorImageCL(int pyramidLevel) {\n        return projectorImageCL[pyramidLevel];\n    }\n    public void setProjectorImageCL(CLImage2d projectorImage0, int minPyramidLevel, int maxPyramidLevel) {\n        if (projectorImageCL == null || projectorImageCL.length != maxPyramidLevel+1) {\n            projectorImageCL = new CLImage2d[maxPyramidLevel+1];\n        }\n        projectorImageCL[minPyramidLevel] = projectorImage0;\n        for (int i = minPyramidLevel+1; i <= maxPyramidLevel; i++) {\n            if (projectorImageCL[i] == null) {\n                int w = projectorImageCL[i-1].width/2;\n                int h = projectorImageCL[i-1].height/2;\n                CLImageFormat format = new CLImageFormat(CLImageFormat.ChannelOrder.RGBA, CLImageFormat.ChannelType.FLOAT);\n                projectorImageCL[i] = context.getCLContext().createImage2d(w, h, format);\n            }\n            context.pyrDown(projectorImageCL[i-1], projectorImageCL[i]);\n        }\n    }\n    public CLImage2d getSurfaceImageCL(int pyramidLevel) {\n        return surfaceImageCL[pyramidLevel];\n    }\n    public void setSurfaceImageCL(CLImage2d surfaceImage0, int pyramidLevels) {\n        if (surfaceImageCL == null || surfaceImageCL.length != pyramidLevels) {\n            surfaceImageCL = new CLImage2d[pyramidLevels];\n        }\n        surfaceImageCL[0] = surfaceImage0;\n        for (int i = 1; i < pyramidLevels; i++) {\n            if (surfaceImageCL[i] == null) {\n                int w = surfaceImageCL[i-1].width/2;\n                int h = surfaceImageCL[i-1].height/2;\n                CLImageFormat format = new CLImageFormat(CLImageFormat.ChannelOrder.RGBA, CLImageFormat.ChannelType.FLOAT);\n                surfaceImageCL[i] = context.getCLContext().createImage2d(w, h, format);\n            }\n            context.pyrDown(surfaceImageCL[i-1], surfaceImageCL[i]);\n        }\n    }\n\n    protected void prepareTransforms(CLBuffer H1Buffer, CLBuffer H2Buffer, CLBuffer XBuffer,\n            int pyramidLevel, ImageTransformer.Parameters[] parameters) {\n        FloatBuffer floatH1 = surfaceTransformer == null ? null : (FloatBuffer)H1Buffer.getBuffer().rewind();\n        FloatBuffer floatH2 = (FloatBuffer)H2Buffer.getBuffer().rewind();\n        FloatBuffer floatX  = (FloatBuffer) XBuffer.getBuffer().rewind();\n        CvMat H1 = H13x3.get();\n        CvMat H2 = H23x3.get();\n        CvMat X  = X4x4.get();\n        for (int i = 0; i < parameters.length; i++) {\n            prepareTransforms(surfaceTransformer == null ? null : H1,\n                    H2, X, pyramidLevel, (ProCamTransformer.Parameters)parameters[i]);\n            for (int j = 0; j < 9; j++) {\n                if (surfaceTransformer != null) {\n                    floatH1.put((float)H1.get(j));\n                }\n                floatH2.put((float)H2.get(j));\n            }\n            for (int j = 0; j < 16; j++) {\n                floatX.put((float)X.get(j));\n            }\n        }\n        if (surfaceTransformer != null) {\n            floatH1.rewind();\n        }\n        floatH2.rewind();\n        floatX.rewind();\n    }\n\n    @Override public void transform(CLImage2d srcImg, CLImage2d subImg, CLImage2d srcDotImg,\n            CLImage2d transImg, CLImage2d dstImg, CLImage2d maskImg,\n            ImageTransformer.Parameters[] parameters, boolean[] inverses,\n            InputData inputData, OutputData outputData) {\n        if (inverses != null) {\n            for (int i = 0; i < inverses.length; i++) {\n                if (inverses[i]) {\n                    throw new UnsupportedOperationException(\"Inverse transform not supported.\");\n                }\n            }\n        }\n\n        prepareTransforms(H1Buffer, H2Buffer, XBuffer, inputData.pyramidLevel, parameters);\n\n        final int dotSize = parameters[0].size();\n        final int localSize = parameters.length > 1 ? parameters.length : (inputData.roiWidth > 32 ? 64 : 32);\n        final int globalSize = JavaCVCL.alignCeil(inputData.roiWidth, localSize);\n        final int reduceSize = globalSize/localSize;\n\n        // allocate buffers if necessary\n        CLBuffer inputBuffer = inputData.getBuffer(context);\n        CLBuffer outputBuffer = outputData.getBuffer(context, dotSize, reduceSize);\n\n        CLEventList list = new CLEventList(1);\n\n        // setup kernel\n        if (surfaceTransformer != null) {\n            context.writeBuffer(H1Buffer, false); // upload H1\n        }\n        context.writeBuffer(H2Buffer, false); // upload H2\n        context.writeBuffer(XBuffer, false); // upload X\n        if (inputData.autoWrite) {\n            inputData.writeBuffer(context);\n        }\n        CLImage2d srcImg2 = projectorImageCL[inputData.pyramidLevel];\n        CLKernel kernel = null;\n        if (subImg == null) {\n            assert parameters.length == 1;\n            kernel = oneKernel.putArg(srcImg2).putArg(srcImg).putArg(dstImg == null ? transImg : dstImg).putArg(maskImg).putArg(H2Buffer);\n        } else if (srcDotImg == null) {\n            assert parameters.length == 1;\n            kernel = subKernel.putArg(srcImg2).putArg(srcImg).putArg(subImg).putArg(transImg).putArg(dstImg).putArg(maskImg).putArg(H2Buffer);\n        } else {\n            assert parameters.length == dotSize;\n            kernel = dotKernel.putArg(srcImg2).putArg(srcImg).putArg(subImg).putArg(srcDotImg).putArg(maskImg).putArg(H2Buffer);\n//System.out.println(kernel.getWorkGroupSize(context.getCLCommandQueue().getDevice()));\n        }\n        if (H1Buffer != null) { kernel.putArg(H1Buffer); } else { kernel.putNullArg(nullSize); }\n        kernel.putArg(XBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();\n        context.executeKernel(kernel, inputData.roiX, 0, 0,\n                globalSize, 1, parameters.length,\n                localSize, 1, parameters.length, list); // execute program\n        if (reduceSize > 1) {\n            reduceKernel.putArg(outputBuffer).rewind();\n            context.executeKernel(reduceKernel, 0, reduceSize, reduceSize);\n        }\n        if (outputData.autoRead) {\n            outputData.readBuffer(context);\n        }\n\n//        CLEvent event = list.getEvent(0);\n//        System.out.println((event.getProfilingInfo(CLEvent.ProfilingCommand.END) -\n//                            event.getProfilingInfo(CLEvent.ProfilingCommand.START))/1000000.0);\n\n//        long res = q.getDevice().getProfilingTimerResolution();\n//        System.out.println(res);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProjectiveColorTransformer.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.util.Arrays;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.javacv.cvkernels.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProjectiveColorTransformer extends ProjectiveTransformer {\n    public ProjectiveColorTransformer(CvMat K1, CvMat K2, CvMat R, CvMat t,\n            CvMat n, double[] referencePoints1, double[] referencePoints2,\n            CvMat X, int numGains, int numBiases) {\n        super(K1, K2, R, t, n, referencePoints1, referencePoints2);\n\n        this.X = X == null ? null : X.clone();\n\n        this.numGains = numGains;\n        this.numBiases = numBiases;\n    }\n\n    protected static ThreadLocal<CvMat>\n            X24x4   = CvMat.createThreadLocal(4, 4),\n            temp3x1 = CvMat.createThreadLocal(3, 1);\n\n    protected CvMat X = null;\n    protected int numGains = 0, numBiases = 0;\n\n    protected CvMat[] X2 = null;\n\n    public CvMat getX() {\n        return X;\n    }\n    public int getNumGains() {\n        return numGains;\n    }\n    public int getNumBiases() {\n        return numBiases;\n    }\n\n    public void transformColor(IplImage srcImage, IplImage dstImage, CvRect roi,\n            int pyramidLevel, ImageTransformer.Parameters parameters, boolean inverse) {\n        Parameters p = ((Parameters)parameters);\n\n        if ((Arrays.equals(p.getColorParameters(), p.getIdentityColorParameters()) &&\n                (X == null || p.fakeIdentity)) || (X == null && numGains == 0 && numBiases == 0)) {\n            if (srcImage != dstImage) {\n                cvCopy(srcImage, dstImage);\n            }\n            return;\n        }\n\n        CvMat X2 = X24x4.get();\n        prepareColorTransform(X2, pyramidLevel, p, inverse);\n        X2.rows(3);\n        // do color transformation\n        if (roi == null) {\n            cvResetImageROI(dstImage);\n        } else {\n            cvSetImageROI(dstImage, roi);\n        }\n        X2.put(0, 3, X2.get(0, 3)*dstImage.highValue());\n        X2.put(1, 3, X2.get(1, 3)*dstImage.highValue());\n        X2.put(2, 3, X2.get(2, 3)*dstImage.highValue());\n        cvTransform(srcImage, dstImage, X2, null);\n        X2.rows(4);\n    }\n\n    protected void prepareColorTransform(CvMat X2, int pyramidLevel, Parameters p, boolean inverse) {\n        CvMat A = p.getA(), b = p.getB();\n\n        cvSetIdentity(X2);\n\n        X2.rows(3); X2.cols(3);\n        if (p.fakeIdentity && !inverse) {\n            X2.put(A);\n        } else if (A != null && X != null) {\n            cvMatMul(X, A, X2);\n        } else if (X == null) {\n            X2.put(A);\n        } else if (A == null) {\n            X2.put(X);\n        }\n\n        X2.rows(4); X2.cols(4);\n        if (b != null) {\n            X2.put(0, 3, b.get(0));\n            X2.put(1, 3, b.get(1));\n            X2.put(2, 3, b.get(2));\n        }\n\n        if (inverse) {\n            // CV_LU doesn't work on OpenCV 2.0 with rows > 3 ...\n            cvInvert(X2, X2, CV_SVD);\n        }\n    }\n\n    @Override public void transform(Data[] data, CvRect roi, ImageTransformer.Parameters[] parameters, boolean[] inverses) {\n        assert data.length == parameters.length;\n        if (kernelData == null || kernelData.capacity() < data.length) {\n            kernelData = new KernelData(data.length);\n        }\n        if (H == null || H.length < data.length) {\n            H = new CvMat[data.length];\n            for (int i = 0; i < H.length; i++) {\n                H[i] = CvMat.create(3, 3);\n            }\n        }\n        if (X2 == null || X2.length < data.length) {\n            X2 = new CvMat[data.length];\n            for (int i = 0; i < X2.length; i++) {\n                X2[i] = CvMat.create(4, 4);\n            }\n        }\n\n        for (int i = 0; i < data.length; i++) {\n            kernelData.position(i);\n\n            kernelData.srcImg(data[i].srcImg);\n            kernelData.srcImg2(null);\n            kernelData.subImg(data[i].subImg);\n            kernelData.srcDotImg(data[i].srcDotImg);\n            kernelData.mask(data[i].mask);\n            kernelData.zeroThreshold(data[i].zeroThreshold);\n            kernelData.outlierThreshold(data[i].outlierThreshold);\n\n            boolean inverse = inverses == null ? false : inverses[i];\n            prepareHomography    (H[i], data[i].pyramidLevel, (Parameters)parameters[i], inverse);\n            prepareColorTransform(X2[i], data[i].pyramidLevel, (Parameters)parameters[i], inverse);\n\n            kernelData.H1(H[i]);\n            kernelData.H2(null);\n            kernelData.X(X2[i]);\n\n            kernelData.transImg(data[i].transImg);\n            kernelData.dstImg(data[i].dstImg);\n            kernelData.dstDstDot(data[i].dstDstDot);\n        }\n\n        long fullCapacity = kernelData.capacity();\n        kernelData.capacity(data.length);\n        multiWarpColorTransform(kernelData, roi, getFillColor());\n        kernelData.capacity(fullCapacity);\n\n        for (int i = 0; i < data.length; i++) {\n            kernelData.position(i);\n            data[i].dstCount        = kernelData.dstCount();\n            data[i].dstCountZero    = kernelData.dstCountZero();\n            data[i].dstCountOutlier = kernelData.dstCountOutlier();\n            data[i].srcDstDot       = kernelData.srcDstDot();\n        }\n    }\n\n    @Override public Parameters createParameters() {\n        return new Parameters();\n    }\n\n    public class Parameters extends ProjectiveTransformer.Parameters {\n        protected Parameters() {\n            identityColorParameters = new double[numGains + numBiases];\n            if (numGains > 0) {\n                A = CvMat.create(3, 3);\n                cvSetIdentity(A);\n            }\n            if (numBiases > 0) {\n                b = CvMat.create(3, 1);\n                cvSetZero(b);\n            }\n\n            switch (numGains) {\n                case 0: assert (A == null); break;\n                case 1: identityColorParameters[0] =\n                            (A.get(0) + A.get(4) + A.get(8))/3; break;\n                case 3: identityColorParameters[0] = A.get(0);\n                        identityColorParameters[1] = A.get(4);\n                        identityColorParameters[2] = A.get(8); break;\n                case 9: A.get(0, identityColorParameters, 0, 9); break;\n                default: assert (false);\n            }\n            switch (numBiases) {\n                case 0: assert (b == null); break;\n                case 1: identityColorParameters[numGains] =\n                            (b.get(0) + b.get(1) + b.get(2))/3;   break;\n                case 3: b.get(0, identityColorParameters, numGains, 3); break;\n                default: assert (false);\n            }\n\n            reset(false);\n        }\n\n        protected double[] colorParameters = null, identityColorParameters = null;\n        private CvMat A = null, b = null;\n\n        public double[] getColorParameters() {\n            return colorParameters;\n        }\n        public double[] getIdentityColorParameters() {\n            return identityColorParameters;\n        }\n\n        @Override public int size() {\n            return super.size() + numGains + numBiases;\n        }\n        @Override public double get(int i) {\n            int s = super.size();\n            if (i < s) {\n                return super.get(i);\n            } else {\n                return colorParameters[i-s];\n            }\n        }\n        @Override public void set(int i, double p) {\n            int s = super.size();\n            if (i < s) {\n                super.set(i, p);\n            } else {\n                if (colorParameters[i-s] != p) {\n                    colorParameters[i-s] = p;\n                    setUpdateNeeded(true);\n                }\n            }\n        }\n        @Override public void reset(boolean asIdentity) {\n            super.reset(asIdentity);\n            resetColor(asIdentity);\n        }\n        public void resetColor(boolean asIdentity) {\n            if (identityColorParameters != null) {\n                if (!Arrays.equals(colorParameters, identityColorParameters) ||\n                        fakeIdentity != asIdentity) {\n                    fakeIdentity = asIdentity;\n                    colorParameters = identityColorParameters.clone();\n                    setUpdateNeeded(true);\n                }\n            }\n        }\n//        @Override public boolean addDelta(int i, double scale) {\n//            int s = super.size();\n//            if (i < s) {\n//                return super.addDelta(i, scale);\n//            } else {\n//                // gradient varies linearly with intensity, so\n//                // the increment value is not very important, but\n//                // referenceCameraImage is good only for the value 1,\n//                // so let's use that\n//                int channel = i-s;\n//                colorParameters[channel] += scale;//1;\n//                setUpdateNeeded(true);\n//                return false;\n//            }\n//        }\n        @Override public void compose(ImageTransformer.Parameters p1, boolean inverse1,\n                ImageTransformer.Parameters p2, boolean inverse2) {\n            super.compose(p1, inverse1, p2, inverse2);\n            composeColor(p1, inverse1, p2, inverse2);\n        }\n        public void composeColor(ImageTransformer.Parameters p1, boolean inverse1,\n                ImageTransformer.Parameters p2, boolean inverse2) {\n            assert (!inverse1 && !inverse2);\n\n            Parameters pp1 = (Parameters)p1, pp2 = (Parameters)p2;\n            CvMat A1 = pp1.getA(), b1 = pp1.getB();\n            CvMat A2 = pp2.getA(), b2 = pp2.getB();\n\n            if (b != null) {\n                if (pp1.fakeIdentity && X != null) {\n                    CvMat temp = temp3x1.get();\n                    cvMatMul(X, b1, temp);\n                    b1 = temp;\n                }\n\n                if (A2 == null && b2 == null) {\n                    cvCopy(b1, b);\n                } else if (b1 == null) {\n                    cvCopy(b2, b);\n                } else if (b2 == null) {\n                    cvMatMul(A2, b1, b);\n                } else {\n                    cvGEMM(A2, b1, 1.0,  b2, 1.0,  b, 0);\n                }\n            }\n\n            if (A != null) {\n                if (A1 == null) {\n                    cvCopy(A2, A);\n                } else if (A2 == null) {\n                    cvCopy(A1, A);\n                } else {\n                    cvMatMul(A2, A1, A);\n                }\n            }\n\n            switch (numGains) {\n                case 0: assert (A == null); break;\n                case 1: colorParameters[0] =\n                            (A.get(0) + A.get(4) + A.get(8))/3; break;\n                case 3: colorParameters[0] = A.get(0);\n                        colorParameters[1] = A.get(4);\n                        colorParameters[2] = A.get(8); break;\n                case 9: A.get(0, colorParameters, 0, 9); break;\n                default: assert (false);\n            }\n            switch (numBiases) {\n                case 0: assert (b == null); break;\n                case 1: colorParameters[numGains] =\n                            (b.get(0) + b.get(1) + b.get(2))/3;   break;\n                case 3: b.get(0, colorParameters, numGains, 3); break;\n                default: assert (false);\n            }\n        }\n\n        public CvMat getA() {\n            update();\n            return A;\n        }\n        public CvMat getB() {\n            update();\n            return b;\n        }\n\n        @Override protected void update() {\n            if (!isUpdateNeeded()) {\n                return;\n            }\n\n            switch (numGains) {\n                case 0: assert (A == null); break;\n                case 1: A.put(0, colorParameters[0]);\n                        A.put(4, colorParameters[0]);\n                        A.put(8, colorParameters[0]); break;\n                case 3: A.put(0, colorParameters[0]);\n                        A.put(4, colorParameters[1]);\n                        A.put(8, colorParameters[2]); break;\n                case 9: A.put(0, colorParameters, 0, 9); break;\n                default: assert (false);\n            }\n            switch (numBiases) {\n                case 0: assert (b == null); break;\n                case 1: b.put(0, colorParameters[numGains]);\n                        b.put(1, colorParameters[numGains]);\n                        b.put(2, colorParameters[numGains]);    break;\n                case 3: b.put(0, colorParameters, numGains, 3); break;\n                default: assert (false);\n            }\n\n            super.update();\n            setUpdateNeeded(false);\n        }\n\n        @Override public Parameters clone() {\n            Parameters p = new Parameters();\n            p.set(this);\n            return p;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProjectiveColorTransformerCL.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport com.jogamp.opencl.CLBuffer;\nimport com.jogamp.opencl.CLEventList;\nimport com.jogamp.opencl.CLImage2d;\nimport com.jogamp.opencl.CLKernel;\nimport java.nio.FloatBuffer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProjectiveColorTransformerCL extends ProjectiveColorTransformer implements ImageTransformerCL {\n    public ProjectiveColorTransformerCL(JavaCVCL context, CvMat K1, CvMat K2, CvMat R, CvMat t,\n            CvMat n, double[] referencePoints1, double[] referencePoints2,\n            CvMat X, int numGains, int numBiases) {\n        super(K1, K2, R, t, n, referencePoints1, referencePoints2, X, numGains, numBiases);\n        final int dotSize = createParameters().size();\n        this.context = context;\n        this.HBuffer = context.getCLContext().createFloatBuffer(dotSize*9,  CLBuffer.Mem.READ_ONLY);\n        this.XBuffer = context.getCLContext().createFloatBuffer(dotSize*16, CLBuffer.Mem.READ_ONLY);\n        if (getClass() == ProjectiveColorTransformerCL.class) {\n            CLKernel[] kernels = context.buildKernels(\n                    JavaCVCL.fastCompilerOptions + \" -DDOT_SIZE=\" + dotSize,\n                    \"ImageTransformer.cl:ProjectiveColorTransformer.cl\",\n                    \"transformOne\", \"transformSub\", \"transformDot\", \"reduceOutputData\");\n            oneKernel    = kernels[0];\n            subKernel    = kernels[1];\n            dotKernel    = kernels[2];\n            reduceKernel = kernels[3];\n        }\n    }\n\n    protected final JavaCVCL context;\n    protected final CLBuffer<FloatBuffer> HBuffer, XBuffer;\n    private CLKernel oneKernel, subKernel, dotKernel, reduceKernel;\n\n    public JavaCVCL getContext() {\n        return context;\n    }\n\n    protected void prepareHomographies(CLBuffer HBuffer, int pyramidLevel,\n            ImageTransformer.Parameters[] parameters, boolean[] inverses) {\n        FloatBuffer floatH = (FloatBuffer)HBuffer.getBuffer().rewind();\n        CvMat H = H3x3.get();\n        for (int i = 0; i < parameters.length; i++) {\n            prepareHomography(H, pyramidLevel, (ProjectiveColorTransformer.Parameters)parameters[i],\n                    inverses == null ? false : inverses[i]);\n            for (int j = 0; j < 9; j++) {\n                floatH.put((float)H.get(j));\n            }\n        }\n        floatH.rewind();\n    }\n\n    protected void prepareColorTransforms(CLBuffer XBuffer, int pyramidLevel,\n            ImageTransformer.Parameters[] parameters, boolean[] inverses) {\n        FloatBuffer floatX = (FloatBuffer)XBuffer.getBuffer().rewind();\n        CvMat X2 = X24x4.get();\n        for (int i = 0; i < parameters.length; i++) {\n            prepareColorTransform(X2, pyramidLevel, (ProjectiveColorTransformer.Parameters)parameters[i],\n                    inverses == null ? false : inverses[i]);\n            for (int j = 0; j < 16; j++) {\n                floatX.put((float)X2.get(j));\n            }\n        }\n        floatX.rewind();\n    }\n\n    @Override public void transform(CLImage2d srcImg, CLImage2d subImg, CLImage2d srcDotImg,\n            CLImage2d transImg, CLImage2d dstImg, CLImage2d maskImg,\n            ImageTransformer.Parameters[] parameters, boolean[] inverses,\n            InputData inputData, OutputData outputData) {\n        prepareHomographies(HBuffer, inputData.pyramidLevel, parameters, inverses);\n        prepareColorTransforms(XBuffer, inputData.pyramidLevel, parameters, inverses);\n\n        final int dotSize = parameters[0].size();\n        final int localSize = parameters.length > 1 ? parameters.length : (inputData.roiWidth > 32 ? 64 : 32);\n        final int globalSize = JavaCVCL.alignCeil(inputData.roiWidth, localSize);\n        final int reduceSize = globalSize/localSize;\n\n        // allocate buffers if necessary\n        CLBuffer inputBuffer = inputData.getBuffer(context);\n        CLBuffer outputBuffer = outputData.getBuffer(context, dotSize, reduceSize);\n\n        CLEventList list = new CLEventList(1);\n\n        // setup kernel\n        context.writeBuffer(HBuffer, false); // upload H\n        context.writeBuffer(XBuffer, false); // upload X\n        if (inputData.autoWrite) {\n            inputData.writeBuffer(context);\n        }\n        CLKernel kernel = null;\n        if (subImg == null) {\n            assert parameters.length == 1;\n            kernel = oneKernel.putArg(srcImg).putArg(dstImg == null ? transImg : dstImg).putArg(maskImg)\n                    .putArg(HBuffer).putArg(XBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();\n        } else if (srcDotImg == null) {\n            assert parameters.length == 1;\n            kernel = subKernel.putArg(srcImg).putArg(subImg).putArg(transImg).putArg(dstImg).putArg(maskImg)\n                    .putArg(HBuffer).putArg(XBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();\n        } else {\n            assert parameters.length == dotSize;\n            kernel = dotKernel.putArg(srcImg).putArg(subImg).putArg(srcDotImg).putArg(maskImg)\n                    .putArg(HBuffer).putArg(XBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();\n        }\n        context.executeKernel(kernel, inputData.roiX, 0, 0,\n                globalSize, 1, parameters.length,\n                localSize, 1, parameters.length, list); // execute program\n        if (reduceSize > 1) {\n            reduceKernel.putArg(outputBuffer).rewind();\n            context.executeKernel(reduceKernel, 0, reduceSize, reduceSize);\n        }\n        if (outputData.autoRead) {\n            outputData.readBuffer(context);\n        }\n\n//        CLEvent event = list.getEvent(0);\n//        System.out.println((event.getProfilingInfo(CLEvent.ProfilingCommand.END) -\n//                            event.getProfilingInfo(CLEvent.ProfilingCommand.START))/1000000.0);\n\n//        long res = q.getDevice().getProfilingTimerResolution();\n//        System.out.println(res);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProjectiveDevice.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.nio.FloatBuffer;\nimport java.util.Arrays;\nimport org.bytedeco.opencv.global.opencv_core;\n\nimport org.bytedeco.opencv.opencv_calib3d.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_calib3d.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProjectiveDevice {\n    public ProjectiveDevice(String name) {\n        Settings s = new Settings();\n        s.name = name;\n        setSettings(s);\n    }\n    public ProjectiveDevice(String name, File file) throws Exception {\n        this(name);\n        readParameters(file);\n    }\n    public ProjectiveDevice(String name, String filename) throws Exception {\n        this(name);\n        readParameters(filename);\n    }\n    public ProjectiveDevice(String name, FileStorage fs) throws Exception {\n        this(name);\n        readParameters(fs);\n    }\n    public ProjectiveDevice(Settings settings) throws Exception {\n        setSettings(settings);\n        if (settings instanceof CalibratedSettings) {\n            readParameters(((CalibratedSettings)settings).parametersFile);\n        }\n    }\n\n    public static class Settings extends BaseChildSettings {\n        public Settings() { }\n        public Settings(ProjectiveDevice.Settings settings) {\n            this.name            = settings.name;\n            this.responseGamma   = settings.responseGamma;\n//            this.nominalDistance = settings.nominalDistance;\n        }\n        String name = \"\";\n        double responseGamma = 0.0;\n//        double nominalDistance = 20000;\n\n        @Override public String getName() {\n            return name;\n        }\n        public void setName(String name) {\n            firePropertyChange(\"name\", this.name, this.name = name);\n        }\n\n        public double getResponseGamma() {\n            return responseGamma;\n        }\n        public void setResponseGamma(double responseGamma) {\n            this.responseGamma = responseGamma;\n        }\n\n//        public double getNominalDistance() {\n//            return nominalDistance;\n//        }\n//        public void setNominalDistance(double nominalDistance) {\n//            this.nominalDistance = nominalDistance;\n//        }\n    }\n\n    public static class CalibrationSettings extends Settings {\n        public CalibrationSettings() { }\n        public CalibrationSettings(ProjectiveDevice.CalibrationSettings settings) {\n            super(settings);\n            this.initAspectRatio = settings.initAspectRatio;\n            this.flags           = settings.flags;\n        }\n\n        double initAspectRatio = 1.0;\n        int flags = CV_CALIB_FIX_K3 | CV_CALIB_FIX_K4 |\n                    CV_CALIB_FIX_K5 | CV_CALIB_FIX_K6 | CV_CALIB_FIX_INTRINSIC;\n\n        public double getInitAspectRatio() {\n            return initAspectRatio;\n        }\n        public void setInitAspectRatio(double initAspectRatio) {\n            this.initAspectRatio = initAspectRatio;\n        }\n\n        public boolean isUseIntrinsicGuess() {\n            return (flags & CV_CALIB_USE_INTRINSIC_GUESS) != 0;\n        }\n        public void setUseIntrinsicGuess(boolean useIntrinsicGuess) {\n            if (useIntrinsicGuess) {\n                flags |= CV_CALIB_USE_INTRINSIC_GUESS;\n            } else {\n                flags &= ~CV_CALIB_USE_INTRINSIC_GUESS;\n            }\n        }\n\n        public boolean isFixAspectRatio() {\n            return (flags & CV_CALIB_FIX_ASPECT_RATIO) != 0;\n        }\n        public void setFixAspectRatio(boolean fixAspectRatio) {\n            if (fixAspectRatio) {\n                flags |= CV_CALIB_FIX_ASPECT_RATIO;\n            } else {\n                flags &= ~CV_CALIB_FIX_ASPECT_RATIO;\n            }\n        }\n\n        public boolean isFixPrincipalPoint() {\n            return (flags & CV_CALIB_FIX_PRINCIPAL_POINT) != 0;\n        }\n        public void setFixPrincipalPoint(boolean fixPrincipalPoint) {\n            if (fixPrincipalPoint) {\n                flags |= CV_CALIB_FIX_PRINCIPAL_POINT;\n            } else {\n                flags &= ~CV_CALIB_FIX_PRINCIPAL_POINT;\n            }\n        }\n\n        public boolean isZeroTangentDist() {\n            return (flags & CV_CALIB_ZERO_TANGENT_DIST) != 0;\n        }\n        public void setZeroTangentDist(boolean zeroTangentDist) {\n            if (zeroTangentDist) {\n                flags |= CV_CALIB_ZERO_TANGENT_DIST;\n            } else {\n                flags &= ~CV_CALIB_ZERO_TANGENT_DIST;\n            }\n        }\n\n        public boolean isFixFocalLength() {\n            return (flags & CV_CALIB_FIX_FOCAL_LENGTH) != 0;\n        }\n        public void setFixFocalLength(boolean fixFocalLength) {\n            if (fixFocalLength) {\n                flags |= CV_CALIB_FIX_FOCAL_LENGTH;\n            } else {\n                flags &= ~CV_CALIB_FIX_FOCAL_LENGTH;\n            }\n        }\n\n        public boolean isFixK1() {\n            return (flags & CV_CALIB_FIX_K1) != 0;\n        }\n        public void setFixK1(boolean fixK1) {\n            if (fixK1) {\n                flags |= CV_CALIB_FIX_K1;\n            } else {\n                flags &= ~CV_CALIB_FIX_K1;\n            }\n        }\n\n        public boolean isFixK2() {\n            return (flags & CV_CALIB_FIX_K2) != 0;\n        }\n        public void setFixK2(boolean fixK2) {\n            if (fixK2) {\n                flags |= CV_CALIB_FIX_K2;\n            } else {\n                flags &= ~CV_CALIB_FIX_K2;\n            }\n        }\n\n        public boolean isFixK3() {\n            return (flags & CV_CALIB_FIX_K3) != 0;\n        }\n        public void setFixK3(boolean fixK3) {\n            if (fixK3) {\n                flags |= CV_CALIB_FIX_K3;\n            } else {\n                flags &= ~CV_CALIB_FIX_K3;\n            }\n        }\n\n        public boolean isFixK4() {\n            return (flags & CV_CALIB_FIX_K4) != 0;\n        }\n        public void setFixK4(boolean fixK4) {\n            if (fixK4) {\n                flags |= CV_CALIB_FIX_K4;\n            } else {\n                flags &= ~CV_CALIB_FIX_K4;\n            }\n        }\n\n        public boolean isFixK5() {\n            return (flags & CV_CALIB_FIX_K5) != 0;\n        }\n        public void setFixK5(boolean fixK5) {\n            if (fixK5) {\n                flags |= CV_CALIB_FIX_K5;\n            } else {\n                flags &= ~CV_CALIB_FIX_K5;\n            }\n        }\n\n        public boolean isFixK6() {\n            return (flags & CV_CALIB_FIX_K6) != 0;\n        }\n        public void setFixK6(boolean fixK6) {\n            if (fixK6) {\n                flags |= CV_CALIB_FIX_K6;\n            } else {\n                flags &= ~CV_CALIB_FIX_K6;\n            }\n        }\n\n        public boolean isRationalModel() {\n            return (flags & CV_CALIB_RATIONAL_MODEL) != 0;\n        }\n        public void setRationalModel(boolean rationalModel) {\n            if (rationalModel) {\n                flags |= CV_CALIB_RATIONAL_MODEL;\n            } else {\n                flags &= ~CV_CALIB_RATIONAL_MODEL;\n            }\n        }\n\n        public boolean isStereoFixIntrinsic() {\n            return (flags & CV_CALIB_FIX_INTRINSIC) != 0;\n        }\n        public void setStereoFixIntrinsic(boolean stereoFixIntrinsic) {\n            if (stereoFixIntrinsic) {\n                flags |= CV_CALIB_FIX_INTRINSIC;\n            } else {\n                flags &= ~CV_CALIB_FIX_INTRINSIC;\n            }\n        }\n\n        public boolean isStereoSameFocalLength() {\n            return (flags & CV_CALIB_SAME_FOCAL_LENGTH) != 0;\n        }\n        public void setStereoSameFocalLength(boolean stereoSameFocalLength) {\n            if (stereoSameFocalLength) {\n                flags |= CV_CALIB_SAME_FOCAL_LENGTH;\n            } else {\n                flags &= ~CV_CALIB_SAME_FOCAL_LENGTH;\n            }\n        }\n    }\n\n    public static class CalibratedSettings extends Settings {\n        public CalibratedSettings() { }\n        public CalibratedSettings(ProjectiveDevice.CalibratedSettings settings) {\n            super(settings);\n            this.parametersFile = settings.parametersFile;\n        }\n        File parametersFile = new File(\"calibration.yaml\");\n\n        public File getParametersFile() {\n            return parametersFile;\n        }\n        public void setParametersFile(File parametersFile) {\n            this.parametersFile = parametersFile;\n        }\n        public String getParametersFilename() {\n            return parametersFile == null ? \"\" : parametersFile.getPath();\n        }\n        public void setParametersFilename(String parametersFilename) {\n            this.parametersFile = parametersFilename == null ||\n                    parametersFilename.length() == 0 ? null : new File(parametersFilename);\n        }\n    }\n\n    private Settings settings;\n    public Settings getSettings() {\n        return settings;\n    }\n    public void setSettings(Settings settings) {\n        this.settings = settings;\n    }\n\n    public int imageWidth = 0, imageHeight = 0;\n\n    public CvMat cameraMatrix = null, distortionCoeffs = null,\n                 extrParams = null, reprojErrs = null;\n    public double avgReprojErr, maxReprojErr;\n//    public double nominalDistance = 0;\n\n    public CvMat R = null, T = null, E = null, F = null;\n    public double avgEpipolarErr, maxEpipolarErr;\n\n    public String colorOrder = \"BGR\";\n    public CvMat colorMixingMatrix = null, additiveLight = null;\n    public double avgColorErr, colorR2 = 1.0;\n\n    public void rescale(int imageWidth, int imageHeight) {\n        if ((imageWidth != this.imageWidth || imageHeight != this.imageHeight) &&\n                cameraMatrix != null) {\n            double sx = (double)imageWidth /this.imageWidth;\n            double sy = (double)imageHeight/this.imageHeight;\n            cameraMatrix.put(0, sx*cameraMatrix.get(0));\n            cameraMatrix.put(1, sx*cameraMatrix.get(1));\n            cameraMatrix.put(2, sx*cameraMatrix.get(2));\n            cameraMatrix.put(3, sy*cameraMatrix.get(3));\n            cameraMatrix.put(4, sy*cameraMatrix.get(4));\n            cameraMatrix.put(5, sy*cameraMatrix.get(5));\n            this.imageWidth  = imageWidth;\n            this.imageHeight = imageHeight;\n            int p = mapsPyramidLevel;\n            undistortMaps1[p] = undistortMaps2[p] = distortMaps1[p] = distortMaps2[p] = null;\n        }\n    }\n\n    public int[] getRGBColorOrder() {\n        int[] order = new int[3];\n        for (int i = 0; i < 3; i++) {\n            switch (Character.toUpperCase(colorOrder.charAt(i))) {\n                case 'B': order[i] = 2; break;\n                case 'G': order[i] = 1; break;\n                case 'R': order[i] = 0; break;\n                default: assert (false);\n            }\n        }\n        return order;\n    }\n\n//    public double getNominalDistance(int objectWidth, int objectHeight) {\n//        double f = (cameraMatrix.get(0)+cameraMatrix.get(4))/(2*cameraMatrix.get(8));\n//        double imageSize = Math.sqrt(imageWidth *imageWidth +\n//                                     imageHeight*imageHeight);\n//        double objectSize = Math.sqrt(objectWidth *objectWidth +\n//                                      objectHeight*objectHeight);\n//        return f*objectSize/imageSize;\n//    }\n//    public double getNominalDistance(MarkedPlane board) {\n//        return getNominalDistance(board.getWidth(), board.getHeight());\n//    }\n\n    //Compensates for radial and tangential distortion. Model From Oulu university.\n    //Code ported from Camera Calibration Toolbox for Matlab by Jean-Yves Bouguet\n    //http://www.vision.caltech.edu/bouguetj/calib_doc/\n    //function name: comp_distortion_oulu()\n    //\n    //INPUT: xd: distorted (normalized) point coordinates in the image plane (2xN matrix)\n    //       k: Distortion coefficients (radial and tangential) (4x1 vector)\n    //\n    //OUTPUT: x: undistorted (normalized) point coordinates in the image plane (2xN matrix)\n    //\n    //Method: Iterative method for compensation.\n    //\n    //NOTE: This compensation has to be done after the subtraction\n    //      of the principal point, and division by the focal length.\n    public static double[] undistort(double[] xd, double[] k) {\n        double k1 = k[0];\n        double k2 = k[1];\n        double k3 = k.length > 4 ? k[4] : 0;\n        // XXX: need to use those new distortion coefficients\n        double k4 = k.length > 5 ? k[5] : 0;\n        double k5 = k.length > 6 ? k[6] : 0;\n        double k6 = k.length > 7 ? k[7] : 0;\n        double p1 = k[2];\n        double p2 = k[3];\n\n        double[] xu = xd.clone(); // initial guess\n\n        for (int i = 0; i < xd.length/2; i++) {\n            double x  = xu[i*2], y  = xu[i*2 + 1];\n            double xo = xd[i*2], yo = xd[i*2 + 1];\n            for (int j = 0; j < 20; j++) {\n                double r_2 = x*x + y*y;\n                double k_radial = 1 + k1*r_2 + k2*r_2*r_2 + k3*r_2*r_2*r_2;\n                double delta_x = 2*p1*x*y         + p2*(r_2 + 2*x*x);\n                double delta_y = p1*(r_2 + 2*y*y) + 2*p2*x*y;\n                x = (xo - delta_x)/k_radial;\n                y = (yo - delta_y)/k_radial;\n            }\n            xu[i*2] = x; xu[i*2 + 1] = y;\n        }\n        return xu;\n    }\n    public double[] undistort(double ... x) {\n        double[] xn = normalize(x, cameraMatrix);\n        double[] xu = undistort(xn, distortionCoeffs.get());\n        return unnormalize(xu, cameraMatrix);\n    }\n\n    public static double[] distort(double[] xu, double[] k) {\n        double k1 = k[0];\n        double k2 = k[1];\n        double k3 = k.length > 4 ? k[4] : 0;\n        // XXX: need to use those new distortion coefficients\n        double k4 = k.length > 5 ? k[5] : 0;\n        double k5 = k.length > 6 ? k[6] : 0;\n        double k6 = k.length > 7 ? k[7] : 0;\n        double p1 = k[2];\n        double p2 = k[3];\n\n        double[] xd = xu.clone();\n\n        for (int i = 0; i < xu.length/2; i++) {\n            double x = xu[i*2    ],\n                   y = xu[i*2 + 1];\n            double r_2 = x*x + y*y;\n            double k_radial = 1 + k1*r_2 + k2*r_2*r_2 + k3*r_2*r_2*r_2;\n            double delta_x = 2*p1*x*y         + p2*(r_2 + 2*x*x);\n            double delta_y = p1*(r_2 + 2*y*y) + 2*p2*x*y;\n            xd[i*2    ] = x*k_radial + delta_x;\n            xd[i*2 + 1] = y*k_radial + delta_y;\n        }\n        return xd;\n    }\n    public double[] distort(double ... x) {\n        double[] xn = normalize(x, cameraMatrix);\n        double[] xd = distort(xn, distortionCoeffs.get());\n        return unnormalize(xd, cameraMatrix);\n    }\n\n    public static double[] normalize(double[] xu, CvMat K) {\n        double[] xn = xu.clone();\n\n        double fx = K.get(0)/K.get(8);\n        double fy = K.get(4)/K.get(8);\n        double dx = K.get(2)/K.get(8);\n        double dy = K.get(5)/K.get(8);\n        double s  = K.get(1)/K.get(8);\n        for (int i = 0; i < xu.length/2; i++) {\n            xn[i*2    ] = (xu[i*2    ] - dx)/fx - s*(xu[i*2 + 1] + dy)/(fx*fy);\n            xn[i*2 + 1] = (xu[i*2 + 1] - dy)/fy;\n        }\n        return xn;\n    }\n    public static double[] unnormalize(double[] xn, CvMat K) {\n        double[] xu = xn.clone();\n\n        double fx = K.get(0)/K.get(8);\n        double fy = K.get(4)/K.get(8);\n        double dx = K.get(2)/K.get(8);\n        double dy = K.get(5)/K.get(8);\n        double s  = K.get(1)/K.get(8);\n        for (int i = 0; i < xn.length/2; i++) {\n            xu[i*2    ] = fx*xn[i*2    ] + dx + s*xn[i*2 + 1];\n            xu[i*2 + 1] = fy*xn[i*2 + 1] + dy;\n        }\n        return xu;\n    }\n\n\n    private boolean fixedPointMaps = false;\n    private int mapsPyramidLevel = 0;\n    private IplImage[] undistortMaps1 = { null }, undistortMaps2 = { null };\n    private IplImage[] distortMaps1 = { null }, distortMaps2 = { null };\n    private IplImage tempImage = null;\n\n    public boolean isFixedPointMaps() {\n        return fixedPointMaps;\n    }\n    public void setFixedPointMaps(boolean fixedPointMaps) {\n        if (this.fixedPointMaps != fixedPointMaps) {\n            this.fixedPointMaps = fixedPointMaps;\n            int p = mapsPyramidLevel;\n            undistortMaps1[p] = undistortMaps2[p] = distortMaps1[p] = distortMaps2[p] = null;\n        }\n    }\n\n    public int getMapsPyramidLevel() {\n        return mapsPyramidLevel;\n    }\n    public void setMapsPyramidLevel(int mapsPyramidLevel) {\n        if (this.mapsPyramidLevel != mapsPyramidLevel) {\n            this.mapsPyramidLevel = mapsPyramidLevel;\n            int p = mapsPyramidLevel;\n            if (p >= undistortMaps1.length || p >= undistortMaps2.length ||\n                    p >= distortMaps1.length || p >= distortMaps2.length) {\n                undistortMaps1 = Arrays.copyOf(undistortMaps1, p+1);\n                undistortMaps2 = Arrays.copyOf(undistortMaps2, p+1);\n                distortMaps1   = Arrays.copyOf(distortMaps1, p+1);\n                distortMaps2   = Arrays.copyOf(distortMaps2, p+1);\n            }\n        }\n    }\n\n    private void initUndistortMaps() {\n        //cvUndistort2(src, dst, cameraMatrix, distortionCoeffs);\n        int p = mapsPyramidLevel;\n        if (undistortMaps1[p] == null || undistortMaps2[p] == null) {\n            if (fixedPointMaps) {\n                undistortMaps1[p] = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_16S, 2);\n                undistortMaps2[p] = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_16U, 1);\n            } else {\n                undistortMaps1[p] = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_32F, 1);\n                undistortMaps2[p] = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_32F, 1);\n            }\n            Mat A = cvarrToMat(cameraMatrix);\n            Mat m1 = cvarrToMat(undistortMaps1[p]);\n            Mat m2 = cvarrToMat(undistortMaps2[p]);\n            initUndistortRectifyMap(A, cvarrToMat(distortionCoeffs), new Mat(), A, m1.size(), m1.type(), m1, m2);\n            if (mapsPyramidLevel > 0) {\n                IplImage map1 = undistortMaps1[p];\n                IplImage map2 = undistortMaps2[p];\n                int w = imageWidth  >> p;\n                int h = imageHeight >> p;\n                undistortMaps1[p] = IplImage.create(w, h, map1.depth(), map1.nChannels());\n                undistortMaps2[p] = IplImage.create(w, h, map2.depth(), map2.nChannels());\n                cvResize(map1, undistortMaps1[p], CV_INTER_NN);\n                cvResize(map2, undistortMaps2[p], CV_INTER_NN);\n//                FloatBuffer m1 = map1.getFloatBuffer();\n//                FloatBuffer n1 = undistortMaps1[p].getFloatBuffer();\n//                for (int i = 0; i < 8; i++) {\n//                    System.out.println(m1.get(1280*2*i) - n1.get(640*i));\n//                }\n            }\n        }\n//        if (undistortMap1 == null || undistortMap2 == null) {\n//            IplImage mapx = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_32F, 1);\n//            IplImage mapy = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_32F, 1);\n//            FloatBuffer bufx = mapx.getFloatBuffer();\n//            FloatBuffer bufy = mapy.getFloatBuffer();\n//            int width  = mapx.width();\n//            int height = mapx.height();\n//            for (int y = 0; y < height; y++) {\n//                for (int x = 0; x < width; x++) {\n//                    double[] undistxy = undistort(x, y);\n//                    bufx.put((float)undistxy[0]);\n//                    bufy.put((float)undistxy[1]);\n//                }\n//            }\n//            if (useFixedPointMaps) {\n//                undistortMap1 = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_16S, 2);\n//                undistortMap2 = IplImage.create(imageWidth, imageHeight, is20or21 ? IPL_DEPTH_16U : IPL_DEPTH_16S, 1);\n//                cvConvertMaps(mapx, mapy, undistortMap1, undistortMap2);\n//                mapx.release();\n//                mapy.release();\n//            } else {\n//                undistortMap1 = mapx;\n//                undistortMap2 = mapy;\n//            }\n//        }\n    }\n    public IplImage getUndistortMap1() {\n        initUndistortMaps();\n        return undistortMaps1[mapsPyramidLevel];\n    }\n    public IplImage getUndistortMap2() {\n        initUndistortMaps();\n        return undistortMaps2[mapsPyramidLevel];\n    }\n    public void undistort(IplImage src, IplImage dst) {\n        if (src != null && dst != null) {\n            initUndistortMaps();\n            cvRemap(src, dst, undistortMaps1[mapsPyramidLevel], undistortMaps2[mapsPyramidLevel],\n                    CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, CvScalar.ZERO);\n        }\n    }\n    public IplImage undistort(IplImage image) {\n        if (image != null) {\n            initUndistortMaps();\n            tempImage = IplImage.createIfNotCompatible(tempImage, image);\n            cvResetImageROI(tempImage);\n            cvRemap(image, tempImage, undistortMaps1[mapsPyramidLevel], undistortMaps2[mapsPyramidLevel],\n                    CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, CvScalar.ZERO);\n            return tempImage;\n        }\n        return null;\n    }\n\n    private void initDistortMaps() {\n        int p = mapsPyramidLevel;\n        if (distortMaps1[p] == null || distortMaps2[p] == null) {\n            IplImage mapx = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_32F, 1);\n            IplImage mapy = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_32F, 1);\n            FloatBuffer bufx = mapx.getFloatBuffer();\n            FloatBuffer bufy = mapy.getFloatBuffer();\n            int width  = mapx.width();\n            int height = mapx.height();\n            for (int y = 0; y < height; y++) {\n                for (int x = 0; x < width; x++) {\n                    double[] distxy = undistort(x, y);\n                    bufx.put((float)distxy[0]);\n                    bufy.put((float)distxy[1]);\n                }\n            }\n            if (fixedPointMaps) {\n                distortMaps1[p] = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_16S, 2);\n                distortMaps2[p] = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_16U /* IPL_DEPTH_16S */, 1);\n                cvConvertMaps(mapx, mapy, distortMaps1[p], distortMaps2[p]);\n                mapx.release();\n                mapy.release();\n            } else {\n                distortMaps1[p] = mapx;\n                distortMaps2[p] = mapy;\n            }\n            if (mapsPyramidLevel > 0) {\n                IplImage map1 = distortMaps1[p];\n                IplImage map2 = distortMaps2[p];\n                int w = imageWidth  >> p;\n                int h = imageHeight >> p;\n                distortMaps1[p] = IplImage.create(w, h, map1.depth(), map1.nChannels());\n                distortMaps2[p] = IplImage.create(w, h, map2.depth(), map2.nChannels());\n                cvResize(map1, distortMaps1[p], CV_INTER_NN);\n                cvResize(map2, distortMaps2[p], CV_INTER_NN);\n            }\n        }\n    }\n    public IplImage getDistortMap1() {\n        initDistortMaps();\n        return distortMaps1[mapsPyramidLevel];\n    }\n    public IplImage getDistortMap2() {\n        initDistortMaps();\n        return distortMaps2[mapsPyramidLevel];\n    }\n    public void distort(IplImage src, IplImage dst) {\n        if (src != null && dst != null) {\n            initDistortMaps();\n            cvRemap(src, dst, distortMaps1[mapsPyramidLevel], distortMaps2[mapsPyramidLevel],\n                    CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, CvScalar.ZERO);\n        }\n    }\n    public IplImage distort(IplImage image) {\n        if (image != null) {\n            initDistortMaps();\n            tempImage = IplImage.createIfNotCompatible(tempImage, image);\n            cvRemap(image, tempImage, distortMaps1[mapsPyramidLevel], distortMaps2[mapsPyramidLevel],\n                    CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, CvScalar.ZERO);\n            return tempImage;\n        }\n        return null;\n    }\n\n    // B = [ (-R^T t)*plane^T - plane^T*(-R^T t) I ] [ (K R)^-1  ]\n    //                                               [  0  0  0  ]\n    // where plane = [ n | d ]\n    // B is a 4x3 matrix\n    private static ThreadLocal<CvMat>\n            temp3x3 = CvMat.createThreadLocal(3, 3);\n    public CvMat getBackProjectionMatrix(CvMat n, double d, CvMat B) {\n        CvMat temp = temp3x3.get();\n\n        temp.cols(1); temp.step(temp.step()/3);\n        B.rows(3);\n        cvGEMM(R, T, -1,    null, 0,  temp,  CV_GEMM_A_T);\n        cvGEMM(temp, n, 1,  null, 0,  B,     CV_GEMM_B_T);\n        double a = cvDotProduct(n, temp) + d;\n        B.put(0, B.get(0) - a);\n        B.put(4, B.get(4) - a);\n        B.put(8, B.get(8) - a);\n        B.rows(4);\n        temp.cols(3); temp.step(temp.step()*3);\n\n        B.put(9, n.get());\n\n        cvMatMul(cameraMatrix, R, temp);\n        cvInvert(temp, temp, CV_LU);\n\n        cvMatMul(B, temp, B);\n        cvConvertScale(B, B, 1/B.get(11), 0);\n\n        return B;\n    }\n\n    private static ThreadLocal<CvMat>\n            B4x3 = CvMat.createThreadLocal(4, 3),\n            a4x1 = CvMat.createThreadLocal(4, 1),\n            t3x1 = CvMat.createThreadLocal(3, 1);\n    public CvMat getFrontoParallelH(double[] roipts, CvMat n, CvMat H) {\n        CvMat B = B4x3.get(), a = a4x1.get(), t = t3x1.get();\n\n        // compute rotation from n to z-axis\n        double s = Math.signum(n.get(2));\n        double[] dir = JavaCV.unitize(-s*n.get(1), s*n.get(0));\n        double theta = Math.acos(s*n.get(2)/JavaCV.norm(n.get()));\n        t.put(theta*dir[0], theta*dir[1], 0.0);\n        Rodrigues(cvarrToMat(t), cvarrToMat(H), null);\n\n        // and from z-axis to device axis\n        cvMatMul(R, H, H);\n\n        double x = 0, y = 0;\n        if (roipts != null) {\n            // find the middle of the ROI\n            double x1 = roipts[0], y1 = roipts[1],\n                   x2 = roipts[4], y2 = roipts[5],\n                   x3 = roipts[2], y3 = roipts[3],\n                   x4 = roipts[6], y4 = roipts[7];\n            double u = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3))/\n                       ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));\n            x = x1 + u*(x2-x1);\n            y = y1 + u*(y2-y1);\n        }\n\n        // compute 3D point from the middle of the ROI, and\n        // form optimal homography for n\n        getBackProjectionMatrix(n, -1, B);\n        t.put(x, y, 1);\n        cvMatMul(B, t, a);\n        H.put(2, a.get(0)/a.get(3));\n        H.put(5, a.get(1)/a.get(3));\n        H.put(8, a.get(2)/a.get(3));\n\n        return H;\n    }\n\n    // returns a homography that can be applied to pixel coordinates\n    private static ThreadLocal<CvMat>\n            relativeR3x3 = CvMat.createThreadLocal(3, 3),\n            relativeT3x1 = CvMat.createThreadLocal(3, 1),\n            R13x3 = CvMat.createThreadLocal(3, 3), P13x4 = CvMat.createThreadLocal(3, 4),\n            R23x3 = CvMat.createThreadLocal(3, 3), P23x4 = CvMat.createThreadLocal(3, 4);\n    public CvMat getRectifyingHomography(ProjectiveDevice peer, CvMat H) {\n        CvMat relativeR = relativeR3x3.get(), relativeT = relativeT3x1.get();\n        cvGEMM(R,         peer.R,  1,  null, 0,  relativeR, CV_GEMM_B_T);\n        cvGEMM(relativeR, peer.T, -1,  T,    1,  relativeT, 0);\n\n        CvMat R1 = R13x3.get(); CvMat P1 = P13x4.get();\n        CvMat R2 = R23x3.get(); CvMat P2 = P23x4.get();\n        Size imageSize = new Size((peer.imageWidth  + imageWidth )/2,\n                                  (peer.imageHeight + imageHeight)/2); // ?\n        stereoRectify(cvarrToMat(peer.cameraMatrix), cvarrToMat(peer.distortionCoeffs),\n                      cvarrToMat(     cameraMatrix), cvarrToMat(     distortionCoeffs),\n                      imageSize, cvarrToMat(relativeR), cvarrToMat(relativeT),\n                      cvarrToMat(R1), cvarrToMat(R2), cvarrToMat(P1), cvarrToMat(P2),\n                      new Mat(), 0, -1, new Size(), null, null);\n        cvMatMul(cameraMatrix, R2, R2);\n        cvInvert(cameraMatrix, R1);\n        cvMatMul(R2, R1, H);\n\n        return H;\n    }\n\n    public static class Exception extends java.lang.Exception {\n        public Exception(String message) { super(message); }\n        public Exception(String message, Throwable cause) { super(message, cause); }\n    }\n\n    public static ProjectiveDevice[] read(String filename) throws Exception {\n        FileStorage fs = new FileStorage(filename, FileStorage.READ);\n        CameraDevice    [] cameraDevices    = CameraDevice   .read(fs);\n        ProjectorDevice [] projectorDevices = ProjectorDevice.read(fs);\n        ProjectiveDevice[] devices = new ProjectiveDevice[cameraDevices.length+projectorDevices.length];\n        int i = 0;\n        for (ProjectiveDevice d : cameraDevices) {\n            devices[i++] = d;\n        }\n        for (ProjectiveDevice d : projectorDevices) {\n            devices[i++] = d;\n        }\n        fs.release();\n        return devices;\n    }\n\n    public static void write(String filename, ProjectiveDevice[] ... devices) {\n        int totalLength = 0;\n        for (ProjectiveDevice[] ds : devices) {\n            totalLength += ds.length;\n        }\n        ProjectiveDevice[] allDevices = new ProjectiveDevice[totalLength];\n        int i = 0;\n        for (ProjectiveDevice[] ds : devices) {\n            for (ProjectiveDevice d : ds) {\n                allDevices[i++] = d;\n            }\n        }\n        write(filename, allDevices);\n    }\n    public static void write(String filename, ProjectiveDevice ... devices) {\n        FileStorage fs = new FileStorage(filename, FileStorage.WRITE);\n\n        shiftLeft(shiftLeft(fs, \"Cameras\"), \"[\");\n        for (ProjectiveDevice d : devices) {\n            if (d instanceof CameraDevice) {\n                opencv_core.write(fs, d.getSettings().getName());\n            }\n        }\n        shiftLeft(fs, \"]\");\n\n        shiftLeft(shiftLeft(fs, \"Projectors\"), \"[\");\n        for (ProjectiveDevice d : devices) {\n            if (d instanceof ProjectorDevice) {\n                opencv_core.write(fs, d.getSettings().getName());\n            }\n        }\n        shiftLeft(fs, \"]\");\n\n        for (ProjectiveDevice d : devices) {\n            d.writeParameters(fs);\n        }\n        fs.release();\n    }\n\n    public void writeParameters(File file) {\n        writeParameters(file.getAbsolutePath());\n    }\n    public void writeParameters(String filename) {\n        FileStorage fs = new FileStorage(filename, FileStorage.WRITE);\n        writeParameters(fs);\n        fs.release();\n    }\n    public void writeParameters(FileStorage fs) {\n        shiftLeft(shiftLeft(fs, getSettings().getName()), \"{\");\n\n        opencv_core.write(fs, \"imageWidth\", imageWidth);\n        opencv_core.write(fs, \"imageHeight\", imageHeight);\n        opencv_core.write(fs, \"responseGamma\", getSettings().getResponseGamma());\n//        opencv_core.write(fs, \"initAspectRatio\", settings.initAspectRatio);\n//        opencv_core.write(fs, \"flags\", getSettings().flags);\n        if (cameraMatrix != null)\n            opencv_core.write(fs, \"cameraMatrix\", cvarrToMat(cameraMatrix));\n        if (distortionCoeffs != null)\n            opencv_core.write(fs, \"distortionCoeffs\", cvarrToMat(distortionCoeffs));\n        if (extrParams != null)\n            opencv_core.write(fs, \"extrParams\", cvarrToMat(extrParams));\n        if (reprojErrs != null)\n            opencv_core.write(fs, \"reprojErrs\", cvarrToMat(reprojErrs));\n        opencv_core.write(fs, \"avgReprojErr\", avgReprojErr);\n        opencv_core.write(fs, \"maxReprojErr\", maxReprojErr);\n//        opencv_core.write(fs, \"nominalDistance\", nominalDistance);\n        if (R != null)\n            opencv_core.write(fs, \"R\", cvarrToMat(R));\n        if (T != null)\n            opencv_core.write(fs, \"T\", cvarrToMat(T));\n        if (E != null)\n            opencv_core.write(fs, \"E\", cvarrToMat(E));\n        if (F != null)\n            opencv_core.write(fs, \"F\", cvarrToMat(F));\n        opencv_core.write(fs, \"avgEpipolarErr\", avgEpipolarErr);\n        opencv_core.write(fs, \"maxEpipolarErr\", maxEpipolarErr);\n\n        opencv_core.write(fs, \"colorOrder\", colorOrder);\n        if (colorMixingMatrix != null)\n            opencv_core.write(fs, \"colorMixingMatrix\", cvarrToMat(colorMixingMatrix));\n        if (additiveLight != null)\n            opencv_core.write(fs, \"additiveLight\", cvarrToMat(additiveLight));\n        opencv_core.write(fs, \"avgColorErr\", avgColorErr);\n        opencv_core.write(fs, \"colorR2\", colorR2);\n\n        shiftLeft(fs, \"}\");\n    }\n\n    public void readParameters(File file) throws Exception {\n        readParameters(file.getAbsolutePath());\n    }\n    public void readParameters(String filename) throws Exception {\n        FileStorage fs = new FileStorage(filename, FileStorage.READ);\n        readParameters(fs);\n        fs.release();\n    }\n    public void readParameters(FileStorage fs) throws Exception {\n        if (fs == null) {\n            throw new Exception(\"Error: FileStorage is null, cannot read parameters for device \" + \n                    getSettings().getName() + \". Is the parametersFile correct?\");\n        }\n        FileNode fn = fs.get(getSettings().getName());\n        if (fn == null) {\n            throw new Exception(\"Error: FileNode is null, cannot read parameters for device \" + \n                    getSettings().getName() + \". Is the name correct?\");\n        }\n\n        FileNode n;\n        if ((n = fn.get(\"imageWidth\")).isInt()) imageWidth = n.asInt();\n        if ((n = fn.get(\"imageHeight\")).isInt()) imageHeight = n.asInt();\n        if ((n = fn.get(\"gamma\")).isReal()) getSettings().setResponseGamma(n.asDouble());\n//        if ((n = fn.get(\"initAspectRatio\")).isReal()) getSettings().setInitAspectRatio(n.asDouble());\n//        if ((n = fn.get(\"flags\")).isInt()) getSettings().setFlags(n.asInt());\n        Mat m = new Mat();\n        opencv_core.read(fn.get(\"cameraMatrix\"), m);\n        cameraMatrix = m.empty() ? null : cvMat(m).clone();\n        opencv_core.read(fn.get(\"distortionCoeffs\"), m);\n        distortionCoeffs = m.empty() ? null : cvMat(m).clone();\n        opencv_core.read(fn.get(\"extrParams\"), m);\n        extrParams = m.empty() ? null : cvMat(m).clone();\n        opencv_core.read(fn.get(\"reprojErrs\"), m);\n        reprojErrs = m.empty() ? null : cvMat(m).clone();\n        if ((n = fn.get(\"avgReprojErr\")).isReal()) avgReprojErr = n.asDouble();\n        if ((n = fn.get(\"maxReprojErr\")).isReal()) maxReprojErr = n.asDouble();\n//        if ((n = fn.get(\"nominalDistance\")).isReal()) nominalDistance = n.asDouble();\n        opencv_core.read(fn.get(\"R\"), m);\n        R = m.empty() ? null : cvMat(m).clone();\n        opencv_core.read(fn.get(\"T\"), m);\n        T = m.empty() ? null : cvMat(m).clone();\n        opencv_core.read(fn.get(\"E\"), m);\n        E = m.empty() ? null : cvMat(m).clone();\n        opencv_core.read(fn.get(\"F\"), m);\n        F = m.empty() ? null : cvMat(m).clone();\n        if ((n = fn.get(\"avgEpipolarErr\")).isReal()) avgEpipolarErr = n.asDouble();\n        if ((n = fn.get(\"maxEpipolarErr\")).isReal()) maxEpipolarErr = n.asDouble();\n\n        if ((n = fn.get(\"colorOrder\")).isString()) colorOrder = n.asBytePointer().getString();\n        opencv_core.read(fn.get(\"colorMixingMatrix\"), m);\n        colorMixingMatrix = m.empty() ? null : cvMat(m).clone();\n        opencv_core.read(fn.get(\"additiveLight\"), m);\n        additiveLight = m.empty() ? null : cvMat(m).clone();\n        if ((n = fn.get(\"avgColorErr\")).isReal()) avgColorErr = n.asDouble();\n        if ((n = fn.get(\"colorR2\")).isReal()) colorR2 = n.asDouble();\n    }\n\n    @Override public String toString() {\n        String s =\n        getSettings().getName() + \" (\" + imageWidth + \" x \" + imageHeight + \")\\n\";\n        for (int i = 0; i < getSettings().getName().length(); i++) {\n            s += \"=\";\n        }\n        s += \"\\n\" +\n        \"Intrinsics\\n\" +\n        \"----------\\n\" +\n        \"camera matrix = \" + (cameraMatrix == null ? \"null\" : cameraMatrix.toString(16)) + \"\\n\" +\n        \"distortion coefficients = \" + (distortionCoeffs == null ? \"null\" : distortionCoeffs) + \"\\n\" +\n        \"reprojection RMS/max error (pixels) = \" + (float)avgReprojErr + \" / \" + (float)maxReprojErr + \"\\n\\n\" +\n\n        \"Extrinsics\\n\" +\n        \"----------\\n\" +\n        \"rotation = \" + (R == null ? \"null\" : R.toString(11)) + \"\\n\" +\n        \"translation = \" + (T == null ? \"null\" : T.toString(14)) + \"\\n\" +\n        \"epipolar RMS/max error (pixels) = \" + (float)avgEpipolarErr + \" / \" + (float)maxEpipolarErr + \"\\n\\n\" +\n\n        \"Color\\n\" +\n        \"-----\\n\" +\n        \"order = \" + colorOrder + \"\\n\" +\n        \"mixing matrix = \" + (colorMixingMatrix == null ? \"null\" : colorMixingMatrix.toString(16)) + \"\\n\" +\n        \"additive light = \" + (additiveLight == null ? \"null\" : additiveLight.toString(17)) + \"\\n\" +\n        \"normalized RMSE (intensity) = \" + (float)avgColorErr + \"\\n\" +\n        \"R^2 (intensity) = \" + (float)colorR2;\n\n        return s;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProjectiveTransformer.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport org.bytedeco.opencv.opencv_calib3d.*;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.javacv.cvkernels.*;\nimport static org.bytedeco.opencv.global.opencv_calib3d.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProjectiveTransformer implements ImageTransformer {\n    public ProjectiveTransformer() {\n        this(null, null, null, null, null, new double[0], null);\n    }\n    public ProjectiveTransformer(double[] referencePoints) {\n        this(null, null, null, null, null, referencePoints, null);\n    }\n    public ProjectiveTransformer(ProjectiveDevice d1, ProjectiveDevice d2, CvMat n,\n            double[] referencePoints1, double[] referencePoints2) {\n        // assuming d1 has identity values, use d2's stuff directly\n        this(d1.cameraMatrix, d2.cameraMatrix, d2.R, d2.T, n, referencePoints1, referencePoints2);\n    }\n    public ProjectiveTransformer(CvMat K1, CvMat K2, CvMat R, CvMat t, CvMat n,\n            double[] referencePoints1, double[] referencePoints2) {\n        this.K1    = K1 == null ? null : K1.clone();\n        this.K2    = K2 == null ? null : K2.clone();\n        this.invK1 = K1 == null ? null : K1.clone();\n        this.invK2 = K2 == null ? null : K2.clone();\n        if (K1 != null) {\n            cvInvert(K1, invK1);\n        }\n        if (K2 != null) {\n            cvInvert(K2, invK2);\n        }\n        this.R = R == null ? null : R.clone();\n        this.t = t == null ? null : t.clone();\n        this.n = n == null ? null : n.clone();\n\n        this.referencePoints1 = referencePoints1 == null ? null : referencePoints1.clone();\n        this.referencePoints2 = referencePoints2 == null ? null : referencePoints2.clone();\n    }\n\n    protected static ThreadLocal<CvMat>\n            H3x3   = CvMat.createThreadLocal(3, 3),\n            pts4x1 = CvMat.createThreadLocal(4, 1, CV_64F, 2);\n\n    protected CvMat K1 = null, K2 = null, invK1 = null, invK2 = null, R = null, t = null, n = null;\n    protected double[] referencePoints1 = null, referencePoints2 = null;\n    protected CvScalar fillColor = cvScalar(0.0, 0.0, 0.0, 1.0);\n\n    protected KernelData kernelData = null;\n    protected CvMat[] H = null;\n\n    public CvScalar getFillColor() {\n        return fillColor;\n    }\n    public void setFillColor(CvScalar fillColor) {\n        this.fillColor = fillColor;\n    }\n\n    public double[] getReferencePoints1() {\n        return referencePoints1;\n    }\n    public double[] getReferencePoints2() {\n        return referencePoints2;\n    }\n    public CvMat getK1() {\n        return K1;\n    }\n    public CvMat getK2() {\n        return K2;\n    }\n    public CvMat getInvK1() {\n        return invK1;\n    }\n    public CvMat getInvK2() {\n        return invK2;\n    }\n    public CvMat getR() {\n        return R;\n    }\n    public CvMat getT() {\n        return t;\n    }\n    public CvMat getN() {\n        return n;\n    }\n\n    protected void prepareHomography(CvMat H, int pyramidLevel, Parameters p, boolean inverse) {\n        if (K2 != null && invK1 != null && R != null && t != null && p.fakeIdentity) {\n            // no identity available for plane parameter...\n            // fakeIdentity needs to be implemented..\n            cvSetIdentity(H);\n            return;\n        }\n\n        if (inverse) {\n            H.put(p.getH());\n        } else {\n            cvInvert(p.getH(), H);\n        }\n\n        // adjust the scale of the transformation based on the pyramid level\n        if (pyramidLevel > 0) {\n            int scale = 1<<pyramidLevel;\n            H.put(2, H.get(2)/scale);\n            H.put(5, H.get(5)/scale);\n            H.put(6, H.get(6)*scale);\n            H.put(7, H.get(7)*scale);\n        }\n    }\n\n    public void transform(IplImage srcImage, IplImage dstImage, CvRect roi, int pyramidLevel,\n            ImageTransformer.Parameters parameters, boolean inverse) {\n        Parameters p = ((Parameters)parameters);\n        if (K2 != null && invK1 != null && R != null && t != null && p.fakeIdentity) {\n            // no identity available for plane parameter...\n            // fakeIdentity needs to be implemented..\n            if (srcImage != dstImage) {\n                cvCopy(srcImage, dstImage);\n            }\n            return;\n        }\n\n        CvMat H = H3x3.get();\n        prepareHomography(H, pyramidLevel, p, true);\n\n        // use ROI not as a sub-image, but as the region we want to fill in\n        // the destination image... so here we compensate for the translation\n        // caused by the ROI inside cvWarpPerspective()\n        if (roi != null && (roi.x() != 0 || roi.y() != 0)) {\n            int x = roi.x(), y = roi.y();\n            if (inverse) {\n                H.put(2, H.get(0)*x + H.get(1)*y + H.get(2));\n                H.put(5, H.get(3)*x + H.get(4)*y + H.get(5));\n                H.put(8, H.get(6)*x + H.get(7)*y + H.get(8));\n            } else {\n                H.put(0, H.get(0) - x*H.get(6));\n                H.put(1, H.get(1) - x*H.get(7));\n                H.put(2, H.get(2) - x*H.get(8));\n                H.put(3, H.get(3) - y*H.get(6));\n                H.put(4, H.get(4) - y*H.get(7));\n                H.put(5, H.get(5) - y*H.get(8));\n            }\n        }\n\n        dstImage.origin(srcImage.origin()); // cvWarpPerspective doesn't use it..\n\n        if (roi == null) {\n            cvResetImageROI(dstImage);\n        } else {\n            cvSetImageROI(dstImage, roi);\n        }\n        cvWarpPerspective(srcImage, dstImage, H, CV_INTER_LINEAR |\n                CV_WARP_FILL_OUTLIERS | (inverse ? CV_WARP_INVERSE_MAP : 0),\n                getFillColor());\n    }\n\n    public void transform(CvMat srcPts, CvMat dstPts, ImageTransformer.Parameters parameters, boolean inverse) {\n        Parameters p = ((Parameters)parameters);\n        CvMat H;\n        if (inverse) {\n            H = H3x3.get();\n            cvInvert(p.getH(), H);\n        } else {\n            H = p.getH();\n        }\n        cvPerspectiveTransform(srcPts, dstPts, H);\n    }\n\n    public void transform(Data[] data, CvRect roi, ImageTransformer.Parameters[] parameters, boolean[] inverses) {\n        assert data.length == parameters.length;\n        if (kernelData == null || kernelData.capacity() < data.length) {\n            kernelData = new KernelData(data.length);\n        }\n        if (H == null || H.length < data.length) {\n            H = new CvMat[data.length];\n            for (int i = 0; i < H.length; i++) {\n                H[i] = CvMat.create(3, 3);\n            }\n        }\n\n        for (int i = 0; i < data.length; i++) {\n            kernelData.position(i);\n\n            kernelData.srcImg(data[i].srcImg);\n            kernelData.srcImg2(null);\n            kernelData.subImg(data[i].subImg);\n            kernelData.srcDotImg(data[i].srcDotImg);\n            kernelData.mask(data[i].mask);\n            kernelData.zeroThreshold(data[i].zeroThreshold);\n            kernelData.outlierThreshold(data[i].outlierThreshold);\n\n            prepareHomography(H[i], data[i].pyramidLevel, (Parameters)parameters[i],\n                    inverses == null ? false : inverses[i]);\n\n            kernelData.H1(H[i]);\n            kernelData.H2(null);\n            kernelData.X (null);\n\n            kernelData.transImg(data[i].transImg);\n            kernelData.dstImg(data[i].dstImg);\n            kernelData.dstDstDot(data[i].dstDstDot);\n        }\n\n        long fullCapacity = kernelData.capacity();\n        kernelData.capacity(data.length);\n        multiWarpColorTransform(kernelData, roi, getFillColor());\n        kernelData.capacity(fullCapacity);\n\n        for (int i = 0; i < data.length; i++) {\n            kernelData.position(i);\n            data[i].dstCount        = kernelData.dstCount();\n            data[i].dstCountZero    = kernelData.dstCountZero();\n            data[i].dstCountOutlier = kernelData.dstCountOutlier();\n            data[i].srcDstDot       = kernelData.srcDstDot();\n        }\n    }\n\n    public Parameters createParameters() {\n        return new Parameters();\n    }\n\n    public class Parameters implements ImageTransformer.Parameters {\n        protected Parameters() {\n            reset(false);\n        }\n\n        protected double[] projectiveParameters = null;\n        private CvMat H = CvMat.create(3, 3), n2 = null, R2 = null, t2 = null;\n        private double constraintError = 0;\n        private boolean updateNeeded = true;\n        protected boolean fakeIdentity = false;\n\n        public boolean isUpdateNeeded() {\n            return updateNeeded;\n        }\n        public void setUpdateNeeded(boolean updateNeeded) {\n            this.updateNeeded = updateNeeded;\n        }\n\n        public int size() {\n            return projectiveParameters.length;\n        }\n        public double[] get() {\n            double[] p = new double[size()];\n            for (int i = 0; i < p.length; i++) {\n                p[i] = get(i);\n            }\n            return p;\n        }\n        public double get(int i) {\n            return projectiveParameters[i];\n        }\n        public void set(double ... p) {\n            for (int i = 0; i < p.length; i++) {\n                set(i, p[i]);\n            }\n        }\n        public void set(int i, double p) {\n            if (projectiveParameters[i] != p) {\n                projectiveParameters[i]  = p;\n                setUpdateNeeded(true);\n            }\n        }\n        public void set(ImageTransformer.Parameters p) {\n            set(p.get());\n            fakeIdentity = ((Parameters)p).fakeIdentity;\n        }\n        public void reset(boolean asIdentity) {\n            setUpdateNeeded(true);\n            if (referencePoints1 != null && (referencePoints1.length == 0 || referencePoints1.length == 8)) {\n                if (referencePoints1.length == 0) {\n//                    if (K2 != null && invK1 != null && n == null) {\n//                        projectiveParameters = new double[] { 1, 0, 0,  0, 1, 0,  0, 0 /*, 1*/,  0, 0, 0};\n//                    } else {\n                        projectiveParameters = new double[] { 1, 0, 0,  0, 1, 0,  0, 0 /*, 1*/ };\n//                    }\n                } else {\n//                    if (K2 != null && invK1 != null && n == null) {\n//                        projectiveParameters = Arrays.copyOf(referencePoints, 11);\n//                    } else {\n                        projectiveParameters = referencePoints1.clone();\n//                    }\n                }\n            } else if (K2 != null && invK1 != null) {\n                if (R != null && t != null) {\n                    // no identity available for this one, so...\n                    //projectiveParameters = new double[] { 0, 0, 0 };\n                    projectiveParameters = new double[] { \n                        referencePoints1[0], referencePoints1[2], referencePoints1[4] };\n                } else if (n != null) {\n                    projectiveParameters = new double[] { 0, 0, 0,  0, 0, 0 };\n                } else {\n                    projectiveParameters = new double[] { 0, 0, 0,  0, 0, 0,  0, 0, 0};\n                }\n            }\n        }\n\n//        public boolean addDelta(int i) {\n//            return addDelta(i, 1);\n//        }\n//\n//        public boolean addDelta(int i, double scale) {\n//            if (referencePoints != null && i < 8) {\n//                // add one pixel..\n//                projectiveParameters[i] += scale;\n//            } else if (K2 != null && invK1 != null) {\n//                if (i < 3 || i >= 8) {\n//                    projectiveParameters[i] += scale;\n//                } else {\n//                    // translation vector\n//\n//                    // assuming a reference plane at [0, 0, 1],\n//                    // this is about 1% of image resolution?\n//                    projectiveParameters[i] += 0.01 * scale;\n//                }\n//            }\n//            setUpdateNeeded(true);\n//            return false;\n//        }\n        public double getConstraintError() {\n            update();\n            return constraintError;\n        }\n        public void set(CvMat setH, boolean inverse) {\n            if (projectiveParameters.length == 8 && referencePoints1 != null) {\n                if (inverse) {\n                    cvInvert(setH, H);\n                } else if (setH != H) {\n                    cvCopy(setH, H);\n                }\n                if (referencePoints1.length == 0) {\n                    // direct homography parameterization\n                    for (int i = 0; i < 8; i++) {\n                        projectiveParameters[i] = H.get(i)/H.get(8);\n                    }\n                } else {\n                    // 4 point parametrization\n                    CvMat pts = pts4x1.get().put(referencePoints1);\n                    cvPerspectiveTransform(pts, pts, H);\n                    pts.get(projectiveParameters);\n                }\n                setUpdateNeeded(true);\n            } else {\n                throw new UnsupportedOperationException(\"Set homography operation not supported.\");\n            }\n        }\n        public void compose(ImageTransformer.Parameters p1, boolean inverse1,\n                ImageTransformer.Parameters p2, boolean inverse2) {\n            Parameters pp1 = (Parameters)p1, pp2 = (Parameters)p2;\n            if (K2 != null && invK1 != null && R != null && t != null && pp1.fakeIdentity) {\n                // no identity available for plane parameter...\n                // fakeIdentity needs to be implemented..\n                return;\n            }\n\n            compose(pp1.getH(), inverse1, pp2.getH(), inverse2);\n        }\n        public void compose(CvMat H1, boolean inverse1, CvMat H2, boolean inverse2) {\n            if (inverse1 && inverse2) {\n                cvMatMul(H2, H1, H);\n                cvInvert(H, H);\n            } else if (inverse1) {\n                cvInvert(H1, H);\n                cvMatMul(H, H2, H);\n            } else if (inverse2) {\n                cvInvert(H2, H);\n                cvMatMul(H1, H, H);\n            } else {\n                cvMatMul(H1, H2, H);\n            }\n            set(H, false);\n        }\n\n        public CvMat getH() {\n            update();\n            return H;\n        }\n        public CvMat getN() {\n            update();\n            return n2;\n        }\n        public CvMat getR() {\n            update();\n            return R2;\n        }\n        public CvMat getT() {\n            update();\n            return t2;\n        }\n\n        protected void update() {\n            if (!isUpdateNeeded()) {\n                return;\n            }\n\n            if (referencePoints1 != null && (referencePoints1.length == 0 || referencePoints1.length == 8)) {\n                if (referencePoints1.length == 0) {\n                    // direct homography parameterization\n                    H.put(0, projectiveParameters, 0, 8);\n                    H.put(8, 1);\n                } else {\n                    // 4 point parameterization\n                    JavaCV.getPerspectiveTransform(referencePoints1, projectiveParameters, H);\n                }\n//                if (K1 != null && invK2 != null && n != null) {\n//                    CvMat Hprime = CvMat.take(3, 3);\n//                    cvCopy(H, Hprime);\n//                    if (R2 == null) {\n//                        R2 = CvMat.create(3, 3);\n//                    }\n//                    if (t2 == null) {\n//                        t2 = CvMat.create(3, 1);\n//                    }\n//\n//                    cvMatMul(invK2,  Hprime, Hprime);\n//                    cvMatMul(Hprime, K1,     Hprime);\n//\n//                    // get 3D rotation and translation, with given n\n//                    constraintError = JavaCV.HnToRt(Hprime, n, R2, t2);\n//                    //System.out.println(constraintError);\n//                    Hprime.pool();\n//                }\n            } else if (K2 != null && invK1 != null) {\n                if (R != null && t != null) {\n                    // 3D plane motion, with given R and t\n//                    if (n2 == null) {\n//                        n2 = CvMat.create(3, 1);\n//                    }\n                    double[] src = referencePoints2; \n                    double[] dst = { projectiveParameters[0], referencePoints1[1],\n                                     projectiveParameters[1], referencePoints1[3],\n                                     projectiveParameters[2], referencePoints1[5] };\n                    if (R2 == null) {\n                        R2 = CvMat.create(3, 3);\n                    }\n                    if (t2 == null) {\n                        t2 = CvMat.create(3, 1);\n                    }\n                    cvTranspose(R, R2);\n                    cvGEMM(R2, t, -1, null, 0, t2, 0);\n                    JavaCV.getPerspectiveTransform(src, dst, invK2, K1, R2, t2, H);\n//cvConvertScale(H, H, 1/H.get(8), 0);\n//System.out.println(H);\n\n//                    n2.put(projectiveParameters);\n//                    // H = R-t*n^T\n//                    cvGEMM(t, n2, -1,  R, 1,  H, CV_GEMM_B_T);\n                } else {\n                    // 3D rotation and translation, with given n\n                    if (n != null) {\n                        n2 = n; // take n from transformer\n                    } else {\n                        if (n2 == null) {\n                            n2 = CvMat.create(3, 1);\n                        }\n                        n2.put(0, projectiveParameters, 8, 3); // take n from parameters\n                    }\n\n                    // put rotation angle and translation in matrices\n                    if (R2 == null) {\n                        R2 = CvMat.create(3, 3);\n                    }\n                    if (t2 == null) {\n                        t2 = CvMat.create(3, 1);\n                    }\n                    t2.put(0, projectiveParameters, 0, 3);\n                    Rodrigues(cvarrToMat(t2), cvarrToMat(R2), null);\n                    t2.put(0, projectiveParameters, 3, 3);\n\n                    // H = R-tn^T\n                    cvGEMM(t2, n2, -1,  R2, 1,  H, CV_GEMM_B_T);\n                }\n//                // H = K2 * H * K1^-1\n//                cvMatMul(K2,    H, H);\n//                cvMatMul(H, invK1, H);\n            }\n\n            setUpdateNeeded(false);\n        }\n\n//        public void project() {\n//            CvMat t2 = getT(), n2 = getN(), R2 = getR();\n//            cvSetIdentity(H);\n//            cvGEMM(t2, n2, -1,  H, 1,  H, CV_GEMM_B_T);\n//            cvMatMul(R2,    H,  H);\n//            cvMatMul(K2,    H,  H);\n//            cvMatMul(H, invK1,  H);\n//\n//            CvMat pts = CvMat.take(4, 1, CV_64F, 2);\n//            pts.put(referencePoints);\n//            cvPerspectiveTransform(pts, pts, H);\n//            pts.get(projectiveParameters);\n//            pts.pool();\n//        }\n\n        public boolean preoptimize() {\n            return false;\n        }\n        public double[] getSubspace() {\n            return null;\n        }\n        public void setSubspace(double ... p) {\n        }\n\n        @Override public Parameters clone() {\n            Parameters p = new Parameters();\n            p.set(this);\n            return p;\n        }\n\n        @Override public String toString() {\n            String s = \"[\";\n            double[] p = get();\n            for (int i = 0; i < p.length; i++) {\n                s += (float)p[i];\n                if (i < p.length-1) {\n                    s+= \", \";\n                }\n            }\n            s += \"]\";\n            return s;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProjectiveTransformerCL.java",
    "content": "/*\n * Copyright (C) 2011-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport com.jogamp.opencl.CLBuffer;\nimport com.jogamp.opencl.CLEventList;\nimport com.jogamp.opencl.CLImage2d;\nimport com.jogamp.opencl.CLKernel;\nimport java.nio.FloatBuffer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProjectiveTransformerCL extends ProjectiveTransformer implements ImageTransformerCL {\n    public ProjectiveTransformerCL(JavaCVCL context) {\n        this(context, null, null, null, null, null, new double[0], null);\n    }\n    public ProjectiveTransformerCL(JavaCVCL context, double[] referencePoints) {\n        this(context, null, null, null, null, null, referencePoints, null);\n    }\n    public ProjectiveTransformerCL(JavaCVCL context, ProjectiveDevice d1, ProjectiveDevice d2, CvMat n,\n            double[] referencePoints1, double[] referencePoints2) {\n        // assuming d1 has identity values, use d2's stuff directly\n        this(context, d1.cameraMatrix, d2.cameraMatrix, d2.R, d2.T, n, referencePoints1, referencePoints2);\n    }\n    public ProjectiveTransformerCL(JavaCVCL context, CvMat K1, CvMat K2, CvMat R, CvMat t, CvMat n,\n            double[] referencePoints1, double[] referencePoints2) {\n        super(K1, K2, R, t, n, referencePoints1, referencePoints2);\n        final int dotSize = createParameters().size();\n        this.context = context;\n        this.HBuffer = context.getCLContext().createFloatBuffer(dotSize*9,  CLBuffer.Mem.READ_ONLY);\n        if (getClass() == ProjectiveTransformerCL.class) {\n            CLKernel[] kernels = context.buildKernels(\n                    JavaCVCL.fastCompilerOptions + \" -DDOT_SIZE=\" + dotSize,\n                    \"ImageTransformer.cl:ProjectiveTransformer.cl\",\n                    \"transformOne\", \"transformSub\", \"transformDot\", \"reduceOutputData\");\n            this.oneKernel    = kernels[0];\n            this.subKernel    = kernels[1];\n            this.dotKernel    = kernels[2];\n            this.reduceKernel = kernels[3];\n        }\n    }\n\n    protected final JavaCVCL context;\n    protected final CLBuffer<FloatBuffer> HBuffer;\n    private CLKernel oneKernel, subKernel, dotKernel, reduceKernel;\n\n    public JavaCVCL getContext() {\n        return context;\n    }\n\n    protected void prepareHomographies(CLBuffer HBuffer, int pyramidLevel, \n            ImageTransformer.Parameters[] parameters, boolean[] inverses) {\n        FloatBuffer floatH = (FloatBuffer)HBuffer.getBuffer().rewind();\n        CvMat H = H3x3.get();\n        for (int i = 0; i < parameters.length; i++) {\n            prepareHomography(H, pyramidLevel, (ProjectiveTransformer.Parameters)parameters[i],\n                    inverses == null ? false : inverses[i]);\n            for (int j = 0; j < 9; j++) {\n                floatH.put((float)H.get(j));\n            }\n        }\n        floatH.rewind();\n    }\n\n    public void transform(CLImage2d srcImg, CLImage2d subImg, CLImage2d srcDotImg,\n            CLImage2d transImg, CLImage2d dstImg, CLImage2d maskImg,\n            ImageTransformer.Parameters[] parameters, boolean[] inverses,\n            InputData inputData, OutputData outputData) {\n        prepareHomographies(HBuffer, inputData.pyramidLevel, parameters, inverses);\n\n        final int dotSize = parameters[0].size();\n        final int localSize = parameters.length > 1 ? parameters.length : (inputData.roiWidth > 32 ? 64 : 32);\n        final int globalSize = JavaCVCL.alignCeil(inputData.roiWidth, localSize);\n        final int reduceSize = globalSize/localSize;\n\n        // allocate buffers if necessary\n        CLBuffer inputBuffer = inputData.getBuffer(context);\n        CLBuffer outputBuffer = outputData.getBuffer(context, dotSize, reduceSize);\n\n        CLEventList list = new CLEventList(1);\n\n        // setup kernel\n        context.writeBuffer(HBuffer, false); // upload H\n        if (inputData.autoWrite) {\n            inputData.writeBuffer(context);\n        }\n        CLKernel kernel = null;\n        if (subImg == null) {\n            assert parameters.length == 1;\n            kernel = oneKernel.putArg(srcImg).putArg(dstImg == null ? transImg : dstImg).putArg(maskImg)\n                    .putArg(HBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();\n        } else if (srcDotImg == null) {\n            assert parameters.length == 1;\n            kernel = subKernel.putArg(srcImg).putArg(subImg).putArg(transImg).putArg(dstImg).putArg(maskImg)\n                    .putArg(HBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();\n        } else {\n            assert parameters.length == dotSize;\n            kernel = dotKernel.putArg(srcImg).putArg(subImg).putArg(srcDotImg).putArg(maskImg)\n                    .putArg(HBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();\n        }\n        context.executeKernel(kernel, inputData.roiX, 0, 0,\n                globalSize, 1, parameters.length,\n                localSize, 1, parameters.length, list); // execute program\n        if (reduceSize > 1) {\n            reduceKernel.putArg(outputBuffer).rewind();\n            context.executeKernel(reduceKernel, 0, reduceSize, reduceSize);\n        }\n        if (outputData.autoRead) {\n            outputData.readBuffer(context);\n        }\n\n//        CLEvent event = list.getEvent(0);\n//        System.out.println(kernel + \" \" + (event.getProfilingInfo(CLEvent.ProfilingCommand.END) -\n//                            event.getProfilingInfo(CLEvent.ProfilingCommand.START))/1000000.0);\n\n//        long res = q.getDevice().getProfilingTimerResolution();\n//        System.out.println(res);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProjectorDevice.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.awt.Dimension;\nimport java.awt.DisplayMode;\nimport java.beans.PropertyChangeListener;\nimport java.lang.reflect.InvocationTargetException;\nimport org.bytedeco.javacpp.Pointer;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProjectorDevice extends ProjectiveDevice {\n    public ProjectorDevice(String name) {\n        super(name);\n    }\n    public ProjectorDevice(String name, String filename) throws Exception {\n        super(name, filename);\n        settings.setImageWidth(imageWidth);\n        settings.setImageHeight(imageHeight);\n    }\n    public ProjectorDevice(String name, FileStorage fs) throws Exception {\n        super(name, fs);\n        settings.setImageWidth(imageWidth);\n        settings.setImageHeight(imageHeight);\n    }\n    public ProjectorDevice(Settings settings) throws Exception {\n        super((ProjectiveDevice.Settings)settings);\n    }\n\n    public interface Settings {\n        String getName();\n        void setName(String name);\n        double getResponseGamma();\n        void setResponseGamma(double gamma);\n\n        int getScreenNumber();\n        void setScreenNumber(int screenNumber);\n        long getLatency();\n        void setLatency(long latency);\n        String getDescription();\n\n        int getImageWidth();\n        void setImageWidth(int imageWidth);\n        int getImageHeight();\n        void setImageHeight(int imageHeight);\n        int getBitDepth();\n        void setBitDepth(int bitDepth);\n        int getRefreshRate();\n        void setRefreshRate(int refreshRate);\n\n        boolean isUseOpenGL();\n        void setUseOpenGL(boolean useOpenGL);\n\n        void addPropertyChangeListener(PropertyChangeListener listener);\n        void removePropertyChangeListener(PropertyChangeListener listener);\n    }\n\n    public static class SettingsImplementation extends ProjectiveDevice.Settings implements Settings {\n        public SettingsImplementation() { name = \"Projector  0\"; setScreenNumber(screenNumber); }\n        public SettingsImplementation(ProjectiveDevice.Settings settings) {\n            super(settings);\n            if (settings instanceof SettingsImplementation) {\n                SettingsImplementation s = (SettingsImplementation)settings;\n                this.screenNumber = s.screenNumber;\n                this.latency      = s.latency;\n                this.imageWidth   = s.imageWidth;\n                this.imageHeight  = s.imageHeight;\n                this.bitDepth     = s.bitDepth;\n                this.refreshRate  = s.refreshRate;\n                this.useOpenGL    = s.useOpenGL;\n            }\n        }\n\n        int screenNumber = CanvasFrame.getScreenDevices().length > 1 ? 1 : 0;\n        long latency = CanvasFrame.DEFAULT_LATENCY;\n\n        public int getScreenNumber() {\n            return screenNumber;\n        }\n        public void setScreenNumber(int screenNumber) {\n            DisplayMode d = CanvasFrame.getDisplayMode(screenNumber);\n            String oldDescription = getDescription();\n            firePropertyChange(\"screenNumber\",  this.screenNumber,  this.screenNumber  = screenNumber);\n            firePropertyChange(\"description\", oldDescription, getDescription());\n            firePropertyChange(\"imageWidth\",    this.imageWidth,    this.imageWidth    = d == null ? 0 : d.getWidth());\n            firePropertyChange(\"imageHeight\",   this.imageHeight,   this.imageHeight   = d == null ? 0 : d.getHeight());\n            firePropertyChange(\"bitDepth\",      this.bitDepth,      this.bitDepth      = d == null ? 0 : d.getBitDepth());\n            firePropertyChange(\"refreshRate\",   this.refreshRate,   this.refreshRate   = d == null ? 0 : d.getRefreshRate());\n            firePropertyChange(\"responseGamma\", this.responseGamma, this.responseGamma = CanvasFrame.getGamma(screenNumber));\n        }\n\n        public long getLatency() {\n            return latency;\n        }\n        public void setLatency(long latency) {\n            this.latency = latency;\n        }\n\n        public String getDescription() {\n            String[] descriptions = null;\n            descriptions = CanvasFrame.getScreenDescriptions();\n\n            if (descriptions != null && screenNumber >= 0 && screenNumber < descriptions.length) {\n                return descriptions[screenNumber];\n            } else {\n                return \"\";\n            }\n        }\n\n        int imageWidth = 0, imageHeight = 0, bitDepth = 0, refreshRate = 0;\n\n        public int getImageWidth() {\n            return imageWidth;\n        }\n        public void setImageWidth(int imageWidth) {\n            firePropertyChange(\"imageWidth\", this.imageWidth, this.imageWidth = imageWidth);\n        }\n\n        public int getImageHeight() {\n            return imageHeight;\n        }\n        public void setImageHeight(int imageHeight) {\n            firePropertyChange(\"imageHeight\", this.imageHeight, this.imageHeight = imageHeight);\n        }\n\n        public int getBitDepth() {\n            return bitDepth;\n        }\n        public void setBitDepth(int bitDepth) {\n            this.bitDepth = bitDepth;\n        }\n\n        public int getRefreshRate() {\n            return refreshRate;\n        }\n        public void setRefreshRate(int refreshRate) {\n            this.refreshRate = refreshRate;\n        }\n\n        private boolean useOpenGL = false;\n\n        public boolean isUseOpenGL() {\n            return useOpenGL;\n        }\n        public void setUseOpenGL(boolean useOpenGL) {\n            this.useOpenGL = useOpenGL;\n        }\n    }\n\n    // pouah.. hurray for Scala!\n    public static class CalibrationSettings extends ProjectiveDevice.CalibrationSettings implements Settings {\n        public CalibrationSettings() { }\n        public CalibrationSettings(ProjectiveDevice.CalibrationSettings settings) {\n            super(settings);\n            if (settings instanceof CalibrationSettings) {\n                CalibrationSettings s = (CalibrationSettings)settings;\n                si = new SettingsImplementation(s.si);\n                this.brightnessBackground = s.brightnessBackground;\n                this.brightnessForeground = s.brightnessForeground;\n            }\n        }\n        SettingsImplementation si = new SettingsImplementation() {\n            @Override public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {\n                CalibrationSettings.this.firePropertyChange(propertyName, oldValue, newValue);\n            }\n        };\n\n        @Override public String getName() { return si.getName(); }\n        @Override public void setName(String name) { si.setName(name); }\n        @Override public double getResponseGamma() { return si.getResponseGamma(); }\n        @Override public void setResponseGamma(double responseGamma) { si.setResponseGamma(responseGamma); }\n//        @Override public double getNominalDistance() { return si.getNominalDistance(); }\n//        @Override public void setNominalDistance(double nominalDistance) { si.setNominalDistance(nominalDistance); }\n\n        public int getScreenNumber() { return si.getScreenNumber(); }\n        public void setScreenNumber(int screenNumber) { si.setScreenNumber(screenNumber); }\n        public long getLatency() { return si.getLatency(); }\n        public void setLatency(long latency) { si.setLatency(latency); }\n        public String getDescription() { return si.getDescription(); }\n\n        public int getImageWidth() { return si.getImageWidth(); }\n        public void setImageWidth(int imageWidth) { si.setImageWidth(imageWidth); }\n        public int getImageHeight() { return si.getImageHeight(); }\n        public void setImageHeight(int imageHeight) { si.setImageHeight(imageHeight); }\n        public int getBitDepth() { return si.getBitDepth(); }\n        public void setBitDepth(int bitDepth) { si.setBitDepth(bitDepth); }\n        public int getRefreshRate() { return si.getRefreshRate(); }\n        public void setRefreshRate(int refreshRate) { si.setRefreshRate(refreshRate); }\n\n        public boolean isUseOpenGL() { return si.isUseOpenGL(); }\n        public void setUseOpenGL(boolean useOpenGL) { si.setUseOpenGL(useOpenGL); }\n\n        double brightnessBackground = 0.0, brightnessForeground = 1.0;\n\n        public double getBrightnessBackground() {\n            return brightnessBackground;\n        }\n        public void setBrightnessBackground(double brightnessBackground) {\n            firePropertyChange(\"brightnessBackground\", this.brightnessBackground,\n                    this.brightnessBackground = brightnessBackground);\n        }\n\n        public double getBrightnessForeground() {\n            return brightnessForeground;\n        }\n        public void setBrightnessForeground(double brightnessForeground) {\n            firePropertyChange(\"brightnessForeground\", this.brightnessForeground,\n                    this.brightnessForeground = brightnessForeground);\n        }\n    }\n\n    public static class CalibratedSettings extends ProjectiveDevice.CalibratedSettings implements Settings {\n        public CalibratedSettings() { }\n        public CalibratedSettings(ProjectiveDevice.CalibratedSettings settings) {\n            super(settings);\n            if (settings instanceof CalibratedSettings) {\n                si = new SettingsImplementation(((CalibratedSettings)settings).si);\n            }\n        }\n        SettingsImplementation si = new SettingsImplementation() {\n            @Override public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {\n                CalibratedSettings.this.firePropertyChange(propertyName, oldValue, newValue);\n            }\n        };\n\n        @Override public String getName() { return si.getName(); }\n        @Override public void setName(String name) { si.setName(name); }\n        @Override public double getResponseGamma() { return si.getResponseGamma(); }\n        @Override public void setResponseGamma(double responseGamma) { si.setResponseGamma(responseGamma); }\n//        @Override public double getNominalDistance() { return si.getNominalDistance(); }\n//        @Override public void setNominalDistance(double nominalDistance) { si.setNominalDistance(nominalDistance); }\n\n        public int getScreenNumber() { return si.getScreenNumber(); }\n        public void setScreenNumber(int screenNumber) { si.setScreenNumber(screenNumber); }\n        public long getLatency() { return si.getLatency(); }\n        public void setLatency(long latency) { si.setLatency(latency); }\n        public String getDescription() { return si.getDescription(); }\n\n        public int getImageWidth() { return si.getImageWidth(); }\n        public void setImageWidth(int imageWidth) { si.setImageWidth(imageWidth); }\n        public int getImageHeight() { return si.getImageHeight(); }\n        public void setImageHeight(int imageHeight) { si.setImageHeight(imageHeight); }\n        public int getBitDepth() { return si.getBitDepth(); }\n        public void setBitDepth(int bitDepth) { si.setBitDepth(bitDepth); }\n        public int getRefreshRate() { return si.getRefreshRate(); }\n        public void setRefreshRate(int refreshRate) { si.setRefreshRate(refreshRate); }\n\n        public boolean isUseOpenGL() { return si.isUseOpenGL(); }\n        public void setUseOpenGL(boolean useOpenGL) { si.setUseOpenGL(useOpenGL); }\n    }\n\n    private Settings settings;\n    @Override public ProjectiveDevice.Settings getSettings() {\n        return (ProjectiveDevice.Settings)settings;\n    }\n    public void setSettings(Settings settings) {\n        setSettings((ProjectiveDevice.Settings)settings);\n    }\n    @Override public void setSettings(ProjectiveDevice.Settings settings) {\n        super.setSettings(settings);\n        if (settings instanceof ProjectiveDevice.CalibrationSettings) {\n            this.settings = new CalibrationSettings((ProjectiveDevice.CalibrationSettings)settings);\n        } else if (settings instanceof ProjectiveDevice.CalibratedSettings) {\n            this.settings = new CalibratedSettings((ProjectiveDevice.CalibratedSettings)settings);\n        } else {\n            this.settings = new SettingsImplementation((ProjectiveDevice.Settings)settings);\n        }\n        if (this.settings.getName() == null || this.settings.getName().length() == 0) {\n            this.settings.setName(\"Projector \" + String.format(\"%2d\", this.settings.getScreenNumber()));\n        }\n    }\n\n    public CanvasFrame createCanvasFrame() throws CanvasFrame.Exception {\n        if (settings.getScreenNumber() < 0) {\n            return null;\n        }\n        DisplayMode d = new DisplayMode(settings.getImageWidth(), settings.getImageHeight(),\n                settings.getBitDepth(), settings.getRefreshRate());\n        CanvasFrame c = null;\n        Throwable cause = null;\n        try {\n            c = Class.forName(CanvasFrame.class.getPackage().getName() + (settings.isUseOpenGL() ? \".GLCanvasFrame\" : \".CanvasFrame\")).\n                    asSubclass(CanvasFrame.class).getConstructor(String.class, int.class, DisplayMode.class, double.class).\n                    newInstance(settings.getName(), settings.getScreenNumber(), d, settings.getResponseGamma());\n        } catch (ClassNotFoundException ex) {\n            cause = ex;\n        } catch (InstantiationException ex) {\n            cause = ex;\n        } catch (IllegalAccessException ex) {\n            cause = ex;\n        } catch (IllegalArgumentException ex) {\n            cause = ex;\n        } catch (NoSuchMethodException ex) {\n            cause = ex;\n        } catch (InvocationTargetException ex) {\n            cause = ex.getCause();\n        }\n        if (cause != null) {\n            if (cause instanceof CanvasFrame.Exception) {\n                throw (CanvasFrame.Exception)cause;\n            } else {\n                throw new CanvasFrame.Exception(\"Failed to create CanvasFrame\", cause);\n            }\n        }\n        c.setLatency(settings.getLatency());\n\n        Dimension size = c.getCanvasSize();\n        if (size.width != imageWidth || size.height != imageHeight) {\n            rescale(size.width, size.height);\n        }\n\n        return c;\n    }\n\n    private static ThreadLocal<CvMat>\n            B4x3  = CvMat.createThreadLocal(4, 3),\n            x23x1 = CvMat.createThreadLocal(3, 1),\n            x34x1 = CvMat.createThreadLocal(4, 1);\n    public double getAttenuation(double x, double y, CvMat n, double d) {\n        CvMat B  = B4x3.get();\n        CvMat x2 = x23x1.get();\n        CvMat x3 = x34x1.get();\n\n        getBackProjectionMatrix(n, d, B);\n        x2.put(x, y, 1);\n        cvMatMul(B, x2, x3);\n\n        // find the direction and the distance of that middle point to the\n        // projector and use it to compute the expected overall attenuation\n        //      cos(theta) * nominal_distance^2 / distance^2\n        // we assume a perfectly Lambertian surface ... ugh ...\n        // at a sufficient distance from the projector... ugh...\n        cvGEMM(R, T, -1, null, 0, x2, CV_GEMM_A_T);\n        x3.rows(3);\n        cvAddWeighted(x3, 1/x3.get(3), x2, -1, 0, x2);\n        double distance2 = cvDotProduct(x2, x2);\n        double distance = Math.sqrt(distance2);\n        double cosangle = -Math.signum(d)*cvDotProduct(x2, n)/\n                (distance * Math.sqrt(cvDotProduct(n, n)));\n        double attenuation = cosangle/distance2;\n//        System.out.println(distance + \" \" + cosangle + \" \" + attenuation);\n        x3.rows(4);\n\n        return attenuation;\n    }\n\n    public static ProjectorDevice[] read(String filename) throws Exception {\n        FileStorage fs = new FileStorage(filename, FileStorage.READ);\n        ProjectorDevice[] devices = read(fs);\n        fs.release();\n        return devices;\n    }\n    public static ProjectorDevice[] read(FileStorage fs) throws Exception {\n        FileNode node = fs.get(\"Projectors\");\n        FileNodeIterator seq = node.begin();\n        int count = (int)seq.remaining();\n\n        ProjectorDevice[] devices = new ProjectorDevice[count];\n        for (int i = 0; i < count; i++, seq.increment()) {\n            FileNode n = seq.multiply();\n            if (n.empty()) continue;\n            String name = n.asBytePointer().getString();\n            devices[i] = new ProjectorDevice(name, fs);\n        }\n        return devices;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ProjectorSettings.java",
    "content": "/*\n * Copyright (C) 2009-2010 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.beans.PropertyChangeListener;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ProjectorSettings extends BaseSettings {\n\n    public ProjectorSettings() {\n        this(false);\n    }\n    public ProjectorSettings(boolean calibrated) {\n        this.calibrated = calibrated;\n    }\n\n    boolean calibrated = false;\n\n    public int getQuantity() {\n        return size();\n    }\n    public void setQuantity(int quantity) {\n        Object[] a = toArray();\n        int i = a.length;\n        while (i > quantity) {\n            remove(a[i-1]);\n            i--;\n        }\n        while (i < quantity) {\n            ProjectorDevice.Settings c = calibrated ? new ProjectorDevice.CalibratedSettings() :\n                                                      new ProjectorDevice.CalibrationSettings();\n            c.setName(\"Projector \" + String.format(\"%2d\", i));\n            c.setScreenNumber(c.getScreenNumber()+i);\n            add(c);\n            for (PropertyChangeListener l : pcSupport.getPropertyChangeListeners()) {\n                ((BaseChildSettings)c).addPropertyChangeListener(l);\n            }\n            i++;\n        }\n        pcSupport.firePropertyChange(\"quantity\", a.length, quantity);\n    }\n\n    @Override public ProjectorDevice.Settings[] toArray() {\n        return (ProjectorDevice.Settings[])toArray(new ProjectorDevice.Settings[size()]);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/RealSense2FrameGrabber.java",
    "content": "/*\n * Copyright (C) 2019-2022 Florian Bruggisser, Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.bytedeco.javacv;\n\nimport org.bytedeco.javacpp.IntPointer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.Pointer;\nimport org.bytedeco.librealsense2.*;\nimport org.bytedeco.opencv.opencv_core.IplImage;\nimport org.bytedeco.opencv.opencv_core.Size;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static org.bytedeco.librealsense2.global.realsense2.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\npublic class RealSense2FrameGrabber extends FrameGrabber {\n\n    private static FrameGrabber.Exception loadingException = null;\n\n    private static final int defaultFrameRate = 30;\n    private static final int defaultWidth = 640;\n    private static final int defaultHeight = 480;\n\n    public static void tryLoad() throws FrameGrabber.Exception {\n        if (loadingException != null) {\n            loadingException.printStackTrace();\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.librealsense2.presets.realsense2.class);\n                System.out.println(\"RealSense2 devices found: \" + getDeviceDescriptions().length);\n            } catch (Throwable t) {\n                throw loadingException = new FrameGrabber.Exception(\"Failed to load \" + RealSense2FrameGrabber.class, t);\n            }\n        }\n    }\n\n    private rs2_error error = new rs2_error();\n    private rs2_context context;\n    private rs2_device device;\n    private rs2_pipeline pipeline;\n    private rs2_config config;\n    private rs2_pipeline_profile pipelineProfile;\n\n    private rs2_frame frameset;\n\n    private int deviceNumber;\n    private List<RealSenseStream> streams = new ArrayList<>();\n\n    private FrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n\n    public RealSense2FrameGrabber() throws Exception {\n        this(0);\n    }\n\n    public RealSense2FrameGrabber(int deviceNumber) throws Exception {\n        this.deviceNumber = deviceNumber;\n\n        // create context\n        this.context = createContext();\n    }\n\n    public List<RealSense2DeviceInfo> getDeviceInfos() throws Exception {\n        List<RealSense2DeviceInfo> devices = new ArrayList<>();\n\n        rs2_device_list deviceList = createDeviceList();\n        int count = rs2_get_device_count(deviceList, error);\n        checkError(error);\n\n        for (int i = 0; i < count; i++) {\n            rs2_device device = createDevice(deviceList, i);\n            devices.add(new RealSense2DeviceInfo(\n                    getDeviceInfo(device, RS2_CAMERA_INFO_NAME),\n                    getDeviceInfo(device, RS2_CAMERA_INFO_SERIAL_NUMBER),\n                    getDeviceInfo(device, RS2_CAMERA_INFO_FIRMWARE_VERSION),\n                    toBoolean(getDeviceInfo(device, RS2_CAMERA_INFO_ADVANCED_MODE)),\n                    toBoolean(getDeviceInfo(device, RS2_CAMERA_INFO_CAMERA_LOCKED))\n            ));\n            rs2_delete_device(device);\n        }\n\n        rs2_delete_device_list(deviceList);\n        return devices;\n    }\n\n    public static String[] getDeviceDescriptions() throws Exception {\n        RealSense2FrameGrabber rs2 = new RealSense2FrameGrabber();\n        List<RealSense2DeviceInfo> infos = rs2.getDeviceInfos();\n        rs2.release();\n\n        String[] deviceDescriptions = new String[infos.size()];\n        for (int i = 0; i < deviceDescriptions.length; i++) {\n            RealSense2DeviceInfo info = infos.get(i);\n            deviceDescriptions[i] = info.toString();\n        }\n\n        return deviceDescriptions;\n    }\n\n    public void disableAllStreams() {\n        streams.clear();\n    }\n\n    public List<RealSenseStream> getEnabledStreams() {\n        return this.streams;\n    }\n\n    public void enableStream(RealSenseStream stream) {\n        streams.add(stream);\n    }\n\n    public void enableColorStream(int width, int height, int frameRate) {\n        enableStream(new RealSenseStream(\n                RS2_STREAM_COLOR,\n                0,\n                new Size(width, height),\n                frameRate,\n                RS2_FORMAT_BGR8\n        ));\n    }\n\n    public void enableDepthStream(int width, int height, int frameRate) {\n        enableStream(new RealSenseStream(\n                RS2_STREAM_DEPTH,\n                0,\n                new Size(width, height),\n                frameRate,\n                RS2_FORMAT_Z16\n        ));\n    }\n\n    public void enableIRStream(int width, int height, int frameRate, int index) {\n        enableStream(new RealSenseStream(\n                RS2_STREAM_INFRARED,\n                index,\n                new Size(width, height),\n                frameRate,\n                RS2_FORMAT_Y8\n        ));\n    }\n\n    public void enableIRStream(int width, int height, int frameRate) {\n        enableIRStream(width, height, frameRate, 1);\n    }\n\n    public void open() throws Exception {\n        // check if device is available\n        if (getDeviceCount() <= 0) {\n            throw new Exception(\"No realsense2 device is connected.\");\n        }\n\n        // create device\n        rs2_device_list devices = createDeviceList();\n        this.device = createDevice(devices, this.deviceNumber);\n        rs2_delete_device_list(devices);\n    }\n\n    @Override\n    public void start() throws Exception {\n        if (this.device == null) {\n            open();\n        }\n\n        // create pipeline\n        this.pipeline = createPipeline();\n        this.config = createConfig();\n\n        // check if streams is not empty\n        if (streams.isEmpty()) {\n            enableAllVideoStreams();\n            Collections.sort(streams);\n        }\n\n        // enable streams\n        for (RealSenseStream stream : streams) {\n            rs2_config_enable_stream(config,\n                    stream.type,\n                    stream.index,\n                    stream.size.width(),\n                    stream.size.height(),\n                    stream.format,\n                    stream.frameRate,\n                    error);\n            checkError(error);\n        }\n\n        // set image width & height to the largest stream\n        RealSenseStream largestStream = getLargestStreamByArea();\n        this.imageWidth = largestStream.size.width();\n        this.imageHeight = largestStream.size.height();\n\n        // start pipeline\n        pipelineProfile = rs2_pipeline_start_with_config(pipeline, config, error);\n        checkError(error);\n    }\n\n    @Override\n    public void stop() throws Exception {\n        rs2_pipeline_stop(this.pipeline, error);\n        checkError(error);\n\n        rs2_release_frame(this.frameset);\n        rs2_delete_pipeline_profile(this.pipelineProfile);\n        rs2_delete_config(this.config);\n        rs2_delete_pipeline(this.pipeline);\n        rs2_delete_device(this.device);\n        this.device = null;\n    }\n\n    private void readNextFrameSet() throws Exception {\n        // release previous frame\n        rs2_release_frame(this.frameset);\n\n        // read frames\n        this.frameset = rs2_pipeline_wait_for_frames(pipeline, RS2_DEFAULT_TIMEOUT, error);\n        checkError(error);\n    }\n\n    @Override\n    public void trigger() throws Exception {\n        // set trigger load flag\n        if (!triggerMode)\n            triggerMode = true;\n\n        // read frames\n        readNextFrameSet();\n    }\n\n    @Override\n    public Frame grab() throws Exception {\n        int videoStreamId = Math.max(0, videoStream);\n        RealSenseStream stream = streams.get(videoStreamId);\n\n        switch (stream.type) {\n            case RS2_STREAM_DEPTH:\n                return grabDepth();\n\n            case RS2_STREAM_INFRARED:\n                return grabIR();\n\n            default:\n                return grabColor();\n        }\n    }\n\n    public Frame grab(int streamType, int streamIndex, int iplDepth, int channels) throws Exception {\n        if (!triggerMode)\n            readNextFrameSet();\n\n        return grabCVFrame(streamType, streamIndex, iplDepth, channels);\n    }\n\n    public float getDistance(int x, int y) throws Exception {\n        rs2_frame frame = findFrameByStreamType(this.frameset, RS2_STREAM_DEPTH, 0);\n        if (frame == null)\n            return -1f;\n\n        float distance = rs2_depth_frame_get_distance(frame, x, y, error);\n        checkError(error);\n        rs2_release_frame(frame);\n        return distance;\n    }\n\n    public Frame grabColor() throws Exception {\n        if (!triggerMode)\n            readNextFrameSet();\n\n        return grabCVFrame(RS2_STREAM_COLOR, 0, IPL_DEPTH_8U, 3);\n    }\n\n    public Frame grabDepth() throws Exception {\n        if (!triggerMode)\n            readNextFrameSet();\n\n        return grabCVFrame(RS2_STREAM_DEPTH, 0, IPL_DEPTH_16U, 1);\n    }\n\n    public Frame grabIR() throws Exception {\n        return grabIR(0);\n    }\n\n    public Frame grabIR(int streamIndex) throws Exception {\n        if (!triggerMode)\n            readNextFrameSet();\n\n        return grabCVFrame(RS2_STREAM_INFRARED, streamIndex, IPL_DEPTH_8U, 1);\n    }\n\n    private RealSenseStream getLargestStreamByArea() {\n        RealSenseStream largest = streams.get(0);\n        for (RealSenseStream rs : streams) {\n            if (rs.size.area() > largest.size.area()) {\n                largest = rs;\n            }\n        }\n        return largest;\n    }\n\n    private Frame grabCVFrame(int streamType, int streamIndex, int iplDepth, int iplChannels) throws Exception {\n        Frame outputFrame;\n\n        // get frame of type if available\n        rs2_frame frame = findFrameByStreamType(this.frameset, streamType, streamIndex);\n        if (frame == null)\n            return null;\n\n        // get frame data\n        Pointer frameData = getFrameData(frame);\n        Size size = getFrameSize(frame);\n\n        // create cv frame\n        IplImage image = IplImage.createHeader(size.width(), size.height(), iplDepth, iplChannels);\n        cvSetData(image, frameData, size.width() * iplChannels * iplDepth / 8);\n        outputFrame = converter.convert(image);\n\n        // add timestamp\n        double timestamp = getFrameTimeStamp(frame);\n        outputFrame.timestamp = Math.round(timestamp);\n\n        // cleanup\n        rs2_release_frame(frame);\n\n        return outputFrame;\n    }\n\n    private rs2_frame findFrameByStreamType(rs2_frame frameset, int streamType, int index) throws Exception {\n        rs2_frame result = null;\n\n        // read frames\n        int frameCount = rs2_embedded_frames_count(frameset, error);\n        checkError(error);\n\n        int i = 0;\n        int searchIndex = 0;\n        while (i < frameCount) {\n            rs2_frame frame = rs2_extract_frame(frameset, i, error);\n            checkError(error);\n\n            // get stream profile data\n            rs2_stream_profile streamProfile = getStreamProfile(frame);\n            StreamProfileData streamProfileData = getStreamProfileData(streamProfile);\n\n            // compare stream type\n            if (streamType == streamProfileData.nativeStreamIndex.get()) {\n                if (searchIndex == index) {\n                    result = frame;\n                    break;\n                }\n                searchIndex++;\n            }\n\n            rs2_release_frame(frame);\n            i++;\n        }\n\n        return result;\n    }\n\n    @Override\n    public void release() {\n        rs2_delete_device(this.device);\n        rs2_delete_context(this.context);\n    }\n\n    public void setSensorOption(Rs2SensorType sensorType, int optionIndex, boolean value) throws Exception {\n        setSensorOption(sensorType, optionIndex, value ? 1f : 0f);\n    }\n\n    public void setSensorOption(Rs2SensorType sensorType, int optionIndex, float value) throws Exception {\n        rs2_sensor[] sensors = getSensors(device);\n\n        for (rs2_sensor sensor : sensors) {\n            checkError(error);\n\n            // check if name matches\n            String name = getSensorInfo(sensor, RS2_CAMERA_INFO_NAME);\n            if (sensorType.getName().equals(name)) {\n                rs2_options options = new rs2_options(sensor);\n                setRs2Option(options, optionIndex, value);\n            }\n\n            // cleanup\n            rs2_delete_sensor(sensor);\n        }\n    }\n\n    private rs2_context createContext() throws Exception {\n        rs2_context context = rs2_create_context(RS2_API_VERSION, error);\n        checkError(error);\n        return context;\n    }\n\n    private rs2_device_list createDeviceList() throws Exception {\n        rs2_device_list deviceList = rs2_query_devices(context, error);\n        checkError(error);\n        return deviceList;\n    }\n\n    private rs2_device createDevice(rs2_device_list deviceList, int index) throws Exception {\n        rs2_device device = rs2_create_device(deviceList, index, error);\n        checkError(error);\n        return device;\n    }\n\n    private rs2_pipeline createPipeline() throws Exception {\n        rs2_pipeline pipeline = rs2_create_pipeline(context, error);\n        checkError(error);\n        return pipeline;\n    }\n\n    private rs2_config createConfig() throws Exception {\n        rs2_config config = rs2_create_config(error);\n        checkError(error);\n        return config;\n    }\n\n    private double getFrameTimeStamp(rs2_frame frame) throws Exception {\n        double timestamp = rs2_get_frame_timestamp(frame, error);\n        checkError(error);\n        return timestamp;\n    }\n\n    private int getDeviceCount() throws Exception {\n        rs2_device_list deviceList = createDeviceList();\n        int count = rs2_get_device_count(deviceList, error);\n\n        checkError(error);\n        rs2_delete_device_list(deviceList);\n        return count;\n    }\n\n    private String getDeviceInfo(rs2_device device, int info) throws Exception {\n        // check if info is supported\n        rs2_error error = new rs2_error();\n        boolean isSupported = toBoolean(rs2_supports_device_info(device, info, error));\n        checkError(error);\n\n        if (!isSupported)\n            return null;\n\n        // read device info\n        String infoText = rs2_get_device_info(device, info, error).getString();\n        checkError(error);\n\n        return infoText;\n    }\n\n    private String getSensorInfo(rs2_sensor sensor, int info) throws Exception {\n        // check if info is supported\n        rs2_error error = new rs2_error();\n        boolean isSupported = toBoolean(rs2_supports_sensor_info(sensor, info, error));\n        checkError(error);\n\n        if (!isSupported)\n            return null;\n\n        // read sensor info\n        String infoText = rs2_get_sensor_info(sensor, info, error).getString();\n        checkError(error);\n\n        return infoText;\n    }\n\n    private Pointer getFrameData(rs2_frame frame) throws Exception {\n        Pointer frameData = rs2_get_frame_data(frame, error);\n        checkError(error);\n        return frameData;\n    }\n\n    private Size getFrameSize(rs2_frame frame) throws Exception {\n        int width = rs2_get_frame_width(frame, error);\n        checkError(error);\n        int height = rs2_get_frame_height(frame, error);\n        checkError(error);\n        return new Size(width, height);\n    }\n\n    private rs2_stream_profile getStreamProfile(rs2_frame frame) throws Exception {\n        rs2_stream_profile streamProfile = rs2_get_frame_stream_profile(frame, error);\n        checkError(error);\n        return streamProfile;\n    }\n\n    private StreamProfileData getStreamProfileData(rs2_stream_profile streamProfile) throws Exception {\n        StreamProfileData profileData = new StreamProfileData();\n\n        if (isStreamProfile(streamProfile, RS2_EXTENSION_VIDEO_PROFILE)) {\n            VideoStreamProfileData videoStreamProfileData = new VideoStreamProfileData();\n\n            rs2_get_video_stream_resolution(streamProfile,\n                    videoStreamProfileData.width,\n                    videoStreamProfileData.height,\n                    error);\n            checkError(error);\n\n            profileData = videoStreamProfileData;\n        }\n\n        rs2_get_stream_profile_data(streamProfile,\n                profileData.nativeStreamIndex,\n                profileData.nativeFormatIndex,\n                profileData.index,\n                profileData.uniqueId,\n                profileData.frameRate,\n                error);\n        checkError(error);\n\n        return profileData;\n    }\n\n    private boolean isSensorExtendableTo(rs2_sensor sensor, int extension) throws Exception {\n        boolean isExtandable = toBoolean(rs2_is_sensor_extendable_to(sensor, extension, error));\n        checkError(error);\n        return isExtandable;\n    }\n\n    private boolean isStreamProfile(rs2_stream_profile profile, int type) throws Exception {\n        boolean isOfType = toBoolean(rs2_stream_profile_is(profile, type, error));\n        checkError(error);\n        return isOfType;\n    }\n\n    private boolean matchesVideoStreamProfile(rs2_stream_profile profile,\n                                              int streamType,\n                                              int streamFormat,\n                                              int frameRate,\n                                              int width,\n                                              int height) throws Exception {\n        VideoStreamProfileData data = (VideoStreamProfileData) getStreamProfileData(profile);\n\n        return streamType == data.nativeStreamIndex.get()\n                && streamFormat == data.nativeFormatIndex.get()\n                && frameRate == data.frameRate.get()\n                && width == data.width.get()\n                && height == data.height.get();\n    }\n\n    private void setRs2Option(rs2_options options, int optionIndex, float value) throws Exception {\n        boolean isSupported = toBoolean(rs2_supports_option(options, optionIndex, error));\n        checkError(error);\n\n        if (!isSupported) {\n            throw new Exception(\"Option \" + optionIndex + \" is not supported!\");\n        }\n\n        rs2_set_option(options, optionIndex, value, error);\n        checkError(error);\n    }\n\n    private rs2_sensor[] getSensors(rs2_device device) throws Exception {\n        rs2_sensor_list sensorList = rs2_query_sensors(device, error);\n        checkError(error);\n\n        int sensorCount = rs2_get_sensors_count(sensorList, error);\n        checkError(error);\n\n        rs2_sensor[] sensors = new rs2_sensor[sensorCount];\n\n        for (int i = 0; i < sensorCount; i++) {\n            rs2_sensor sensor = rs2_create_sensor(sensorList, i, error);\n            checkError(error);\n\n            sensors[i] = sensor;\n        }\n\n        rs2_delete_sensor_list(sensorList);\n\n        return sensors;\n    }\n\n    private rs2_stream_profile[] getStreamProfiles(rs2_sensor sensor) throws Exception {\n        rs2_stream_profile_list streamList = rs2_get_stream_profiles(sensor, error);\n        checkError(error);\n\n        int streamProfileCount = rs2_get_stream_profiles_count(streamList, error);\n        checkError(error);\n\n        rs2_stream_profile[] profiles = new rs2_stream_profile[streamProfileCount];\n\n        for (int i = 0; i < streamProfileCount; i++) {\n            rs2_stream_profile profile = rs2_get_stream_profile(streamList, i, error);\n            checkError(error);\n\n            profiles[i] = profile;\n        }\n\n        rs2_delete_stream_profiles_list(streamList);\n        return profiles;\n    }\n\n    private void enableAllVideoStreams() throws Exception {\n        for (rs2_sensor sensor : getSensors(device)) {\n            if (!isSensorExtendableTo(sensor, RS2_EXTENSION_VIDEO)) {\n                rs2_delete_sensor(sensor);\n                continue;\n            }\n\n            for (rs2_stream_profile profile : getStreamProfiles(sensor)) {\n                int rsFrameRate = frameRate > 0 ? (int) frameRate : defaultFrameRate;\n                int rsWidth = imageWidth > 0 ? imageWidth : defaultWidth;\n                int rsHeight = imageWidth > 0 ? imageWidth : defaultHeight;\n\n                if (matchesVideoStreamProfile(profile, RS2_STREAM_DEPTH, RS2_FORMAT_Z16, rsFrameRate, rsWidth, rsHeight)) {\n                    enableDepthStream(imageWidth, imageHeight, rsFrameRate);\n                } else if (matchesVideoStreamProfile(profile, RS2_STREAM_COLOR, RS2_FORMAT_RGB8, rsFrameRate, rsWidth, rsHeight)) {\n                    enableColorStream(imageWidth, imageHeight, rsFrameRate);\n                } else if (matchesVideoStreamProfile(profile, RS2_STREAM_INFRARED, RS2_FORMAT_Y8, rsFrameRate, rsWidth, rsHeight)) {\n                    enableIRStream(imageWidth, imageHeight, rsFrameRate);\n                }\n            }\n\n            rs2_delete_sensor(sensor);\n        }\n    }\n\n    private static void checkError(rs2_error e) throws Exception {\n        if (!e.isNull()) {\n            throw new Exception(String.format(\"rs_error was raised when calling %s(%s):\\n%s\\n\",\n                    rs2_get_failed_function(e).getString(),\n                    rs2_get_failed_args(e).getString(),\n                    rs2_get_error_message(e).getString()));\n        }\n    }\n\n    private static boolean toBoolean(int value) {\n        return value >= 1;\n    }\n\n    private static boolean toBoolean(String value) {\n        if (value == null)\n            return false;\n\n        return value.equals(\"YES\");\n    }\n\n    static class StreamProfileData {\n        IntPointer nativeStreamIndex = new IntPointer(1);\n        IntPointer nativeFormatIndex = new IntPointer(1);\n        IntPointer index = new IntPointer(1);\n        IntPointer uniqueId = new IntPointer(1);\n        IntPointer frameRate = new IntPointer(1);\n    }\n\n    static class VideoStreamProfileData extends StreamProfileData {\n        IntPointer width = new IntPointer(1);\n        IntPointer height = new IntPointer(1);\n    }\n\n    public static class RealSenseStream implements Comparable<RealSenseStream> {\n        private int type;\n        private int index;\n        private Size size;\n        private int frameRate;\n        private int format;\n\n        public RealSenseStream(int type, int index, Size size, int frameRate, int format) {\n            this.type = type;\n            this.index = index;\n            this.size = size;\n            this.frameRate = frameRate;\n            this.format = format;\n        }\n\n        public int getType() {\n            return type;\n        }\n\n        public int getIndex() {\n            return index;\n        }\n\n        public Size getSize() {\n            return size;\n        }\n\n        public int getFrameRate() {\n            return frameRate;\n        }\n\n        public int getFormat() {\n            return format;\n        }\n\n        @Override\n        public int compareTo(RealSenseStream o) {\n            return Integer.compare(getType(), o.getType());\n        }\n    }\n\n    public static class RealSense2DeviceInfo {\n        private String name;\n        private String serialNumber;\n        private String firmware;\n        private boolean inAdvancedMode;\n        private boolean locked;\n\n        RealSense2DeviceInfo(String name, String serialNumber, String firmware, boolean inAdvancedMode, boolean locked) {\n            this.name = name;\n            this.serialNumber = serialNumber;\n            this.firmware = firmware;\n            this.inAdvancedMode = inAdvancedMode;\n            this.locked = locked;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public String getSerialNumber() {\n            return serialNumber;\n        }\n\n        public String getFirmware() {\n            return firmware;\n        }\n\n        public boolean isInAdvancedMode() {\n            return inAdvancedMode;\n        }\n\n        public boolean isLocked() {\n            return locked;\n        }\n\n        @Override\n        public String toString() {\n            return String.format(\"%s\", name);\n        }\n    }\n\n    public enum Rs2SensorType {\n        StereoModule(\"Stereo Module\"),\n        RGBCamera(\"RGB Camera\");\n\n        private String name;\n\n        Rs2SensorType(String name) {\n            this.name = name;\n        }\n\n        public String getName() {\n            return name;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/RealSenseFrameGrabber.java",
    "content": "/*\n * Copyright (C) 2014 Jeremy Laviole, Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.ShortBuffer;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Loader;\nimport org.bytedeco.javacpp.Pointer;\n\nimport org.bytedeco.librealsense.*;\nimport org.bytedeco.librealsense.global.RealSense;\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Jeremy Laviole\n */\npublic class RealSenseFrameGrabber extends FrameGrabber {\n\n    public static String[] getDeviceDescriptions() throws FrameGrabber.Exception {\n        tryLoad();\n        String[] desc = new String[context.get_device_count()];\n        for (int i = 0; i < desc.length; i++) {\n            desc[i] = context.get_device(i).get_name().getString();\n        }\n        return desc;\n    }\n\n    public static int DEFAULT_DEPTH_WIDTH = 640;\n    public static int DEFAULT_DEPTH_HEIGHT = 480;\n    public static int DEFAULT_COLOR_WIDTH = 1280;\n    public static int DEFAULT_COLOR_HEIGHT = 720;\n    public static int DEFAULT_COLOR_FRAMERATE = 30;\n\n    private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;\n    private int depthImageWidth = DEFAULT_DEPTH_WIDTH;\n    private int depthImageHeight = DEFAULT_DEPTH_HEIGHT;\n    private int depthFrameRate = 30;\n\n    private int IRImageWidth = DEFAULT_DEPTH_WIDTH;\n    private int IRImageHeight = DEFAULT_DEPTH_HEIGHT;\n    private int IRFrameRate = 30;\n\n    public ByteOrder getByteOrder() {\n        return byteOrder;\n    }\n\n    public void setByteOrder(ByteOrder byteOrder) {\n        this.byteOrder = byteOrder;\n    }\n\n    public static RealSenseFrameGrabber createDefault(int deviceNumber) throws FrameGrabber.Exception {\n        return new RealSenseFrameGrabber(deviceNumber);\n    }\n\n    public static RealSenseFrameGrabber createDefault(File deviceFile) throws Exception {\n        throw new Exception(RealSenseFrameGrabber.class + \" does not support File devices.\");\n    }\n\n    public static RealSenseFrameGrabber createDefault(String devicePath) throws Exception {\n        throw new Exception(RealSenseFrameGrabber.class + \" does not support path.\");\n    }\n\n    private static FrameGrabber.Exception loadingException = null;\n\n    public static void tryLoad() throws FrameGrabber.Exception {\n        if (loadingException != null) {\n            loadingException.printStackTrace();\n            throw loadingException;\n        } else {\n            try {\n                if (context != null) {\n                    return;\n                }\n                Loader.load(RealSense.class);\n\n                // Context is shared accross cameras. \n                context = new context();\n                System.out.println(\"RealSense devices found: \" + context.get_device_count());\n            } catch (Throwable t) {\n                throw loadingException = new FrameGrabber.Exception(\"Failed to load \" + RealSenseFrameGrabber.class, t);\n            }\n        }\n    }\n\n    private static context context = null;\n    private int deviceNumber = 0;\n    private device device = null;\n    private static device globalDevice = null;\n    private boolean depth = false; // default to \"video\"\n    private boolean colorEnabled = false;\n    private boolean depthEnabled = false;\n    private boolean IREnabled = false;\n    private FrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n\n    public RealSenseFrameGrabber(int deviceNumber) {\n        this.deviceNumber = deviceNumber;\n    }\n\n    public static void main(String[] args) {\n        context context = new context();\n        System.out.println(\"Devices found: \" + context.get_device_count());\n        device device = context.get_device(0);\n        System.out.println(\"Using device 0, an \" + device.get_name());\n        System.out.println(\" Serial number: \" + device.get_serial());\n    }\n\n    public void enableColorStream() {\n        if (!colorEnabled) {\n            if (imageWidth == 0) {\n                imageWidth = DEFAULT_COLOR_WIDTH;\n            }\n            if (imageHeight == 0) {\n                imageHeight = DEFAULT_COLOR_HEIGHT;\n            }\n            if (frameRate == 0) {\n                frameRate = DEFAULT_COLOR_FRAMERATE;\n            }\n            colorEnabled = true;\n        }\n    }\n\n    public void disableColorStream() {\n        if (colorEnabled) {\n            device.disable_stream(RealSense.color);\n            colorEnabled = false;\n        }\n    }\n\n    public void enableDepthStream() {\n        if (!depthEnabled) {\n\n            depthEnabled = true;\n        }\n    }\n\n    public void disableDepthStream() {\n        if (depthEnabled) {\n            device.disable_stream(RealSense.depth);\n            depthEnabled = false;\n        }\n    }\n\n    public void enableIRStream() {\n        if (!IREnabled) {\n\n            IREnabled = true;\n        }\n    }\n\n    public void disableIRStream() {\n        if (IREnabled) {\n            device.disable_stream(RealSense.infrared);\n            IREnabled = false;\n        }\n    }\n\n    public void release() throws FrameGrabber.Exception {\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    /**\n     * Warning can lead to unsafe situations.\n     *\n     * @return\n     */\n    public device getRealSenseDevice() {\n        return this.device;\n    }\n\n    public float getDepthScale() {\n        return device.get_depth_scale();\n    }\n\n    @Override\n    public double getFrameRate() {  // TODO: check this. \n        return super.getFrameRate();\n    }\n\n    private boolean startedOnce = false;\n    private boolean behaveAsColorFrameGrabber = false;\n\n    public device loadDevice() throws FrameGrabber.Exception {\n        if (context == null) {\n            context = new context();\n        }\n        if (context == null || context.get_device_count() <= deviceNumber) {\n            throw new Exception(\"FATAL error: Realsense camera: \" + deviceNumber + \" not connected/found\");\n        }\n        device = context.get_device(deviceNumber);\n        return device;\n    }\n\n    @Override\n    public void start() throws FrameGrabber.Exception {\n\n        // There is already a device, we reboot everything (for Procamcalib).\n        if (globalDevice != null) {\n            globalDevice.close();\n            context.close();\n            globalDevice = null;\n            context = null;\n        }\n\n        if (context == null) {\n            context = new context();\n        }\n        if (context == null || context.get_device_count() <= deviceNumber) {\n            throw new Exception(\"FATAL error: Realsense camera: \" + deviceNumber + \" not connected/found\");\n        }\n\n        if (device == null) {\n            device = context.get_device(deviceNumber);\n        }\n        globalDevice = device;\n\n        // Choose the camera to enable by format.\n        if (format != null) {\n            switch (format) {\n                case \"rgb\":\n                    this.enableColorStream();\n                    break;\n                case \"ir\":\n                    this.enableIRStream();\n                    break;\n                case \"depth\":\n                    this.enableDepthStream();\n                    break;\n            }\n        }\n\n        if (colorEnabled) {\n            device.enable_stream(RealSense.color, imageWidth, imageHeight, RealSense.rgb8, (int) frameRate);\n        }\n        if (IREnabled) {\n            device.enable_stream(RealSense.infrared, IRImageWidth, IRImageHeight, RealSense.y8, IRFrameRate);\n        }\n        if (depthEnabled) {\n            device.enable_stream(RealSense.depth, depthImageWidth, depthImageHeight, RealSense.z16, depthFrameRate);\n        }\n        // if no stream is select, just get the color.\n        if (!colorEnabled && !IREnabled && !depthEnabled) {\n            enableColorStream();\n            device.enable_stream(RealSense.color, imageWidth, imageHeight, RealSense.rgb8, (int) frameRate);\n            behaveAsColorFrameGrabber = true;\n        }\n        device.start();\n    }\n\n    /**\n     *\n     * @throws Exception\n     */\n    @Override\n    public void stop() throws FrameGrabber.Exception {\n        device.stop();\n//        colorEnabled = false;\n//        IREnabled = false;\n//        depthEnabled = false;\n        frameNumber = 0;\n    }\n    private Pointer rawDepthImageData = new Pointer((Pointer) null),\n            rawVideoImageData = new Pointer((Pointer) null),\n            rawIRImageData = new Pointer((Pointer) null);\n    private IplImage rawDepthImage = null, rawVideoImage = null, rawIRImage = null, returnImage = null;\n\n    public IplImage grabDepth() {\n\n        if (!depthEnabled) {\n            System.out.println(\"Depth stream not enabled, impossible to get the image.\");\n            return null;\n        }\n        rawDepthImageData = device.get_frame_data(RealSense.depth);\n//        ShortBuffer bb = data.position(0).limit(640 * 480 * 2).asByteBuffer().asShortBuffer();\n\n        int iplDepth = IPL_DEPTH_16U, channels = 1;\n        int deviceWidth = device.get_stream_width(RealSense.depth);\n        int deviceHeight = device.get_stream_height(RealSense.depth);\n\n        // AUTOMATIC\n//        int deviceWidth = 0;\n//        int deviceHeight = 0;\n        if (rawDepthImage == null || rawDepthImage.width() != deviceWidth || rawDepthImage.height() != deviceHeight) {\n            rawDepthImage = IplImage.createHeader(deviceWidth, deviceHeight, iplDepth, channels);\n        }\n\n        cvSetData(rawDepthImage, rawDepthImageData, deviceWidth * channels * iplDepth / 8);\n\n//        if (iplDepth > 8 && !ByteOrder.nativeOrder().equals(byteOrder)) {\n//            // ack, the camera's endianness doesn't correspond to our machine ...\n//            // swap bytes of 16-bit images\n//            ByteBuffer bb = rawDepthImage.getByteBuffer();\n//            ShortBuffer in = bb.order(ByteOrder.BIG_ENDIAN).asShortBuffer();\n//            ShortBuffer out = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();\n//            out.put(in);\n//        }\n        return rawDepthImage;\n    }\n\n    public IplImage grabVideo() {\n\n        if (!colorEnabled) {\n            System.out.println(\"Color stream not enabled, impossible to get the image.\");\n            return null;\n        }\n\n        int iplDepth = IPL_DEPTH_8U, channels = 3;\n\n        rawVideoImageData = device.get_frame_data(RealSense.color);\n        int deviceWidth = device.get_stream_width(RealSense.color);\n        int deviceHeight = device.get_stream_height(RealSense.color);\n\n        if (rawVideoImage == null || rawVideoImage.width() != deviceWidth || rawVideoImage.height() != deviceHeight) {\n            rawVideoImage = IplImage.createHeader(deviceWidth, deviceHeight, iplDepth, channels);\n        }\n\n        cvSetData(rawVideoImage, rawVideoImageData, deviceWidth * channels * iplDepth / 8);\n\n//        if (iplDepth > 8 && !ByteOrder.nativeOrder().equals(byteOrder)) {\n//            // ack, the camera's endianness doesn't correspond to our machine ...\n//            // swap bytes of 16-bit images\n//            ByteBuffer bb = rawVideoImage.getByteBuffer();\n//            ShortBuffer in = bb.order(ByteOrder.BIG_ENDIAN).asShortBuffer();\n//            ShortBuffer out = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();\n//            out.put(in);\n//        }\n//        if (channels == 3) {\n//            cvCvtColor(rawVideoImage, rawVideoImage, CV_BGR2RGB);\n//        }\n        return rawVideoImage;\n    }\n\n    public IplImage grabIR() {\n\n        if (!IREnabled) {\n            System.out.println(\"IR stream not enabled, impossible to get the image.\");\n            return null;\n        }\n\n        int iplDepth = IPL_DEPTH_8U, channels = 1;\n\n        rawIRImageData = device.get_frame_data(RealSense.infrared);\n\n        int deviceWidth = device.get_stream_width(RealSense.infrared);\n        int deviceHeight = device.get_stream_height(RealSense.infrared);\n\n        if (rawIRImage == null || rawIRImage.width() != deviceWidth || rawIRImage.height() != deviceHeight) {\n            rawIRImage = IplImage.createHeader(deviceWidth, deviceHeight, iplDepth, channels);\n        }\n        cvSetData(rawIRImage, rawIRImageData, deviceWidth * channels * iplDepth / 8);\n\n//        if (iplDepth > 8 && !ByteOrder.nativeOrder().equals(byteOrder)) {\n//            // ack, the camera's endianness doesn't correspond to our machine ...\n//            // swap bytes of 16-bit images\n//            ByteBuffer bb = rawIRImage.getByteBuffer();\n//            ShortBuffer in = bb.order(ByteOrder.BIG_ENDIAN).asShortBuffer();\n//            ShortBuffer out = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();\n//            out.put(in);\n//        }\n        return rawIRImage;\n    }\n\n    /**\n     *\n     * @return null grabs all images, get them with grabColor, grabDepth, and\n     * grabIR instead.\n     * @throws org.bytedeco.javacv.FrameGrabber.Exception\n     */\n    public Frame grab() throws Exception {\n        device.wait_for_frames();\n\n//        frameNumber++; \n        // For Framegrabber\n        if (colorEnabled && behaveAsColorFrameGrabber) {\n            IplImage image = grabVideo();\n\n            if (returnImage == null) {\n                int deviceWidth = device.get_stream_width(RealSense.color);\n                int deviceHeight = device.get_stream_height(RealSense.color);\n//                returnImage = IplImage.create(deviceWidth, deviceHeight, IPL_DEPTH_8U, 3);\n                returnImage = IplImage.create(deviceWidth, deviceHeight, IPL_DEPTH_8U, 1);\n            }\n            cvCvtColor(image, returnImage, CV_BGR2GRAY);\n            return converter.convert(returnImage);\n        } else {\n            if (IREnabled) {\n                return converter.convert(grabIR());\n            } else {\n                if (depthEnabled) {\n                    \n                    // Fake colors\n                    IplImage image = grabDepth();\n                    if (returnImage == null) {\n                        int deviceWidth = device.get_stream_width(RealSense.depth);\n                        int deviceHeight = device.get_stream_height(RealSense.depth);\n//                returnImage = IplImage.create(deviceWidth, deviceHeight, IPL_DEPTH_8U, 3);\n                        returnImage = IplImage.create(deviceWidth, deviceHeight, IPL_DEPTH_8U, 1);\n                    } \n                    return converter.convert(returnImage);\n                }\n            }\n        }\n\n        return null;\n    }\n\n    @Override\n    public void trigger() throws Exception {\n        device.wait_for_frames();\n    }\n\n    public int getDepthImageWidth() {\n        return depthImageWidth;\n    }\n\n    public void setDepthImageWidth(int depthImageWidth) {\n        this.depthImageWidth = depthImageWidth;\n    }\n\n    public int getDepthImageHeight() {\n        return depthImageHeight;\n    }\n\n    public void setDepthImageHeight(int depthImageHeight) {\n        this.depthImageHeight = depthImageHeight;\n    }\n\n    public int getIRImageWidth() {\n        return IRImageWidth;\n    }\n\n    public void setIRImageWidth(int IRImageWidth) {\n        this.IRImageWidth = IRImageWidth;\n    }\n\n    public int getIRImageHeight() {\n        return IRImageHeight;\n    }\n\n    public void setIRImageHeight(int IRImageHeight) {\n        this.IRImageHeight = IRImageHeight;\n    }\n\n    public int getDepthFrameRate() {\n        return depthFrameRate;\n    }\n\n    public void setDepthFrameRate(int frameRate) {\n        this.depthFrameRate = frameRate;\n    }\n\n    public int getIRFrameRate() {\n        return IRFrameRate;\n    }\n\n    public void setIRFrameRate(int IRFrameRate) {\n        this.IRFrameRate = IRFrameRate;\n    }\n\n    @Override\n    public double getGamma() {\n        // I guess a default gamma of 2.2 is reasonable...\n        if (gamma == 0.0) {\n            return 2.2;\n        } else {\n            return gamma;\n        }\n    }\n\n    // Gamma from the device is not good.\n//    @Override\n//    public double getGamma(){\n//        double gamma = device.get_option(RealSense.RS_OPTION_COLOR_GAMMA);\n//        System.out.println(\"Getting cameraGamma \" + gamma);\n//        return gamma;\n//    }\n// --- Presets --- \n    public void setPreset(int preset) {\n        /* Provide access to several recommend sets of option presets for ivcam */\n        RealSense.apply_ivcam_preset(device, preset);\n    }\n\n    public void setShortRange() {\n        setPreset(RealSense.RS_IVCAM_PRESET_SHORT_RANGE);\n    }\n\n    public void setLongRange() {\n        setPreset(RealSense.RS_IVCAM_PRESET_LONG_RANGE);\n    }\n\n    public void setMidRange() {\n        setPreset(RealSense.RS_IVCAM_PRESET_MID_RANGE);\n    }\n\n    public void setDefaultPreset() {\n        setPreset(RealSense.RS_IVCAM_PRESET_DEFAULT);\n    }\n\n    public void setObjectScanningPreset() {\n        setPreset(RealSense.RS_IVCAM_PRESET_OBJECT_SCANNING);\n    }\n\n    public void setCursorPreset() {\n        setPreset(RealSense.RS_IVCAM_PRESET_GR_CURSOR);\n    }\n\n    public void setGestureRecognitionPreset() {\n        setPreset(RealSense.RS_IVCAM_PRESET_GESTURE_RECOGNITION);\n    }\n\n    public void setBackgroundSegmentationPreset() {\n        setPreset(RealSense.RS_IVCAM_PRESET_BACKGROUND_SEGMENTATION);\n    }\n\n    public void setIROnlyPreset() {\n        setPreset(RealSense.RS_IVCAM_PRESET_IR_ONLY);\n    }\n\n    // ---- Options ---- \n    public void setOption(int option, int value) {\n        device.set_option(option, value);\n    }\n\n    /**\n     * Enable / disable color backlight compensation\n     */\n    public void set(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_BACKLIGHT_COMPENSATION, value);\n    }\n\n    /**\n     * Color image brightness\n     */\n    public void setColorBrightness(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_BRIGHTNESS, value);\n    }\n\n    /**\n     * COLOR image contrast\n     */\n    public void setColorContrast(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_CONTRAST, value);\n    }\n\n    /**\n     * Controls exposure time of color camera. Setting any value will disable\n     * auto exposure\n     */\n    public void setColorExposure(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_EXPOSURE, value);\n    }\n\n    /**\n     * Color image gain\n     */\n    public void setColorGain(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_GAIN, value);\n    }\n\n    /**\n     * Color image gamma setting\n     */\n    public void setColorGamma(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_GAMMA, value);\n    }\n\n    /**\n     * Color image hue\n     */\n    public void setColorHue(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_HUE, value);\n    }\n\n    /**\n     * Color image saturation setting\n     */\n    public void setColorSaturation(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_SATURATION, value);\n    }\n\n    /**\n     * Color image sharpness setting\n     */\n    public void setColorSharpness(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_SHARPNESS, value);\n    }\n\n    /**\n     * Controls white balance of color image. Setting any value will disable\n     * auto white balance\n     */\n    public void setColorWhiteBalance(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_WHITE_BALANCE, value);\n    }\n\n    /**\n     * Enable / disable color image auto-exposure\n     */\n    public void setColorEnableAutoExposure(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_ENABLE_AUTO_EXPOSURE, value);\n    }\n\n    /**\n     * Enable / disable color image auto-white-balance\n     */\n    public void setColorEnableAutoWhiteBalance(int value) {\n        setOption(RealSense.RS_OPTION_COLOR_ENABLE_AUTO_WHITE_BALANCE, value);\n    }\n\n    /**\n     * Power of the F200 / SR300 projector, with 0 meaning projector off\n     */\n    public void setLaserPower(int value) {\n        setOption(RealSense.RS_OPTION_F200_LASER_POWER, value);\n    }\n\n    /**\n     * Set the number of patterns projected per frame. The higher the accuracy\n     * value the more patterns projected. Increasing the number of patterns help\n     * to achieve better accuracy. Note that this control is affecting the Depth\n     * FPS\n     */\n    public void setAccuracy(int value) {\n        setOption(RealSense.RS_OPTION_F200_ACCURACY, value);\n    }\n\n    /**\n     * Motion vs. Range trade-off, with lower values allowing for better motion\n     * sensitivity and higher values allowing for better depth range\n     */\n    public void setMotionRange(int value) {\n        setOption(RealSense.RS_OPTION_F200_MOTION_RANGE, value);\n    }\n\n    /**\n     * Set the filter to apply to each depth frame. Each one of the filter is\n     * optimized per the application requirements\n     */\n    public void setFilterOption(int value) {\n        setOption(RealSense.RS_OPTION_F200_FILTER_OPTION, value);\n    }\n\n    /**\n     * The confidence level threshold used by the Depth algorithm pipe to set\n     * whether a pixel will get a valid range or will be marked with invalid\n     * range\n     */\n    public void setConfidenceThreshold(int value) {\n        setOption(RealSense.RS_OPTION_F200_CONFIDENCE_THRESHOLD, value);\n    }\n\n    /**\n     * (F200-only) Allows to reduce FPS without restarting streaming. Valid\n     * values are {2, 5, 15, 30, 60}\n     */\n    public void setDynamicFPS(int value) {\n        setOption(RealSense.RS_OPTION_F200_DYNAMIC_FPS, value);\n    }\n\n    /**\n     * Enables / disables R200 auto-exposure. This will affect both IR and depth\n     * image.\n     */\n    public void setLR_AutoExposureEnabled(int value) {\n        setOption(RealSense.RS_OPTION_R200_LR_AUTO_EXPOSURE_ENABLED, value);\n    }\n\n    /**\n     * IR image gain\n     */\n    public void setLR_Gain(int value) {\n        setOption(RealSense.RS_OPTION_R200_LR_GAIN, value);\n    }\n\n    /**\n     * This control allows manual adjustment of the exposure time value for the\n     * L/R imagers\n     */\n    public void setLR_Exposure(int value) {\n        setOption(RealSense.RS_OPTION_R200_LR_EXPOSURE, value);\n    }\n\n    /**\n     * Enables / disables R200 emitter\n     */\n    public void setEmitterEnabled(int value) {\n        setOption(RealSense.RS_OPTION_R200_EMITTER_ENABLED, value);\n    }\n\n    /**\n     * Micrometers per increment in integer depth values, 1000 is default (mm\n     * scale). Set before streaming\n     */\n    public void setDepthUnits(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_UNITS, value);\n    }\n\n    /**\n     * Minimum depth in current depth units that will be output. Any values less\n     * than �Min Depth� will be mapped to 0 during the conversion between\n     * disparity and depth. Set before streaming\n     */\n    public void setDepthClampMin(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CLAMP_MIN, value);\n    }\n\n    /**\n     * Maximum depth in current depth units that will be output. Any values\n     * greater than �Max Depth� will be mapped to 0 during the conversion\n     * between disparity and depth. Set before streaming\n     */\n    public void setDepthClampMax(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CLAMP_MAX, value);\n    }\n\n    /**\n     * The disparity scale factor used when in disparity output mode. Can only\n     * be set before streaming\n     */\n    public void setDisparityMultiplier(int value) {\n        setOption(RealSense.RS_OPTION_R200_DISPARITY_MULTIPLIER, value);\n    }\n\n    /**\n     * {0 - 512}. Can only be set before streaming starts\n     */\n    public void setDisparityShift(int value) {\n        setOption(RealSense.RS_OPTION_R200_DISPARITY_SHIFT, value);\n    }\n\n    /**\n     * (Requires LR-Auto-Exposure ON) Mean intensity set point\n     */\n    public void setAutoExposureMeanIntensitySetPoint(int value) {\n        setOption(RealSense.RS_OPTION_R200_AUTO_EXPOSURE_MEAN_INTENSITY_SET_POINT, value);\n    }\n\n    /**\n     * (Requires LR-Auto-Exposure ON) Bright ratio set point\n     */\n    public void setAutoExposureBrightRatioSetPoint(int value) {\n        setOption(RealSense.RS_OPTION_R200_AUTO_EXPOSURE_BRIGHT_RATIO_SET_POINT, value);\n    }\n\n    /**\n     * (Requires LR-Auto-Exposure ON) Kp Gain\n     */\n    public void setAutoExposureKpGain(int value) {\n        setOption(RealSense.RS_OPTION_R200_AUTO_EXPOSURE_KP_GAIN, value);\n    }\n\n    /**\n     * (Requires LR-Auto-Exposure ON) Kp Exposure\n     */\n    public void setAutoExposureKpExposure(int value) {\n        setOption(RealSense.RS_OPTION_R200_AUTO_EXPOSURE_KP_EXPOSURE, value);\n    }\n\n    /**\n     * (Requires LR-Auto-Exposure ON) Kp Dark Threshold\n     */\n    public void setAutoExposureKpDarkThreshold(int value) {\n        setOption(RealSense.RS_OPTION_R200_AUTO_EXPOSURE_KP_DARK_THRESHOLD, value);\n    }\n\n    /**\n     * (Requires LR-Auto-Exposure ON) Auto-Exposure region-of-interest top edge\n     * (in pixels)\n     */\n    public void setAutoExposureTopEdge(int value) {\n        setOption(RealSense.RS_OPTION_R200_AUTO_EXPOSURE_TOP_EDGE, value);\n    }\n\n    /**\n     * (Requires LR-Auto-Exposure ON) Auto-Exposure region-of-interest bottom\n     * edge (in pixels)\n     */\n    public void setAutoExposureBottomEdge(int value) {\n        setOption(RealSense.RS_OPTION_R200_AUTO_EXPOSURE_BOTTOM_EDGE, value);\n    }\n\n    /**\n     * (Requires LR-Auto-Exposure ON) Auto-Exposure region-of-interest left edge\n     * (in pixels)\n     */\n    public void setAutoExposureLeftEdge(int value) {\n        setOption(RealSense.RS_OPTION_R200_AUTO_EXPOSURE_LEFT_EDGE, value);\n    }\n\n    /**\n     * (Requires LR-Auto-Exposure ON) Auto-Exposure region-of-interest right\n     * edge (in pixels)\n     */\n    public void setAutoExposureRightEdge(int value) {\n        setOption(RealSense.RS_OPTION_R200_AUTO_EXPOSURE_RIGHT_EDGE, value);\n    }\n\n    /**\n     * Value to subtract when estimating the median of the correlation surface\n     */\n    public void setDepthControlEstimateMedianDecrement(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_ESTIMATE_MEDIAN_DECREMENT, value);\n    }\n\n    /**\n     * Value to add when estimating the median of the correlation surface\n     */\n    public void setDepthControlEstimateMedianIncrement(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_ESTIMATE_MEDIAN_INCREMENT, value);\n    }\n\n    /**\n     * A threshold by how much the winning score must beat the median\n     */\n    public void setDepthControlMedianThreshold(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_MEDIAN_THRESHOLD, value);\n    }\n\n    /**\n     * The minimum correlation score that is considered acceptable\n     */\n    public void setDepthControlMinimumThreshold(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_SCORE_MINIMUM_THRESHOLD, value);\n    }\n\n    /**\n     * The maximum correlation score that is considered acceptable\n     */\n    public void setDepthControlScoreMaximumThreshold(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_SCORE_MAXIMUM_THRESHOLD, value);\n    }\n\n    /**\n     * A parameter for determining whether the texture in the region is\n     * sufficient to justify a depth result\n     */\n    public void setDepthControlTextureCountThreshold(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_TEXTURE_COUNT_THRESHOLD, value);\n    }\n\n    /**\n     * A parameter for determining whether the texture in the region is\n     * sufficient to justify a depth result\n     */\n    public void setDepthControlTextureDifference(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_TEXTURE_DIFFERENCE_THRESHOLD, value);\n    }\n\n    /**\n     * A threshold on how much the minimum correlation score must differ from\n     * the next best score\n     */\n    public void setDepthControlSecondPeakThreshold(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_SECOND_PEAK_THRESHOLD, value);\n    }\n\n    /**\n     * Neighbor threshold value for depth calculation\n     */\n    public void setDepthControlNeighborThreshold(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_NEIGHBOR_THRESHOLD, value);\n    }\n\n    /**\n     * Left-Right threshold value for depth calculation\n     */\n    public void setDepthControlLRThreshold(int value) {\n        setOption(RealSense.RS_OPTION_R200_DEPTH_CONTROL_LR_THRESHOLD, value);\n    }\n\n    /**\n     * Fisheye image exposure time in msec\n     */\n    public void setFisheyeExposure(int value) {\n        setOption(RealSense.RS_OPTION_FISHEYE_EXPOSURE, value);\n    }\n\n    /**\n     * Fisheye image gain\n     */\n    public void setFisheyeGain(int value) {\n        setOption(RealSense.RS_OPTION_FISHEYE_GAIN, value);\n    }\n\n    /**\n     * Enables / disables fisheye strobe. When enabled this will align\n     * timestamps to common clock-domain with the motion events\n     */\n    public void setFisheyeStobe(int value) {\n        setOption(RealSense.RS_OPTION_FISHEYE_STROBE, value);\n    }\n\n    /**\n     * Enables / disables fisheye external trigger mode. When enabled fisheye\n     * image will be aquired in-sync with the depth image\n     */\n    public void setFisheyeExternalTrigger(int value) {\n        setOption(RealSense.RS_OPTION_FISHEYE_EXTERNAL_TRIGGER, value);\n    }\n\n    /**\n     * Enable / disable fisheye auto-exposure\n     */\n    public void setFisheyeEnableAutoExposure(int value) {\n        setOption(RealSense.RS_OPTION_FISHEYE_ENABLE_AUTO_EXPOSURE, value);\n    }\n\n    /**\n     * 0 - static auto-exposure, 1 - anti-flicker auto-exposure, 2 - hybrid\n     */\n    public void setFisheyeAutoExposureMode(int value) {\n        setOption(RealSense.RS_OPTION_FISHEYE_AUTO_EXPOSURE_MODE, value);\n    }\n\n    /**\n     * Fisheye auto-exposure anti-flicker rate, can be 50 or 60 Hz\n     */\n    public void setFisheyeAutoExposureAntiflickerRate(int value) {\n        setOption(RealSense.RS_OPTION_FISHEYE_AUTO_EXPOSURE_ANTIFLICKER_RATE, value);\n    }\n\n    /**\n     * In Fisheye auto-exposure sample frame every given number of pixels\n     */\n    public void setFisheyeAutoExposurePixelSampleRate(int value) {\n        setOption(RealSense.RS_OPTION_FISHEYE_AUTO_EXPOSURE_PIXEL_SAMPLE_RATE, value);\n    }\n\n    /**\n     * In Fisheye auto-exposure sample every given number of frames\n     */\n    public void setFisheyeAutoExposureSkipFrames(int value) {\n        setOption(RealSense.RS_OPTION_FISHEYE_AUTO_EXPOSURE_SKIP_FRAMES, value);\n    }\n\n    /**\n     * Number of frames the user is allowed to keep per stream. Trying to\n     * hold-on to more frames will cause frame-drops.\n     */\n    public void setFramesQueueSize(int value) {\n        setOption(RealSense.RS_OPTION_FRAMES_QUEUE_SIZE, value);\n    }\n\n    /**\n     * Enable / disable fetching log data from the device\n     */\n    public void setHardwareLoggerEnabled(int value) {\n        setOption(RealSense.RS_OPTION_HARDWARE_LOGGER_ENABLED, value);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/ReflectanceInitializer.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.ByteBuffer;\nimport java.nio.FloatBuffer;\nimport java.util.logging.Logger;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class ReflectanceInitializer {\n\n    public ReflectanceInitializer(CameraDevice cameraDevice, ProjectorDevice projectorDevice,\n            int channels, GNImageAligner.Settings alignerSettings) {\n        this(cameraDevice, projectorDevice, channels, alignerSettings, 51, 0.01);\n    }\n    public ReflectanceInitializer(CameraDevice cameraDevice, ProjectorDevice projectorDevice,\n            int channels, GNImageAligner.Settings alignerSettings, int smoothingSize, double reflectanceMin) {\n        this.alignerSettings = alignerSettings;\n        this.smoothingSize   = smoothingSize;\n        this.reflectanceMin  = reflectanceMin;\n        this.cameraDevice    = cameraDevice;\n        this.projectorDevice = projectorDevice;\n        this.projectorImages = new IplImage[3];\n\n        for (int i = 0; i < projectorImages.length; i++) {\n            projectorImages[i] = IplImage.create(projectorDevice.imageWidth,\n                    projectorDevice.imageHeight, IPL_DEPTH_32F, channels);\n        }\n\n        // capture \"black\" image (illuminated by ambient light only)\n        cvSetZero(projectorImages[0]);\n        // capture \"white\" image (projector illumination + ambient light)\n        cvSet(projectorImages[1], CvScalar.ONE);\n        // capture image with some texture easy to register... ugh...\n        CvMat H = mat3x3.get();\n        projectorDevice.getRectifyingHomography(cameraDevice, H);\n        JavaCV.fractalTriangleWave(projectorImages[2], H);\n    }\n\n    private static ThreadLocal<CvMat>\n            mat3x1 = CvMat.createThreadLocal(3, 1),\n            mat3x3 = CvMat.createThreadLocal(3, 3),\n            mat4x4 = CvMat.createThreadLocal(4, 4);\n\n    private GNImageAligner.Settings alignerSettings;\n    private int smoothingSize;\n    private double reflectanceMin;\n    private CameraDevice cameraDevice;\n    private ProjectorDevice projectorDevice;\n    private IplImage[] projectorImages;\n\n    public IplImage[] getProjectorImages() {\n        return projectorImages;\n    }\n\n    public IplImage initializeReflectance(IplImage[] cameraImages, IplImage reflectance,\n            double[] roiPts, double[] gainAmbientLight) {\n        int w        = cameraImages[0].width();\n        int h        = cameraImages[0].height();\n        int channels = cameraImages[0].nChannels();\n\n        IplImage mask = IplImage.create(w, h, IPL_DEPTH_8U, 1);\n        cvSetZero(mask);\n        cvFillConvexPoly(mask, new CvPoint(roiPts.length/2).put((byte)(16-cameraDevice.getMapsPyramidLevel()), roiPts),\n                4, CvScalar.WHITE, 8, 16);\n\n        // make the images very very smooth to compensate for small movements\n        IplImage float1 = cameraImages[0];\n        IplImage float2 = cameraImages[1];\n        cvCopy(float2, reflectance);\n        cvSmooth(float1, float1, CV_GAUSSIAN, smoothingSize, 0, 0, 0);\n        cvSmooth(float2, float2, CV_GAUSSIAN, smoothingSize, 0, 0, 0);\n\n        // remove ambient light of image1 from image2 -> image2\n        cvSub(float2, float1, float2, null);\n\n        // remove distortion caused by the mixing matrix of the projector light\n        // and recover (very very smooth) reflectance map\n        CvMat p = mat3x1.get();\n        p.put(1.0, 1.0, 1.0); // white\n        cvMatMul(projectorDevice.colorMixingMatrix, p, p);\n        CvMat invp;\n        if (float2.nChannels() == 4) {\n            invp = mat4x4.get();\n            invp.put(1/p.get(0), 0, 0, 0,\n                     0, 1/p.get(1), 0, 0,\n                     0, 0, 1/p.get(2), 0,\n                     0, 0, 0, 1);\n        } else {\n            invp = mat3x3.get();\n            invp.put(1/p.get(0), 0, 0,\n                     0, 1/p.get(1), 0,\n                     0, 0, 1/p.get(2));\n        }\n        cvTransform(float2, float2, invp, null);\n\n        // recover (very very smooth) ambient light by removing distortions\n        // caused by the reflectance map\n        // cvDiv(image1, image3, image1, 1);\n        // cvDiv() doesn't support division by zero...\n        FloatBuffer fb1 = float1.getFloatBuffer();\n        FloatBuffer fb2 = float2.getFloatBuffer();\n        ByteBuffer  mb  = mask.getByteBuffer();\n        assert fb1.capacity() == fb2.capacity()/3;\n        assert fb1.capacity() == mb.capacity()/3;\n        int[] nPixels = new int[channels];\n        for (int i = 0, j = 0; j < fb1.capacity(); i++, j+=channels) {\n            for (int z = 0; z < channels; z++) {\n                float ra = fb1.get(j+z);\n                float r  = fb2.get(j+z);\n                float a  = r == 0 ? 0 : ra/r;\n                fb1.put(j+z, a);\n                if (mb.get(i) != 0) {\n                    if (r > reflectanceMin) {\n                        nPixels[z]++;\n                        gainAmbientLight[z+1] += a;\n                    }\n                }\n            }\n        }\n        gainAmbientLight[0] = 1.0; // assume projector gain = 1.0\n        for (int z = 0; z < gainAmbientLight.length-1; z++) {\n            gainAmbientLight[z+1] = nPixels[z] == 0 ? 0 : gainAmbientLight[z+1]/nPixels[z];\n        }\n//        System.out.println(ambientLight[0] + \" \" + ambientLight[1] + \" \" + ambientLight[2]);\n\n        // recover sharp reflectance map by using the original image2 and smooth ambient light\n        cvAddS(float1, cvScalar(p.get(0), p.get(1), p.get(2), 0.0), float1, null);\n        cvDiv(reflectance, float1, reflectance, 1.0);\n\n        cvNot(mask, mask);\n        // increase region a bit so that the resulting image can be\n        // interpolated or averaged properly within the region of interest...\n        cvErode(mask, mask, null, 15);\n        cvSet(reflectance, CvScalar.ZERO, mask);\n\n        return reflectance;\n    }\n\n    public CvMat initializePlaneParameters(IplImage reflectance, IplImage cameraImage,\n            double[] referencePoints, double[] roiPts, double[] gainAmbientLight) {\n        ProCamTransformer transformer = new ProCamTransformer(referencePoints, cameraDevice, projectorDevice, null);\n        transformer.setProjectorImage(projectorImages[2], 0, alignerSettings.pyramidLevelMax);\n\n        ProCamTransformer.Parameters parameters = transformer.createParameters();\n//        parameters.set(8,  0);\n//        parameters.set(9,  0);\n//        parameters.set(10, -1/cameraDevice.getSettings().nominalDistance);\n        final int gainAmbientLightStart = parameters.size() - gainAmbientLight.length;\n        final int gainAmbientLightEnd   = parameters.size();\n        for (int i = gainAmbientLightStart; i < gainAmbientLightEnd; i++) {\n            parameters.set(i, gainAmbientLight[i-gainAmbientLightStart]);\n        }\n        ImageAligner aligner = new GNImageAligner(transformer, parameters,\n                reflectance, roiPts, cameraImage, alignerSettings);\n\n        double[] delta = new double[parameters.size()+1];\n        boolean converged = false;\n        long iterationsStartTime = System.currentTimeMillis();\n        int iterations = 0;\n        while (!converged && iterations < 100) {\n            converged = aligner.iterate(delta);\n            iterations++;\n        }\n        parameters = (ProCamTransformer.Parameters)aligner.getParameters();\n//        for (int i = 0; i < gainAmbientLight.length; i++) {\n//            gainAmbientLight[i] = parameters.get(11+i);\n//        }\n        Logger.getLogger(ReflectanceInitializer.class.getName()).info(\n            \"iteratingTime = \" + (System.currentTimeMillis()-iterationsStartTime) +\n                \"  iterations = \" + iterations + \"  objectiveRMSE = \" + (float)aligner.getRMSE());\n        return parameters.getN0();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/Seekable.java",
    "content": "/*\n * Copyright (C) 2019 Sven Vorlauf\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\npublic interface Seekable {\n\n    public void seek(long offset, int whence);\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/SeekableByteArrayOutputStream.java",
    "content": "/*\n * Copyright (C) 2019 Sven Vorlauf\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.ByteArrayOutputStream;\n\npublic class SeekableByteArrayOutputStream extends ByteArrayOutputStream implements Seekable {\n\n    long position;\n\n    @Override public void seek(long position, int whence) {\n        if (position < 0 || position > count || whence != 0)\n            throw new IllegalArgumentException();\n        this.position = position;\n    }\n\n    @Override public synchronized void write(int b) {\n        if (position < count) {\n            buf[(int) position] = (byte) b; // position < count <= MAX_INT\n        } else {\n            super.write(b);\n        }\n        position++;\n    }\n\n    @Override public synchronized void write(byte[] b, int off, int len) {\n        if (position < count) {\n            for (int i = 0 ; i < len ; i++) {\n                write(b[off + i]); // should be changed for bigegr arrays\n            }\n        } else {\n            super.write(b, off, len);\n            position = count;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/VideoInputFrameGrabber.java",
    "content": "/*\n * Copyright (C) 2011-2013 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.io.File;\nimport org.bytedeco.javacpp.BytePointer;\nimport org.bytedeco.javacpp.Loader;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport org.bytedeco.opencv.opencv_imgproc.*;\nimport org.bytedeco.videoinput.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_imgproc.*;\nimport static org.bytedeco.videoinput.global.videoInputLib.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class VideoInputFrameGrabber extends FrameGrabber {\n    public static String[] getDeviceDescriptions() throws Exception {\n        tryLoad();\n\n        int count = videoInput.listDevices();\n        String[] descriptions = new String[count];\n        for (int i = 0; i < descriptions.length; i++) {\n            descriptions[i] = videoInput.getDeviceName(i).getString();\n        }\n        return descriptions;\n    }\n\n    public static VideoInputFrameGrabber createDefault(File deviceFile)   throws Exception { throw new Exception(VideoInputFrameGrabber.class + \" does not support device files.\"); }\n    public static VideoInputFrameGrabber createDefault(String devicePath) throws Exception { throw new Exception(VideoInputFrameGrabber.class + \" does not support device paths.\"); }\n    public static VideoInputFrameGrabber createDefault(int deviceNumber)  throws Exception { return new VideoInputFrameGrabber(deviceNumber); }\n\n    private static Exception loadingException = null;\n    public static void tryLoad() throws Exception {\n        if (loadingException != null) {\n            throw loadingException;\n        } else {\n            try {\n                Loader.load(org.bytedeco.videoinput.global.videoInputLib.class);\n            } catch (Throwable t) {\n                throw loadingException = new Exception(\"Failed to load \" + VideoInputFrameGrabber.class, t);\n            }\n        }\n    }\n\n    public VideoInputFrameGrabber(int deviceNumber) {\n        this.deviceNumber = deviceNumber;\n    }\n    public void release() throws Exception {\n        stop();\n    }\n    @Override protected void finalize() throws Throwable {\n        super.finalize();\n        release();\n    }\n\n    private int deviceNumber = 0;\n    private videoInput myVideoInput = null;\n    private IplImage bgrImage = null, grayImage = null;\n    private BytePointer bgrImageData = null;\n    private FrameConverter converter = new OpenCVFrameConverter.ToIplImage();\n\n    @Override public double getGamma() {\n        // default to a gamma of 2.2 for cheap Webcams, DV cameras, etc.\n        if (gamma == 0.0) {\n            return 2.2;\n        } else {\n            return gamma;\n        }\n    }\n\n    @Override public int getImageWidth() {\n        return myVideoInput == null ? super.getImageWidth() : myVideoInput.getWidth(deviceNumber);\n    }\n\n    @Override public int getImageHeight() {\n        return myVideoInput == null ? super.getImageHeight() : myVideoInput.getHeight(deviceNumber);\n    }\n\n    public void start() throws Exception {\n        start(-1);\n    }\n    public void start(int connection) throws Exception {\n        myVideoInput = new videoInput();\n        if (frameRate > 0) {\n            myVideoInput.setIdealFramerate(deviceNumber, (int)frameRate);\n        }\n        if (!myVideoInput.setupDevice(deviceNumber, imageWidth  > 0 ? imageWidth  : 640,\n                                                    imageHeight > 0 ? imageHeight : 480, connection)) {\n            myVideoInput = null;\n            throw new Exception(\"videoInput.setupDevice() Error: Could not setup device.\");\n        }\n        if (format != null && format.length() > 0) {\n            int f = format.equals(\"VI_NTSC_M\")   ? VI_NTSC_M   :\n                    format.equals(\"VI_PAL_B\")    ? VI_PAL_B    :\n                    format.equals(\"VI_PAL_D\")    ? VI_PAL_D    :\n                    format.equals(\"VI_PAL_G\")    ? VI_PAL_G    :\n                    format.equals(\"VI_PAL_H\")    ? VI_PAL_H    :\n                    format.equals(\"VI_PAL_I\")    ? VI_PAL_I    :\n                    format.equals(\"VI_PAL_M\")    ? VI_PAL_M    :\n                    format.equals(\"VI_PAL_N\")    ? VI_PAL_N    :\n                    format.equals(\"VI_PAL_NC\")   ? VI_PAL_NC   :\n                    format.equals(\"VI_SECAM_B\")  ? VI_SECAM_B  :\n                    format.equals(\"VI_SECAM_D\")  ? VI_SECAM_D  :\n                    format.equals(\"VI_SECAM_G\")  ? VI_SECAM_G  :\n                    format.equals(\"VI_SECAM_H\")  ? VI_SECAM_H  :\n                    format.equals(\"VI_SECAM_K\")  ? VI_SECAM_K  :\n                    format.equals(\"VI_SECAM_K1\") ? VI_SECAM_K1 :\n                    format.equals(\"VI_SECAM_L\")  ? VI_SECAM_L  :\n                    format.equals(\"VI_NTSC_M_J\") ? VI_NTSC_M_J :\n                    format.equals(\"VI_NTSC_433\") ? VI_NTSC_433 : -1;\n            if (f >= 0 && !myVideoInput.setFormat(deviceNumber, f)) {\n                throw new Exception(\"videoInput.setFormat() Error: Could not set format \" + format + \".\");\n            }\n        }\n    }\n\n    public void stop() throws Exception {\n        if (myVideoInput != null) {\n            myVideoInput.stopDevice(deviceNumber);\n            myVideoInput = null;\n        }\n    }\n\n    public void trigger() throws Exception {\n        if (myVideoInput == null) {\n            throw new Exception(\"videoInput is null. (Has start() been called?)\");\n        }\n        int w = myVideoInput.getWidth(deviceNumber), h = myVideoInput.getHeight(deviceNumber);\n        if (bgrImage == null || bgrImage.width() != w || bgrImage.height() != h) {\n            bgrImage = IplImage.create(w, h, IPL_DEPTH_8U, 3);\n            bgrImageData = bgrImage.imageData();\n        }\n\n        for (int i = 0; i < numBuffers+1; i++) {\n            myVideoInput.getPixels(deviceNumber, bgrImageData, false, true);\n        }\n    }\n\n    public Frame grab() throws Exception {\n        if (myVideoInput == null) {\n            throw new Exception(\"videoInput is null. (Has start() been called?)\");\n        }\n        int w = myVideoInput.getWidth(deviceNumber), h = myVideoInput.getHeight(deviceNumber);\n        if (bgrImage == null || bgrImage.width() != w || bgrImage.height() != h) {\n            bgrImage = IplImage.create(w, h, IPL_DEPTH_8U, 3);\n            bgrImageData = bgrImage.imageData();\n        }\n\n        if (!myVideoInput.getPixels(deviceNumber, bgrImageData, false, true)) {\n            throw new Exception(\"videoInput.getPixels() Error: Could not get pixels.\");\n        }\n        timestamp = System.nanoTime()/1000;\n\n        if (imageMode == ImageMode.GRAY) {\n            if (grayImage == null) {\n                grayImage = IplImage.create(w, h, IPL_DEPTH_8U, 1);\n            }\n            cvCvtColor(bgrImage, grayImage, CV_BGR2GRAY);\n            return converter.convert(grayImage);\n        } else {\n            return converter.convert(bgrImage);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/bytedeco/javacv/cvkernels.java",
    "content": "/*\n * Copyright (C) 2009-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.bytedeco.javacv;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\n\nimport org.bytedeco.opencv.opencv_core.*;\nimport static org.bytedeco.opencv.global.opencv_core.*;\n\n/**\n *\n * @author Samuel Audet\n */\npublic class cvkernels extends org.bytedeco.opencv.cvkernels {\n\n    private static class ParallelData {\n        KernelData data = null;\n        CvRect roi = new CvRect();\n    }\n    private static ThreadLocal<ParallelData[]> parallelData = new ThreadLocal<ParallelData[]>() {\n        @Override protected ParallelData[] initialValue() {\n            ParallelData[] pd = new ParallelData[Parallel.getNumThreads()];\n            for (int i = 0; i < pd.length; i++) {\n                pd[i] = new ParallelData();\n            }\n            return pd;\n        }\n    };\n\n    public static void multiWarpColorTransform(final KernelData data, final CvRect roi, final CvScalar fillColor) {\n        final int size = (int)data.capacity();\n        final ParallelData[] pd = parallelData.get();\n\n        // Copy all data to completely independent data sets\n        for (int i = 0; i < pd.length; i++) {\n            if (pd[i].data == null || pd[i].data.capacity() < size) {\n                pd[i].data = new KernelData(size);\n                for (int j = 0; j < size; j++) {\n                    KernelData d = pd[i].data.position(j);\n                    data.position(j);\n                    if (data.dstDstDot() != null) {\n                        d.dstDstDot(ByteBuffer.allocateDirect(data.dstDstDot().capacity()*8).\n                                order(ByteOrder.nativeOrder()).asDoubleBuffer());\n                    }\n                }\n            }\n            for (int j = 0; j < size; j++) {\n                KernelData d = pd[i].data.position(j);\n                d.put(data.position(j));\n                d.dstDstDot(d.dstDstDot()); // reset dstDstDot pointer\n            }\n        }\n\n        // Transform in parallel on multiple threads\n        final IplImage img = data.position(0).srcImg();\n        final int depth = img.depth();\n        final int x, y, w, h;\n        if (roi != null) {\n            x = roi.x();     y = roi.y();\n            w = roi.width(); h = roi.height();\n        } else {\n            x = 0;           y = 0;\n            w = img.width(); h = img.height();\n        }\n        Parallel.loop(y, y+h, pd.length, new Parallel.Looper() {\n        public void loop(int from, int to, int looperID) {\n            CvRect r = pd[looperID].roi.x(x).y(from).width(w).height(to-from);\n            if (depth == IPL_DEPTH_32F) {\n                multiWarpColorTransform32F(pd[looperID].data.position(0), size, r, fillColor);\n            } else if (depth == IPL_DEPTH_8U) {\n                multiWarpColorTransform8U(pd[looperID].data.position(0), size, r, fillColor);\n            } else {\n                assert false;\n            }\n        }});\n\n        // Reduce data as required\n        for (int i = 0; i < size; i++) {\n            int dstCount = 0;\n            int dstCountZero = 0;\n            int dstCountOutlier = 0;\n            double srcDstDot = 0;\n            double[] dstDstDot = null;\n            if (data.dstDstDot() != null) {\n                dstDstDot = new double[data.dstDstDot().capacity()];\n            }\n            for (int j = 0; j < pd.length; j++) {\n                KernelData d = pd[j].data.position(i);\n                dstCount        += d.dstCount();\n                dstCountZero    += d.dstCountZero();\n                dstCountOutlier += d.dstCountOutlier();\n                srcDstDot       += d.srcDstDot();\n                if (dstDstDot != null && d.dstDstDot() != null) {\n                    for (int k = 0; k < dstDstDot.length; k++) {\n                        dstDstDot[k] += d.dstDstDot().get(k);\n                    }\n                }\n            }\n            data.position(i);\n            data.dstCount(dstCount);\n            data.dstCountZero(dstCountZero);\n            data.dstCountOutlier(dstCountOutlier);\n            data.srcDstDot(srcDstDot);\n            if (dstDstDot != null && data.dstDstDot() != null) {\n                data.dstDstDot().position(0);\n                data.dstDstDot().put(dstDstDot);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java9/module-info.java",
    "content": "module org.bytedeco.javacv {\n    exports org.bytedeco.javacv;\n    requires java.desktop;\n    requires javafx.graphics;\n    requires transitive org.bytedeco.javacpp;\n    requires org.bytedeco.opencv;\n    requires org.bytedeco.ffmpeg;\n    requires org.bytedeco.flycapture;\n    requires org.bytedeco.libdc1394;\n    requires org.bytedeco.libfreenect;\n    requires org.bytedeco.libfreenect2;\n    requires org.bytedeco.librealsense;\n    requires org.bytedeco.librealsense2;\n    requires org.bytedeco.videoinput;\n    requires org.bytedeco.artoolkitplus;\n//    requires org.bytedeco.flandmark;\n    requires org.bytedeco.leptonica;\n    requires org.bytedeco.tesseract;\n}\n"
  },
  {
    "path": "src/main/resources/org/bytedeco/javacv/ImageTransformer.cl",
    "content": "/*\n * Copyright (C) 2011-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n                          CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;\n\n//const sampler_t linearSampler = CLK_NORMALIZED_COORDS_FALSE |\n//                                CLK_ADDRESS_CLAMP | CLK_FILTER_LINEAR;\n\ninline float4 readLinear(read_only image2d_t img, float2 xy) {\n    float2 xy00 = floor(xy);\n    float dx = xy.x - xy00.x;\n    float dy = xy.y - xy00.y;\n    float4 rgba  = (1-dx)*(1-dy)*read_imagef(img, sampler, xy00);\n           rgba +=    dx *(1-dy)*read_imagef(img, sampler, xy00 + (float2)(1, 0));\n           rgba += (1-dx)*   dy *read_imagef(img, sampler, xy00 + (float2)(0, 1));\n           rgba +=    dx *   dy *read_imagef(img, sampler, xy00 + (float2)(1, 1));\n    return rgba;\n}\n\n#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable\n\ninline void atomicAddFloat(global float* address, float val) {\n    global int* address_as_int = (global int*)address;\n    while (val != 0.0f) {\n        val += as_float(atom_xchg(address_as_int, as_int(0.0f)));\n        val  = as_float(atom_xchg(address_as_int, as_int(val)));\n    }\n}\n\n// Bit Twiddling Hacks\n// http://graphics.stanford.edu/~seander/bithacks.html\ninline int ceilPow2(int v) {\n    v--;\n    v |= v >> 1;\n    v |= v >> 2;\n    v |= v >> 4;\n    v |= v >> 8;\n    v |= v >> 16;\n    return ++v;\n}\n\ninline int reduceSumInt(float value, int i, int size, local void* scratch) {\n    local float *scratchi = (local float*)scratch;\n    scratchi[i] = value;\n    barrier(CLK_LOCAL_MEM_FENCE);\n    for (int offset = ceilPow2(size)/2; offset > 0; offset >>= 1) {\n        if (i < offset && i + offset < size) {\n            scratchi[i] += scratchi[i + offset];\n        }\n        barrier(CLK_LOCAL_MEM_FENCE);\n    }\n    return scratchi[0];\n}\n\ninline float reduceSumFloat(float value, int i, int size, local void* scratch) {\n    local float *scratchf = (local float*)scratch;\n    scratchf[i] = value;\n    barrier(CLK_LOCAL_MEM_FENCE);\n    for (int offset = ceilPow2(size)/2; offset > 0; offset >>= 1) {\n        if (i < offset && i + offset < size) {\n            scratchf[i] += scratchf[i + offset];\n        }\n        barrier(CLK_LOCAL_MEM_FENCE);\n    }\n    return scratchf[0];\n}\n\nstruct InputData {\n    int roiY, roiHeight;\n    float zeroThreshold, outlierThreshold;\n};\n\nstruct OutputData {\n    int groupsFinished, dstCount, dstCountZero, dstCountOutlier;\n    float srcDstDot[DOT_SIZE], dstDstDot[DOT_SIZE][DOT_SIZE];\n};\n\ninline void multiWarpColorTransform(read_only image2d_t srcImg, read_only image2d_t srcImg2, read_only image2d_t subImg,\n        read_only image2d_t dotImg, write_only image2d_t transImg, write_only image2d_t dstImg, read_only image2d_t maskImg,\n        int width, int height, constant float _H[][9], constant float _H2[][9], constant float/*4*/ _X[][16/*4*/], int size,\n        constant struct InputData *inputData, global struct OutputData *outputData,\n        bool haveSubImg, bool haveDotImg, bool haveTransImg, bool haveDstImg, bool haveMaskImg) {\n    const int x = get_global_id(0), gx = get_group_id(0), lx = get_local_id(0), lsx = get_local_size(0);\n    const int y = get_global_id(1), gy = get_group_id(1), ly = get_local_id(1), lsy = get_local_size(1);\n    const int z = get_global_id(2), gz = get_group_id(2), lz = get_local_id(2), lsz = get_local_size(2);\n\n    int dstCount = 0, dstCountZero = 0, dstCountOutlier = 0;\n    float srcDstDot = 0, dstDstDot = 0;\n    local float scratch[DOT_SIZE + 1][DOT_SIZE | 1][3];\n\n    local float H[DOT_SIZE][9], H2[DOT_SIZE][9], X[DOT_SIZE][12];\n    if (lx < size) {\n        for (int j = 0; j < 9; j++) {\n            H[lx][j] = _H[lx][j];\n            if (_H2) H2[lx][j] = _H2[lx][j];\n        }\n        for (int j = 0; j < 12; j++) {\n            if (_X) X[lx][j] = _X[lx][j];\n        }\n    }\n    barrier(CLK_LOCAL_MEM_FENCE);\n\n    for (int y = inputData->roiY; y < inputData->roiY + inputData->roiHeight; y++) {\n        const int2 xy = (int2)(x, y);\n        float4 dotRGB = 0, dstRGB = 0;\n\n        if (x >= width) {\n            goto skipPixel;\n        }\n\n        if (haveMaskImg) {\n            if (read_imagei(maskImg, sampler, xy).x == 0) {\n                goto skipPixel;\n            } else {\n                dstCount++;\n            }\n        }\n\n        if (haveDotImg) {\n            float zeroThreshold2    = inputData->zeroThreshold    * inputData->zeroThreshold;\n            float outlierThreshold2 = inputData->outlierThreshold * inputData->outlierThreshold;\n\n            dotRGB = read_imagef(dotImg, sampler, xy);\n            float norm2 = dot(dotRGB.xyz, dotRGB.xyz);\n            if (norm2 < zeroThreshold2) {\n                dstCountZero++;\n                goto skipPixel;\n            } else if (outlierThreshold2 > 0 && norm2 > outlierThreshold2) {\n                dstCountOutlier++;\n                goto skipPixel;\n            }\n        }\n\n        float u = H[lz][0]*x + H[lz][1]*y + H[lz][2];\n        float v = H[lz][3]*x + H[lz][4]*y + H[lz][5];\n        float w = H[lz][6]*x + H[lz][7]*y + H[lz][8];\n        float inv_w = native_recip(w);\n        float2 uv = inv_w*(float2)(u, v);// + 0.5f;\n//        float4 srcRGB = read_imagef(srcImg, linearSampler, uv);\n        float4 srcRGB = readLinear(srcImg, uv);\n        if (_X) {\n//            srcRGB.w = 1;\n//            dstRGB = (float4)(dot(X[lz][0], srcRGB), dot(X[lz][1], srcRGB),\n//                              dot(X[lz][2], srcRGB), dot(X[lz][3], srcRGB));\n            dstRGB.x = X[lz][0]*srcRGB.x + X[lz][1]*srcRGB.y + X[lz][2] *srcRGB.z + X[lz][3];\n            dstRGB.y = X[lz][4]*srcRGB.x + X[lz][5]*srcRGB.y + X[lz][6] *srcRGB.z + X[lz][7];\n            dstRGB.z = X[lz][8]*srcRGB.x + X[lz][9]*srcRGB.y + X[lz][10]*srcRGB.z + X[lz][11];\n        } else {\n            dstRGB = srcRGB;\n        }\n        if (_H2) {\n            float u2 = H2[lz][0]*x + H2[lz][1]*y + H2[lz][2];\n            float v2 = H2[lz][3]*x + H2[lz][4]*y + H2[lz][5];\n            float w2 = H2[lz][6]*x + H2[lz][7]*y + H2[lz][8];\n            float inv_w2 = native_recip(w2);\n            float2 uv2 = inv_w2*(float2)(u2, v2);// + 0.5f;\n//            dstRGB *= read_imagef(srcImg2, linearSampler, uv2);\n            dstRGB *= readLinear(srcImg2, uv2);\n        }\n        dstRGB.w = 1;\n        if (haveTransImg) {\n            write_imagef(transImg, xy, dstRGB);\n        }\n        if (haveSubImg) {\n            dstRGB.xyz -= read_imagef(subImg, sampler, xy).xyz;\n        }\n        if (haveDstImg) {\n            write_imagef(dstImg, xy, dstRGB);\n        }\n        if (haveDotImg) {\n            srcDstDot += dot(dotRGB.xyz, dstRGB.xyz);\n        }\n\n        if (size == 1) {\n            float zeroThreshold2    = inputData->zeroThreshold    * inputData->zeroThreshold;\n            float outlierThreshold2 = inputData->outlierThreshold * inputData->outlierThreshold;\n\n            float norm2 = dot(dstRGB.xyz, dstRGB.xyz);\n            if (norm2 < zeroThreshold2) {\n                dstCountZero++;\n            } else if (outlierThreshold2 > 0 && norm2 > outlierThreshold2) {\n                dstCountOutlier++;\n            } else {\n                dstDstDot += norm2;\n            }\n        }\nskipPixel:\n        if (size > 1) {\n            scratch[lz][lx][0] = dstRGB.x;\n            scratch[lz][lx][1] = dstRGB.y;\n            scratch[lz][lx][2] = dstRGB.z;\n            barrier(CLK_LOCAL_MEM_FENCE);\n#pragma unroll\n            for (int i = 0; i < size; i++) {\n                dstDstDot += scratch[lz][i][0]*scratch[lx][i][0] +\n                             scratch[lz][i][1]*scratch[lx][i][1] +\n                             scratch[lz][i][2]*scratch[lx][i][2];\n            }\n            barrier(CLK_LOCAL_MEM_FENCE);\n        }\n    }\n\n    if (lz == 0) {\n        dstCount = reduceSumInt(dstCount, lx, lsx, scratch);\n        dstCountZero = reduceSumInt(dstCountZero, lx, lsx, scratch);\n        dstCountOutlier = reduceSumInt(dstCountOutlier, lx, lsx, scratch);\n        if (lx == 0) {\n            outputData[gx].dstCount = dstCount;\n            outputData[gx].dstCountZero = dstCountZero;\n            outputData[gx].dstCountOutlier = dstCountOutlier;\n        }\n    }\n    if (size == 1) {\n        if (haveDotImg) srcDstDot = reduceSumFloat(srcDstDot, lx, lsx, scratch);\n        dstDstDot = reduceSumFloat(dstDstDot, lx, lsx, scratch);\n        if (lx == 0) {\n            if (haveDotImg) outputData[gx].srcDstDot[0] = srcDstDot;\n            outputData[gx].dstDstDot[0][0] = dstDstDot;\n        }\n    } else {\n        if (haveDotImg) {\n            srcDstDot = reduceSumFloat(srcDstDot, lx, lsx, scratch[lz]);\n            if (lx == 0) {\n                outputData[gx].srcDstDot[lz] = srcDstDot;\n            }\n        }\n        outputData[gx].dstDstDot[lz][lx] = dstDstDot;\n    }\n}\n\nkernel void reduceOutputData(global struct OutputData *outputData) {\n    const int x = get_global_id(0), gx = get_group_id(0), lx = get_local_id(0), lsx = get_local_size(0);\n    local int scratch[256];\n\n    int dstCount = reduceSumInt(outputData[x].dstCount, lx, lsx, scratch);\n    int dstCountZero = reduceSumInt(outputData[x].dstCountZero, lx, lsx, scratch);\n    int dstCountOutlier = reduceSumInt(outputData[x].dstCountOutlier, lx, lsx, scratch);\n    if (lx == 0) {\n        outputData[0].dstCount = dstCount;\n        outputData[0].dstCountZero = dstCountZero;\n        outputData[0].dstCountOutlier = dstCountOutlier;\n    }\n    for (int i = 0; i < DOT_SIZE; i++) {\n        float srcDstDot = reduceSumFloat(outputData[x].srcDstDot[i], lx, lsx, scratch);\n        float dstDstDot = reduceSumFloat(outputData[x].dstDstDot[i][i], lx, lsx, scratch);\n        if (lx == 0) {\n            outputData[0].srcDstDot[i] = srcDstDot;\n            outputData[0].dstDstDot[i][i] = dstDstDot;\n        }\n        for (int j = i+1; j < DOT_SIZE; j++) {\n            float dstDstDot = reduceSumFloat(outputData[x].dstDstDot[i][j], lx, lsx, scratch);\n            if (lx == 0) {\n                outputData[0].dstDstDot[i][j] = dstDstDot;\n                outputData[0].dstDstDot[j][i] = dstDstDot;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/resources/org/bytedeco/javacv/JavaCV.cl",
    "content": "/*\n * Copyright (C) 2011-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n                          CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n\n//const sampler_t linearSampler = CLK_NORMALIZED_COORDS_FALSE |\n//                                CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR;\n\n#define SENSOR_PATTERN_RGGB (int2)(0,0)\n#define SENSOR_PATTERN_GBRG (int2)(1,0)\n#define SENSOR_PATTERN_GRBG (int2)(0,1)\n#define SENSOR_PATTERN_BGGR (int2)(1,1)\n\ninline float4 readBayer(read_only image2d_t img, int2 xy, int2 sensorPattern) {\n    float p1 = read_imagef(img, sampler, xy).s0;\n    float p2 = read_imagef(img, sampler, xy + (int2)(1, 0)).s0;\n    float p3 = read_imagef(img, sampler, xy + (int2)(0, 1)).s0;\n    float p4 = read_imagef(img, sampler, xy + (int2)(1, 1)).s0;\n\n    xy = (xy + sensorPattern) % 2;\n    if (all(xy == SENSOR_PATTERN_RGGB)) {\n        return (float4)(p1, (p2 + p3)/2, p4, 1);\n    } else if (all(xy == SENSOR_PATTERN_GBRG)) {\n        return (float4)(p2, (p1 + p4)/2, p3, 1);\n    } else if (all(xy == SENSOR_PATTERN_GRBG)) {\n        return (float4)(p3, (p1 + p4)/2, p2, 1);\n    } else {//(all(xy == SENSOR_PATTERN_BGGR))\n        return (float4)(p4, (p2 + p3)/2, p1, 1);\n    }\n}\n\ninline float4 readLinear(read_only image2d_t img, float2 xy) {\n    int2 xy00 = convert_int2_rtn(xy);\n    float4 img00 = read_imagef(img, sampler, xy00);\n    float4 img10 = read_imagef(img, sampler, xy00 + (int2)(1, 0));\n    float4 img01 = read_imagef(img, sampler, xy00 + (int2)(0, 1));\n    float4 img11 = read_imagef(img, sampler, xy00 + (int2)(1, 1));\n    float4 img0 = mix(img00, img10, xy.x - xy00.x);\n    float4 img1 = mix(img01, img11, xy.x - xy00.x);\n    return mix(img0, img1, xy.y - xy00.y);\n}\n\ninline float4 readBayerLinear(read_only image2d_t img, float2 xy, int2 sensorPattern) {\n    int2 xy00 = convert_int2_rtn(xy);\n    float4 img00 = readBayer(img, xy00, sensorPattern);\n    float4 img10 = readBayer(img, xy00 + (int2)(1, 0), sensorPattern);\n    float4 img01 = readBayer(img, xy00 + (int2)(0, 1), sensorPattern);\n    float4 img11 = readBayer(img, xy00 + (int2)(1, 1), sensorPattern);\n    float4 img0 = mix(img00, img10, xy.x - xy00.x);\n    float4 img1 = mix(img01, img11, xy.x - xy00.x);\n    return mix(img0, img1, xy.y - xy00.y);\n}\n\nkernel void pyrDown(read_only image2d_t srcImg, write_only image2d_t dstImg) {\n    int x = get_global_id(0), y = get_global_id(1);\n    int lx = get_local_id(0), ly = get_local_id(1);\n    int x2 = x*2, y2 = y*2;\n    int lx2 = lx*2, ly2 = ly*2;\n\n    if (x >= get_image_width(dstImg) || y >= get_image_height(dstImg)) {\n        return;\n    }\n\n#define S(x,y) read_imagef(srcImg, sampler, (int2)(x, y))\n    float4 rgb = (1.0/256.0)*(S(x2-2, y2-2) + S(x2+2, y2-2) + S(x2-2, y2+2) + S(x2+2, y2+2)) +\n                 (4.0/256.0)*(S(x2-1, y2-2) + S(x2+1, y2-2) + S(x2-2, y2-1) + S(x2+2, y2-1) +\n                              S(x2-2, y2+1) + S(x2+2, y2+1) + S(x2-1, y2+2) + S(x2+1, y2+2)) +\n                 (6.0/256.0)*(S(x2  , y2-2) + S(x2-2, y2  ) + S(x2+2, y2  ) + S(x2  , y2+2)) +\n                (16.0/256.0)*(S(x2-1, y2-1) + S(x2+1, y2-1) + S(x2-1, y2+1) + S(x2+1, y2+1)) +\n                (24.0/256.0)*(S(x2  , y2-1) + S(x2-1, y2  ) + S(x2+1, y2  ) + S(x2  , y2+1)) +\n                (36.0/256.0)* S(x2  , y2  );\n#undef S\n\n    rgb.w = 1;\n    write_imagef(dstImg, (int2)(x, y), rgb);\n}\n\nkernel void remap(read_only image2d_t srcImg, write_only image2d_t dstImg,\n        read_only image2d_t mapxImg, read_only image2d_t mapyImg) {\n    int x = get_global_id(0), y = get_global_id(1);\n    int2 xy = (int2)(x, y);\n\n    if (x >= get_image_width(dstImg) || y >= get_image_height(dstImg)) {\n        return;\n    }\n\n    float x2 = read_imagef(mapxImg, sampler, xy).s0;\n    float y2 = read_imagef(mapyImg, sampler, xy).s0;\n    float2 xy2 = (float2)(x2, y2);\n    float4 rgb = readLinear(srcImg, xy2);\n    write_imagef(dstImg, xy, rgb);\n}\n\nkernel void remapBayer(read_only image2d_t srcImg, write_only image2d_t dstImg,\n        read_only image2d_t mapxImg, read_only image2d_t mapyImg, int2 sensorPattern) {\n    int x = get_global_id(0), y = get_global_id(1);\n    int2 xy = (int2)(x, y);\n\n    if (x >= get_image_width(dstImg) || y >= get_image_height(dstImg)) {\n        return;\n    }\n\n    float x2 = read_imagef(mapxImg, sampler, xy).s0;\n    float y2 = read_imagef(mapyImg, sampler, xy).s0;\n    float2 xy2 = (float2)(x2, y2);\n    float4 rgb = readBayerLinear(srcImg, xy2, sensorPattern);\n    write_imagef(dstImg, xy, rgb);\n}\n"
  },
  {
    "path": "src/main/resources/org/bytedeco/javacv/ProCamTransformer.cl",
    "content": "/*\n * Copyright (C) 2011-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * transform kernel (one transform, no mask)\n */\nkernel void transformOne(read_only image2d_t srcImg, read_only image2d_t srcImg2, write_only image2d_t dstImg,\n        constant float H1[9], constant float H2[9], constant float4 X[4],\n        constant struct InputData *inputData, global struct OutputData *outputData) {\n    int w = get_image_width(dstImg);\n    int h = get_image_height(dstImg);\n    multiWarpColorTransform(srcImg, srcImg2, 0, 0, 0, dstImg, 0, w, h,\n            H1, H2, X, 1, inputData, outputData, false, false, false, true, false);\n}\n\n/**\n * transform kernel (one transform with an image to subtract and a mask)\n */\nkernel void transformSub(read_only image2d_t srcImg, read_only image2d_t srcImg2, read_only image2d_t subImg,\n        write_only image2d_t transImg, write_only image2d_t dstImg,\n        read_only image2d_t maskImg, constant float H1[9], constant float H2[9], constant float4 X[4],\n        constant struct InputData *inputData, global struct OutputData *outputData) {\n    int w = get_image_width(maskImg);\n    int h = get_image_height(maskImg);\n    multiWarpColorTransform(srcImg, srcImg2, subImg, 0, transImg, dstImg, maskImg, w, h,\n            H1, H2, X, 1, inputData, outputData, true, false, true, true, true);\n}\n\n/**\n * transform kernel (dot products only)\n */\nkernel void transformDot(read_only image2d_t srcImg, read_only image2d_t srcImg2, read_only image2d_t subImg,\n        read_only image2d_t dotImg, read_only image2d_t maskImg,\n        constant float H1[][9], constant float H2[][9], constant float4 X[][4],\n        constant struct InputData *inputData, global struct OutputData *outputData) {\n    int w = get_image_width(maskImg);\n    int h = get_image_height(maskImg);\n    multiWarpColorTransform(srcImg, srcImg2, subImg, dotImg, 0, 0, maskImg, w, h,\n            H1, H2, X, DOT_SIZE, inputData, outputData, true, true, false, false, true);\n}\n"
  },
  {
    "path": "src/main/resources/org/bytedeco/javacv/ProjectiveColorTransformer.cl",
    "content": "/*\n * Copyright (C) 2011-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * transform kernel (one transform, no mask)\n */\nkernel void transformOne(read_only image2d_t srcImg, write_only image2d_t dstImg,\n        constant float H[9], constant float4 X[4],\n        constant struct InputData *inputData, global struct OutputData *outputData) {\n    int w = get_image_width(dstImg);\n    int h = get_image_height(dstImg);\n    multiWarpColorTransform(srcImg, 0, 0, 0, 0, dstImg, 0, w, h,\n            H, 0, X, 1, inputData, outputData, false, false, false, true, false);\n}\n\n/**\n * transform kernel (one transform with an image to subtract and a mask)\n */\nkernel void transformSub(read_only image2d_t srcImg, read_only image2d_t subImg,\n        write_only image2d_t transImg, write_only image2d_t dstImg,\n        read_only image2d_t maskImg, constant float H[9], constant float4 X[4],\n        constant struct InputData *inputData, global struct OutputData *outputData) {\n    int w = get_image_width(maskImg);\n    int h = get_image_height(maskImg);\n    multiWarpColorTransform(srcImg, 0, subImg, 0, transImg, dstImg, maskImg, w, h,\n            H, 0, X, 1, inputData, outputData, true, false, true, true, true);\n}\n\n/**\n * transform kernel (dot products only)\n */\nkernel void transformDot(read_only image2d_t srcImg, read_only image2d_t subImg,\n        read_only image2d_t dotImg, read_only image2d_t maskImg,\n        constant float H[][9], constant float4 X[][4],\n        constant struct InputData *inputData, global struct OutputData *outputData) {\n    int w = get_image_width(maskImg);\n    int h = get_image_height(maskImg);\n    multiWarpColorTransform(srcImg, 0, subImg, dotImg, 0, 0, maskImg, w, h,\n            H, 0, X, DOT_SIZE, inputData, outputData, true, true, false, false, true);\n}\n"
  },
  {
    "path": "src/main/resources/org/bytedeco/javacv/ProjectiveTransformer.cl",
    "content": "/*\n * Copyright (C) 2011-2012 Samuel Audet\n *\n * Licensed either under the Apache License, Version 2.0, or (at your option)\n * under the terms of the GNU General Public License as published by\n * the Free Software Foundation (subject to the \"Classpath\" exception),\n * either version 2, or any later version (collectively, the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *     http://www.gnu.org/licenses/\n *     http://www.gnu.org/software/classpath/license.html\n *\n * or as provided in the LICENSE.txt file that accompanied this code.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * transform kernel (one transform, no mask)\n */\nkernel void transformOne(read_only image2d_t srcImg, write_only image2d_t dstImg,\n        constant float H[9],\n        constant struct InputData *inputData, global struct OutputData *outputData) {\n    int w = get_image_width(dstImg);\n    int h = get_image_height(dstImg);\n    multiWarpColorTransform(srcImg, 0, 0, 0, 0, dstImg, 0, w, h,\n            H, 0, 0, 1, inputData, outputData, false, false, false, true, false);\n}\n\n/**\n * transform kernel (one transform with an image to subtract and a mask)\n */\nkernel void transformSub(read_only image2d_t srcImg, read_only image2d_t subImg,\n        write_only image2d_t transImg, write_only image2d_t dstImg,\n        read_only image2d_t maskImg, constant float H[9],\n        constant struct InputData *inputData, global struct OutputData *outputData) {\n    int w = get_image_width(maskImg);\n    int h = get_image_height(maskImg);\n    multiWarpColorTransform(srcImg, 0, subImg, 0, transImg, dstImg, maskImg, w, h,\n            H, 0, 0, 1, inputData, outputData, true, false, true, true, true);\n}\n\n/**\n * transform kernel (dot products only)\n */\nkernel void transformDot(read_only image2d_t srcImg, read_only image2d_t subImg,\n        read_only image2d_t dotImg, read_only image2d_t maskImg,\n        constant float H[][9],\n        constant struct InputData *inputData, global struct OutputData *outputData) {\n    int w = get_image_width(maskImg);\n    int h = get_image_height(maskImg);\n    multiWarpColorTransform(srcImg, 0, subImg, dotImg, 0, 0, maskImg, w, h,\n            H, 0, 0, DOT_SIZE, inputData, outputData, true, true, false, false, true);\n}\n"
  }
]