├── .formatter.exs ├── .gitconfig ├── .githooks └── pre-commit ├── .github ├── scripts │ ├── do-build.sh │ ├── script-to-build-freebsd.sh │ └── script-to-build-gnu.sh └── workflows │ ├── freebsd-precompile-x86_64.yml │ ├── linux-cuda-gnu.yml │ ├── linux-precompile-aarch64-cuda-gnu.yml │ ├── linux-precompile-gnu.yml │ ├── linux-precompile-musl.yml │ ├── linux-precompile-x86_64-cuda-gnu.yml │ ├── linux-x86_64.yml │ ├── macos-apple-device-precompile.yml │ ├── macos-precompile.yml │ ├── macos-x86_64.yml │ ├── nerves-build.yml │ ├── windows-precompile-cuda.yml │ ├── windows-precompile.yml │ └── windows-x86_64.yml ├── .gitignore ├── .gitlab-ci.yml ├── CHANGELOG.md ├── CHANGELOG.v0.1.md ├── CMakeLists.txt ├── Cheatsheet.cheatmd ├── LICENSE ├── LICENSE-OpenCV ├── Makefile ├── Makefile.win ├── README.md ├── assets ├── logo.png └── repository-open-graph.png ├── c_src ├── ArgInfo.hpp ├── erlcompat.hpp ├── evision.cpp ├── evision_consts.h ├── evision_custom_headers.h ├── evision_custom_headers │ ├── .gitkeep │ ├── evision_LSDDetector.hpp │ ├── evision_async.hpp │ ├── evision_barcode.hpp │ ├── evision_cchecker.hpp │ ├── evision_cuda.hpp │ ├── evision_dnn.hpp │ ├── evision_features2d.hpp │ ├── evision_flann.hpp │ ├── evision_linemod.hpp │ ├── evision_ml.hpp │ ├── evision_phase_unwrapping.hpp │ ├── evision_ppf_match_3d.hpp │ ├── evision_stereo.hpp │ ├── evision_stitching.hpp │ ├── evision_tracking.hpp │ ├── evision_umat.hpp │ ├── evision_video.hpp │ ├── evision_videoio.hpp │ ├── evision_xfeatures2d.hpp │ └── evision_ximgproc.hpp ├── modules │ ├── evision_backend │ │ ├── abs.h │ │ ├── add.h │ │ ├── backend.h │ │ ├── bitwise_and.h │ │ ├── bitwise_not.h │ │ ├── bitwise_or.h │ │ ├── bitwise_xor.h │ │ ├── broadcast.h │ │ ├── ceil.h │ │ ├── clip.h │ │ ├── cmp.h │ │ ├── divide.h │ │ ├── expm1.h │ │ ├── eye.h │ │ ├── floor.h │ │ ├── from_binary.h │ │ ├── logical_and.h │ │ ├── logical_or.h │ │ ├── logical_xor.h │ │ ├── matrix_multiply.h │ │ ├── multiply.h │ │ ├── negate.h │ │ ├── reshape.h │ │ ├── round.h │ │ ├── sign.h │ │ ├── subtract.h │ │ ├── to_batched.h │ │ ├── to_binary.h │ │ └── transpose.h │ ├── evision_cuda.cc │ ├── evision_cuda.h │ ├── evision_cuda_ipc.cc │ ├── evision_cuda_ipc.h │ ├── evision_gpumat.h │ ├── evision_highgui.h │ ├── evision_imdecode.h │ ├── evision_mat.h │ ├── evision_mat_api.h │ ├── evision_mat_utils.hpp │ ├── evision_video_api.h │ └── evision_videocapture.h ├── nif_utils.hpp └── windows_fix │ └── windows_fix.cpp ├── cc_toolchain ├── aarch64-linux-gnu.cmake ├── aarch64-linux-musl.cmake ├── aarch64-windows-msvc.cmake ├── armv6-nerves-linux-gnueabihf.cmake ├── armv7l-linux-gnueabihf.cmake ├── i686-linux-gnu.cmake ├── ppc64le-linux-gnu.cmake ├── riscv64-linux-gnu.cmake ├── riscv64-linux-musl.cmake ├── s390x-linux-gnu.cmake ├── x86_64-linux-musl.cmake └── zig.toolchain.cmake ├── config └── config.exs ├── do_release.sh ├── evision_precompiled.erl ├── examples ├── README.md ├── cifar10.livemd ├── densenet121_benchmark.livemd ├── dnn-detection-model.livemd ├── dnn-googlenet.livemd ├── dnn-u2net_human_seg.livemd ├── dnn-u2net_portrait.livemd ├── evision_erlang_dnn_demo.erl ├── find_and_draw_contours.livemd ├── getting_started.livemd ├── magic_image.livemd ├── ml-decision_tree_and_random_forest.livemd ├── ml-svm.livemd ├── ocl-default_opencl_device_info.livemd ├── pca.livemd ├── photo-hdr.livemd ├── qrcode.livemd ├── stitching.livemd ├── sudoku.livemd ├── warp_perspective.livemd └── warp_polar.livemd ├── gleam_src ├── evision │ ├── highgui.gleam │ ├── mat.gleam │ └── types.gleam └── evision_mat.erl ├── lib ├── assets │ ├── base.js │ ├── main.css │ ├── main.js │ ├── ml.js │ └── zoo.js ├── evision │ ├── application.ex │ └── backend.ex ├── evision_highgui.ex ├── evision_ipc_handle.ex ├── evision_mat.ex ├── evision_structurise.ex ├── evision_windows_fix.ex ├── evision_wx.ex ├── mix │ └── tasks │ │ └── evision.fetch_precompiled.ex ├── smartcell │ ├── evision_smartcell.ex │ ├── evision_smartcell_helper.ex │ ├── evision_zoo.ex │ ├── ml_dtrees.ex │ ├── ml_rtrees.ex │ ├── ml_svm.ex │ ├── ml_traindata.ex │ └── simple_list.ex └── zoo │ ├── face_detection │ ├── face_detection.ex │ └── yunet.ex │ ├── face_recognition │ ├── face_recognition.ex │ └── sface.ex │ ├── image_classification │ ├── image_classification.ex │ ├── mobilenet_v1.ex │ ├── mobilenet_v2.ex │ └── pp_resnet.ex │ ├── image_segmentation │ ├── image_segmentation.ex │ └── pp_humanseg.ex │ ├── text_detection │ ├── db.ex │ ├── ppocrv3.ex │ └── text_detection.ex │ ├── text_recognition │ ├── crnn.ex │ └── text_recognition.ex │ ├── utils │ └── http.ex │ └── zoo.ex ├── mix.exs ├── mix.lock ├── patches └── apply_patch.py ├── py_src ├── arg_info.py ├── class_info.py ├── class_prop.py ├── erl_enum_expression_generator.py ├── evision_extra_functions.py ├── evision_structures.py ├── evision_templates.py ├── fixes.py ├── format_strings.py ├── func_info.py ├── func_variant.py ├── gen2.py ├── hdr_parser.py ├── helper.py ├── module_generator.py ├── namespace.py └── py2e.py ├── rebar.config ├── rebar.lock ├── scripts ├── download_opencv.ps1 ├── download_opencv.sh ├── download_opencv_contrib.ps1 └── download_opencv_contrib.sh ├── src ├── evision.app.src ├── evision_highgui.erl ├── evision_internal_structurise.erl ├── evision_mat.erl └── evision_windows_fix.erl └── test ├── README.md ├── dnn_detection_model_test.exs ├── dnn_detection_test.exs ├── dnn_text_detection_model_db_test.exs ├── dnn_text_detection_model_east_test.exs ├── downloading_list.txt ├── evision_backend_test.exs ├── evision_dnn_test.exs ├── evision_face_test.exs ├── evision_features2d_test.exs ├── evision_gpumat_test.exs ├── evision_keypoint_test.exs ├── evision_mat_test.exs ├── evision_test.exs ├── evision_xfeatures2d_test.exs ├── ml_dtree_test.exs ├── ml_rtree_test.exs ├── ml_svm_test.exs ├── orb_test.exs ├── pca_test.exs ├── photo_hdr_test.exs ├── qr_detector_test.exs ├── qr_encoder_test.exs ├── test_helper.exs ├── testdata ├── 1023_6.png ├── back.jpg ├── color_checker.etf ├── dnn_detection_test.jpg ├── dog.jpg ├── front.jpg ├── imreadmulti_test.tiff ├── models │ ├── .gitkeep │ └── coco_names.txt ├── pca_test.jpg ├── photo_hdr_test │ ├── .gitkeep │ └── list.txt ├── qr_detector_test.png ├── straight_qrcode.bin ├── sudoku_puzzle.webp ├── svm_test.png ├── test-circle-grid.jpg ├── test.jpg ├── test.png ├── videocapture_test.mp4 ├── warp_perspective.png ├── warp_polar.png └── yolov4.cfg ├── videocapture_test.exs └── videowriter_test.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: [ 4 | "{mix,.formatter}.exs", 5 | "{config,test}/**/*.{ex,exs}", 6 | "lib/*.{ex,exs}", 7 | "lib/smartcell/**.{ex,exs}", 8 | "lib/zoo/**.{ex,exs}" 9 | ] 10 | ] 11 | -------------------------------------------------------------------------------- /.gitconfig: -------------------------------------------------------------------------------- 1 | [core] 2 | hooksPath = .githooks 3 | -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # optionally check spell if codespell is installed 4 | # pip install codespell 5 | # git config --local include.path ../.gitconfig 6 | 7 | codespell="$(which codespell)" 8 | if [ -x "${codespell}" ]; then 9 | "${codespell}" -S _build,deps,./lib/generated,3rd_party,.elixir_ls,erl_crash.dump,doc,test,.git,.cache,cmake_opencv_*,cmake_evision,cover,./c_src/evision_generated_funcs.h,./c_src/evision_generated_types_content.h,.githook -L usign 10 | if [ $? -eq 0 ]; then 11 | exit 0 12 | else 13 | exit 1 14 | fi 15 | fi 16 | exit 0 17 | -------------------------------------------------------------------------------- /.github/scripts/do-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | CUDA_PIN=$1 6 | CUDA_DEB=$2 7 | CUDA_TOOLKIT=$3 8 | CUDA_ID=$4 9 | CUDNN_DEB=$5 10 | CUDNN_PACKAGE=$6 11 | CUDNN_ID=$7 12 | OTP_VERSION=$8 13 | ELIXIR_VERSION=$9 14 | TRIPLET=${10} 15 | GITHUB_REF=${11} 16 | 17 | export LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8 DEBIAN_FRONTEND=noninteractive 18 | DEBIAN_FRONTEND=noninteractive apt-get update && \ 19 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 20 | build-essential gcc g++ make autoconf m4 libncurses5-dev libssl-dev \ 21 | cmake make ninja-build git wget ca-certificates libtinfo5 \ 22 | automake autoconf pkg-config bc unzip zip curl gzip python3 libeigen3-dev \ 23 | locales libtool libtool-bin libopenblas-dev libfreetype-dev \ 24 | libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev \ 25 | gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ 26 | gstreamer1.0-libav gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 \ 27 | gstreamer1.0-qt5 gstreamer1.0-pulseaudio sudo 28 | echo "LC_ALL=en_US.UTF-8" >> /etc/environment 29 | echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen 30 | echo "LANG=en_US.UTF-8" > /etc/locale.conf 31 | locale-gen en_US.UTF-8 32 | 33 | curl -fSL "${CUDA_PIN}" -o cuda.pin 34 | mv cuda.pin /etc/apt/preferences.d/cuda-repository-pin-600 35 | curl -fSL "${CUDA_DEB}" -o cuda.deb 36 | sudo dpkg -i cuda.deb 37 | sudo cp /var/cuda-repo-*/cuda-*-keyring.gpg /usr/share/keyrings/ 38 | sudo apt-get update 39 | sudo apt-get -y install "${CUDA_TOOLKIT}" 40 | sudo rm -rf cuda.deb 41 | 42 | curl -fSL "${CUDNN_DEB}" -o cudnn.deb 43 | sudo dpkg -i cudnn.deb 44 | sudo cp /var/cudnn-local-repo-*/cudnn-*-keyring.gpg /usr/share/keyrings/ 45 | sudo apt-get update 46 | sudo apt-get -y install "${CUDNN_PACKAGE}" 47 | sudo rm -rf cudnn.deb 48 | 49 | export PATH="/usr/local/cuda/bin:${PATH}" 50 | 51 | mkdir -p ./cache/otp 52 | curl -fSL "https://github.com/cocoa-xu/otp-build/releases/download/v${OTP_VERSION}/otp-${TRIPLET}.tar.gz" -o "./cache/otp/otp-v${OTP_VERSION}-${TRIPLET}.tar.gz" 53 | export ROOT_DIR="$(pwd)" 54 | cd ./cache/otp 55 | tar -xzf "otp-v${OTP_VERSION}-${TRIPLET}.tar.gz" 56 | cd "${ROOT_DIR}" 57 | 58 | ELIXIR_VERSION="1.16.2" 59 | export PATH="$(pwd)/./cache/otp/usr/local/bin:$(pwd)/./cache/elixir/elixir-${ELIXIR_VERSION}/bin:${PATH}" 60 | export ERL_ROOTDIR="$(pwd)/./cache/otp/usr/local/lib/erlang" 61 | mkdir -p ./cache/elixir 62 | curl -fSL "https://github.com/elixir-lang/elixir/archive/refs/tags/v${ELIXIR_VERSION}.tar.gz" -o "./cache/elixir/elixir-${ELIXIR_VERSION}.tar.gz" 63 | cd ./cache/elixir 64 | tar -xzf "elixir-${ELIXIR_VERSION}.tar.gz" 65 | cd "elixir-${ELIXIR_VERSION}" 66 | make -j$(nproc) install 67 | 68 | cd "${ROOT_DIR}" 69 | mix local.hex --force 70 | mix local.rebar --force 71 | 72 | export MIX_ENV=prod NIF_VERSION="2.16" EVISION_PREFER_PRECOMPILED="false" EVISION_GENERATE_LANG="erlang,elixir" EVISION_ENABLE_CUDA="true" EVISION_ENABLE_CONTRIB="true" 73 | cd /work 74 | mix deps.get 75 | mix compile 76 | 77 | export PKG_NAME="evision-nif_${NIF_VERSION}-${TRIPLET}-contrib-cuda${CUDA_ID}-cudnn${CUDNN_ID}-${GITHUB_REF##*/v}" 78 | mkdir -p "${PKG_NAME}" 79 | 80 | export PRIV_DIR="$(pwd)/_build/${MIX_ENV}/lib/evision/priv" 81 | mv "${PRIV_DIR}/include" /tmp/include 82 | cp -a "${PRIV_DIR}" "${PKG_NAME}" 83 | cp -a lib/generated "${PKG_NAME}/elixir_generated" 84 | cp -a src/generated "${PKG_NAME}/erlang_generated" 85 | tar -czf "${PKG_NAME}.tar.gz" "${PKG_NAME}" 86 | rm -rf "${PKG_NAME}" 87 | ls -lah "${PKG_NAME}.tar.gz" 88 | mkdir -p artifacts 89 | mv "${PKG_NAME}.tar.gz" artifacts 90 | cd artifacts 91 | sha256sum "${PKG_NAME}.tar.gz" | tee "${PKG_NAME}.tar.gz.sha256" 92 | sudo chmod a+rw "${PKG_NAME}.tar.gz" "${PKG_NAME}.tar.gz.sha256" 93 | -------------------------------------------------------------------------------- /.github/scripts/script-to-build-freebsd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | rm -rf lib/generated && rm -rf src/generated 6 | echo "PKG_NAME: ${PKG_NAME}" 7 | mkdir -p ${PKG_NAME} 8 | export PRIV_DIR="${ROOT_DIR}/_build/prod/lib/evision/priv" 9 | 10 | mix compile 11 | 12 | mv "${PRIV_DIR}/include" /tmp/include 13 | cp -a "${PRIV_DIR}" "${PKG_NAME}" 14 | cp -a lib/generated "${PKG_NAME}/elixir_generated" 15 | cp -a src/generated "${PKG_NAME}/erlang_generated" 16 | tar -czf "${PKG_NAME}.tar.gz" "${PKG_NAME}" 17 | rm -rf "${PKG_NAME}" 18 | ls -lah "${PKG_NAME}.tar.gz" 19 | mv "${PKG_NAME}.tar.gz" "artifacts" 20 | cd artifacts 21 | sha256sum "${PKG_NAME}.tar.gz" | tee "${PKG_NAME}.tar.gz.sha256" 22 | mv "${PKG_NAME}.tar.gz.sha256" ${ROOT_DIR} 23 | mv "${PKG_NAME}.tar.gz" ${ROOT_DIR} 24 | cd ${ROOT_DIR} 25 | -------------------------------------------------------------------------------- /.github/scripts/script-to-build-gnu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | CUDA_PIN=$1 6 | CUDA_DEB=$2 7 | CUDA_TOOLKIT=$3 8 | CUDA_ID=$4 9 | CUDNN_DEB=$5 10 | CUDNN_PACKAGE=$6 11 | CUDNN_ID=$7 12 | OTP_VERSION=$8 13 | ELIXIR_VERSION=$9 14 | TRIPLET=${10} 15 | GITHUB_REF=${11} 16 | IMAGE_NAME="ubuntu:20.04" 17 | 18 | sudo docker run --privileged --network=host --platform=linux/arm64 --rm -v $(pwd):/work "${IMAGE_NAME}" \ 19 | sh -c "chmod a+x /work/do-build.sh && /work/do-build.sh '${CUDA_PIN}' '${CUDA_DEB}' '${CUDA_TOOLKIT}' '${CUDA_ID}' '${CUDNN_DEB}' '${CUDNN_PACKAGE}' '${CUDNN_ID}' '${OTP_VERSION}' '${ELIXIR_VERSION}' '${TRIPLET}' '${GITHUB_REF}'" 20 | sudo chown -R $(id -u):$(id -g) . 21 | sudo chmod a+rw -R ./artifacts 22 | -------------------------------------------------------------------------------- /.github/workflows/linux-precompile-aarch64-cuda-gnu.yml: -------------------------------------------------------------------------------- 1 | name: linux-precompile-aarch64-cuda-gnu 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | aarch64-linux-gnu-cuda: 14 | runs-on: ${{ matrix.pair.runner }} 15 | env: 16 | OTP_VERSION: "25.3.2.12" 17 | ELIXIR_VERSION: "1.16.1" 18 | TRIPLET: "aarch64-linux-gnu" 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | include: 23 | - pair: 24 | arch: aarch64-linux-gnu 25 | runner: ubicloud-standard-4-arm 26 | cuda_id: "11" 27 | cudnn_id: "8" 28 | cuda_pin: "https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/sbsa/cuda-ubuntu2004.pin" 29 | cuda_deb: "https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda-repo-ubuntu2004-11-8-local_11.8.0-520.61.05-1_arm64.deb" 30 | cuda_toolkit: "cuda" 31 | cudnn_deb: "https://mirrors.uwucocoa.moe/cuda/cudnn-local-repo-ubuntu2004-8.9.7.29_1.0-1_arm64.deb" 32 | cudnn_package: "libcudnn8-dev" 33 | - pair: 34 | arch: aarch64-linux-gnu 35 | runner: ubicloud-standard-4-arm 36 | cuda_id: "11" 37 | cudnn_id: "9" 38 | cuda_pin: "https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/sbsa/cuda-ubuntu2004.pin" 39 | cuda_deb: "https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda-repo-ubuntu2004-11-8-local_11.8.0-520.61.05-1_arm64.deb" 40 | cuda_toolkit: "cuda" 41 | cudnn_deb: "https://developer.download.nvidia.com/compute/cudnn/9.2.0/local_installers/cudnn-local-repo-ubuntu2004-9.2.0_1.0-1_arm64.deb" 42 | cudnn_package: "cudnn-cuda-12" 43 | - pair: 44 | arch: aarch64-linux-gnu 45 | runner: ubicloud-standard-4-arm 46 | cuda_id: "12" 47 | cudnn_id: "8" 48 | cuda_pin: "https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/sbsa/cuda-ubuntu2004.pin" 49 | cuda_deb: "https://developer.download.nvidia.com/compute/cuda/12.5.1/local_installers/cuda-repo-ubuntu2004-12-5-local_12.5.1-555.42.06-1_arm64.deb" 50 | cuda_toolkit: "cuda-toolkit-12-5" 51 | cudnn_deb: "https://mirrors.uwucocoa.moe/cuda/cudnn-local-repo-ubuntu2004-8.9.7.29_1.0-1_arm64.deb" 52 | cudnn_package: "libcudnn8-dev" 53 | - pair: 54 | arch: aarch64-linux-gnu 55 | runner: ubicloud-standard-4-arm 56 | cuda_id: "12" 57 | cudnn_id: "9" 58 | cuda_pin: "https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/sbsa/cuda-ubuntu2004.pin" 59 | cuda_deb: "https://developer.download.nvidia.com/compute/cuda/12.5.1/local_installers/cuda-repo-ubuntu2004-12-5-local_12.5.1-555.42.06-1_arm64.deb" 60 | cuda_toolkit: "cuda-toolkit-12-5" 61 | cudnn_deb: "https://developer.download.nvidia.com/compute/cudnn/9.2.0/local_installers/cudnn-local-repo-ubuntu2004-9.2.0_1.0-1_arm64.deb" 62 | cudnn_package: "cudnn-cuda-12" 63 | 64 | name: aarch64-linux-gnu-cuda${{ matrix.pair.cuda_id }}-cudnn${{ matrix.pair.cudnn_id }} 65 | steps: 66 | - name: Checkout 67 | uses: actions/checkout@v4 68 | 69 | - name: Mix compile (with contrib modules) 70 | run: | 71 | ls -la 72 | cp -a .github/scripts/*.sh ./ 73 | chmod +x *.sh 74 | ./script-to-build-gnu.sh "${{ matrix.pair.cuda_pin }}" "${{ matrix.pair.cuda_deb }}" "${{ matrix.pair.cuda_toolkit }}" "${{ matrix.pair.cuda_id }}" "${{ matrix.pair.cudnn_deb }}" "${{ matrix.pair.cudnn_package }}" "${{ matrix.pair.cudnn_id }}" "${{ env.OTP_VERSION }}" "${{ env.ELIXIR_VERSION }}" "${{ env.TRIPLET }}" "${GITHUB_REF}" 75 | 76 | - uses: softprops/action-gh-release@v2 77 | with: 78 | files: | 79 | artifacts/*.tar.gz 80 | artifacts/*.sha256 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /cover 3 | /deps 4 | /doc 5 | /.fetch 6 | erl_crash.dump 7 | *.ez 8 | *.beam 9 | /config/*.secret.exs 10 | .elixir_ls/ 11 | /c_src/evision_generated_* 12 | /c_src/evision_signatures.json 13 | /c_src/evision_custom_headers.h 14 | /c_src/configuration.private.hpp 15 | /c_src/headers.txt 16 | /c_src/headers-contrib.txt 17 | .DS_Store 18 | .vscode/ 19 | .idea/ 20 | .cache/ 21 | /cmake_*/ 22 | /priv/ 23 | *.pyc 24 | /*.xcodeproj/ 25 | /c_src/evision_custom_headers/* 26 | /lib/evision.ex 27 | /src/evision.erl 28 | 3rd_party/cache 29 | 3rd_party/opencv 30 | /lib/generated 31 | /src/generated 32 | 33 | # testdata 34 | /test/testdata/models/ 35 | /test/testdata/photo_hdr_test/ 36 | /test/testdata/yolov4.weights 37 | /test/testdata/efficientnet-b7.onnx 38 | /test/testdata/DB_IC15_resnet18.onnx 39 | /test/testdata/frozen_east_text_detection.pb 40 | /test/testdata/frozen_east_text_detection.pb.part1 41 | /test/testdata/frozen_east_text_detection.pb.part2 42 | 43 | # examples 44 | /examples/Lenna_test_image.png 45 | /examples/classification_classes_ILSVRC2012.txt 46 | /examples/coco_names.txt 47 | /examples/dnn_detection_test.jpg 48 | /examples/space_shuttle.jpg 49 | /examples/ssd_mobilenet_v2_coco_2018_03_29** 50 | /examples/test.png 51 | 52 | # nvcc 53 | c_src/modules/evision_cuda.o 54 | 55 | # we don't need to track checksum.exs in git 56 | # it should only be tracked by hex.pm (in `Evision.MixProject.package()[:files]`) 57 | checksum.exs 58 | 59 | # manually track this file in git 60 | checksum.erl 61 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/assets/logo.png -------------------------------------------------------------------------------- /assets/repository-open-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/assets/repository-open-graph.png -------------------------------------------------------------------------------- /c_src/ArgInfo.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ARGINFO_HPP 2 | #define ARGINFO_HPP 3 | 4 | class ArgInfo 5 | { 6 | private: 7 | static const uint32_t arg_outputarg_flag = 0x1; 8 | static const uint32_t arg_arithm_op_src_flag = 0x2; 9 | static const uint32_t arg_pathlike_flag = 0x4; 10 | static const uint32_t arg_nd_mat_flag = 0x8; 11 | static const uint32_t arg_has_default_flag = 0x10; 12 | 13 | public: 14 | const char* name; 15 | bool outputarg; 16 | bool arithm_op_src; 17 | bool pathlike; 18 | bool nd_mat; 19 | bool has_default; // <- added in evision 20 | // more fields may be added if necessary 21 | 22 | ArgInfo(const char* name_, uint32_t arg_) : 23 | name(name_), 24 | outputarg((arg_ & arg_outputarg_flag) != 0), 25 | arithm_op_src((arg_ & arg_arithm_op_src_flag) != 0), 26 | pathlike((arg_ & arg_pathlike_flag) != 0), 27 | nd_mat((arg_ & arg_nd_mat_flag) != 0), 28 | has_default((arg_ & arg_has_default_flag) != 0) {} 29 | 30 | private: 31 | ArgInfo(const ArgInfo&) = delete; 32 | ArgInfo& operator=(const ArgInfo&) = delete; 33 | }; 34 | 35 | #endif // ARGINFO_HPP 36 | -------------------------------------------------------------------------------- /c_src/evision_consts.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_CONSTS_H 2 | #pragma once 3 | 4 | #include 5 | 6 | // nil 7 | static ERL_NIF_TERM kAtomNil; 8 | // true 9 | static ERL_NIF_TERM kAtomTrue; 10 | // false 11 | static ERL_NIF_TERM kAtomFalse; 12 | // out_of_memory 13 | static ERL_NIF_TERM kAtomOutOfMemory; 14 | // ref 15 | static ERL_NIF_TERM kAtomRef; 16 | // class 17 | static ERL_NIF_TERM kAtomClass; 18 | // dims 19 | static ERL_NIF_TERM kAtomDims; 20 | // channels 21 | static ERL_NIF_TERM kAtomChannels; 22 | // type 23 | static ERL_NIF_TERM kAtomType; 24 | // data 25 | static ERL_NIF_TERM kAtomData; 26 | // raw_type 27 | static ERL_NIF_TERM kAtomRawType; 28 | // elemSize 29 | static ERL_NIF_TERM kAtomElemSize; 30 | // step 31 | static ERL_NIF_TERM kAtomStep; 32 | // device_id 33 | static ERL_NIF_TERM kAtomDeviceID; 34 | // shape 35 | static ERL_NIF_TERM kAtomShape; 36 | // __struct__ 37 | static ERL_NIF_TERM kAtomStructKey; 38 | // Elixir.Nx.Tensor 39 | static ERL_NIF_TERM kAtomNxTensorModule; 40 | // nx_tensor (only for internal use) 41 | static ERL_NIF_TERM kAtomNxTensor; 42 | // Elixir.Nx.Tensor 43 | static ERL_NIF_TERM kAtomEvisionMatModule; 44 | 45 | // atoms for types 46 | static ERL_NIF_TERM kAtomU; 47 | static ERL_NIF_TERM kAtomS; 48 | static ERL_NIF_TERM kAtomF; 49 | static ERL_NIF_TERM kAtomU8; 50 | static ERL_NIF_TERM kAtomS8; 51 | static ERL_NIF_TERM kAtomU16; 52 | static ERL_NIF_TERM kAtomS16; 53 | static ERL_NIF_TERM kAtomU32; 54 | static ERL_NIF_TERM kAtomS32; 55 | static ERL_NIF_TERM kAtomU64; 56 | static ERL_NIF_TERM kAtomS64; 57 | static ERL_NIF_TERM kAtomF16; 58 | static ERL_NIF_TERM kAtomF32; 59 | static ERL_NIF_TERM kAtomF64; 60 | static ERL_NIF_TERM kAtomUser; 61 | 62 | // atoms for cv::Moments 63 | static ERL_NIF_TERM kAtomM00; 64 | static ERL_NIF_TERM kAtomM10; 65 | static ERL_NIF_TERM kAtomM01; 66 | static ERL_NIF_TERM kAtomM20; 67 | static ERL_NIF_TERM kAtomM11; 68 | static ERL_NIF_TERM kAtomM02; 69 | static ERL_NIF_TERM kAtomM30; 70 | static ERL_NIF_TERM kAtomM21; 71 | static ERL_NIF_TERM kAtomM12; 72 | static ERL_NIF_TERM kAtomM03; 73 | static ERL_NIF_TERM kAtomMu20; 74 | static ERL_NIF_TERM kAtomMu11; 75 | static ERL_NIF_TERM kAtomMu02; 76 | static ERL_NIF_TERM kAtomMu30; 77 | static ERL_NIF_TERM kAtomMu21; 78 | static ERL_NIF_TERM kAtomMu12; 79 | static ERL_NIF_TERM kAtomMu03; 80 | static ERL_NIF_TERM kAtomNu20; 81 | static ERL_NIF_TERM kAtomNu11; 82 | static ERL_NIF_TERM kAtomNu02; 83 | static ERL_NIF_TERM kAtomNu30; 84 | static ERL_NIF_TERM kAtomNu21; 85 | static ERL_NIF_TERM kAtomNu12; 86 | static ERL_NIF_TERM kAtomNu03; 87 | 88 | #endif // EVISION_CONSTS_H 89 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers.h: -------------------------------------------------------------------------------- 1 | //user-defined headers 2 | #include "evision_custom_headers/evision_async.hpp" 3 | #include "evision_custom_headers/evision_cuda.hpp" 4 | #include "evision_custom_headers/evision_dnn.hpp" 5 | #include "evision_custom_headers/evision_umat.hpp" 6 | #include "evision_custom_headers/evision_flann.hpp" 7 | #include "evision_custom_headers/evision_ml.hpp" 8 | #include "evision_custom_headers/evision_features2d.hpp" 9 | #include "evision_custom_headers/evision_videoio.hpp" 10 | #include "evision_custom_headers/evision_stitching.hpp" 11 | #include "evision_custom_headers/evision_video.hpp" 12 | 13 | // opencv contrib 14 | #include "evision_custom_headers/evision_barcode.hpp" 15 | #include "evision_custom_headers/evision_cchecker.hpp" 16 | #include "evision_custom_headers/evision_linemod.hpp" 17 | #include "evision_custom_headers/evision_LSDDetector.hpp" 18 | #include "evision_custom_headers/evision_phase_unwrapping.hpp" 19 | #include "evision_custom_headers/evision_ppf_match_3d.hpp" 20 | #include "evision_custom_headers/evision_stereo.hpp" 21 | #include "evision_custom_headers/evision_tracking.hpp" 22 | #include "evision_custom_headers/evision_xfeatures2d.hpp" 23 | #include "evision_custom_headers/evision_ximgproc.hpp" 24 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/c_src/evision_custom_headers/.gitkeep -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_LSDDetector.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_LINE_DESCRIPTOR 2 | #include "opencv2/line_descriptor.hpp" 3 | 4 | template<> struct evisionVecConverter 5 | { 6 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 7 | { 8 | return evision_to_generic_vec(env, obj, value, info); 9 | } 10 | 11 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 12 | { 13 | return evision_from_generic_vec(env, value); 14 | } 15 | }; 16 | 17 | typedef std::vector vector_KeyLine; 18 | typedef std::vector > vector_vector_KeyLine; 19 | 20 | #endif // HAVE_OPENCV_LINE_DESCRIPTOR 21 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_async.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_CORE 2 | 3 | #include "opencv2/core/async.hpp" 4 | 5 | CV_ERL_TO_CLASS(AsyncArray); 6 | CV_ERL_FROM_CLASS(AsyncArray); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_barcode.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_BARCODE 2 | 3 | #include "opencv2/phase_unwrapping/histogramphaseunwrapping.hpp" 4 | 5 | typedef std::vector vector_BarcodeType; 6 | 7 | template<> struct evisionVecConverter 8 | { 9 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 10 | { 11 | return evision_to_generic_vec(env, obj, value, info); 12 | } 13 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 14 | { 15 | 16 | return evision_from_generic_vec(env, value); 17 | } 18 | }; 19 | 20 | template<> 21 | bool evision_to(ErlNifEnv *env, ERL_NIF_TERM o, std::vector& types, const ArgInfo& info) 22 | { 23 | return evisionVecConverter::to(env, o, types, info); 24 | } 25 | #endif // HAVE_OPENCV_BARCODE 26 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_cchecker.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_DNN 2 | #ifdef HAVE_OPENCV_MCC 3 | 4 | #include "opencv2/mcc.hpp" 5 | 6 | template <> 7 | struct evisionVecConverter> 8 | { 9 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector> &value, 10 | const ArgInfo &info) 11 | { 12 | return evision_to_generic_vec(env, obj, value, info); 13 | } 14 | 15 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector> &value) 16 | { 17 | return evision_from_generic_vec(env, value); 18 | } 19 | }; 20 | typedef std::vector> vector_Ptr_CChecker; 21 | typedef dnn::Net dnn_Net; 22 | 23 | #endif // HAVE_OPENCV_MCC 24 | #endif // HAVE_OPENCV_DNN 25 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_cuda.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_CORE 2 | 3 | #include "opencv2/core/cuda.hpp" 4 | 5 | typedef std::vector vector_GpuMat; 6 | typedef cuda::GpuMat::Allocator GpuMat_Allocator; 7 | typedef cuda::HostMem::AllocType HostMem_AllocType; 8 | typedef cuda::Event::CreateFlags Event_CreateFlags; 9 | typedef cuda::GpuMat cuda_GpuMat; 10 | typedef cuda::Stream cuda_Stream; 11 | typedef cuda::Event cuda_Event; 12 | typedef cuda::HostMem cuda_HostMem; 13 | 14 | template<> struct evisionVecConverter 15 | { 16 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 17 | { 18 | return evision_to_generic_vec(env, obj, value, info); 19 | } 20 | 21 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 22 | { 23 | return evision_from_generic_vec(env, value); 24 | } 25 | }; 26 | 27 | CV_ERL_TO_CLASS(cuda::GpuMat); 28 | CV_ERL_TO_CLASS(cuda::Stream); 29 | CV_ERL_TO_CLASS(cuda::Event); 30 | CV_ERL_TO_CLASS(cuda::HostMem); 31 | 32 | CV_ERL_TO_CLASS_PTR(cuda::GpuMat); 33 | CV_ERL_TO_CLASS_PTR(cuda::GpuMat::Allocator); 34 | 35 | CV_ERL_FROM_CLASS(cuda::GpuMat); 36 | CV_ERL_FROM_CLASS(cuda::Stream); 37 | CV_ERL_FROM_CLASS(cuda::HostMem); 38 | 39 | CV_ERL_FROM_CLASS_PTR(cuda::GpuMat::Allocator); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_features2d.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_FEATURES2D 2 | typedef SimpleBlobDetector::Params SimpleBlobDetector_Params; 3 | typedef AKAZE::DescriptorType AKAZE_DescriptorType; 4 | typedef AgastFeatureDetector::DetectorType AgastFeatureDetector_DetectorType; 5 | typedef FastFeatureDetector::DetectorType FastFeatureDetector_DetectorType; 6 | typedef DescriptorMatcher::MatcherType DescriptorMatcher_MatcherType; 7 | typedef KAZE::DiffusivityType KAZE_DiffusivityType; 8 | typedef ORB::ScoreType ORB_ScoreType; 9 | #endif -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_flann.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_FLANN 2 | typedef cvflann::flann_distance_t cvflann_flann_distance_t; 3 | typedef cvflann::flann_algorithm_t cvflann_flann_algorithm_t; 4 | 5 | // template<> 6 | // ERL_NIF_TERM evision_from(ErlNifEnv *env, const cvflann_flann_algorithm_t& value) 7 | // { 8 | // return enif_make_int(env, int(value)); 9 | // } 10 | 11 | // template<> 12 | // ERL_NIF_TERM evision_from(ErlNifEnv *env, const cvflann_flann_distance_t& value) 13 | // { 14 | // return enif_make_int(env, int(value)); 15 | // } 16 | 17 | template<> 18 | bool evision_to(ErlNifEnv *env, ERL_NIF_TERM o, cv::flann::IndexParams& p, const ArgInfo& info) 19 | { 20 | CV_UNUSED(info); 21 | bool ok = true; 22 | ERL_NIF_TERM key; 23 | ERL_NIF_TERM value; 24 | 25 | if(enif_is_map(env, o)) { 26 | ErlNifMapIterator iter; 27 | enif_map_iterator_create(env, o, &iter, ERL_NIF_MAP_ITERATOR_FIRST); 28 | while(enif_map_iterator_get_pair(env, &iter, &key, &value)) 29 | { 30 | // get key 31 | std::string k; 32 | if (!evision::nif::get(env, key, k)) 33 | { 34 | ok = false; 35 | break; 36 | } 37 | // get value 38 | bool val; 39 | int i32; 40 | double f64; 41 | if( evision::nif::get(env, value, &val) ) 42 | { 43 | p.setBool(k, val); 44 | } 45 | else if( enif_get_int(env, value, &i32) ) 46 | { 47 | if( strcmp(k.c_str(), "algorithm") == 0 ) 48 | p.setAlgorithm(i32); 49 | else 50 | p.setInt(k, i32); 51 | } 52 | else if( enif_get_double(env, value, &f64) ) 53 | { 54 | p.setDouble(k, f64); 55 | } 56 | else 57 | { 58 | std::string val_str; 59 | if (!evision::nif::get(env, value, val_str)) 60 | { 61 | ok = false; 62 | break; 63 | } 64 | p.setString(k, val_str); 65 | } 66 | enif_map_iterator_next(env, &iter); 67 | } 68 | enif_map_iterator_destroy(env, &iter); 69 | } 70 | 71 | return ok; 72 | } 73 | 74 | template<> 75 | bool evision_to(ErlNifEnv *env, ERL_NIF_TERM obj, cv::flann::SearchParams & value, const ArgInfo& info) 76 | { 77 | return evision_to(env, obj, value, info); 78 | } 79 | 80 | // template<> 81 | // bool evision_to(ErlNifEnv *env, ERL_NIF_TERM o, cvflann::flann_distance_t& dist, const ArgInfo& info) 82 | // { 83 | // int d = (int)dist; 84 | // bool ok = evision_to(env, o, d, info); 85 | // dist = (cvflann::flann_distance_t)d; 86 | // return ok; 87 | // } 88 | #endif 89 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_linemod.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_RGBD 2 | #include "opencv2/core/saturate.hpp" 3 | 4 | template<> struct evisionVecConverter 5 | { 6 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 7 | { 8 | return evision_to_generic_vec(env, obj, value, info); 9 | } 10 | 11 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 12 | { 13 | return evision_from_generic_vec(env, value); 14 | } 15 | }; 16 | 17 | template<> struct evisionVecConverter 18 | { 19 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 20 | { 21 | return evision_to_generic_vec(env, obj, value, info); 22 | } 23 | 24 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 25 | { 26 | return evision_from_generic_vec(env, value); 27 | } 28 | }; 29 | 30 | template<> struct evisionVecConverter 31 | { 32 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 33 | { 34 | return evision_to_generic_vec(env, obj, value, info); 35 | } 36 | 37 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 38 | { 39 | return evision_from_generic_vec(env, value); 40 | } 41 | }; 42 | 43 | template<> struct evisionVecConverter > 44 | { 45 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector >& value, const ArgInfo& info) 46 | { 47 | return evision_to_generic_vec(env, obj, value, info); 48 | } 49 | 50 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector >& value) 51 | { 52 | return evision_from_generic_vec(env, value); 53 | } 54 | }; 55 | 56 | typedef std::vector vector_Match; 57 | typedef std::vector vector_Template; 58 | typedef std::vector vector_Feature; 59 | typedef std::vector > vector_Ptr_Modality; 60 | #endif 61 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_ml.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_ML 2 | 3 | template<> 4 | bool evision_to(ErlNifEnv *env, ERL_NIF_TERM obj, CvTermCriteria& dst, const ArgInfo& info) 5 | { 6 | CV_UNUSED(info); 7 | if (evision::nif::check_nil(env, obj)) { 8 | return true; 9 | } 10 | 11 | const ERL_NIF_TERM *terms; 12 | int length; 13 | if (!enif_get_tuple(env, obj, &length, &terms)) { 14 | failmsg(env, "Can't parse '%s' as TermCriteria." 15 | "Input argument is not a tuple", 16 | info.name); 17 | return false; 18 | } 19 | const std::size_t sequenceSize = length; 20 | if (sequenceSize != 3) 21 | { 22 | failmsg(env, "Can't parse '%s' as CvTermCriteria. Expected sequence length 3, got %lu", 23 | info.name, sequenceSize); 24 | return false; 25 | } 26 | 27 | { 28 | const String typeItemName = format("'%s' type", info.name); 29 | const ArgInfo typeItemInfo(typeItemName.c_str(), false); 30 | if (!evision_to(env, terms[0], dst.type, typeItemInfo)) 31 | { 32 | return false; 33 | } 34 | } 35 | { 36 | const String maxIterItemName = format("'%s' max_iter", info.name); 37 | const ArgInfo maxIterItemInfo(maxIterItemName.c_str(), false); 38 | if (!evision_to(env, terms[1], dst.max_iter, maxIterItemInfo)) 39 | { 40 | return false; 41 | } 42 | } 43 | { 44 | const String epsilonItemName = format("'%s' epsilon", info.name); 45 | const ArgInfo epsilonItemInfo(epsilonItemName.c_str(), false); 46 | if (!evision_to(env, terms[2], dst.epsilon, epsilonItemInfo)) 47 | { 48 | return false; 49 | } 50 | } 51 | 52 | return true; 53 | } 54 | 55 | template<> 56 | bool evision_to(ErlNifEnv *env, ERL_NIF_TERM obj, CvSlice& r, const ArgInfo& info) 57 | { 58 | CV_UNUSED(info); 59 | if (evision::nif::check_nil(env, obj)) { 60 | return true; 61 | } 62 | 63 | const ERL_NIF_TERM *terms; 64 | int length; 65 | if (!enif_get_tuple(env, obj, &length, &terms)) { 66 | failmsg(env, "Can't parse '%s' as CvSlice." 67 | "Input argument is not a tuple", 68 | info.name); 69 | return false; 70 | } 71 | const std::size_t sequenceSize = length; 72 | if(sequenceSize == 0) 73 | { 74 | r = CV_WHOLE_SEQ; 75 | return true; 76 | } 77 | 78 | if (sequenceSize != 2) 79 | { 80 | failmsg(env, "Can't parse '%s' as CvSlice. Expected sequence length 2, got %lu", 81 | info.name, sequenceSize); 82 | return false; 83 | } 84 | { 85 | const String startIndexItemName = format("'%s' start index", info.name); 86 | const ArgInfo startIndexItemInfo(startIndexItemName.c_str(), false); 87 | if (!evision_to(env, terms[0], r.start_index, startIndexItemInfo)) 88 | { 89 | return false; 90 | } 91 | } 92 | { 93 | const String endIndexItemName = format("'%s' end index", info.name); 94 | const ArgInfo endIndexItemInfo(endIndexItemName.c_str(), false); 95 | if (!evision_to(env, terms[1], r.end_index, endIndexItemInfo)) 96 | { 97 | return false; 98 | } 99 | } 100 | return true; 101 | } 102 | 103 | #endif // HAVE_OPENCV_ML 104 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_phase_unwrapping.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_PHASE_UNWRAPPING 2 | typedef cv::phase_unwrapping::HistogramPhaseUnwrapping::Params HistogramPhaseUnwrapping_Params; 3 | #endif 4 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_ppf_match_3d.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_SURFACE_MATCHING 2 | 3 | template<> struct evisionVecConverter 4 | { 5 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 6 | { 7 | return evision_to_generic_vec(env, obj, value, info); 8 | } 9 | 10 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 11 | { 12 | return evision_from_generic_vec(env, value); 13 | } 14 | }; 15 | 16 | typedef std::vector vector_Pose3DPtr; 17 | 18 | #endif // HAVE_OPENCV_SURFACE_MATCHING 19 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_stereo.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_STEREO 2 | typedef std::vector vector_MatchQuasiDense; 3 | 4 | template<> struct evisionVecConverter 5 | { 6 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 7 | { 8 | return evision_to_generic_vec(env, obj, value, info); 9 | } 10 | 11 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 12 | { 13 | return evision_from_generic_vec(env, value); 14 | } 15 | }; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_stitching.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_STITCHING 2 | 3 | typedef Stitcher::Status Status; 4 | typedef Stitcher::Mode Mode; 5 | 6 | typedef std::vector vector_ImageFeatures; 7 | typedef std::vector vector_MatchesInfo; 8 | typedef std::vector vector_CameraParams; 9 | 10 | template<> struct evisionVecConverter 11 | { 12 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 13 | { 14 | return evision_to_generic_vec(env, obj, value, info); 15 | } 16 | 17 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 18 | { 19 | return evision_from_generic_vec(env, value); 20 | } 21 | }; 22 | 23 | template<> struct evisionVecConverter 24 | { 25 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 26 | { 27 | return evision_to_generic_vec(env, obj, value, info); 28 | } 29 | 30 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 31 | { 32 | return evision_from_generic_vec(env, value); 33 | } 34 | }; 35 | 36 | template<> struct evisionVecConverter 37 | { 38 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 39 | { 40 | return evision_to_generic_vec(env, obj, value, info); 41 | } 42 | 43 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 44 | { 45 | return evision_from_generic_vec(env, value); 46 | } 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_tracking.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_TRACKING 2 | typedef TrackerCSRT::Params TrackerCSRT_Params; 3 | typedef TrackerKCF::Params TrackerKCF_Params; 4 | #endif 5 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_umat.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_CORE 2 | 3 | #include "opencv2/core/mat.hpp" 4 | 5 | typedef std::vector vector_Range; 6 | 7 | CV_ERL_TO_CLASS(UMat); 8 | CV_ERL_FROM_CLASS(UMat); 9 | 10 | static bool cv_mappable_to(const Ptr& src, Ptr& dst) 11 | { 12 | //dst.reset(new UMat(src->getUMat(ACCESS_RW))); 13 | dst.reset(new UMat()); 14 | src->copyTo(*dst); 15 | return true; 16 | } 17 | 18 | static void* cv_UMat_queue() 19 | { 20 | return cv::ocl::Queue::getDefault().ptr(); 21 | } 22 | 23 | static void* cv_UMat_context() 24 | { 25 | return cv::ocl::Context::getDefault().ptr(); 26 | } 27 | 28 | static Mat cv_UMat_get(const UMat* _self) 29 | { 30 | Mat m; 31 | _self->copyTo(m); 32 | return m; 33 | } 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_video.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_VIDEO 2 | typedef TrackerMIL::Params TrackerMIL_Params; 3 | typedef TrackerGOTURN::Params TrackerGOTURN_Params; 4 | typedef TrackerDaSiamRPN::Params TrackerDaSiamRPN_Params; 5 | 6 | #if (CV_VERSION_MAJOR >= 4 && CV_VERSION_MINOR >= 7) 7 | typedef TrackerNano::Params TrackerNano_Params; 8 | #endif 9 | 10 | #if (CV_VERSION_MAJOR >= 4 && CV_VERSION_MINOR >= 9) 11 | typedef TrackerVit::Params TrackerVit_Params; 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_videoio.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_VIDEOIO 2 | typedef std::vector vector_VideoCaptureAPIs; 3 | typedef std::vector vector_VideoCapture; 4 | 5 | template<> struct evisionVecConverter 6 | { 7 | static bool to(ErlNifEnv *env, ERL_NIF_TERM obj, std::vector& value, const ArgInfo& info) 8 | { 9 | return evision_to_generic_vec(env, obj, value, info); 10 | } 11 | 12 | static ERL_NIF_TERM from(ErlNifEnv *env, const std::vector& value) 13 | { 14 | return evision_from_generic_vec(env, value); 15 | } 16 | }; 17 | 18 | template<> 19 | bool evision_to(ErlNifEnv *env, ERL_NIF_TERM o, std::vector& apis, const ArgInfo& info) 20 | { 21 | return evisionVecConverter::to(env, o, apis, info); 22 | } 23 | 24 | #endif // HAVE_OPENCV_VIDEOIO 25 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_xfeatures2d.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_XFEATURES2D 2 | 3 | #include "opencv2/xfeatures2d.hpp" 4 | using cv::xfeatures2d::DAISY; 5 | 6 | typedef DAISY::NormalizationType DAISY_NormalizationType; 7 | 8 | // Compatibility 9 | // SIFT is moved to the main repository 10 | 11 | namespace cv { 12 | namespace xfeatures2d { 13 | 14 | /** Use cv.SIFT_create() instead */ 15 | CV_WRAP static inline 16 | Ptr SIFT_create(int nfeatures = 0, int nOctaveLayers = 3, 17 | double contrastThreshold = 0.04, double edgeThreshold = 10, 18 | double sigma = 1.6) 19 | { 20 | return SIFT::create(nfeatures, nOctaveLayers, contrastThreshold, edgeThreshold, sigma); 21 | } 22 | 23 | }} // namespace 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /c_src/evision_custom_headers/evision_ximgproc.hpp: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_XIMGPROC 2 | typedef cv::ximgproc::EdgeDrawing::Params EdgeDrawing_Params; 3 | #endif 4 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/abs.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_ABS_H 2 | #define EVISION_BACKEND_ABS_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_abs, evision_cv_mat_abs, 1 8 | // @evision nif: def mat_abs(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_abs(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat img; 18 | Mat ret; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("img", 0))) { 21 | int error_flag = false; 22 | ERRWRAP2(ret = Mat(cv::abs(img)), env, error_flag, error_term); 23 | if (!error_flag) { 24 | return evision_from(env, ret); 25 | } 26 | } 27 | } 28 | 29 | if (error_term != 0) return error_term; 30 | else return enif_make_badarg(env); 31 | } 32 | 33 | #endif // EVISION_BACKEND_ABS_H 34 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/add.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_ADD_H 2 | #define EVISION_BACKEND_ADD_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | #include "../evision_mat_utils.hpp" 7 | 8 | // @evision c: mat_add, evision_cv_mat_add, 1 9 | // @evision nif: def mat_add(_opts \\ []), do: :erlang.nif_error(:undefined) 10 | static ERL_NIF_TERM evision_cv_mat_add(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 11 | using namespace cv; 12 | using namespace evision::nif; 13 | ERL_NIF_TERM error_term = 0; 14 | std::map erl_terms; 15 | int nif_opts_index = 0; 16 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 17 | 18 | { 19 | Mat l; 20 | Mat r; 21 | 22 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 23 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 24 | Mat ret; 25 | int error_flag = false; 26 | ERRWRAP2(cv::add(l, r, ret, cv::noArray(), -1), env, error_flag, error_term); 27 | if (!error_flag) { 28 | return evision_from(env, ret); 29 | } 30 | } 31 | } 32 | 33 | if (error_term != 0) return error_term; 34 | else return enif_make_badarg(env); 35 | } 36 | 37 | // @evision c: mat_add_typed, evision_cv_mat_add_typed, 1 38 | // @evision nif: def mat_add_typed(_opts \\ []), do: :erlang.nif_error(:undefined) 39 | static ERL_NIF_TERM evision_cv_mat_add_typed(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 40 | using namespace cv; 41 | ERL_NIF_TERM error_term = 0; 42 | std::map erl_terms; 43 | int nif_opts_index = 0; 44 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 45 | 46 | { 47 | Mat lhs; 48 | Mat rhs; 49 | std::string t; 50 | int l = 0; 51 | 52 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "lhs"), lhs, ArgInfo("lhs", 0)) && 53 | evision_to_safe(env, evision_get_kw(env, erl_terms, "rhs"), rhs, ArgInfo("rhs", 0)) && 54 | evision_to_safe(env, evision_get_kw(env, erl_terms, "t"), t, ArgInfo("t", 0)) && 55 | evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0))) { 56 | int type; 57 | if (!get_binary_type(t, l, 0, type)) return evision::nif::error(env, "not implemented for the given type"); 58 | 59 | Mat ret; 60 | int error_flag = false; 61 | ERRWRAP2(cv::add(lhs, rhs, ret, cv::noArray(), type), env, error_flag, error_term); 62 | if (!error_flag) { 63 | return evision_from(env, ret); 64 | } 65 | } 66 | } 67 | 68 | if (error_term != 0) return error_term; 69 | else return enif_make_badarg(env); 70 | } 71 | 72 | #endif // EVISION_BACKEND_ADD_H 73 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/backend.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_BACKEND_H 2 | #define EVISION_BACKEND_BACKEND_H 3 | 4 | #include "abs.h" 5 | #include "add.h" 6 | #include "bitwise_and.h" 7 | #include "bitwise_not.h" 8 | #include "bitwise_or.h" 9 | #include "bitwise_xor.h" 10 | #include "broadcast.h" 11 | #include "ceil.h" 12 | #include "clip.h" 13 | #include "cmp.h" 14 | #include "divide.h" 15 | #include "expm1.h" 16 | #include "eye.h" 17 | #include "floor.h" 18 | #include "from_binary.h" 19 | #include "logical_and.h" 20 | #include "logical_or.h" 21 | #include "logical_xor.h" 22 | #include "matrix_multiply.h" 23 | #include "multiply.h" 24 | #include "negate.h" 25 | #include "reshape.h" 26 | #include "round.h" 27 | #include "sign.h" 28 | #include "subtract.h" 29 | #include "to_batched.h" 30 | #include "to_binary.h" 31 | #include "transpose.h" 32 | 33 | #endif // EVISION_BACKEND_BACKEND_H 34 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/bitwise_and.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_BITWISE_AND_H 2 | #define EVISION_BACKEND_BITWISE_AND_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_bitwise_and, evision_cv_mat_bitwise_and, 1 8 | // @evision nif: def mat_bitwise_and(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_bitwise_and(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat l; 18 | Mat r; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 21 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 22 | Mat ret; 23 | int error_flag = false; 24 | ERRWRAP2(cv::bitwise_and(l, r, ret), env, error_flag, error_term); 25 | if (!error_flag) { 26 | return evision_from(env, ret); 27 | } 28 | } 29 | } 30 | 31 | if (error_term != 0) return error_term; 32 | else return enif_make_badarg(env); 33 | } 34 | 35 | #endif // EVISION_BACKEND_BITWISE_AND_H 36 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/bitwise_not.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_BITWISE_NOT_H 2 | #define EVISION_BACKEND_BITWISE_NOT_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_bitwise_not, evision_cv_mat_bitwise_not, 1 8 | // @evision nif: def mat_bitwise_not(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_bitwise_not(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat img; 18 | 19 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("img", 0))) { 20 | Mat ret; 21 | 22 | int error_flag = false; 23 | ERRWRAP2(ret = Mat(~img), env, error_flag, error_term); 24 | if (!error_flag) { 25 | return evision_from(env, ret); 26 | } 27 | } 28 | } 29 | 30 | if (error_term != 0) return error_term; 31 | else return enif_make_badarg(env); 32 | } 33 | 34 | #endif // EVISION_BACKEND_BITWISE_NOT_H 35 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/bitwise_or.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_BITWISE_OR_H 2 | #define EVISION_BACKEND_BITWISE_OR_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_bitwise_or, evision_cv_mat_bitwise_or, 1 8 | // @evision nif: def mat_bitwise_or(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_bitwise_or(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat l; 18 | Mat r; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 21 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 22 | Mat ret; 23 | 24 | int error_flag = false; 25 | ERRWRAP2(cv::bitwise_or(l, r, ret), env, error_flag, error_term); 26 | if (!error_flag) { 27 | return evision_from(env, ret); 28 | } 29 | } 30 | } 31 | 32 | if (error_term != 0) return error_term; 33 | else return enif_make_badarg(env); 34 | } 35 | 36 | #endif // EVISION_BACKEND_BITWISE_OR_H 37 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/bitwise_xor.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_BITWISE_XOR_H 2 | #define EVISION_BACKEND_BITWISE_XOR_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_bitwise_xor, evision_cv_mat_bitwise_xor, 1 8 | // @evision nif: def mat_bitwise_xor(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_bitwise_xor(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat l; 18 | Mat r; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 21 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 22 | Mat ret; 23 | 24 | int error_flag = false; 25 | ERRWRAP2(cv::bitwise_xor(l, r, ret), env, error_flag, error_term); 26 | if (!error_flag) { 27 | return evision_from(env, ret); 28 | } 29 | } 30 | } 31 | 32 | if (error_term != 0) return error_term; 33 | else return enif_make_badarg(env); 34 | } 35 | 36 | #endif // EVISION_BACKEND_BITWISE_XOR_H 37 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/ceil.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_CEIL_H 2 | #define EVISION_BACKEND_CEIL_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_ceil, evision_cv_mat_ceil, 1 8 | // @evision nif: def mat_ceil(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_ceil(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat img; 18 | 19 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("img", 0))) { 20 | int type = img.type(); 21 | uint8_t depth = type & CV_MAT_DEPTH_MASK; 22 | if (depth == CV_32F) { 23 | auto ptr = img.ptr(); 24 | size_t count = img.total(); 25 | for (size_t i = 0; i < count; ++i) { 26 | ptr[i] = ceilf(ptr[i]); 27 | } 28 | return evision_from(env, img); 29 | } else if (depth == CV_64F) { 30 | auto ptr = img.ptr(); 31 | size_t count = img.total(); 32 | for (size_t i = 0; i < count; ++i) { 33 | ptr[i] = ceil(ptr[i]); 34 | } 35 | return evision_from(env, img); 36 | } else { 37 | return evision_from(env, img); 38 | } 39 | } 40 | } 41 | 42 | if (error_term != 0) return error_term; 43 | else return enif_make_badarg(env); 44 | } 45 | 46 | #endif // EVISION_BACKEND_CEIL_H 47 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/clip.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_CLIP_H 2 | #define EVISION_BACKEND_CLIP_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_clip, evision_cv_mat_clip, 1 8 | // @evision nif: def mat_clip(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_clip(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | int error_flag = false; 16 | 17 | { 18 | Mat img; 19 | double lower; 20 | double upper; 21 | 22 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("img", 0)) && 23 | evision_to_safe(env, evision_get_kw(env, erl_terms, "lower"), lower, ArgInfo("lower", 0)) && 24 | evision_to_safe(env, evision_get_kw(env, erl_terms, "upper"), upper, ArgInfo("upper", 0))) { 25 | ERRWRAP2(img.setTo(lower, img < lower), env, error_flag, error_term); 26 | if (!error_flag) { 27 | ERRWRAP2(img.setTo(lower, img > lower), env, error_flag, error_term); 28 | if (!error_flag) { 29 | return evision_from(env, img); 30 | } 31 | } 32 | } 33 | } 34 | 35 | if (error_flag) return error_term; 36 | else return enif_make_badarg(env); 37 | } 38 | 39 | #endif // EVISION_BACKEND_CLIP_H 40 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/cmp.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_CMP_H 2 | #define EVISION_BACKEND_CMP_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_cmp, evision_cv_mat_cmp, 1 8 | // @evision nif: def mat_cmp(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_cmp(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | int error_flag = false; 16 | 17 | { 18 | Mat l; 19 | Mat r; 20 | std::string type; 21 | 22 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 23 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0)) && 24 | evision_to_safe(env, evision_get_kw(env, erl_terms, "type"), type, ArgInfo("type", 0))) { 25 | 26 | int cmpop = -1; 27 | if (type == "eq") cmpop = cv::CMP_EQ; 28 | else if (type == "gt") cmpop = cv::CMP_GT; 29 | else if (type == "ge") cmpop = cv::CMP_GE; 30 | else if (type == "lt") cmpop = cv::CMP_LT; 31 | else if (type == "le") cmpop = cv::CMP_LE; 32 | else if (type == "ne") cmpop = cv::CMP_NE; 33 | else { 34 | return evision::nif::error(env, "not implemented for the requested compare type, only 'eq', 'gt', 'ge', 'lt', 'le' and 'ne' are supported."); 35 | } 36 | 37 | Mat ret; 38 | 39 | ERRWRAP2(cv::compare(l, r, ret, cmpop), env, error_flag, error_term); 40 | if (!error_flag) { 41 | ERRWRAP2(ret = ret / 255, env, error_flag, error_term); 42 | if (!error_flag) { 43 | return evision_from(env, ret); 44 | } 45 | } 46 | } 47 | } 48 | 49 | if (error_flag) return error_term; 50 | else return enif_make_badarg(env); 51 | } 52 | 53 | #endif // EVISION_BACKEND_CMP_H 54 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/divide.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_DIVIDE_H 2 | #define EVISION_BACKEND_DIVIDE_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_divide, evision_cv_mat_divide, 1 8 | // @evision nif: def mat_divide(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_divide(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | int error_flag = false; 16 | 17 | { 18 | Mat l; 19 | Mat r; 20 | 21 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 22 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 23 | Mat ret; 24 | 25 | ERRWRAP2(cv::divide(l, r, ret, 1, -1), env, error_flag, error_term); 26 | if (!error_flag) { 27 | return evision_from(env, ret); 28 | } 29 | } 30 | } 31 | 32 | if (error_flag) return error_term; 33 | else return enif_make_badarg(env); 34 | } 35 | 36 | // @evision c: mat_divide_typed, evision_cv_mat_divide_typed, 1 37 | // @evision nif: def mat_divide_typed(_opts \\ []), do: :erlang.nif_error(:undefined) 38 | static ERL_NIF_TERM evision_cv_mat_divide_typed(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 39 | using namespace cv; 40 | ERL_NIF_TERM error_term = 0; 41 | std::map erl_terms; 42 | int nif_opts_index = 0; 43 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 44 | 45 | { 46 | Mat lhs; 47 | Mat rhs; 48 | std::string t; 49 | int l = 0; 50 | 51 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "lhs"), lhs, ArgInfo("lhs", 0)) && 52 | evision_to_safe(env, evision_get_kw(env, erl_terms, "rhs"), rhs, ArgInfo("rhs", 0)) && 53 | evision_to_safe(env, evision_get_kw(env, erl_terms, "t"), t, ArgInfo("t", 0)) && 54 | evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0))) { 55 | int type; 56 | if (!get_binary_type(t, l, 0, type)) return evision::nif::error(env, "not implemented for the given type"); 57 | Mat ret; 58 | cv::divide(lhs, rhs, ret, 1, type); 59 | return evision_from(env, ret); 60 | } 61 | } 62 | 63 | if (error_term != 0) return error_term; 64 | else return enif_make_badarg(env); 65 | } 66 | 67 | #endif // EVISION_BACKEND_DIVIDE_H 68 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/expm1.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_EXPM1_H 2 | #define EVISION_BACKEND_EXPM1_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_expm1, evision_cv_mat_expm1, 1 8 | // @evision nif: def mat_expm1(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_expm1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | int error_flag = false; 16 | 17 | { 18 | Mat img; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("img", 0))) { 21 | Mat ret; 22 | 23 | ERRWRAP2(cv::exp(img, ret), env, error_flag, error_term); 24 | if (!error_flag) { 25 | ERRWRAP2(ret = Mat(ret - 1), env, error_flag, error_term); 26 | if (!error_flag) { 27 | return evision_from(env, ret); 28 | } 29 | } 30 | cv::exp(img, ret); 31 | return evision_from(env, Mat(ret - 1)); 32 | } 33 | } 34 | 35 | if (error_flag) return error_term; 36 | else return enif_make_badarg(env); 37 | } 38 | 39 | #endif // EVISION_BACKEND_EXPM1_H 40 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/eye.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_EYE_H 2 | #define EVISION_BACKEND_EYE_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_eye,evision_cv_mat_eye,1 8 | // @evision nif: def mat_eye(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_eye(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | std::string t; 18 | int l = 0; 19 | uint64_t n = 0; 20 | 21 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "n"), n, ArgInfo("n", 0)) && 22 | evision_to_safe(env, evision_get_kw(env, erl_terms, "t"), t, ArgInfo("t", 0)) && 23 | evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0))) { 24 | int type = 0; 25 | if (!get_binary_type(t, l, 1, type)) return evision::nif::error(env, "not implemented for the given type"); 26 | 27 | Mat ret; 28 | int error_flag = false; 29 | ERRWRAP2(ret = Mat::eye((int)n, (int)n, type), env, error_flag, error_term); 30 | if (!error_flag) { 31 | return evision_from(env, ret); 32 | } 33 | } 34 | } 35 | 36 | if (error_term != 0) return error_term; 37 | else return enif_make_badarg(env); 38 | } 39 | 40 | #endif // EVISION_BACKEND_EYE_H 41 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/floor.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_FLOOR_H 2 | #define EVISION_BACKEND_FLOOR_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_floor, evision_cv_mat_floor, 1 8 | // @evision nif: def mat_floor(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_floor(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat img; 18 | 19 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("img", 0))) { 20 | int type = img.type(); 21 | uint8_t depth = type & CV_MAT_DEPTH_MASK; 22 | if (depth == CV_32F) { 23 | auto ptr = img.ptr(); 24 | size_t count = img.total(); 25 | for (size_t i = 0; i < count; ++i) { 26 | ptr[i] = floorf(ptr[i]); 27 | } 28 | return evision_from(env, img); 29 | } else if (depth == CV_64F) { 30 | auto ptr = img.ptr(); 31 | size_t count = img.total(); 32 | for (size_t i = 0; i < count; ++i) { 33 | ptr[i] = floor(ptr[i]); 34 | } 35 | return evision_from(env, img); 36 | } else { 37 | return evision_from(env, img); 38 | } 39 | } 40 | } 41 | 42 | if (error_term != 0) return error_term; 43 | else return enif_make_badarg(env); 44 | } 45 | 46 | #endif // EVISION_BACKEND_FLOOR_H 47 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/logical_and.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_LOGICAL_AND_H 2 | #define EVISION_BACKEND_LOGICAL_AND_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_logical_and, evision_cv_mat_logical_and, 1 8 | // @evision nif: def mat_logical_and(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_logical_and(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat l; 18 | Mat r; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 21 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 22 | Mat ret; 23 | int error_flag = false; 24 | ERRWRAP2(ret = l & r, env, error_flag, error_term); 25 | if (!error_flag) { 26 | return evision_from(env, ret); 27 | } 28 | } 29 | } 30 | 31 | if (error_term != 0) return error_term; 32 | else return enif_make_badarg(env); 33 | } 34 | 35 | #endif // EVISION_BACKEND_LOGICAL_AND_H 36 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/logical_or.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_LOGICAL_OR_H 2 | #define EVISION_BACKEND_LOGICAL_OR_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_logical_or, evision_cv_mat_logical_or, 1 8 | // @evision nif: def mat_logical_or(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_logical_or(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat l; 18 | Mat r; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 21 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 22 | Mat ret; 23 | int error_flag = false; 24 | ERRWRAP2(ret = l | r, env, error_flag, error_term); 25 | if (!error_flag) { 26 | return evision_from(env, ret); 27 | } 28 | } 29 | } 30 | 31 | if (error_term != 0) return error_term; 32 | else return enif_make_badarg(env); 33 | } 34 | 35 | #endif // EVISION_BACKEND_LOGICAL_OR_H 36 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/logical_xor.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_LOGICAL_XOR_H 2 | #define EVISION_BACKEND_LOGICAL_XOR_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_logical_xor, evision_cv_mat_logical_xor, 1 8 | // @evision nif: def mat_logical_xor(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_logical_xor(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat l; 18 | Mat r; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 21 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 22 | Mat ret; 23 | int error_flag = false; 24 | ERRWRAP2(ret = (l | r) & (l != r), env, error_flag, error_term); 25 | if (!error_flag) { 26 | return evision_from(env, ret); 27 | } 28 | } 29 | } 30 | 31 | if (error_term != 0) return error_term; 32 | else return enif_make_badarg(env); 33 | } 34 | 35 | #endif // EVISION_BACKEND_LOGICAL_XOR_H 36 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/matrix_multiply.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_MATRIX_MULTIPLY_H 2 | #define EVISION_BACKEND_MATRIX_MULTIPLY_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_matrix_multiply, evision_cv_mat_matrix_multiply, 1 8 | // @evision nif: def mat_matrix_multiply(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_matrix_multiply(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | int error_flag = false; 16 | 17 | { 18 | Mat lhs; 19 | Mat rhs; 20 | std::string t; 21 | int l = 0; 22 | 23 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "lhs"), lhs, ArgInfo("lhs", 0)) && 24 | evision_to_safe(env, evision_get_kw(env, erl_terms, "rhs"), rhs, ArgInfo("rhs", 0)) && 25 | evision_to_safe(env, evision_get_kw(env, erl_terms, "t"), t, ArgInfo("t", 0)) && 26 | evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0))) { 27 | int type; 28 | Mat ret; 29 | if (get_binary_type(t, l, 0, type)) { 30 | ERRWRAP2(ret = lhs * rhs, env, error_flag, error_term); 31 | if (!error_flag) { 32 | ERRWRAP2(ret.convertTo(ret, type), env, error_flag, error_term); 33 | if (!error_flag) { 34 | return evision_from(env, ret); 35 | } 36 | } 37 | } else { 38 | ERRWRAP2(ret = Mat(lhs * rhs), env, error_flag, error_term); 39 | if (!error_flag) { 40 | return evision_from(env, ret); 41 | } 42 | } 43 | } 44 | } 45 | 46 | if (error_flag) return error_term; 47 | else return enif_make_badarg(env); 48 | } 49 | 50 | #endif // EVISION_BACKEND_MATRIX_MULTIPLY_H 51 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/multiply.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_MULTIPLY_H 2 | #define EVISION_BACKEND_MULTIPLY_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_multiply, evision_cv_mat_multiply, 1 8 | // @evision nif: def mat_multiply(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_multiply(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat l; 18 | Mat r; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 21 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 22 | Mat ret; 23 | 24 | int error_flag = false; 25 | ERRWRAP2(cv::multiply(l, r, ret, 1, -1), env, error_flag, error_term); 26 | if (!error_flag) { 27 | return evision_from(env, ret); 28 | } 29 | } 30 | } 31 | 32 | if (error_term != 0) return error_term; 33 | else return enif_make_badarg(env); 34 | } 35 | 36 | // @evision c: mat_multiply_typed,evision_cv_mat_multiply_typed,1 37 | // @evision nif: def mat_multiply_typed(_opts \\ []), do: :erlang.nif_error(:undefined) 38 | static ERL_NIF_TERM evision_cv_mat_multiply_typed(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 39 | using namespace cv; 40 | ERL_NIF_TERM error_term = 0; 41 | std::map erl_terms; 42 | int nif_opts_index = 0; 43 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 44 | 45 | { 46 | Mat lhs; 47 | Mat rhs; 48 | std::string t; 49 | int l = 0; 50 | 51 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "lhs"), lhs, ArgInfo("lhs", 0)) && 52 | evision_to_safe(env, evision_get_kw(env, erl_terms, "rhs"), rhs, ArgInfo("rhs", 0)) && 53 | evision_to_safe(env, evision_get_kw(env, erl_terms, "t"), t, ArgInfo("t", 0)) && 54 | evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0))) { 55 | int type; 56 | if (!get_binary_type(t, l, 0, type)) return evision::nif::error(env, "not implemented for the given type"); 57 | Mat ret; 58 | cv::multiply(lhs, rhs, ret, 1, type); 59 | return evision_from(env, ret); 60 | } 61 | } 62 | 63 | if (error_term != 0) return error_term; 64 | else return enif_make_badarg(env); 65 | } 66 | 67 | #endif // EVISION_BACKEND_MULTIPLY_H 68 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/negate.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_NEGATE_H 2 | #define EVISION_BACKEND_NEGATE_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_negate, evision_cv_mat_negate, 1 8 | // @evision nif: def mat_negate(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_negate(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat img; 18 | 19 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("img", 0))) { 20 | int type = img.type(); 21 | uint8_t depth = type & CV_MAT_DEPTH_MASK; 22 | 23 | if (depth == CV_8U) { 24 | auto ptr = img.ptr(); 25 | size_t count = img.total(); 26 | for (size_t i = 0; i < count; ++i) { 27 | ptr[i] = 0 - ptr[i]; 28 | } 29 | return evision_from(env, img); 30 | } else if (depth == CV_16U) { 31 | auto ptr = img.ptr(); 32 | size_t count = img.total(); 33 | for (size_t i = 0; i < count; ++i) { 34 | ptr[i] = 0 - ptr[i]; 35 | } 36 | return evision_from(env, img); 37 | } else { 38 | Mat out = Mat(-img); 39 | return evision_from(env, out); 40 | } 41 | } 42 | } 43 | 44 | if (error_term != 0) return error_term; 45 | else return enif_make_badarg(env); 46 | } 47 | 48 | #endif // EVISION_BACKEND_NEGATE_H 49 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/reshape.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_RESHAPE_H 2 | #define EVISION_BACKEND_RESHAPE_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_reshape, evision_cv_mat_reshape, 1 8 | // @evision nif: def mat_reshape(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_reshape(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat mat; 18 | std::vector shape; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "mat"), mat, ArgInfo("mat", 0)) && 21 | evision_to_safe(env, evision_get_kw(env, erl_terms, "shape"), shape, ArgInfo("shape", 0))) { 22 | Mat ret; 23 | 24 | int error_flag = false; 25 | ERRWRAP2(ret = mat.reshape(0, shape), env, error_flag, error_term); 26 | if (!error_flag) { 27 | return evision_from(env, ret); 28 | } 29 | } 30 | } 31 | 32 | if (error_term != 0) return error_term; 33 | else return enif_make_badarg(env); 34 | } 35 | 36 | #endif // EVISION_BACKEND_RESHAPE_H 37 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/round.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_ROUND_H 2 | #define EVISION_BACKEND_ROUND_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_round, evision_cv_mat_round, 1 8 | // @evision nif: def mat_round(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_round(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat img; 18 | 19 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("img", 0))) { 20 | int type = img.type(); 21 | uint8_t depth = type & CV_MAT_DEPTH_MASK; 22 | if (depth == CV_32F) { 23 | auto ptr = img.ptr(); 24 | size_t count = img.total(); 25 | for (size_t i = 0; i < count; ++i) { 26 | ptr[i] = roundf(ptr[i]); 27 | } 28 | return evision_from(env, img); 29 | } else if (depth == CV_64F) { 30 | auto ptr = img.ptr(); 31 | size_t count = img.total(); 32 | for (size_t i = 0; i < count; ++i) { 33 | ptr[i] = round(ptr[i]); 34 | } 35 | return evision_from(env, img); 36 | } else { 37 | return evision_from(env, img); 38 | } 39 | } 40 | } 41 | 42 | if (error_term != 0) return error_term; 43 | else return enif_make_badarg(env); 44 | } 45 | 46 | #endif // EVISION_BACKEND_ROUND_H 47 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/sign.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_SIGN_H 2 | #define EVISION_BACKEND_SIGN_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_sign, evision_cv_mat_sign, 1 8 | // @evision nif: def mat_sign(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_sign(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | int error_flag = false; 16 | 17 | { 18 | Mat img; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("img", 0))) { 21 | ERRWRAP2(img.setTo(1, img > 0), env, error_flag, error_term); 22 | if (!error_flag) { 23 | ERRWRAP2(img.setTo(0, img == 0), env, error_flag, error_term); 24 | if (!error_flag) { 25 | ERRWRAP2(img.setTo(-1, img < 0), env, error_flag, error_term); 26 | if (!error_flag) { 27 | return evision_from(env, img); 28 | } 29 | } 30 | } 31 | } 32 | } 33 | 34 | if (error_flag) return error_term; 35 | else return enif_make_badarg(env); 36 | } 37 | 38 | #endif // EVISION_BACKEND_SIGN_H 39 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/subtract.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_SUBTRACT_H 2 | #define EVISION_BACKEND_SUBTRACT_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_subtract, evision_cv_mat_subtract, 1 8 | // @evision nif: def mat_subtract(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_subtract(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | Mat l; 18 | Mat r; 19 | 20 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0)) && 21 | evision_to_safe(env, evision_get_kw(env, erl_terms, "r"), r, ArgInfo("r", 0))) { 22 | Mat ret; 23 | 24 | int error_flag = false; 25 | ERRWRAP2(cv::subtract(l, r, ret, cv::noArray(), -1), env, error_flag, error_term); 26 | if (!error_flag) { 27 | return evision_from(env, ret); 28 | } 29 | } 30 | } 31 | 32 | if (error_term != 0) return error_term; 33 | else return enif_make_badarg(env); 34 | } 35 | 36 | // @evision c: mat_subtract_typed,evision_cv_mat_subtract_typed,1 37 | // @evision nif: def mat_subtract_typed(_opts \\ []), do: :erlang.nif_error(:undefined) 38 | static ERL_NIF_TERM evision_cv_mat_subtract_typed(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 39 | using namespace cv; 40 | ERL_NIF_TERM error_term = 0; 41 | std::map erl_terms; 42 | int nif_opts_index = 0; 43 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 44 | 45 | { 46 | Mat lhs; 47 | Mat rhs; 48 | std::string t; 49 | int l = 0; 50 | 51 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "lhs"), lhs, ArgInfo("lhs", 0)) && 52 | evision_to_safe(env, evision_get_kw(env, erl_terms, "rhs"), rhs, ArgInfo("rhs", 0)) && 53 | evision_to_safe(env, evision_get_kw(env, erl_terms, "t"), t, ArgInfo("t", 0)) && 54 | evision_to_safe(env, evision_get_kw(env, erl_terms, "l"), l, ArgInfo("l", 0))) { 55 | int type; 56 | if (!get_binary_type(t, l, 0, type)) return evision::nif::error(env, "not implemented for the given type"); 57 | Mat ret; 58 | cv::subtract(lhs, rhs, ret, cv::noArray(), type); 59 | return evision_from(env, ret); 60 | } 61 | } 62 | 63 | if (error_term != 0) return error_term; 64 | else return enif_make_badarg(env); 65 | } 66 | 67 | #endif // EVISION_BACKEND_SUBTRACT_H 68 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/to_batched.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_TO_BATCHED_LIST_H 2 | #define EVISION_BACKEND_TO_BATCHED_LIST_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_to_batched,evision_cv_mat_to_batched,1 8 | // @evision nif: def mat_to_batched(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_to_batched(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | Mat img; 17 | uint64_t batch_size; 18 | std::vector as_shape; 19 | std::string leftover; 20 | 21 | if (evision_to_safe(env, evision_get_kw(env, erl_terms, "img"), img, ArgInfo("l", 0)) && 22 | evision_to_safe(env, evision_get_kw(env, erl_terms, "batch_size"), batch_size, ArgInfo("batch_size", 0)) && 23 | evision_to_safe(env, evision_get_kw(env, erl_terms, "as_shape"), as_shape, ArgInfo("as_shape", 0)) && 24 | evision_to_safe(env, evision_get_kw(env, erl_terms, "leftover"), leftover, ArgInfo("leftover", 0))) { 25 | if (leftover != "repeat" && leftover != "discard") { 26 | return evision::nif::error(env, "to_batched failed: invalid option value for leftover. Valid values are :repeat and :discard"); 27 | } 28 | 29 | size_t mat_num_elem = img.total(); 30 | size_t as_shape_num_elem = 1; 31 | for (size_t i = 0; i < as_shape.size(); i++) { 32 | as_shape_num_elem *= as_shape[i]; 33 | } 34 | if (mat_num_elem != as_shape_num_elem) { 35 | return evision::nif::error(env, "to_batched failed: cannot treated matrix as the request shape"); 36 | } 37 | 38 | uint64_t remainder = as_shape[0] % batch_size; 39 | uint64_t num_full_batches = as_shape[0] / batch_size; 40 | uint64_t slice_size = as_shape_num_elem / as_shape[0] * img.elemSize(); 41 | uint64_t batch_bytes = slice_size * batch_size; 42 | 43 | unsigned num_batches = (unsigned)num_full_batches; 44 | if (remainder != 0) { 45 | if (leftover == "repeat") { 46 | num_batches += 1; 47 | } 48 | } 49 | 50 | ERL_NIF_TERM * batches = (ERL_NIF_TERM * )enif_alloc(sizeof(ERL_NIF_TERM) * num_batches); 51 | char * data = (char *)img.data; 52 | // skip the first (batches) 53 | int ndims = (int)as_shape.size(); 54 | as_shape[0] = (int)batch_size; 55 | int * sizes = (int *)as_shape.data(); 56 | 57 | // deal with full batches 58 | char * offset_data = data; 59 | for (size_t i = 0; i < num_full_batches; i++) { 60 | Mat mat = Mat(ndims, sizes, img.type(), offset_data); 61 | batches[i] = evision_from(env, mat.clone()); 62 | offset_data += batch_bytes; 63 | } 64 | // deal with leftover 65 | if (num_batches != num_full_batches) { 66 | char * last_batch_data = (char *)enif_alloc(batch_bytes); 67 | 68 | // copy leftover first 69 | uint64_t leftover_bytes = slice_size * remainder; 70 | memcpy(last_batch_data, offset_data, leftover_bytes); 71 | 72 | // repeat from the beginning 73 | uint64_t repeat_bytes = batch_bytes - leftover_bytes; 74 | 75 | memcpy(last_batch_data + leftover_bytes, data, repeat_bytes); 76 | Mat mat = Mat(ndims, sizes, img.type(), last_batch_data); 77 | batches[num_batches - 1] = evision_from(env, mat.clone()); 78 | enif_free(last_batch_data); 79 | } 80 | 81 | ERL_NIF_TERM ret = enif_make_list_from_array(env, batches, num_batches); 82 | enif_free(batches); 83 | 84 | return ret; 85 | } 86 | 87 | if (error_term != 0) return error_term; 88 | else return enif_make_badarg(env); 89 | } 90 | 91 | #endif // EVISION_BACKEND_TO_BATCHED_LIST_H 92 | -------------------------------------------------------------------------------- /c_src/modules/evision_backend/to_binary.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_BACKEND_TO_BINARY_H 2 | #define EVISION_BACKEND_TO_BINARY_H 3 | 4 | #include 5 | #include "../../ArgInfo.hpp" 6 | 7 | // @evision c: mat_to_binary,evision_cv_mat_to_binary,1 8 | // @evision nif: def mat_to_binary(_opts \\ []), do: :erlang.nif_error(:undefined) 9 | static ERL_NIF_TERM evision_cv_mat_to_binary(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { 10 | using namespace cv; 11 | ERL_NIF_TERM error_term = 0; 12 | std::map erl_terms; 13 | int nif_opts_index = 0; 14 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 15 | 16 | { 17 | // const char *keywords[] = {"img", NULL}; 18 | // zero-copy to_binary 19 | evision_res * res; 20 | if( enif_get_resource(env, evision_get_kw(env, erl_terms, "img"), evision_res::type, (void **)&res) ) { 21 | size_t bin_size = res->val->total() * res->val->elemSize(); 22 | ERL_NIF_TERM out_bin_term = enif_make_resource_binary(env, res, res->val->data, bin_size); 23 | return out_bin_term; 24 | } 25 | } 26 | 27 | if (error_term != 0) return error_term; 28 | else return enif_make_badarg(env); 29 | } 30 | 31 | #endif // EVISION_BACKEND_TO_BINARY_H 32 | -------------------------------------------------------------------------------- /c_src/modules/evision_cuda.cc: -------------------------------------------------------------------------------- 1 | #include "evision_cuda.h" 2 | 3 | #ifdef CUDA_ENABLED 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | std::optional, int>> 14 | get_cuda_ipc_handle(std::uintptr_t ptr) { 15 | void *device_ptr = reinterpret_cast(ptr); 16 | cudaIpcMemHandle_t ipc_handle; 17 | cudaError_t status = cudaIpcGetMemHandle(&ipc_handle, device_ptr); 18 | if (status != cudaSuccess) { 19 | return std::nullopt; 20 | } 21 | 22 | cudaPointerAttributes attributes{}; 23 | status = cudaPointerGetAttributes(&attributes, device_ptr); 24 | if (status != cudaSuccess) { 25 | return std::nullopt; 26 | } 27 | 28 | // Assuming sizeof(cudaIpcMemHandle_t) is constant 29 | const size_t size = sizeof(cudaIpcMemHandle_t); 30 | 31 | // Copy the memory handle to a byte array 32 | std::vector result(size); 33 | memcpy(result.data(), &ipc_handle, size); 34 | 35 | return std::make_pair(result, attributes.device); 36 | } 37 | 38 | std::pair get_pointer_for_ipc_handle(uint8_t *handle_bin, 39 | size_t handle_size, 40 | int device_id) { 41 | std::ostringstream error_msg_stream; 42 | if (handle_size != sizeof(cudaIpcMemHandle_t)) { 43 | error_msg_stream << "IPC handle size does not match " 44 | "sizeof(cudaIpcMemHandle_t), IPC handle size = " 45 | << handle_size << ", sizeof(cudaIpcMemHandle_t) = " 46 | << sizeof(cudaIpcMemHandle_t); 47 | return std::make_pair(nullptr, error_msg_stream.str()); 48 | } 49 | 50 | unsigned char ipc_handle_data[sizeof(cudaIpcMemHandle_t)]; 51 | for (size_t i = 0; i < sizeof(cudaIpcMemHandle_t); i++) { 52 | ipc_handle_data[i] = handle_bin[i]; 53 | } 54 | 55 | cudaIpcMemHandle_t ipc_handle; 56 | memcpy(&ipc_handle, ipc_handle_data, sizeof(cudaIpcMemHandle_t)); 57 | 58 | void *ptr = nullptr; 59 | cudaError_t cuda_status = cudaSetDevice(device_id); 60 | if (cuda_status != cudaSuccess) { 61 | error_msg_stream << "Error setting CUDA device: " 62 | << cudaGetErrorString(cuda_status); 63 | return std::make_pair(nullptr, error_msg_stream.str()); 64 | } 65 | 66 | cuda_status = cudaIpcOpenMemHandle((void **)&ptr, ipc_handle, 67 | cudaIpcMemLazyEnablePeerAccess); 68 | if (cuda_status != cudaSuccess) { 69 | error_msg_stream << "Error opening CUDA IPC memory handle: " 70 | << cudaGetErrorString(cuda_status); 71 | return std::make_pair(nullptr, error_msg_stream.str()); 72 | } 73 | 74 | return std::make_pair(ptr, ""); 75 | } 76 | 77 | std::optional get_gpumat_device_id(void * ptr) { 78 | if (ptr == nullptr) { 79 | return std::nullopt; 80 | } 81 | cudaPointerAttributes attributes{}; 82 | cudaError_t status = cudaPointerGetAttributes(&attributes, ptr); 83 | if (status != cudaSuccess) { 84 | return std::nullopt; 85 | } 86 | return attributes.device; 87 | } 88 | #else 89 | std::optional, int>> 90 | get_cuda_ipc_handle(std::uintptr_t ptr) { 91 | (void)ptr; 92 | return std::nullopt; 93 | } 94 | 95 | std::pair get_pointer_for_ipc_handle(uint8_t *handle_bin, 96 | size_t handle_size, 97 | int device_id) { 98 | (void)handle_bin; 99 | (void)handle_size; 100 | (void)device_id; 101 | return std::make_pair(nullptr, "CUDA is not enabled in this build"); 102 | } 103 | 104 | std::optional get_gpumat_device_id(void * ptr) { 105 | (void)ptr; 106 | return std::nullopt; 107 | } 108 | #endif 109 | -------------------------------------------------------------------------------- /c_src/modules/evision_cuda.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_CUDA_H 2 | #define EVISION_CUDA_H 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | std::optional, int>> get_cuda_ipc_handle(std::uintptr_t ptr); 11 | std::pair get_pointer_for_ipc_handle(uint8_t*, size_t, int); 12 | std::optional get_gpumat_device_id(void * ptr); 13 | 14 | #endif // EVISION_CUDA_H 15 | -------------------------------------------------------------------------------- /c_src/modules/evision_cuda_ipc.cc: -------------------------------------------------------------------------------- 1 | #include "evision_cuda_ipc.h" 2 | 3 | #ifdef CUDA_HOST_IPC_ENABLED 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | // Function to create or open a shared memory object and set its size 13 | int get_ipc_handle(const char* memname, size_t memsize) { 14 | int fd = shm_open(memname, O_CREAT | O_RDWR, 0666); 15 | if (fd == -1) { 16 | return -1; 17 | } 18 | 19 | if (ftruncate(fd, memsize) == -1) { 20 | close(fd); 21 | return -1; 22 | } 23 | 24 | return fd; 25 | } 26 | 27 | // Function to map the shared memory in this process 28 | void* open_ipc_handle(int fd, size_t memsize) { 29 | std::cout << "fd: " << fd << std::endl; 30 | std::cout << "memsize: " << memsize << std::endl; 31 | void* ptr = mmap(NULL, memsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 32 | if (ptr == MAP_FAILED) { 33 | perror("mmap"); 34 | return nullptr; 35 | } 36 | std::cout << "ptr: " << ptr << std::endl; 37 | return ptr; 38 | } 39 | 40 | int close_ipc_handle(int fd, void* ptr, char* memname, size_t memsize) { 41 | if (munmap(ptr, memsize) == -1) { 42 | return -1; 43 | } 44 | 45 | if (close(fd) == -1) { 46 | return -1; 47 | } 48 | 49 | shm_unlink(memname); 50 | 51 | return 0; 52 | } 53 | 54 | #else 55 | 56 | int get_ipc_handle(const char* memname, size_t memsize) { 57 | return -1; 58 | } 59 | void* open_ipc_handle(int fd, size_t memsize) { 60 | return nullptr; 61 | } 62 | int close_ipc_handle(int fd, void* ptr, char* memname, size_t memsize) { 63 | return -1; 64 | } 65 | 66 | #endif // CUDA_HOST_IPC_ENABLED 67 | -------------------------------------------------------------------------------- /c_src/modules/evision_cuda_ipc.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_CUDA_IPC_H 2 | #define EVISION_CUDA_IPC_H 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | int get_ipc_handle(const char* memname, size_t memsize); 9 | void* open_ipc_handle(int fd, size_t memsize); 10 | int close_ipc_handle(int fd, void* ptr, char* memname, size_t memsize); 11 | 12 | #endif // EVISION_CUDA_IPC_H 13 | -------------------------------------------------------------------------------- /c_src/modules/evision_imdecode.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_IMGCODECS 2 | 3 | #ifndef EVISION_OPENCV_IMDECODE_H 4 | #define EVISION_OPENCV_IMDECODE_H 5 | 6 | // @evision enable_with: imgcodecs 7 | 8 | #include 9 | #include "../nif_utils.hpp" 10 | 11 | using namespace evision::nif; 12 | 13 | // @evision c: imdecode,evision_cv_imdecode,1 14 | // @evision nif: def imdecode(_opts \\ []), do: :erlang.nif_error(:undefined) 15 | static ERL_NIF_TERM evision_cv_imdecode(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 16 | { 17 | using namespace cv; 18 | ERL_NIF_TERM error_term = 0; 19 | std::map erl_terms; 20 | int nif_opts_index = 0; // <- autogenerated value 21 | if (nif_opts_index < argc) { 22 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 23 | } 24 | 25 | { 26 | ERL_NIF_TERM erl_binary = evision_get_kw(env, erl_terms, "buf"); 27 | ErlNifBinary buf; 28 | int flags; 29 | if (enif_inspect_binary(env, erl_binary, &buf) && 30 | evision_to_safe(env, evision_get_kw(env, erl_terms, "flags"), flags, ArgInfo("flags", 0))) { 31 | Mat retval; 32 | int error_flag = false; 33 | cv::Mat bufMat = cv::Mat(1, buf.size, CV_8UC1, buf.data); 34 | ERRWRAP2(retval = cv::imdecode(bufMat, flags), env, error_flag, error_term); 35 | if (!error_flag) { 36 | return evision_from(env, retval); 37 | } 38 | } 39 | } 40 | 41 | if (error_term != 0) return error_term; 42 | else return enif_make_badarg(env); 43 | } 44 | 45 | #endif // EVISION_OPENCV_IMDECODE_H 46 | 47 | #endif // HAVE_OPENCV_IMGCODECS 48 | -------------------------------------------------------------------------------- /c_src/modules/evision_mat_api.h: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_MAT_API_H 2 | #define EVISION_MAT_API_H 3 | #pragma once 4 | 5 | #include 6 | #include "../evision_consts.h" 7 | 8 | static ERL_NIF_TERM __evision_get_mat_type(ErlNifEnv *env, int type) { 9 | uint8_t depth = type & CV_MAT_DEPTH_MASK; 10 | 11 | switch ( depth ) { 12 | case CV_8U: return enif_make_tuple2(env, kAtomU, enif_make_uint64(env, 8)); 13 | case CV_8S: return enif_make_tuple2(env, kAtomS, enif_make_uint64(env, 8)); 14 | case CV_16U: return enif_make_tuple2(env, kAtomU, enif_make_uint64(env, 16)); 15 | case CV_16S: return enif_make_tuple2(env, kAtomS, enif_make_uint64(env, 16)); 16 | case CV_32S: return enif_make_tuple2(env, kAtomS, enif_make_uint64(env, 32)); 17 | case CV_32F: return enif_make_tuple2(env, kAtomF, enif_make_uint64(env, 32)); 18 | case CV_64F: return enif_make_tuple2(env, kAtomF, enif_make_uint64(env, 64)); 19 | case CV_16F: return enif_make_tuple2(env, kAtomF, enif_make_uint64(env, 16)); 20 | default: return enif_make_tuple2(env, kAtomUser, enif_make_uint64(env, depth)); 21 | } 22 | } 23 | 24 | static ERL_NIF_TERM _evision_get_mat_type(ErlNifEnv *env, const cv::Mat& img) { 25 | int type = img.type(); 26 | return __evision_get_mat_type(env, type); 27 | } 28 | 29 | static ERL_NIF_TERM _evision_get_mat_shape(ErlNifEnv *env, const cv::Mat& img) { 30 | cv::MatSize size = img.size; 31 | int channels = img.channels(); 32 | int dims = size.dims(); 33 | int include_channels = 0; 34 | if (!(img.type() == CV_8S || img.type() == CV_8U || img.type() == CV_16F \ 35 | || img.type() == CV_16S || img.type() == CV_16U || img.type() == CV_32S \ 36 | || img.type() == CV_32F || img.type() == CV_64F)) { 37 | dims += 1; 38 | include_channels = 1; 39 | } 40 | ERL_NIF_TERM* shape = (ERL_NIF_TERM *)enif_alloc(sizeof(ERL_NIF_TERM) * dims); 41 | 42 | for (int i = 0; i < size.dims(); i++) { 43 | shape[i] = enif_make_int(env, size[i]); 44 | } 45 | if (include_channels) { 46 | shape[dims - 1] = enif_make_int(env, channels); 47 | } 48 | #ifdef GLEAM_EVISION 49 | ERL_NIF_TERM ret = enif_make_list_from_array(env, shape, dims); 50 | #else 51 | ERL_NIF_TERM ret = enif_make_tuple_from_array(env, shape, dims); 52 | #endif 53 | enif_free(shape); 54 | return ret; 55 | } 56 | 57 | static ERL_NIF_TERM _evision_make_mat_resource_into_map(ErlNifEnv *env, const cv::Mat& m, ERL_NIF_TERM res_term) { 58 | const size_t num_items = 7; 59 | size_t item_index = 0; 60 | 61 | ERL_NIF_TERM keys[num_items]; 62 | ERL_NIF_TERM values[num_items]; 63 | 64 | keys[item_index] = kAtomChannels; 65 | values[item_index] = enif_make_int(env, m.channels()); 66 | item_index++; 67 | 68 | keys[item_index] = kAtomDims; 69 | values[item_index] = enif_make_int(env, m.dims); 70 | item_index++; 71 | 72 | keys[item_index] = kAtomType; 73 | values[item_index] = _evision_get_mat_type(env, m); 74 | item_index++; 75 | 76 | keys[item_index] = kAtomRawType; 77 | values[item_index] = enif_make_int(env, m.type()); 78 | item_index++; 79 | 80 | keys[item_index] = kAtomShape; 81 | values[item_index] = _evision_get_mat_shape(env, m); 82 | item_index++; 83 | 84 | keys[item_index] = kAtomRef; 85 | values[item_index] = res_term; 86 | item_index++; 87 | 88 | keys[item_index] = kAtomClass; 89 | values[item_index] = kAtomEvisionMatModule; 90 | item_index++; 91 | 92 | ERL_NIF_TERM map; 93 | if (enif_make_map_from_arrays(env, keys, values, item_index, &map)) { 94 | return map; 95 | } else { 96 | return evision::nif::error(env, "error when making map from arrays"); 97 | } 98 | } 99 | 100 | #endif // EVISION_MAT_API_H 101 | -------------------------------------------------------------------------------- /c_src/modules/evision_mat_utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVISION_MAT_UTILS_HPP 2 | #define EVISION_MAT_UTILS_HPP 3 | #pragma once 4 | 5 | int get_binary_type(const std::string& t, int l, int n, int& type) { 6 | if (t == "u") { 7 | if (l == 8) { 8 | if (n != 0) type = CV_8UC(n); 9 | else type = CV_8U; 10 | return true; 11 | } 12 | if (l == 16) { 13 | if (n != 0) type = CV_16UC(n); 14 | else type = CV_16U; 15 | return true; 16 | } 17 | } else if (t == "s") { 18 | if (l == 8) { 19 | if (n != 0) type = CV_8SC(n); 20 | else type = CV_8S; 21 | return true; 22 | } 23 | if (l == 16) { 24 | if (n != 0) type = CV_16SC(n); 25 | else type = CV_16S; 26 | return true; 27 | } 28 | if (l == 32) { 29 | if (n != 0) type = CV_32SC(n); 30 | else type = CV_32S; 31 | return true; 32 | } 33 | } else if (t == "f") { 34 | if (l == 16) { 35 | if (n != 0) type = CV_16FC(n); 36 | else type = CV_16F; 37 | return true; 38 | } 39 | if (l == 32) { 40 | if (n != 0) type = CV_32FC(n); 41 | else type = CV_32F; 42 | return true; 43 | } 44 | if (l == 64) { 45 | if (n != 0) type = CV_64FC(n); 46 | else type = CV_64F; 47 | return true; 48 | } 49 | } 50 | return false; 51 | } 52 | 53 | int get_compact_type(const std::string& compact, int n, int& type) { 54 | if (compact[0] == 'u') { 55 | if (compact == "u8") { 56 | if (n != 0) type = CV_8UC(n); 57 | else type = CV_8U; 58 | return true; 59 | } 60 | if (compact == "u16") { 61 | if (n != 0) type = CV_16UC(n); 62 | else type = CV_16U; 63 | return true; 64 | } 65 | } else if (compact[0] == 's') { 66 | if (compact == "s8") { 67 | if (n != 0) type = CV_8SC(n); 68 | else type = CV_8S; 69 | return true; 70 | } 71 | if (compact == "s16") { 72 | if (n != 0) type = CV_16SC(n); 73 | else type = CV_16S; 74 | return true; 75 | } 76 | if (compact == "s32") { 77 | if (n != 0) type = CV_32SC(n); 78 | else type = CV_32S; 79 | return true; 80 | } 81 | } else if (compact[0] == 'f') { 82 | if (compact == "f16") { 83 | if (n != 0) type = CV_16FC(n); 84 | else type = CV_16F; 85 | return true; 86 | } 87 | if (compact == "f32") { 88 | if (n != 0) type = CV_32FC(n); 89 | else type = CV_32F; 90 | return true; 91 | } 92 | if (compact == "f64") { 93 | if (n != 0) type = CV_64FC(n); 94 | else type = CV_64F; 95 | return true; 96 | } 97 | } 98 | return false; 99 | } 100 | 101 | static void _evision_binary_unref(void *buf, ErlNifEnv *env) { 102 | // enif_fprintf(stderr, "freed nif_env\n"); 103 | // freeing env frees unref all terms it contains 104 | enif_free_env(env); 105 | return; 106 | } 107 | 108 | static ERL_NIF_TERM _evision_binary_ref(ERL_NIF_TERM bin_term, evision_res * zero_copy_mat) { 109 | // adapted from https://github.com/akash-akya/zero_copy/blob/master/c_src/zero_copy.c 110 | ErlNifBinary bin; 111 | ERL_NIF_TERM term; 112 | ErlNifEnv *new_env; 113 | 114 | zero_copy_mat->in_buf = nullptr; 115 | zero_copy_mat->in_ref = nullptr; 116 | 117 | // keep reference to binary by creating new nif-env and copying 118 | // binary-term reference to it 119 | new_env = enif_alloc_env(); 120 | term = enif_make_copy(new_env, bin_term); 121 | 122 | if (!enif_inspect_binary(new_env, term, &bin)) { 123 | enif_free_env(new_env); 124 | return 1; 125 | } 126 | 127 | // Note that we are *NOT* copying the binary data 128 | zero_copy_mat->in_buf = bin.data; 129 | 130 | // input buffer specific opaque data which will be passed as second 131 | // argument to finalizer during unref 132 | zero_copy_mat->in_ref = (void *)new_env; 133 | // function to be called to unref the input data 134 | zero_copy_mat->in_unref = (void (*)(void *, void *))_evision_binary_unref; 135 | 136 | return 0; 137 | } 138 | 139 | #endif // EVISION_MAT_UTILS_HPP 140 | -------------------------------------------------------------------------------- /c_src/modules/evision_video_api.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_VIDEO 2 | 3 | #ifndef EVISION_VIDEO_API_H 4 | #define EVISION_VIDEO_API_H 5 | 6 | template <> 7 | ERL_NIF_TERM evision_from_as_map(ErlNifEnv *env, const cv::Ptr& cap, ERL_NIF_TERM res_term, const char * class_name, bool& success) { 8 | const size_t num_items = 7; 9 | size_t item_index = 0; 10 | 11 | ERL_NIF_TERM keys[num_items]; 12 | ERL_NIF_TERM values[num_items]; 13 | 14 | keys[item_index] = evision::nif::atom(env, "frame_width"); 15 | values[item_index] = enif_make_double(env, cap->get(cv::CAP_PROP_FRAME_WIDTH)); 16 | item_index++; 17 | 18 | keys[item_index] = evision::nif::atom(env, "frame_height"); 19 | values[item_index] = enif_make_double(env, cap->get(cv::CAP_PROP_FRAME_HEIGHT)); 20 | item_index++; 21 | 22 | keys[item_index] = evision::nif::atom(env, "fps"); 23 | values[item_index] = enif_make_double(env, cap->get(cv::CAP_PROP_FPS)); 24 | item_index++; 25 | 26 | keys[item_index] = evision::nif::atom(env, "isOpened"); 27 | values[item_index] = evision::nif::atom(env, cap->isOpened() ? "true" : "false"); 28 | item_index++; 29 | 30 | keys[item_index] = evision::nif::atom(env, "frame_count"); 31 | values[item_index] = enif_make_double(env, cap->get(cv::CAP_PROP_FRAME_COUNT)); 32 | item_index++; 33 | 34 | keys[item_index] = evision::nif::atom(env, "ref"); 35 | values[item_index] = res_term; 36 | item_index++; 37 | 38 | keys[item_index] = evision::nif::atom(env, "class"); 39 | values[item_index] = evision::nif::atom(env, class_name); 40 | item_index++; 41 | 42 | ERL_NIF_TERM map; 43 | if (enif_make_map_from_arrays(env, keys, values, item_index, &map)) { 44 | success = true; 45 | return map; 46 | } else { 47 | success = false; 48 | return evision::nif::error(env, "enif_make_map_from_arrays failed in evision_from_as_map"); 49 | } 50 | } 51 | 52 | #endif // EVISION_VIDEO_API_H 53 | 54 | #endif // HAVE_OPENCV_VIDEO 55 | -------------------------------------------------------------------------------- /c_src/modules/evision_videocapture.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_OPENCV_VIDEO 2 | 3 | // @evision enable_with: video 4 | 5 | #ifndef EVISION_VIDEOCAPTURE_H 6 | #define EVISION_VIDEOCAPTURE_H 7 | 8 | // @evision c: videoCapture_waitAny,evision_cv_videoCapture_waitAny,1 9 | // @evision nif: def videoCapture_waitAny(_opts \\ []), do: :erlang.nif_error(:undefined) 10 | static ERL_NIF_TERM evision_cv_videoCapture_waitAny(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 11 | { 12 | using namespace cv; 13 | ERL_NIF_TERM error_term = 0; 14 | std::map erl_terms; 15 | int nif_opts_index = 0; // <- autogenerated value 16 | if (nif_opts_index < argc) { 17 | evision::nif::parse_arg(env, nif_opts_index, argv, erl_terms); 18 | } 19 | std::vector> ptr_streams; 20 | std::vector readyIndex; 21 | int64 timeoutNs=0; 22 | bool retval; 23 | 24 | if( evision_to_safe(env, evision_get_kw(env, erl_terms, "streams"), ptr_streams, ArgInfo("streams", 0)) && 25 | evision_to_safe(env, evision_get_kw(env, erl_terms, "timeoutNs"), timeoutNs, ArgInfo("timeoutNs", 0x8)) ) 26 | { 27 | int error_flag = false; 28 | size_t n = ptr_streams.size(); 29 | std::vector streams; 30 | for (size_t i = 0; i < n; i++) { 31 | auto& ptr = ptr_streams[i]; 32 | streams.emplace_back(*ptr.get()); 33 | } 34 | 35 | ERRWRAP2(retval = cv::VideoCapture::waitAny(streams, readyIndex, timeoutNs), env, error_flag, error_term); 36 | if (!error_flag) { 37 | if (retval) { 38 | // code_ret_1_tuple_except_bool 39 | return evision_from(env, readyIndex); 40 | } else { 41 | return evision::nif::atom(env, "false"); 42 | }; 43 | } 44 | } 45 | 46 | if (error_term != 0) return error_term; 47 | else return enif_make_badarg(env); 48 | } 49 | 50 | #endif // EVISION_VIDEOCAPTURE_H 51 | 52 | #endif // HAVE_OPENCV_VIDEO 53 | -------------------------------------------------------------------------------- /cc_toolchain/aarch64-linux-gnu.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for aarch64-linux-gnu. 3 | # 4 | set(CMAKE_SYSTEM_NAME Linux) 5 | set(CMAKE_SYSTEM_PROCESSOR aarch64) 6 | 7 | set(CMAKE_C_COMPILER "/usr/bin/aarch64-linux-gnu-gcc") 8 | set(CMAKE_CXX_COMPILER "/usr/bin/aarch64-linux-gnu-g++") 9 | 10 | set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu) 11 | 12 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 14 | 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | -------------------------------------------------------------------------------- /cc_toolchain/aarch64-linux-musl.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for aarch64-linux-musl. 3 | # 4 | set(CMAKE_SYSTEM_NAME Linux) 5 | set(CMAKE_SYSTEM_PROCESSOR aarch64) 6 | set(TARGET_SYS aarch64-linux-musl) 7 | include(${CMAKE_CURRENT_LIST_DIR}/zig.toolchain.cmake) 8 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") 9 | 10 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 11 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 12 | 13 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 14 | -------------------------------------------------------------------------------- /cc_toolchain/aarch64-windows-msvc.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for aarch64-windows-mvsc. 3 | # 4 | set(CMAKE_CROSSCOMPILING TRUE) 5 | set(CMAKE_SYSTEM_NAME Windows) 6 | set(CMAKE_GENERATOR_PLATFORM ARM64) 7 | set(CMAKE_SYSTEM_PROCESSOR aarch64) 8 | 9 | set(ENV_PATH "$ENV{PATH}") 10 | set(ARM64_CL_PATH) 11 | foreach(P IN LISTS ENV_PATH) 12 | string(TOLOWER "${P}" LP) 13 | if("${LP}" MATCHES "(.*)\\\\msvc\\\\(.*)bin\\\\hostx64\\\\arm64$") 14 | set(ARM64_CL_PATH "${P}") 15 | endif() 16 | endforeach() 17 | 18 | string(REPLACE "\\" "/" FORWARD_SLASH_PATH "${ARM64_CL_PATH}") 19 | find_program(CL cl.exe HINTS "${FORWARD_SLASH_PATH}" PATHS "${FORWARD_SLASH_PATH}" NO_CACHE NO_DEFAULT_PATH NO_SYSTEM_ENVIRONMENT_PATH) 20 | 21 | if(NOT CL) 22 | message(FATAL_ERROR "Cannot find cl.exe") 23 | else() 24 | message(STATUS "Found cl.exe: ${CL}") 25 | endif() 26 | 27 | set(CMAKE_C_COMPILER "${CL}") 28 | set(CMAKE_CXX_COMPILER "${CL}") 29 | -------------------------------------------------------------------------------- /cc_toolchain/armv6-nerves-linux-gnueabihf.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for armv6 on Linux (with nerves toolchain). 3 | # 4 | # curl -fSL https://github.com/nerves-project/toolchains/releases/download/v13.2.0/nerves_toolchain_armv6_nerves_linux_gnueabihf-linux_x86_64-13.2.0-363664F.tar.xz -o nerves_toolchain_armv6_nerves_linux_gnueabihf-linux_x86_64-13.2.0-363664F.tar.xz 5 | # tar -xf nerves_toolchain_armv6_nerves_linux_gnueabihf-linux_x86_64-13.2.0-363664F.tar.xz 6 | # sudo mv nerves_toolchain_armv6_nerves_linux_gnueabihf-linux_x86_64-13.2.0 /usr/local/bin/nerves_toolchain_armv6_nerves_linux_gnueabihf-linux_x86_64-13.2.0 7 | # 8 | set(CMAKE_SYSTEM_NAME Linux) 9 | set(CMAKE_SYSTEM_PROCESSOR armv6) 10 | 11 | set(CMAKE_C_COMPILER "/usr/local/bin/nerves_toolchain_armv6_nerves_linux_gnueabihf-linux_x86_64-13.2.0/bin/armv6-nerves-linux-gnueabihf-gcc") 12 | set(CMAKE_CXX_COMPILER "/usr/local/bin/nerves_toolchain_armv6_nerves_linux_gnueabihf-linux_x86_64-13.2.0/bin/armv6-nerves-linux-gnueabihf-g++") 13 | 14 | set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf) 15 | 16 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 18 | 19 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 20 | -------------------------------------------------------------------------------- /cc_toolchain/armv7l-linux-gnueabihf.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for armv7l-linux-gnueabihf. 3 | # 4 | set(CMAKE_SYSTEM_NAME Linux) 5 | set(CMAKE_SYSTEM_PROCESSOR armv7l) 6 | 7 | set(CMAKE_C_COMPILER "/usr/bin/arm-linux-gnueabihf-gcc-9") 8 | set(CMAKE_CXX_COMPILER "/usr/bin/arm-linux-gnueabihf-g++-9") 9 | 10 | set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf) 11 | 12 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 14 | 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | -------------------------------------------------------------------------------- /cc_toolchain/i686-linux-gnu.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for i686-linux-gnu. 3 | # 4 | set(CMAKE_SYSTEM_NAME Linux) 5 | set(CMAKE_SYSTEM_PROCESSOR i686) 6 | 7 | set(CMAKE_C_COMPILER "/usr/bin/i686-linux-gnu-gcc") 8 | set(CMAKE_CXX_COMPILER "/usr/bin/i686-linux-gnu-g++") 9 | 10 | set(CMAKE_FIND_ROOT_PATH /usr/i686-linux-gnu) 11 | 12 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 14 | 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | -------------------------------------------------------------------------------- /cc_toolchain/ppc64le-linux-gnu.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for ppc64le-linux-gnu. 3 | # 4 | set(CMAKE_SYSTEM_NAME Linux) 5 | set(CMAKE_SYSTEM_PROCESSOR ppc64le) 6 | 7 | set(CMAKE_C_COMPILER "/usr/bin/powerpc64le-linux-gnu-gcc") 8 | set(CMAKE_CXX_COMPILER "/usr/bin/powerpc64le-linux-gnu-g++") 9 | 10 | set(CMAKE_FIND_ROOT_PATH /usr/powerpc64le-linux-gnu) 11 | 12 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 14 | 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | -------------------------------------------------------------------------------- /cc_toolchain/riscv64-linux-gnu.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for riscv64-linux-gnu. 3 | # 4 | set(CMAKE_SYSTEM_NAME Linux) 5 | set(CMAKE_SYSTEM_PROCESSOR riscv64) 6 | 7 | set(CMAKE_C_COMPILER "/usr/bin/riscv64-linux-gnu-gcc") 8 | set(CMAKE_CXX_COMPILER "/usr/bin/riscv64-linux-gnu-g++") 9 | 10 | set(CMAKE_FIND_ROOT_PATH /usr/riscv64-linux-gnu) 11 | 12 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 14 | 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | -------------------------------------------------------------------------------- /cc_toolchain/riscv64-linux-musl.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for riscv64-linux-musl. 3 | # 4 | set(CMAKE_SYSTEM_NAME Linux) 5 | set(CMAKE_SYSTEM_PROCESSOR riscv64) 6 | set(TARGET_SYS riscv64-linux-musl) 7 | include(${CMAKE_CURRENT_LIST_DIR}/zig.toolchain.cmake) 8 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") 9 | 10 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 11 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 12 | 13 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 14 | -------------------------------------------------------------------------------- /cc_toolchain/s390x-linux-gnu.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for s390x-linux-gnu. 3 | # 4 | set(CMAKE_SYSTEM_NAME Linux) 5 | set(CMAKE_SYSTEM_PROCESSOR s390x) 6 | 7 | set(CMAKE_C_COMPILER "/usr/bin/s390x-linux-gnu-gcc") 8 | set(CMAKE_CXX_COMPILER "/usr/bin/s390x-linux-gnu-g++") 9 | 10 | set(CMAKE_FIND_ROOT_PATH /usr/s390x-linux-gnu) 11 | 12 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 14 | 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | -------------------------------------------------------------------------------- /cc_toolchain/x86_64-linux-musl.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Toolchain file for cross-compiling for x86_64-linux-musl. 3 | # 4 | set(CMAKE_SYSTEM_NAME Linux) 5 | set(CMAKE_SYSTEM_PROCESSOR x86_64) 6 | set(TARGET_SYS x86_64-linux-musl) 7 | include(${CMAKE_CURRENT_LIST_DIR}/zig.toolchain.cmake) 8 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") 9 | 10 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 11 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 12 | 13 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 14 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | config :evision, 4 | unsupported_type_map: %{ 5 | {:s, 64} => {:f, 64}, 6 | {:u, 64} => {:f, 64}, 7 | {:u, 32} => {:f, 32} 8 | } 9 | 10 | config :evision, display_inline_image_iterm2: true 11 | 12 | # Only valid when `display_inline_image_iterm2` is `true` and using in iTerm2 13 | ## 14 | ## Maximum size for the image to be rendered in iTerm2, {height, width} 15 | ## 16 | ## Image will not be displayed if either its height or width exceeds these limits. 17 | config :evision, display_inline_image_max_size: {8192, 8192} 18 | 19 | # Only valid when `:kino` >= 0.7 and using in livebook 20 | # 21 | ## Image Encoding Type 22 | ## 23 | ## When rendering a 2D image with Kino in Livebook 24 | ## the image will first be encoded into either :png or :jpeg 25 | ## 26 | ## - :png usually has better quality because it is lossless compression, 27 | ## however, it uses more bandwidth to transfer 28 | ## 29 | ## - :jpeg require less bandwidth to pass from the backend to the livebook frontend, 30 | ## but it is lossy compression 31 | ## 32 | ## The default value is :png 33 | config :evision, kino_render_image_encoding: :png 34 | 35 | # Only valid when `:kino` >= 0.7 and using in livebook 36 | # 37 | ## Maximum size for the image to be rendered in Kino, {height, width} 38 | ## 39 | ## Image will not be rendered in Kino if either its height or width exceeds these limits. 40 | config :evision, kino_render_image_max_size: {8192, 8192} 41 | 42 | # Only valid when `:kino` >= 0.7 and using in livebook 43 | # 44 | ## Configure the order of Kino.Render tabs in livebook 45 | ## Default order is `[:image, :raw, :numerical]` 46 | ## and the corresponding tabs will be: 47 | ## Image | Raw | Numerical 48 | ## 49 | ## Note that the `:image` tab will be ignored if the `Evision.Mat` is not a 2D image. 50 | ## 51 | ## Also, it's possible to specify any combination (any subset) of these tabs, 52 | ## including the empty one, `[]`, and in that case, the output content in the livebook 53 | ## cell will be the same as `:raw` but without any tabs 54 | ## -- simply put, `[]` means to only do the basic inspect and not use Kino.Layout.tabs 55 | ## 56 | ## **It's worth noting that `[] != nil`, because `nil` is default return value when `kino_render_tab_order`** 57 | ## **is not configured -- hence evision will use the default order, `[:image, :raw, :numerical]` in such case** 58 | ## 59 | ## When only specifying one type, i.e., `[:image]`, `[:raw]` or `[:numerical]`, 60 | ## only one tab will be there 61 | ## 62 | ## Furthermore, when `kino_render_tab_order` is configured to `[:image]`, and the 63 | ## `Evision.Mat` is not a 2D image, 64 | ## then it will automatically fallback to `:raw` 65 | ## -- simply put, `:image` in this case (only specifying one type) means: 66 | ## display the Evision.Mat as an image whenever possible, and fallback to `:raw` 67 | ## if it's not a 2D image 68 | config :evision, 69 | kino_render_tab_order: [ 70 | :image, 71 | :raw, 72 | :numerical 73 | ] 74 | -------------------------------------------------------------------------------- /do_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # evision hex.pm release script 4 | # please run this script in the root of the project on an x86_64 Linux machine 5 | set -xe 6 | 7 | export MIX_ENV=docs 8 | export EVISION_PREFER_PRECOMPILED=true 9 | export EVISION_ENABLE_CUDA=true 10 | export EVISION_CUDA_VERSION=12 11 | export EVISION_CUDNN_VERSION=9 12 | export NIF_VERSION=2.16 13 | export EVISION_VERSION="$(grep 'def version, do: "' mix.exs | grep -P -oh '(\d+).(\d+).(\d+)')" 14 | 15 | export TARBALL_FILENAME="evision-nif_${NIF_VERSION}-x86_64-linux-gnu-contrib-cuda${EVISION_CUDA_VERSION}-cudnn${EVISION_CUDNN_VERSION}-${EVISION_VERSION}" 16 | export TARBALL_NAME="${TARBALL_FILENAME}.tar.gz" 17 | curl -fSL "https://github.com/cocoa-xu/evision/releases/download/v${EVISION_VERSION}/${TARBALL_NAME}" -o "/tmp/${TARBALL_NAME}" 18 | export TARBALL_SHA256="$(sha256sum "/tmp/${TARBALL_NAME}" | cut -d ' ' -f 1)" 19 | cat < checksum.exs 20 | %{ 21 | "${TARBALL_NAME}" => "sha256:${TARBALL_SHA256}" 22 | } 23 | EOF 24 | rm -rf release 25 | mkdir -p release 26 | tar xf "/tmp/${TARBALL_NAME}" -C release 27 | rm -rf lib/generated 28 | mv "release/${TARBALL_FILENAME}/elixir_generated" lib/generated 29 | EVISION_FETCH_PRECOMPILED=true mix evision.fetch --all 30 | MIX_ENV=docs mix hex.publish 31 | -------------------------------------------------------------------------------- /examples/dnn-u2net_human_seg.livemd: -------------------------------------------------------------------------------- 1 | # U2Net Human Segmentation 2 | 3 | ```elixir 4 | # set `EVISION_PREFER_PRECOMPILED` to `false` 5 | # if you prefer `:evision` to be compiled from source 6 | # note that to compile from source, you may need at least 1GB RAM 7 | # System.put_env("EVISION_PREFER_PRECOMPILED", "false") 8 | 9 | Mix.install( 10 | [ 11 | {:evision, "~> 0.2"}, 12 | {:req, "~> 0.5"}, 13 | {:kino, "~> 0.11"} 14 | ], 15 | system_env: [ 16 | {"EVISION_PREFER_PRECOMPILED", true} 17 | ] 18 | ) 19 | ``` 20 | 21 | ## Load Model 22 | 23 | ```elixir 24 | # download the model from 25 | # https://drive.google.com/uc?export=download&id=19Gg2sbBkFBExkUuNfj4yz8SLMMDC7eg2 26 | net = Evision.DNN.readNetFromONNX("u2net_human_seg.onnx") 27 | ``` 28 | 29 | ## Get Input Image 30 | 31 | ```elixir 32 | content_image_input = Kino.Input.image("Content image") 33 | ``` 34 | 35 | ```elixir 36 | img = 37 | case Kino.Input.read(content_image_input) do 38 | %{file_ref: file_ref, height: height, width: width} -> 39 | file_ref 40 | |> Kino.Input.file_path() 41 | |> File.read!() 42 | |> Evision.Mat.from_binary({:u, 8}, height, width, 3) 43 | |> Evision.cvtColor(Evision.Constant.cv_COLOR_RGB2BGR()) 44 | 45 | _ -> 46 | raise RuntimeError, "please upload an image in Kino" 47 | end 48 | 49 | img 50 | ``` 51 | 52 | ## Feed the input image to the model 53 | 54 | ```elixir 55 | input_size = 320 56 | 57 | blob = 58 | Evision.DNN.blobFromImage( 59 | img, 60 | scalefactor: 1.0 / 255.0, 61 | size: {input_size, input_size}, 62 | swapRB: true 63 | ) 64 | ``` 65 | 66 | ```elixir 67 | Evision.DNN.Net.setInput(net, blob) 68 | [d0] = Evision.DNN.Net.forward(net) 69 | d0 70 | ``` 71 | 72 | ## Postprocessing 73 | 74 | ```elixir 75 | normPred = fn d -> 76 | ad0 = Nx.reshape(d, {:auto}) 77 | ma = ad0[Nx.argmax(d)] 78 | mi = ad0[Nx.argmin(d)] 79 | Nx.divide(Nx.subtract(d, mi), Nx.subtract(ma, mi)) 80 | end 81 | ``` 82 | 83 | ```elixir 84 | d0_tensor = Evision.Mat.to_nx(d0, Nx.BinaryBackend) 85 | pred = normPred.(d0_tensor) 86 | ``` 87 | 88 | ## Visualisation 89 | 90 | ```elixir 91 | show_output = fn {input_img, predict} -> 92 | {h, w, _} = input_img.shape 93 | 94 | predict = Nx.reshape(predict, {input_size, input_size}) 95 | img_p = Nx.as_type(Nx.multiply(predict, 255), :u8) 96 | Evision.resize(img_p, {w, h}) 97 | end 98 | 99 | show_output.({img, pred}) 100 | ``` 101 | -------------------------------------------------------------------------------- /examples/ocl-default_opencl_device_info.livemd: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Print Default OpenCL Device Information 4 | 5 | ```elixir 6 | Mix.install([ 7 | {:evision, "~> 0.1"} 8 | ]) 9 | ``` 10 | 11 | ## OpenCL 12 | 13 | ```elixir 14 | if Evision.OCL.haveOpenCL() do 15 | IO.puts("OpenCL is available") 16 | 17 | case Evision.OCL.Device.getDefault() do 18 | device = %Evision.OCL.Device{} -> 19 | IO.puts("Device name: #{Evision.OCL.Device.name(device)}") 20 | IO.puts("Vendor name: #{Evision.OCL.Device.vendorName(device)}") 21 | IO.puts("Vendor ID: #{Evision.OCL.Device.vendorID(device)}") 22 | IO.puts("Type: #{Evision.OCL.Device.type(device)}") 23 | IO.puts("Available: #{Evision.OCL.Device.available(device)}") 24 | IO.puts("Image Support: #{Evision.OCL.Device.imageSupport(device)}") 25 | IO.puts("Image 2D Max Height: #{Evision.OCL.Device.image2DMaxHeight(device)}") 26 | IO.puts("Image 2D Max Width: #{Evision.OCL.Device.image2DMaxWidth(device)}") 27 | IO.puts("Image 3D Max Height: #{Evision.OCL.Device.image3DMaxHeight(device)}") 28 | IO.puts("Image 3D Max Width: #{Evision.OCL.Device.image3DMaxWidth(device)}") 29 | IO.puts("Image 3D Max Depth: #{Evision.OCL.Device.image3DMaxDepth(device)}") 30 | IO.puts("Image Max Array Size: #{Evision.OCL.Device.imageMaxArraySize(device)} bytes") 31 | IO.puts("Image Max Buffer Size: #{Evision.OCL.Device.imageMaxBufferSize(device)} bytes") 32 | IO.puts("Device version major: #{Evision.OCL.Device.deviceVersionMajor(device)}") 33 | IO.puts("Device version minor: #{Evision.OCL.Device.deviceVersionMinor(device)}") 34 | IO.puts("Driver version: #{Evision.OCL.Device.driverVersion(device)}") 35 | IO.puts("OpenCL compiler available: #{Evision.OCL.Device.compilerAvailable(device)}") 36 | IO.puts("OpenCL linker available: #{Evision.OCL.Device.linkerAvailable(device)}") 37 | IO.puts("Execution Capabilities: #{Evision.OCL.Device.executionCapabilities(device)}") 38 | IO.puts("Global MemSize: #{Evision.OCL.Device.globalMemSize(device)} bytes") 39 | IO.puts("Local MemSize: #{Evision.OCL.Device.localMemSize(device)} bytes") 40 | IO.puts("Half FP Config: #{Evision.OCL.Device.halfFPConfig(device)}") 41 | IO.puts("Single FP Config: #{Evision.OCL.Device.singleFPConfig(device)}") 42 | IO.puts("Double FP Config: #{Evision.OCL.Device.doubleFPConfig(device)}") 43 | 44 | IO.puts( 45 | "Extensions: #{Enum.join(String.split(Evision.OCL.Device.extensions(device), " "), "\n ")}" 46 | ) 47 | 48 | IO.puts("OpenCL Version: #{Evision.OCL.Device.openCLVersion(device)}") 49 | IO.puts("OpenCL C Version: #{Evision.OCL.Device.openCL_C_Version(device)}") 50 | IO.puts("OpenCL addressBits: #{Evision.OCL.Device.addressBits(device)}") 51 | IO.puts("Max Clock Frequency: #{Evision.OCL.Device.maxClockFrequency(device)} MHz") 52 | IO.puts("Max Compute Units: #{Evision.OCL.Device.maxComputeUnits(device)}") 53 | end 54 | else 55 | IO.puts("OpenCL not available") 56 | end 57 | ``` 58 | -------------------------------------------------------------------------------- /examples/stitching.livemd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # Evision Example - Stitching Multiple Photos into A Panorama Photo 6 | 7 | ```elixir 8 | Mix.install([ 9 | {:evision, "~> 0.2"}, 10 | {:kino, "~> 0.7"}, 11 | {:req, "~> 0.5"} 12 | ], system_env: [ 13 | # optional, defaults to `true` 14 | # set `EVISION_PREFER_PRECOMPILED` to `false` 15 | # if you prefer `:evision` to be compiled from source 16 | # note that to compile from source, you may need at least 1GB RAM 17 | {"EVISION_PREFER_PRECOMPILED", true}, 18 | 19 | # optional, defaults to `true` 20 | # set `EVISION_ENABLE_CONTRIB` to `false` 21 | # if you don't need modules from `opencv_contrib` 22 | {"EVISION_ENABLE_CONTRIB", true}, 23 | 24 | # optional, defaults to `false` 25 | # set `EVISION_ENABLE_CUDA` to `true` 26 | # if you wish to use CUDA related functions 27 | # note that `EVISION_ENABLE_CONTRIB` also has to be `true` 28 | # because cuda related modules come from the `opencv_contrib` repo 29 | {"EVISION_ENABLE_CUDA", false}, 30 | 31 | # required when 32 | # - `EVISION_ENABLE_CUDA` is `true` 33 | # - and `EVISION_PREFER_PRECOMPILED` is `true` 34 | # 35 | # set `EVISION_CUDA_VERSION` to the version that matches 36 | # your local CUDA runtime version 37 | # 38 | # current available versions are 39 | # - 118 40 | # - 121 41 | {"EVISION_CUDA_VERSION", "118"}, 42 | 43 | # require for Windows users when 44 | # - `EVISION_ENABLE_CUDA` is `true` 45 | # set `EVISION_CUDA_RUNTIME_DIR` to the directory that contains 46 | # CUDA runtime libraries 47 | {"EVISION_CUDA_RUNTIME_DIR", "C:/PATH/TO/CUDA/RUNTIME"} 48 | ]) 49 | ``` 50 | 51 | 52 | 53 | ``` 54 | :ok 55 | ``` 56 | 57 | ## Define Some Helper Functions 58 | 59 | ```elixir 60 | defmodule Helper do 61 | def download!(url, save_as, overwrite? \\ false) do 62 | unless File.exists?(save_as) do 63 | Req.get!(url, http_errors: :raise, into: File.stream!(save_as), cache: not overwrite?) 64 | end 65 | 66 | :ok 67 | end 68 | end 69 | ``` 70 | 71 | 72 | 73 | ``` 74 | {:module, Helper, <<70, 79, 82, 49, 0, 0, 10, ...>>, {:download!, 3}} 75 | ``` 76 | 77 | ## Download Test Images 78 | 79 | ```elixir 80 | # change to the file's directory 81 | # or somewhere you have write permission 82 | File.cd!(__DIR__) 83 | 84 | # https://github.com/opencv/opencv_extra/tree/4.x/testdata/stitching 85 | Helper.download!( 86 | "https://raw.githubusercontent.com/opencv/opencv_extra/master/testdata/stitching/a1.png", 87 | "a1.png" 88 | ) 89 | 90 | Helper.download!( 91 | "https://raw.githubusercontent.com/opencv/opencv_extra/master/testdata/stitching/a2.png", 92 | "a2.png" 93 | ) 94 | 95 | Helper.download!( 96 | "https://raw.githubusercontent.com/opencv/opencv_extra/master/testdata/stitching/a3.png", 97 | "a3.png" 98 | ) 99 | ``` 100 | 101 | 102 | 103 | ``` 104 | :ok 105 | ``` 106 | 107 | ## Stitching 108 | 109 | ```elixir 110 | alias Evision, as: Cv 111 | 112 | a1 = Cv.imread("./a1.png") 113 | a2 = Cv.imread("./a2.png") 114 | a3 = Cv.imread("./a3.png") 115 | sticher = Cv.Stitcher.create() 116 | {status_code, result} = Cv.Stitcher.stitch(sticher, [a1, a2, a3]) 117 | 0 = status_code 118 | # status_code should be 0 (OK), 119 | # for other status_code, please refer to https://github.com/opencv/opencv/blob/4.5.4/modules/stitching/include/opencv2/stitching.hpp#L152 120 | 121 | Cv.imencode(".png", result) 122 | |> Kino.Image.new(:png) 123 | ``` 124 | -------------------------------------------------------------------------------- /gleam_src/evision/highgui.gleam: -------------------------------------------------------------------------------- 1 | import evision/mat.{type Mat} 2 | import evision/types.{type Void} 3 | 4 | @external(erlang, "evision_highgui", "imshow") 5 | pub fn imshow(win_name: String, mat: Mat) -> Void 6 | 7 | @external(erlang, "evision_highgui", "waitKey") 8 | pub fn wait_key(delay: Int) -> Void 9 | 10 | @external(erlang, "evision_highgui", "destroyWindow") 11 | pub fn destroy_window(win_name: String) -> Void 12 | 13 | @external(erlang, "evision_highgui", "destroyAllWindows") 14 | pub fn destroy_all_windows() -> Void 15 | -------------------------------------------------------------------------------- /gleam_src/evision/types.gleam: -------------------------------------------------------------------------------- 1 | import gleam/erlang/atom.{type Atom} 2 | 3 | pub type Void { 4 | Void 5 | } 6 | 7 | pub type DType = 8 | #(Atom, Int) 9 | -------------------------------------------------------------------------------- /lib/assets/base.js: -------------------------------------------------------------------------------- 1 | const BaseSelect = { 2 | name: "BaseSelect", 3 | 4 | props: { 5 | label: { 6 | type: String, 7 | default: "", 8 | }, 9 | labelTooltip: { 10 | type: String, 11 | default: "", 12 | }, 13 | selectClass: { 14 | type: String, 15 | default: "input", 16 | }, 17 | modelValue: { 18 | type: String, 19 | default: "", 20 | }, 21 | options: { 22 | type: Array, 23 | default: [], 24 | required: true, 25 | }, 26 | required: { 27 | type: Boolean, 28 | default: false, 29 | }, 30 | inline: { 31 | type: Boolean, 32 | default: false, 33 | }, 34 | grow: { 35 | type: Boolean, 36 | default: false, 37 | }, 38 | }, 39 | 40 | methods: { 41 | available(value, options) { 42 | return value 43 | ? options.map((option) => option.value).includes(value) 44 | : true; 45 | }, 46 | hasTooltip(labelTooltip) { 47 | return labelTooltip.length > 0; 48 | } 49 | }, 50 | 51 | template: ` 52 |
53 | 56 | 59 | 78 |
79 | `, 80 | }; 81 | 82 | const BaseInput = { 83 | name: "BaseInput", 84 | props: { 85 | label: { 86 | type: String, 87 | default: "", 88 | }, 89 | labelTooltip: { 90 | type: String, 91 | default: "", 92 | }, 93 | inputClass: { 94 | type: String, 95 | default: "input", 96 | }, 97 | modelValue: { 98 | type: [String, Number], 99 | default: "", 100 | }, 101 | inline: { 102 | type: Boolean, 103 | default: false, 104 | }, 105 | grow: { 106 | type: Boolean, 107 | default: false, 108 | }, 109 | number: { 110 | type: Boolean, 111 | default: false, 112 | }, 113 | }, 114 | 115 | computed: { 116 | emptyClass() { 117 | if (this.modelValue === "") { 118 | return "empty"; 119 | } 120 | }, 121 | }, 122 | 123 | methods: { 124 | hasTooltip(labelTooltip) { 125 | return labelTooltip.length > 0; 126 | } 127 | }, 128 | 129 | template: ` 130 |
131 | 134 | 137 | 143 |
144 | `, 145 | }; 146 | 147 | const SmartCellConfig = (ctx, info, extra_components, extra_data) => { 148 | return { 149 | components: { 150 | BaseInput: BaseInput, 151 | BaseSelect: BaseSelect, 152 | ...extra_components 153 | }, 154 | 155 | data() { 156 | return { 157 | fields: info.fields, 158 | ...extra_data 159 | }; 160 | }, 161 | 162 | methods: { 163 | handleFieldChange(event) { 164 | const { name: field, value } = event.target; 165 | 166 | if (field) { 167 | if (info.id == "evision.zoo") { 168 | ctx.pushEvent("update_field", { field, value }); 169 | } else { 170 | const sub_value = field.split(".").reduce((data, key) => data[key], this.fields); 171 | ctx.pushEvent("update_field", { field, value: sub_value }); 172 | } 173 | } 174 | }, 175 | } 176 | } 177 | }; 178 | 179 | export const Base = { 180 | BaseInput: BaseInput, 181 | BaseSelect: BaseSelect, 182 | SmartCellConfig: SmartCellConfig 183 | }; 184 | -------------------------------------------------------------------------------- /lib/assets/main.js: -------------------------------------------------------------------------------- 1 | import * as Vue from "https://cdn.jsdelivr.net/npm/vue@3.2.26/dist/vue.esm-browser.prod.js"; 2 | import { ML } from "./ml.js"; 3 | import { Zoo } from "./zoo.js"; 4 | 5 | export async function init(ctx, info) { 6 | await Promise.all([ 7 | ctx.importCSS("main.css"), 8 | ctx.importCSS("https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap"), 9 | ctx.importCSS("https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.min.css") 10 | ]); 11 | 12 | var appConfig = {}; 13 | if (info.id.startsWith("evision.ml") && ML.SmartCell.hasOwnProperty(info.id)) { 14 | appConfig = ML.SmartCell[info.id](ctx, info); 15 | } else if (info.id == "evision.zoo") { 16 | appConfig = Zoo.SmartCell[info.id](ctx, info); 17 | } 18 | 19 | const app = Vue.createApp(appConfig).mount(ctx.root); 20 | 21 | ctx.handleEvent("update", ({ fields }) => { 22 | setValues(fields); 23 | }); 24 | 25 | ctx.handleSync(() => { 26 | // Synchronously invokes change listeners 27 | document.activeElement && 28 | document.activeElement.dispatchEvent( 29 | new Event("change", { bubbles: true }) 30 | ); 31 | }); 32 | 33 | function setValues(fields) { 34 | for (const field in fields) { 35 | app.fields[field] = fields[field]; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/evision/application.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.Application do 2 | @moduledoc false 3 | use Application 4 | 5 | def start(_type, _args) do 6 | if Code.ensure_loaded?(Kino), do: Evision.SmartCell.register_smartcells(Evision.SmartCell.Zoo) 7 | Supervisor.start_link([], strategy: :one_for_one) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/evision_highgui.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.HighGui do 2 | @moduledoc """ 3 | High-level Graphical User Interface 4 | """ 5 | 6 | @doc """ 7 | Show a mat in a named window 8 | 9 | #### Positional Arguments 10 | 11 | - **winname**. `String` 12 | 13 | The name of the window. 14 | 15 | - **mat**. `Evision.Mat` 16 | 17 | The image. 18 | 19 | #### Example 20 | 21 | ```elixir 22 | iex> mat = Evision.imread!("example.jpg") 23 | iex> Evision.imshow("OpenCV", mat) 24 | # the following line may be necessary on your system 25 | # will try to improve this later 26 | iex> Evision.waitkey(0) 27 | ``` 28 | 29 | """ 30 | @doc namespace: :"cv.highgui" 31 | @spec imshow(String.t(), Evision.Mat.maybe_mat_in() | Nx.Tensor.t()) :: 32 | :ok | {:error, String.t()} 33 | def imshow(winname, mat) when is_binary(winname) and (is_reference(mat) or is_struct(mat)) do 34 | mat = Evision.Internal.Structurise.from_struct(mat) 35 | :evision_nif.imshow(winname: winname, mat: mat) 36 | end 37 | 38 | @doc """ 39 | Wait for user keyboard event for a `delay` amount of time (ms) 40 | 41 | #### Positional Arguments 42 | 43 | - **delay**. `int` 44 | 45 | Wait for `delay` ms. 46 | 47 | """ 48 | @doc namespace: :"cv.highgui" 49 | @spec waitKey(integer()) :: :ok | {:error, String.t()} 50 | def waitKey(delay) when is_integer(delay) do 51 | :evision_nif.waitKey(delay: delay) 52 | end 53 | 54 | @doc """ 55 | Close a named window 56 | 57 | #### Positional Arguments 58 | 59 | - **winname**. `String` 60 | 61 | The name of the window. 62 | 63 | """ 64 | @doc namespace: :"cv.highgui" 65 | @spec destroyWindow(String.t()) :: :ok | {:error, String.t()} 66 | def destroyWindow(winname) when is_binary(winname) do 67 | :evision_nif.destroyWindow(winname: winname) 68 | end 69 | 70 | @doc """ 71 | Close all windows 72 | """ 73 | @doc namespace: :"cv.highgui" 74 | @spec destroyAllWindows() :: :ok 75 | def destroyAllWindows do 76 | :evision_nif.destroyAllWindows() 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/evision_structurise.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.Internal.Structurise do 2 | @moduledoc false 3 | 4 | @spec to_struct(term()) :: {:ok, term()} | {:error, String.t()} | term() 5 | def to_struct(any) 6 | 7 | def to_struct({:ok, ret}), do: to_struct_ok(ret) 8 | 9 | def to_struct(ret = %{:class => module_name}) when is_atom(module_name) do 10 | if Code.ensure_loaded?(module_name) do 11 | module_name.to_struct(ret) 12 | else 13 | ret 14 | end 15 | end 16 | 17 | def to_struct(tuple) when is_tuple(tuple) do 18 | Enum.map(Tuple.to_list(tuple), fn elem -> 19 | to_struct(elem) 20 | end) 21 | |> List.to_tuple() 22 | end 23 | 24 | def to_struct(list) when is_list(list) do 25 | Enum.map(list, fn elem -> 26 | to_struct(elem) 27 | end) 28 | end 29 | 30 | def to_struct(pass_through), do: pass_through 31 | 32 | @spec to_struct_ok(term()) :: {:ok, term()} 33 | def to_struct_ok(any) 34 | 35 | def to_struct_ok(ret = %{:class => _module_name}) do 36 | {:ok, to_struct(ret)} 37 | end 38 | 39 | def to_struct_ok(tuple) when is_tuple(tuple) do 40 | {:ok, to_struct(tuple)} 41 | end 42 | 43 | def to_struct_ok(list) when is_list(list) do 44 | {:ok, to_struct(list)} 45 | end 46 | 47 | def to_struct_ok(pass_through), do: {:ok, pass_through} 48 | 49 | def to_compact_type({:u, 8}), do: :u8 50 | def to_compact_type({:u, 16}), do: :u16 51 | def to_compact_type({:u, 32}), do: :u32 52 | def to_compact_type({:u, 64}), do: :u64 53 | def to_compact_type({:s, 8}), do: :s8 54 | def to_compact_type({:s, 16}), do: :s16 55 | def to_compact_type({:s, 32}), do: :s32 56 | def to_compact_type({:s, 64}), do: :s64 57 | def to_compact_type({:f, 16}), do: :f16 58 | def to_compact_type({:f, 32}), do: :f32 59 | def to_compact_type({:f, 64}), do: :f64 60 | def to_compact_type({:c, 32}), do: :c32 61 | def to_compact_type({:c, 64}), do: :c64 62 | 63 | def to_compact_type(atom) when is_atom(atom) do 64 | atom 65 | end 66 | 67 | # specialised for Evision.Backend 68 | @spec from_struct(Nx.Tensor.t()) :: reference() 69 | def from_struct(%Nx.Tensor{data: %Evision.Backend{ref: %Evision.Mat{ref: ref}}}) do 70 | ref 71 | end 72 | 73 | # for generic Nx.Tensor 74 | @spec from_struct(Nx.Tensor.t()) :: reference() 75 | def from_struct(%Nx.Tensor{} = tensor) do 76 | %{ 77 | tensor 78 | | data: Nx.to_binary(tensor), 79 | type: to_compact_type(tensor.type), 80 | __struct__: :nx_tensor 81 | } 82 | end 83 | 84 | @spec from_struct(%{ref: reference()}) :: reference() 85 | def from_struct(%{ref: ref}) do 86 | ref 87 | end 88 | 89 | @spec from_struct(tuple()) :: tuple() 90 | def from_struct(tuple) when is_tuple(tuple) do 91 | from_struct(Tuple.to_list(tuple)) 92 | |> List.to_tuple() 93 | end 94 | 95 | @spec from_struct(list()) :: list() 96 | def from_struct(list) when is_list(list) do 97 | if Keyword.keyword?(list) do 98 | Enum.map(Keyword.keys(list), fn key -> 99 | {key, from_struct(Keyword.fetch!(list, key))} 100 | end) 101 | |> Keyword.new() 102 | else 103 | Enum.map(list, fn elem -> 104 | from_struct(elem) 105 | end) 106 | end 107 | end 108 | 109 | @spec from_struct(term()) :: term() 110 | def from_struct(pass_through), do: pass_through 111 | end 112 | -------------------------------------------------------------------------------- /lib/evision_windows_fix.ex: -------------------------------------------------------------------------------- 1 | defmodule :evision_windows_fix do 2 | @moduledoc false 3 | @on_load :__on_load__ 4 | def __on_load__ do 5 | case :os.type() do 6 | {:win32, _} -> 7 | path = :filename.join(:code.priv_dir(:evision), ~c"windows_fix") 8 | :erlang.load_nif(path, 0) 9 | run_once() 10 | 11 | _ -> 12 | :ok 13 | end 14 | end 15 | 16 | def run_once do 17 | case :os.type() do 18 | {:win32, _} -> 19 | :erlang.nif_error(:undefined) 20 | 21 | _ -> 22 | :ok 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/smartcell/evision_smartcell.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.SmartCell do 2 | @moduledoc """ 3 | Evision SmartCell Collection 4 | 5 | To use smartcell in Livebook, `:kino >= 0.7` is required 6 | 7 | ```elixir 8 | defp deps do 9 | [ 10 | # ... 11 | {:kino, "~> 0.7"}, 12 | # ... 13 | ] 14 | end 15 | ``` 16 | """ 17 | 18 | @doc """ 19 | Get all available smartcells. 20 | 21 | To register smartcells to `:kino`, please use `Evision.SmartCell.register_smartcells/1`. 22 | """ 23 | @spec available_smartcells :: [module()] 24 | def available_smartcells do 25 | [ 26 | Evision.SmartCell.ML.TrainData, 27 | Evision.SmartCell.ML.SVM, 28 | Evision.SmartCell.ML.DTrees, 29 | Evision.SmartCell.ML.RTrees, 30 | Evision.SmartCell.Zoo 31 | ] 32 | end 33 | 34 | @compile {:no_warn_undefined, Kino.SmartCell} 35 | 36 | @doc """ 37 | Register Smartcells 38 | 39 | It will register all available smartcells by default. 40 | 41 | To see all available smartcells, please use `Evision.SmartCell.available_smartcells/0`. 42 | """ 43 | @spec register_smartcells([module()] | module()) :: :ok 44 | def register_smartcells(smartcells \\ available_smartcells()) 45 | 46 | def register_smartcells(smartcells) when is_list(smartcells) do 47 | if Code.ensure_loaded?(Kino.SmartCell) do 48 | Enum.each(smartcells, fn sc -> 49 | if Code.ensure_loaded?(sc) do 50 | Kino.SmartCell.register(sc) 51 | else 52 | raise RuntimeError, "Cannot register smartcell: #{inspect(sc)}" 53 | end 54 | end) 55 | else 56 | raise RuntimeError, """ 57 | `:kino >= 0.7` is required to use smartcells in Livebook. 58 | Please add `{:kino, "~> 0.7"}` to the dependency list. 59 | 60 | Note that `Evision.SmartCell.Zoo` has a another optional dependency, 61 | `{:progress_bar, "~> 2.0}`, which is used for displaying the 62 | model download progress. 63 | """ 64 | end 65 | end 66 | 67 | def register_smartcells(smartcell) when is_atom(smartcell) do 68 | register_smartcells([smartcell]) 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/smartcell/evision_smartcell_helper.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.SmartCell.Helper do 2 | @moduledoc false 3 | 4 | def quoted_var(nil), do: nil 5 | def quoted_var(string), do: {String.to_atom(string), [], nil} 6 | 7 | def to_update(value, type, opts \\ []) 8 | 9 | def to_update(value, :string, opts) do 10 | must_in = opts[:must_in] || [value] 11 | 12 | if Enum.member?(must_in, value) do 13 | value 14 | else 15 | nil 16 | end 17 | end 18 | 19 | def to_update(value, :boolean, _opts) do 20 | if is_binary(value) do 21 | case value do 22 | "true" -> true 23 | "false" -> false 24 | _ -> nil 25 | end 26 | else 27 | if is_boolean(value) do 28 | value 29 | else 30 | nil 31 | end 32 | end 33 | end 34 | 35 | def to_update(value, :integer, opts) do 36 | minimum = opts[:minimum] 37 | maximum = opts[:maximum] 38 | 39 | case Integer.parse(value) do 40 | {n, ""} -> 41 | n = 42 | if minimum != nil and n < minimum do 43 | minimum 44 | else 45 | n 46 | end 47 | 48 | if maximum != nil and n > maximum do 49 | maximum 50 | else 51 | n 52 | end 53 | 54 | _ -> 55 | nil 56 | end 57 | end 58 | 59 | def to_update(value, :number, opts) do 60 | minimum = opts[:minimum] 61 | maximum = opts[:maximum] 62 | 63 | case Float.parse(value) do 64 | {n, ""} -> 65 | n = 66 | if minimum != nil and n < minimum do 67 | minimum 68 | else 69 | n 70 | end 71 | 72 | if maximum != nil and n > maximum do 73 | maximum 74 | else 75 | n 76 | end 77 | 78 | _ -> 79 | nil 80 | end 81 | end 82 | 83 | @evision_types [:f32, :f64, :u8, :u16, :s8, :s16, :s32] 84 | def to_update(value, :type, opts) do 85 | allowed_types = opts[:allowed_types] || @evision_types 86 | 87 | if Enum.member?(allowed_types, String.to_atom(value)) do 88 | value 89 | else 90 | nil 91 | end 92 | end 93 | 94 | def update_key_with_module(fields, key, module, conflicit \\ :loaded, on_condition) do 95 | if on_condition.(fields, key) do 96 | fill_or_merge_defaults(fields, key, module, conflicit) 97 | else 98 | fields 99 | end 100 | end 101 | 102 | def fill_or_merge_defaults(fields, key, module, conflicit \\ :loaded) do 103 | if fields[key] == nil do 104 | Map.put(fields, key, module.defaults()) 105 | else 106 | merge_properties(fields[key], module.defaults(), conflicit) 107 | end 108 | end 109 | 110 | def to_inner_updates(inner_name, inner_module, field, value, ctx) do 111 | inner_fields = ctx.assigns.fields[inner_name] 112 | updated_fields = inner_module.to_updates(inner_fields, field, value) 113 | %{inner_name => Map.merge(inner_fields, updated_fields)} 114 | end 115 | 116 | def merge_properties(loaded, default, conflicit \\ :loaded) do 117 | Map.merge(loaded, default, fn _key, loaded_value, default_specs -> 118 | case conflicit do 119 | :loaded -> 120 | if loaded_value != nil do 121 | loaded_value 122 | else 123 | default_specs[:default] 124 | end 125 | 126 | :default -> 127 | default_specs[:default] 128 | end 129 | end) 130 | end 131 | end 132 | -------------------------------------------------------------------------------- /lib/smartcell/simple_list.ex: -------------------------------------------------------------------------------- 1 | if !Code.ensure_loaded?(Kino.SmartCell) do 2 | defmodule Evision.SmartCell.SimpleList do 3 | end 4 | else 5 | defmodule Evision.SmartCell.SimpleList do 6 | # code taken from 7 | # https://github.com/livebook-dev/kino_bumblebee/blob/main/lib/kino/bumblebee/scored_list.ex 8 | 9 | @moduledoc """ 10 | A kino for displaying a list of labels. 11 | This kino is primarily used to present top classification predictions. 12 | ## Examples 13 | predictions = [ 14 | "malamute", 15 | "Siberian husky", 16 | "Eskimo dog", 17 | "Tibetan mastiff", 18 | "German shepherd" 19 | ] 20 | Evision.SmartCell.SimpleList.new(predictions) 21 | """ 22 | 23 | use Kino.JS 24 | 25 | @type t :: Kino.JS.t() 26 | 27 | @doc """ 28 | Creates a new kino displaying the given list of items. 29 | Expects a list of tuples, each element being the label and its score. 30 | """ 31 | @spec new(list({String.t(), number()})) :: t() 32 | def new(items) when is_list(items) do 33 | Kino.JS.new(__MODULE__, items) 34 | end 35 | 36 | asset "main.js" do 37 | """ 38 | export function init(ctx, items) { 39 | ctx.importCSS("main.css"); 40 | ctx.importCSS( 41 | "https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" 42 | ); 43 | ctx.root.innerHTML = ` 44 |
45 |
    46 | ${items.map((item) => (` 47 |
  1. 48 |
    49 |
    ${item}
    50 |
    51 |
  2. 52 | `)).join("")} 53 |
54 |
55 | `; 56 | } 57 | """ 58 | end 59 | 60 | asset "main.css" do 61 | """ 62 | .app { 63 | font-family: "JetBrains Mono", monospace; 64 | font-size: 14px; 65 | color: #8BA2FF; 66 | } 67 | .list { 68 | padding: 0; 69 | margin: 0; 70 | } 71 | .item { 72 | display: flex; 73 | justify-content: space-between; 74 | } 75 | .item:not(:first-child) { 76 | margin-top: 16px; 77 | } 78 | .label { 79 | flex-grow: 1; 80 | margin-right: 8px; 81 | } 82 | """ 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/zoo/face_detection/face_detection.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.Zoo.FaceDetection do 2 | @moduledoc """ 3 | Face detection model collection. 4 | """ 5 | 6 | @modules %{ 7 | "yunet" => Evision.Zoo.FaceDetection.YuNet, 8 | "yunet_quant" => Evision.Zoo.FaceDetection.YuNet, 9 | } 10 | @module_list Enum.uniq(Map.values(@modules)) 11 | 12 | def modules, do: @modules 13 | def module_list, do: @module_list 14 | 15 | def id, do: "face_detection" 16 | 17 | def label, do: "Face detection" 18 | 19 | def smartcell_tasks do 20 | %{ 21 | id: id(), 22 | label: label(), 23 | variants: Enum.reduce(module_list(), [], fn module, acc -> module.smartcell_tasks() ++ acc end) 24 | } 25 | end 26 | 27 | @doc """ 28 | Generate quoted code from smart cell attrs. 29 | """ 30 | @spec to_quoted(map()) :: list() 31 | def to_quoted(%{"task_id" => "face_detection", "variant_id" => variant_id} = attrs) do 32 | module = Map.get(modules(), variant_id) 33 | if module do 34 | module.to_quoted(attrs) 35 | else 36 | [] 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/zoo/face_recognition/face_recognition.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.Zoo.FaceRecognition do 2 | @moduledoc """ 3 | Face recognition model collection. 4 | """ 5 | 6 | @modules %{ 7 | "sface" => Evision.Zoo.FaceRecognition.SFace, 8 | "sface_quant" => Evision.Zoo.FaceRecognition.SFace, 9 | } 10 | @module_list Enum.uniq(Map.values(@modules)) 11 | 12 | def modules, do: @modules 13 | def module_list, do: @module_list 14 | 15 | def id, do: "face_recognition" 16 | 17 | def label, do: "Face recognition" 18 | 19 | def smartcell_tasks do 20 | %{ 21 | id: id(), 22 | label: label(), 23 | variants: Enum.reduce(module_list(), [], fn module, acc -> module.smartcell_tasks() ++ acc end) 24 | } 25 | end 26 | 27 | @doc """ 28 | Generate quoted code from smart cell attrs. 29 | """ 30 | @spec to_quoted(map()) :: list() 31 | def to_quoted(%{"task_id" => "face_recognition", "variant_id" => variant_id} = attrs) do 32 | module = Map.get(modules(), variant_id) 33 | if module do 34 | module.to_quoted(attrs) 35 | else 36 | [] 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/zoo/image_classification/image_classification.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.Zoo.ImageClassification do 2 | @moduledoc """ 3 | Image classfication model collection. 4 | """ 5 | 6 | 7 | @modules %{ 8 | "pp_resnet" => Evision.Zoo.ImageClassification.PPResNet, 9 | "pp_resnet_quant" => Evision.Zoo.ImageClassification.PPResNet, 10 | "mobilenet_v1" => Evision.Zoo.ImageClassification.MobileNetV1, 11 | "mobilenet_v1_quant" => Evision.Zoo.ImageClassification.MobileNetV1, 12 | "mobilenet_v2" => Evision.Zoo.ImageClassification.MobileNetV2, 13 | "mobilenet_v2_quant" => Evision.Zoo.ImageClassification.MobileNetV2, 14 | } 15 | @module_list Enum.uniq(Map.values(@modules)) 16 | 17 | def modules, do: @modules 18 | def module_list, do: @module_list 19 | 20 | def id, do: "image_classification" 21 | 22 | def label, do: "Image Classification" 23 | 24 | def smartcell_tasks do 25 | %{ 26 | id: id(), 27 | label: label(), 28 | variants: Enum.reduce(module_list(), [], fn module, acc -> module.smartcell_tasks() ++ acc end) 29 | } 30 | end 31 | 32 | @doc """ 33 | Generate quoted code from smart cell attrs. 34 | """ 35 | @spec to_quoted(map()) :: list() 36 | def to_quoted(%{"task_id" => "image_classification", "variant_id" => variant_id} = attrs) do 37 | module = Map.get(modules(), variant_id) 38 | if module do 39 | module.to_quoted(attrs) 40 | else 41 | [] 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/zoo/image_segmentation/image_segmentation.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.Zoo.ImageSegmentation do 2 | @moduledoc """ 3 | Image segmentation model collection. 4 | """ 5 | 6 | import Bitwise 7 | 8 | @modules %{ 9 | "pp_humanseg" => Evision.Zoo.ImageSegmentation.PPHumanSeg, 10 | "pp_humanseg_quant" => Evision.Zoo.ImageSegmentation.PPHumanSeg, 11 | } 12 | @module_list Enum.uniq(Map.values(@modules)) 13 | 14 | def modules, do: @modules 15 | def module_list, do: @module_list 16 | 17 | def id, do: "image_segmentation" 18 | 19 | def label, do: "Image Segmentation" 20 | 21 | def smartcell_tasks do 22 | %{ 23 | id: id(), 24 | label: label(), 25 | variants: Enum.reduce(module_list(), [], fn module, acc -> module.smartcell_tasks() ++ acc end) 26 | } 27 | end 28 | 29 | @doc """ 30 | Generate quoted code from smart cell attrs. 31 | """ 32 | @spec to_quoted(map()) :: list() 33 | def to_quoted(%{"task_id" => "image_segmentation", "variant_id" => variant_id} = attrs) do 34 | module = Map.get(modules(), variant_id) 35 | if module do 36 | module.to_quoted(attrs) 37 | else 38 | [] 39 | end 40 | end 41 | 42 | def color_map(num_classes) do 43 | color_map = 44 | for i <- 0..num_classes, reduce: [] do 45 | color_map -> 46 | colors = gen_color_map(i, i, 0, [0, 0, 0]) 47 | [colors | color_map] 48 | end 49 | tl(Enum.reverse(color_map)) 50 | end 51 | 52 | defp gen_color_map(0, _, _, colors), do: colors 53 | defp gen_color_map(lab, i, j, [c1, c2, c3]) do 54 | c1 = bor(c1, bsl(band(bsr(lab, 0), 1), 7 - j)) 55 | c2 = bor(c2, bsl(band(bsr(lab, 1), 1), 7 - j)) 56 | c3 = bor(c3, bsl(band(bsr(lab, 2), 1), 7 - j)) 57 | 58 | gen_color_map(lab >>> 3, i, j + 1, [c1, c2, c3]) 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/zoo/text_detection/text_detection.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.Zoo.TextDetection do 2 | @moduledoc """ 3 | Text detection model collection. 4 | """ 5 | 6 | @modules %{ 7 | "db_ic15_resnet18" => Evision.Zoo.TextDetection.DB, 8 | "db_ic15_resnet50" => Evision.Zoo.TextDetection.DB, 9 | "db_td500_resnet18" => Evision.Zoo.TextDetection.DB, 10 | "db_td500_resnet50" => Evision.Zoo.TextDetection.DB, 11 | "ppocrv3_en" => Evision.Zoo.TextDetection.PPOCRV3, 12 | "ppocrv3_en_int8" => Evision.Zoo.TextDetection.PPOCRV3, 13 | "ppocrv3_cn" => Evision.Zoo.TextDetection.PPOCRV3, 14 | "ppocrv3_cn_int8" => Evision.Zoo.TextDetection.PPOCRV3 15 | } 16 | @module_list Enum.uniq(Map.values(@modules)) 17 | 18 | def modules, do: @modules 19 | def module_list, do: @module_list 20 | 21 | def id, do: "text_detection" 22 | 23 | def label, do: "Text detection" 24 | 25 | def smartcell_tasks do 26 | %{ 27 | id: id(), 28 | label: label(), 29 | variants: Enum.reduce(module_list(), [], fn module, acc -> module.smartcell_tasks() ++ acc end) 30 | } 31 | end 32 | 33 | @doc """ 34 | Generate quoted code from smart cell attrs. 35 | """ 36 | @spec to_quoted(map()) :: list() 37 | def to_quoted(%{"task_id" => "text_detection", "variant_id" => variant_id} = attrs) do 38 | module = Map.get(modules(), variant_id) 39 | if module do 40 | module.to_quoted(attrs) 41 | else 42 | [] 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/zoo/text_recognition/text_recognition.ex: -------------------------------------------------------------------------------- 1 | defmodule Evision.Zoo.TextRecognition do 2 | @moduledoc """ 3 | Text recognition model collection. 4 | """ 5 | 6 | @modules %{ 7 | "crnn_en" => Evision.Zoo.TextRecognition.CRNN, 8 | "crnn_en_fp16" => Evision.Zoo.TextRecognition.CRNN, 9 | "crnn_en_int8" => Evision.Zoo.TextRecognition.CRNN, 10 | "crnn_ch" => Evision.Zoo.TextRecognition.CRNN, 11 | "crnn_ch_fp16" => Evision.Zoo.TextRecognition.CRNN, 12 | "crnn_ch_int8" => Evision.Zoo.TextRecognition.CRNN, 13 | "crnn_cn" => Evision.Zoo.TextRecognition.CRNN, 14 | "crnn_cn_int8" => Evision.Zoo.TextRecognition.CRNN, 15 | } 16 | @module_list Enum.uniq(Map.values(@modules)) 17 | 18 | def modules, do: @modules 19 | def module_list, do: @module_list 20 | 21 | def id, do: "text_recognition" 22 | 23 | def label, do: "Text recognition" 24 | 25 | def smartcell_tasks do 26 | %{ 27 | id: id(), 28 | label: label(), 29 | variants: Enum.reduce(module_list(), [], fn module, acc -> module.smartcell_tasks() ++ acc end) 30 | } 31 | end 32 | 33 | @doc """ 34 | Generate quoted code from smart cell attrs. 35 | """ 36 | @spec to_quoted(map()) :: list() 37 | def to_quoted(%{"task_id" => "text_recognition", "variant_id" => variant_id} = attrs) do 38 | module = Map.get(modules(), variant_id) 39 | if module do 40 | module.to_quoted(attrs) 41 | else 42 | [] 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /py_src/arg_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | from helper import * 6 | 7 | 8 | class ArgInfo(object): 9 | def __init__(self, atype, name, default_value, modifiers=(), 10 | enclosing_arg=None): 11 | # type: (ArgInfo, str, str, str, tuple[str, ...], ArgInfo | None) -> None 12 | self.tp = handle_ptr(atype) 13 | self.name = name 14 | self.defval = default_value 15 | self._modifiers = tuple(modifiers) 16 | self.isarray = False 17 | self.is_smart_ptr = self.tp.startswith('Ptr<') # FIXIT: handle through modifiers - need to modify parser 18 | self.arraylen = 0 19 | self.arraycvt = None 20 | for m in self._modifiers: 21 | if m.startswith("/A"): 22 | self.isarray = True 23 | self.arraylen = m[2:].strip() 24 | elif m.startswith("/CA"): 25 | self.isarray = True 26 | self.arraycvt = m[2:].strip() 27 | self.py_inputarg = False 28 | self.py_outputarg = False 29 | self.enclosing_arg = enclosing_arg 30 | 31 | def __str__(self): 32 | return 'ArgInfo("{}", tp="{}", default="{}", in={}, out={})'.format( 33 | self.name, self.tp, self.defval, self.inputarg, 34 | self.outputarg 35 | ) 36 | 37 | def __repr__(self): 38 | return str(self) 39 | 40 | @property 41 | def export_name(self): 42 | return self.name 43 | 44 | @property 45 | def inputarg(self): 46 | return '/O' not in self._modifiers 47 | 48 | @property 49 | def arithm_op_src_arg(self): 50 | return '/AOS' in self._modifiers 51 | 52 | @property 53 | def outputarg(self): 54 | return '/O' in self._modifiers or '/IO' in self._modifiers 55 | 56 | @property 57 | def pathlike(self): 58 | return '/PATH' in self._modifiers 59 | 60 | @property 61 | def nd_mat(self): 62 | return '/ND' in self._modifiers 63 | 64 | @property 65 | def has_default(self): 66 | return self.defval == self.tp + "()" 67 | 68 | @property 69 | def returnarg(self): 70 | return self.outputarg 71 | 72 | @property 73 | def isrvalueref(self): 74 | return '/RRef' in self._modifiers 75 | 76 | @property 77 | def full_name(self): 78 | if self.enclosing_arg is None: 79 | return self.name 80 | return self.enclosing_arg.name + '.' + self.name 81 | 82 | def isbig(self): 83 | return self.tp in ["Mat", "vector_Mat", 84 | "cuda::GpuMat", "cuda_GpuMat", "GpuMat", 85 | "vector_GpuMat", "vector_cuda_GpuMat", 86 | "UMat", "vector_UMat"] # or self.tp.startswith("vector") 87 | 88 | def crepr(self, overwrite_defval=None): 89 | # has_default = 0 90 | # if (defval is not None and len(defval) > 0) or self.defval == self.tp + "()": 91 | # has_default = 1 92 | has_default = self.defval == self.tp + "()" 93 | if overwrite_defval is not None and len(overwrite_defval) > 0: 94 | has_default = 1 95 | arg = 0x01 if self.outputarg else 0x0 96 | arg += 0x02 if self.arithm_op_src_arg else 0x0 97 | arg += 0x04 if self.pathlike else 0x0 98 | arg += 0x08 if self.nd_mat else 0x0 99 | arg += 0x10 if has_default else 0x0 100 | return "ArgInfo(\"%s\", %d)" % (self.name, arg) 101 | -------------------------------------------------------------------------------- /py_src/class_prop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | class ClassProp(object): 6 | def __init__(self, decl): 7 | self.tp = decl[0].replace("*", "_ptr") 8 | self.name = decl[1] 9 | self.readonly = True 10 | if "/RW" in decl[3]: 11 | self.readonly = False 12 | -------------------------------------------------------------------------------- /py_src/format_strings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | class FormatStrings: 6 | string = 's' 7 | unsigned_char = 'b' 8 | short_int = 'h' 9 | int = 'i' 10 | unsigned_int = 'I' 11 | long = 'l' 12 | unsigned_long = 'k' 13 | long_long = 'L' 14 | unsigned_long_long = 'K' 15 | size_t = 'n' 16 | float = 'f' 17 | double = 'd' 18 | object = 'O' 19 | -------------------------------------------------------------------------------- /py_src/namespace.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | class Namespace(object): 6 | def __init__(self): 7 | self.funcs = {} 8 | self.consts = {} 9 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | {deps, []}. 3 | 4 | {pre_hooks, 5 | [{"(linux|darwin|solaris)", compile, "make EVISION_PREFER_PRECOMPILED=true EVISION_COMPILE_WITH_REBAR=true EVISION_MAKE=make"}, 6 | {"(freebsd)", compile, "gmake EVISION_PREFER_PRECOMPILED=true EVISION_COMPILE_WITH_REBAR=true EVISION_MAKE=gmake"}, 7 | {"win32", compile, "nmake"} 8 | ]}. 9 | 10 | {edoc_opts, 11 | [ 12 | {doclet, edoc_doclet_chunks}, 13 | {layout, edoc_layout_chunks}, 14 | {preprocess, true}, 15 | {dir, "_build/default/lib/evision/doc"} 16 | ]}. 17 | 18 | {project_plugins, [rebar3_ex_doc]}. 19 | 20 | {hex, [{doc, ex_doc}]}. 21 | 22 | {ex_doc, [ 23 | {source_url, <<"https://github.com/cocoa-xu/evision">>}, 24 | {extras, [<<"README.md">>, <<"CHANGELOG.md">>, <<"LICENSE">>, <<"LICENSE-OpenCV">>]}, 25 | {main, <<"README.md">>}]}. 26 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /scripts/download_opencv.ps1: -------------------------------------------------------------------------------- 1 | $opencv_ver=$args[0] 2 | $opencv_cache_dir=$args[1] 3 | $opencv_root_dir=$args[2] 4 | 5 | $opencv_zip_url="https://github.com/opencv/opencv/archive/$opencv_ver.zip" 6 | $opencv_source_zip="$opencv_cache_dir\\opencv-$opencv_ver.zip" 7 | $opencv_source_root="$opencv_root_dir\\opencv-$opencv_ver" 8 | 9 | if(-Not(Test-Path -PathType container -Path $opencv_cache_dir)) 10 | { 11 | New-Item -Path $opencv_cache_dir -ItemType Container 12 | } 13 | if(-Not(Test-Path -PathType container -Path $opencv_root_dir)) 14 | { 15 | New-Item -Path $opencv_root_dir -ItemType Container 16 | } 17 | 18 | if (-Not(Test-Path -Path $opencv_source_root -PathType container)) 19 | { 20 | if (-Not(Test-Path -Path $opencv_source_zip -PathType Leaf)) 21 | { 22 | (New-Object Net.WebClient).DownloadFile($opencv_zip_url,$opencv_source_zip) 23 | } 24 | Expand-Archive -Path $opencv_source_zip -DestinationPath $opencv_root_dir 25 | } 26 | -------------------------------------------------------------------------------- /scripts/download_opencv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OPENCV_VER="$1" 4 | OPENCV_CACHE_DIR="$2" 5 | OPENCV_ROOT_DIR="$3" 6 | 7 | OPENCV_ZIP_URL="https://github.com/opencv/opencv/archive/${OPENCV_VER}.zip" 8 | OPENCV_SOURCE_ZIP="${OPENCV_CACHE_DIR}/opencv-${OPENCV_VER}.zip" 9 | 10 | download_opencv() { 11 | if [ ! -f "${OPENCV_SOURCE_ZIP}" ]; then 12 | echo "Downloading OpenCV ${OPENCV_VER}..." 13 | mkdir -p "${OPENCV_CACHE_DIR}" 14 | if [ -e "$(which wget)" ]; then 15 | wget --quiet "${OPENCV_ZIP_URL}" -O "${OPENCV_SOURCE_ZIP}" 16 | elif [ -e "$(which curl)" ]; then 17 | curl -fSsL "${OPENCV_ZIP_URL}" -o "${OPENCV_SOURCE_ZIP}" 18 | else 19 | echo "No wget or curl found, please install one of them" 20 | exit 1 21 | fi 22 | fi 23 | } 24 | 25 | unzip_opencv() { 26 | if [ ! -d "${OPENCV_ROOT_DIR}/opencv-${OPENCV_VER}" ]; then 27 | echo "Unzipping OpenCV ${OPENCV_VER}..." 28 | mkdir -p "${OPENCV_ROOT_DIR}" 29 | if [ -e "$(which unzip)" ]; then 30 | unzip -qq "${OPENCV_SOURCE_ZIP}" -d "${OPENCV_ROOT_DIR}" ; 31 | elif [ -e "$(which 7z)" ]; then 32 | 7z x "${OPENCV_SOURCE_ZIP}" -o"${OPENCV_ROOT_DIR}" ; 33 | else 34 | echo "No unzip or 7z found, please install one of them" 35 | exit 1 36 | fi 37 | fi 38 | } 39 | 40 | download_opencv && unzip_opencv 41 | -------------------------------------------------------------------------------- /scripts/download_opencv_contrib.ps1: -------------------------------------------------------------------------------- 1 | $opencv_ver=$args[0] 2 | $opencv_cache_dir=$args[1] 3 | $opencv_root_dir=$args[2] 4 | 5 | $opencv_contrib_zip_url="https://github.com/opencv/opencv_contrib/archive/$opencv_ver.zip" 6 | $opencv_contrib_source_zip="$opencv_cache_dir\\opencv_contrib-$opencv_ver.zip" 7 | $opencv_source_root="$opencv_root_dir\\opencv_contrib-$opencv_ver" 8 | 9 | if(-Not(Test-Path -PathType container -Path $opencv_cache_dir)) 10 | { 11 | New-Item -Path $opencv_cache_dir -ItemType Container 12 | } 13 | if(-Not(Test-Path -PathType container -Path $opencv_root_dir)) 14 | { 15 | New-Item -Path $opencv_root_dir -ItemType Container 16 | } 17 | 18 | if (-Not(Test-Path -Path $opencv_source_root -PathType container)) 19 | { 20 | if (-Not(Test-Path -Path $opencv_contrib_source_zip -PathType Leaf)) 21 | { 22 | (New-Object Net.WebClient).DownloadFile($opencv_contrib_zip_url,$opencv_contrib_source_zip) 23 | } 24 | Expand-Archive -Path $opencv_contrib_source_zip -DestinationPath $opencv_root_dir 25 | } 26 | -------------------------------------------------------------------------------- /scripts/download_opencv_contrib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OPENCV_VER="$1" 4 | OPENCV_CACHE_DIR="$2" 5 | OPENCV_ROOT_DIR="$3" 6 | 7 | OPENCV_CONTRIB_ZIP_URL="https://github.com/opencv/opencv_contrib/archive/${OPENCV_VER}.zip" 8 | OPENCV_CONTRIB_SOURCE_ZIP="${OPENCV_CACHE_DIR}/opencv_contrib-${OPENCV_VER}.zip" 9 | 10 | download_opencv_contrib() { 11 | if [ ! -f "${OPENCV_CONTRIB_SOURCE_ZIP}" ]; then 12 | echo "Downloading OpenCV contrib ${OPENCV_VER}..." 13 | mkdir -p "${OPENCV_CACHE_DIR}" 14 | if [ -e "$(which wget)" ]; then 15 | wget --quiet "${OPENCV_CONTRIB_ZIP_URL}" -O "${OPENCV_CONTRIB_SOURCE_ZIP}" 16 | elif [ -e "$(which curl)" ]; then 17 | curl -fSsL "${OPENCV_CONTRIB_ZIP_URL}" -o "${OPENCV_CONTRIB_SOURCE_ZIP}" 18 | else 19 | echo "No wget or curl found, please install one of them" 20 | exit 1 21 | fi 22 | fi 23 | } 24 | 25 | unzip_opencv_contrib() { 26 | if [ ! -d "${OPENCV_ROOT_DIR}/opencv_contrib-${OPENCV_VER}" ]; then 27 | echo "Unzipping OpenCV contrib ${OPENCV_VER}..." 28 | mkdir -p "${OPENCV_ROOT_DIR}" 29 | if [ -e "$(which unzip)" ]; then 30 | unzip -qq "${OPENCV_CONTRIB_SOURCE_ZIP}" -d "${OPENCV_ROOT_DIR}" ; 31 | elif [ -e "$(which 7z)" ]; then 32 | 7z x "${OPENCV_CONTRIB_SOURCE_ZIP}" -o"${OPENCV_ROOT_DIR}" ; 33 | else 34 | echo "No unzip or 7z found, please install one of them" 35 | exit 1 36 | fi 37 | fi 38 | } 39 | 40 | download_opencv_contrib && unzip_opencv_contrib 41 | -------------------------------------------------------------------------------- /src/evision.app.src: -------------------------------------------------------------------------------- 1 | {application, evision, 2 | [{description, "OpenCV BEAM binding."}, 3 | {vsn, "0.2.13"}, 4 | {registered, []}, 5 | {applications, 6 | [kernel, 7 | stdlib 8 | ]}, 9 | {env,[]}, 10 | {modules, []}, 11 | 12 | {licenses, ["Apache-2.0"]}, 13 | {links, [{"GitHub", "https://github.com/cocoa-xu/evision"}]}, 14 | {exclude_paths, ["mix.exs", "mix.lock", ".formatter.exs"]}, 15 | {doc, "doc"} 16 | ]}. 17 | -------------------------------------------------------------------------------- /src/evision_highgui.erl: -------------------------------------------------------------------------------- 1 | -module(evision_highgui). 2 | -compile(nowarn_export_all). 3 | -compile([export_all]). 4 | 5 | imshow(WinName, Mat) when is_binary(WinName) -> 6 | evision_nif:imshow([{winname, WinName}, {mat, evision_internal_structurise:from_struct(Mat)}]). 7 | 8 | waitKey(Delay) when is_integer(Delay) -> 9 | evision_nif:waitKey([{delay, Delay}]). 10 | 11 | destroyWindow(WinName) when is_binary(WinName) -> 12 | evision_nif:destroyWindow([{winname, WinName}]). 13 | 14 | destroyAllWindows() -> 15 | evision_nif:destroyAllWindows(). 16 | -------------------------------------------------------------------------------- /src/evision_internal_structurise.erl: -------------------------------------------------------------------------------- 1 | -module(evision_internal_structurise). 2 | -export([to_struct/1, from_struct/1]). 3 | 4 | to_struct(Ret) when is_map(Ret) -> 5 | case maps:is_key(class, Ret) of 6 | true -> 7 | Class = maps:get(class, Ret), 8 | ClassName = atom_to_list(Class), 9 | Module = case string:prefix(ClassName, "Elixir.Evision.") of 10 | nomatch -> 11 | list_to_atom(string:to_lower(io_lib:fwrite("evision_~s", [ClassName]))); 12 | Name -> 13 | list_to_atom(string:to_lower("evision_" ++ Name)) 14 | end, 15 | try Module:to_struct(Ret) of 16 | Result -> 17 | Result 18 | catch 19 | _ -> 20 | Ret 21 | end; 22 | false -> 23 | Ret 24 | end; 25 | to_struct(Tuple) when is_tuple(Tuple) -> 26 | List = [to_struct(element(I, Tuple)) || I <- lists:seq(1, tuple_size(Tuple))], 27 | list_to_tuple(List); 28 | to_struct(List) when is_list(List) -> 29 | lists:map(fun (X) -> to_struct(X) end, List); 30 | to_struct(PassThrough) -> 31 | PassThrough. 32 | 33 | from_struct(MaybeRecord) when is_tuple(MaybeRecord), tuple_size(MaybeRecord) > 0, is_atom(element(1, MaybeRecord)) -> 34 | RecordName = element(1, MaybeRecord), 35 | try RecordName:from_struct(MaybeRecord) of 36 | Result -> 37 | Result 38 | catch 39 | _ -> 40 | List = [to_struct(element(I, MaybeRecord)) || I <- lists:seq(1, tuple_size(MaybeRecord))], 41 | list_to_tuple(List) 42 | end; 43 | from_struct(Tuple) when is_tuple(Tuple) -> 44 | List = [to_struct(element(I, Tuple)) || I <- lists:seq(1, tuple_size(Tuple))], 45 | list_to_tuple(List); 46 | from_struct(List) when is_list(List) -> 47 | lists:map(fun (X) -> from_struct(X) end, List); 48 | from_struct(PassThrough) -> 49 | PassThrough. 50 | -------------------------------------------------------------------------------- /src/evision_windows_fix.erl: -------------------------------------------------------------------------------- 1 | -module(evision_windows_fix). 2 | -compile(nowarn_export_all). 3 | -compile([export_all]). 4 | 5 | -on_load(init/0). 6 | 7 | -define(APPNAME, evision). 8 | -define(LIBNAME, windows_fix). 9 | 10 | init() -> 11 | case os:type() of 12 | {win32, _} -> 13 | SoName = case code:priv_dir(?APPNAME) of 14 | {error, bad_name} -> 15 | case filelib:is_dir(filename:join(["..", priv])) of 16 | true -> 17 | filename:join(["..", priv, ?LIBNAME]); 18 | _ -> 19 | filename:join([priv, ?LIBNAME]) 20 | end; 21 | Dir -> 22 | filename:join(Dir, ?LIBNAME) 23 | end, 24 | erlang:load_nif(SoName, 0), 25 | run_once(); 26 | _ -> 27 | ok 28 | end. 29 | 30 | not_loaded(Line) -> 31 | erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}). 32 | 33 | run_once() -> 34 | case os:type() of 35 | {win32, _} -> 36 | case erlang:load_nif(filename:join(code:priv_dir(?APPNAME), ?LIBNAME), 0) of 37 | true -> 38 | ok; 39 | _ -> 40 | not_loaded(?LINE) 41 | end; 42 | _ -> 43 | ok 44 | end. 45 | 46 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ## Tests 2 | 3 | As we allow users to select a subset of OpenCV modules, all tests have corresponding tags and, by default, they will be 4 | enabled by the availability of the module (i.e., will be tested if the corresponding module is compiled). 5 | 6 | However, some test data is relative large thus we don't store them in git (to avoid long cloning time and/or wasting the 7 | disk space if you don't want to run tests). Therefore, we have a special tag named `:require_downloading`. If you'd like 8 | to test the ones that require downloading test data, you can do 9 | 10 | ```shell 11 | mix test --include require_downloading 12 | ``` 13 | 14 | Another special tag is `:require_ffmpeg`. OpenCV can compile and use video module when any of the following points is met 15 | 1. your system has native support for camera 16 | 2. your system has native support for some video files 17 | 3. ffmpeg related libraries are installed 18 | 19 | Therefore, the availability of the video module does not imply that some tests can decode or encode the test video files 20 | correctly, as the formats specified in these tests may not support by the native library provided by your system. 21 | 22 | Another thing is that, even if it is possible to test the camera, it would require setting up a virtual camera in the CI 23 | environment, and that would introduce much work. So we won't consider writing test for cameras at the moment. 24 | 25 | Therefore, if you have ffmpeg installed and the OpenCV library is compiled with ffmpeg, then you can enable these tests 26 | by 27 | 28 | ```shell 29 | mix test --include require_ffmpeg 30 | ``` 31 | 32 | 33 | ### downloading_list.txt 34 | 35 | ```shell 36 | grep ' @download_file' *.exs > downloading_list.txt 37 | ``` 38 | -------------------------------------------------------------------------------- /test/dnn_detection_model_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.DNN.DetectionModel.Test do 2 | use ExUnit.Case 3 | alias Evision.DNN.DetectionModel 4 | 5 | @moduletag timeout: 600_000 6 | @tag :dnn 7 | @tag :require_downloading 8 | @download_file "https://github.com/AlexeyAB/darknet/releases/download/yolov4/yolov4.weights" 9 | 10 | test "yolov4" do 11 | dog = Path.join([__DIR__, "testdata", "dog.jpg"]) 12 | weights = Path.join([__DIR__, "testdata", "yolov4.weights"]) 13 | config = Path.join([__DIR__, "testdata", "yolov4.cfg"]) 14 | mat = Evision.imread(dog) 15 | 16 | Evision.TestHelper.download!(@download_file, weights) 17 | 18 | net = Evision.DNN.readNet(weights, config: config, framework: "") 19 | 20 | model = DetectionModel.detectionModel(net) 21 | 22 | model = 23 | DetectionModel.setInputParams(model, 24 | scale: 1.0, 25 | size: {416, 416}, 26 | mean: {0, 0, 0}, 27 | swapRB: true, 28 | crop: false 29 | ) 30 | 31 | {classes, _, _} = DetectionModel.detect(model, mat) 32 | 33 | assert classes == [ 34 | 0, 35 | 24, 36 | 24, 37 | 24, 38 | 0, 39 | 24, 40 | 24, 41 | 0, 42 | 47, 43 | 9, 44 | 9, 45 | 9, 46 | 9, 47 | 9, 48 | 9, 49 | 26, 50 | 24, 51 | 12, 52 | 12, 53 | 47, 54 | 24, 55 | 24, 56 | 24, 57 | 26, 58 | 26, 59 | 26, 60 | 26, 61 | 26, 62 | 26, 63 | 26, 64 | 47, 65 | 26, 66 | 26 67 | ] 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /test/dnn_text_detection_model_db_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.DNN.TextDetectionModelDB.Test do 2 | use ExUnit.Case 3 | alias Evision.DNN.TextDetectionModelDB 4 | 5 | @moduletag timeout: 600_000 6 | @tag :dnn 7 | @tag :require_downloading 8 | @download_file "https://github.com/cocoa-xu/evision-extra/raw/main/testdata/text-detection/DB_IC15_resnet18.onnx" 9 | test "Text Detection DB" do 10 | weights = Path.join([__DIR__, "testdata", "DB_IC15_resnet18.onnx"]) 11 | text_image = Evision.imread(Path.join([__DIR__, "testdata", "1023_6.png"])) 12 | 13 | Evision.TestHelper.download!( 14 | @download_file, 15 | weights 16 | ) 17 | 18 | net = Evision.DNN.readNet(weights, config: "", framework: "") 19 | 20 | model = 21 | TextDetectionModelDB.textDetectionModelDB(net) 22 | |> TextDetectionModelDB.setInputParams( 23 | scale: 1.0 / 255.0, 24 | size: {736, 736}, 25 | mean: {122.67891434, 116.66876762, 104.00698793}, 26 | swapRB: false, 27 | crop: false 28 | ) 29 | |> TextDetectionModelDB.setBinaryThreshold(0.3) 30 | |> TextDetectionModelDB.setPolygonThreshold(0.5) 31 | |> TextDetectionModelDB.setMaxCandidates(200) 32 | |> TextDetectionModelDB.setUnclipRatio(2.0) 33 | 34 | {vertices, confidences} = TextDetectionModelDB.detect(model, text_image) 35 | 36 | assert vertices == [[{2, 19}, {2, 8}, {27, 5}, {27, 17}]] 37 | assert Enum.count(confidences) > 0 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/dnn_text_detection_model_east_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.DNN.TextDetectionModelEAST.Test do 2 | use ExUnit.Case 3 | alias Evision.DNN.TextDetectionModelEAST 4 | 5 | @moduletag timeout: 600_000 6 | @tag :dnn 7 | @tag :require_downloading 8 | @download_file_1 "https://github.com/cocoa-xu/evision-extra/raw/main/testdata/text-detection/frozen_east_text_detection.pb.part1" 9 | @download_file_2 "https://github.com/cocoa-xu/evision-extra/raw/main/testdata/text-detection/frozen_east_text_detection.pb.part2" 10 | test "Text Detection EAST" do 11 | weights_p1 = Path.join([__DIR__, "testdata", "frozen_east_text_detection.pb.part1"]) 12 | weights_p2 = Path.join([__DIR__, "testdata", "frozen_east_text_detection.pb.part2"]) 13 | weights = Path.join([__DIR__, "testdata", "frozen_east_text_detection.pb"]) 14 | text_image = Evision.imread(Path.join([__DIR__, "testdata", "1023_6.png"])) 15 | 16 | Evision.TestHelper.download!(@download_file_1, weights_p1) 17 | Evision.TestHelper.download!(@download_file_2, weights_p2) 18 | data_p1 = File.read!(weights_p1) 19 | data_p2 = File.read!(weights_p2) 20 | File.write!(weights, data_p1 <> data_p2) 21 | 22 | net = Evision.DNN.readNet(weights, config: "", framework: "") 23 | 24 | model = 25 | TextDetectionModelEAST.textDetectionModelEAST(net) 26 | |> TextDetectionModelEAST.setInputParams( 27 | scale: 1.0, 28 | size: {320, 320}, 29 | mean: {123.68, 116.78, 103.94}, 30 | swapRB: true, 31 | crop: false 32 | ) 33 | |> TextDetectionModelEAST.setConfidenceThreshold(0.5) 34 | |> TextDetectionModelEAST.setNMSThreshold(0.4) 35 | 36 | {vertices, confidences} = TextDetectionModelEAST.detect(model, text_image) 37 | 38 | assert vertices == [[{3, 16}, {3, 8}, {50, 7}, {50, 16}]] 39 | assert confidences == [0.998794674873352] 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/downloading_list.txt: -------------------------------------------------------------------------------- 1 | dnn_detection_model_test.exs: @download_file "https://github.com/AlexeyAB/darknet/releases/download/yolov4/yolov4.weights" 2 | dnn_detection_model_test.exs: @download_file, 3 | dnn_detection_test.exs: @download_file_1 "https://raw.githubusercontent.com/opencv/opencv_extra/master/testdata/dnn/ssd_mobilenet_v2_coco_2018_03_29.pbtxt" 4 | dnn_detection_test.exs: @download_file_2 "http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz" 5 | dnn_text_detection_model_db_test.exs: @download_file "https://github.com/cocoa-xu/evision-extra/raw/main/testdata/text-detection/DB_IC15_resnet18.onnx" 6 | dnn_text_detection_model_db_test.exs: @download_file, 7 | dnn_text_detection_model_east_test.exs: @download_file_1 "https://github.com/cocoa-xu/evision-extra/raw/main/testdata/text-detection/frozen_east_text_detection.pb.part1" 8 | dnn_text_detection_model_east_test.exs: @download_file_2 "https://github.com/cocoa-xu/evision-extra/raw/main/testdata/text-detection/frozen_east_text_detection.pb.part2" 9 | photo_hdr_test.exs: @download_file_1 "https://raw.githubusercontent.com/opencv/opencv_extra/4.x/testdata/cv/hdr/exposures/list.txt" 10 | photo_hdr_test.exs: @download_file_2 "https://raw.githubusercontent.com/opencv/opencv_extra/4.x/testdata/cv/hdr/exposures/" 11 | -------------------------------------------------------------------------------- /test/evision_backend_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.Backend.Test do 2 | use ExUnit.Case 3 | doctest Evision.Backend 4 | end 5 | -------------------------------------------------------------------------------- /test/evision_dnn_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.DNN.Test do 2 | use ExUnit.Case 3 | 4 | test "nmsBoxes/{4,5}" do 5 | assert [0] == Evision.DNN.nmsBoxes([{0, 1, 2, 3}], [1], 0.4, 0.3) 6 | assert [0] == Evision.DNN.nmsBoxes(Nx.tensor([[0, 1, 2, 3]]), [1], 0.4, 0.3) 7 | assert [0] == Evision.DNN.nmsBoxes(Evision.Mat.literal([[0, 1, 2, 3]], :f64), [1], 0.4, 0.3) 8 | end 9 | 10 | test "nmsBoxesBatched/{5,6}" do 11 | assert [0] == Evision.DNN.nmsBoxesBatched([{0, 1, 2, 3}], [1], [1], 0.4, 0.3) 12 | assert [0] == Evision.DNN.nmsBoxesBatched(Nx.tensor([[0, 1, 2, 3]]), [1], [1], 0.4, 0.3) 13 | 14 | assert [0] == 15 | Evision.DNN.nmsBoxesBatched( 16 | Evision.Mat.literal([[0, 1, 2, 3]], :f64), 17 | [1], 18 | [1], 19 | 0.4, 20 | 0.3 21 | ) 22 | end 23 | 24 | test "softNMSBoxes/{4,5}" do 25 | assert {[1.0], [0]} == Evision.DNN.softNMSBoxes([{0, 1, 2, 3}], [1], 0.4, 0.3) 26 | assert {[1.0], [0]} == Evision.DNN.softNMSBoxes(Nx.tensor([[0, 1, 2, 3]]), [1], 0.4, 0.3) 27 | 28 | assert {[1.0], [0]} == 29 | Evision.DNN.softNMSBoxes(Evision.Mat.literal([[0, 1, 2, 3]], :f64), [1], 0.4, 0.3) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/evision_face_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.Face.FaceRecognizerTest do 2 | use ExUnit.Case 3 | 4 | setup do 5 | %Evision.Mat{} = 6 | image = 7 | Evision.imread( 8 | Path.join([__DIR__, "testdata", "back.jpg"]), 9 | flags: Evision.Constant.cv_IMREAD_GRAYSCALE() 10 | ) 11 | 12 | %{image: Evision.Mat.as_type(image, :s32)} 13 | end 14 | 15 | describe "functions from base class Evision.Face.FaceRecognizer is available in sub-classes" do 16 | test "Evision.Face.EigenFaceRecognizer.t()", %{image: image} do 17 | module = Evision.Face.EigenFaceRecognizer 18 | face_recogniser = module.create() 19 | face_recogniser = module.train(face_recogniser, [image], Nx.tensor([0], type: :s32)) 20 | assert face_recogniser.__struct__ == module 21 | end 22 | 23 | test "Evision.Face.FisherFaceRecognizer.t()", %{image: image} do 24 | module = Evision.Face.FisherFaceRecognizer 25 | face_recogniser = module.create() 26 | 27 | face_recogniser = 28 | module.train(face_recogniser, [image, image], Nx.tensor([0, 1], type: :s32)) 29 | 30 | assert face_recogniser.__struct__ == module 31 | end 32 | 33 | test "Evision.Face.LBPHFaceRecognizer.t()", %{image: image} do 34 | module = Evision.Face.LBPHFaceRecognizer 35 | face_recogniser = module.create() 36 | face_recogniser = module.train(face_recogniser, [image], Nx.tensor([0], type: :s32)) 37 | assert face_recogniser.__struct__ == module 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/evision_features2d_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.Features2D.Test do 2 | use ExUnit.Case 3 | 4 | setup do 5 | %Evision.Mat{} = image = Evision.imread(Path.join([__DIR__, "testdata", "back.jpg"])) 6 | opts = [nfeatures: 500] 7 | %{image: image, opts: opts} 8 | end 9 | 10 | describe "implicit cast to Evision.Features2D" do 11 | test "cast from Evision.AKAZE.t()", %{image: image} do 12 | detector = Evision.AKAZE.create(nil) 13 | features = Evision.Detail.computeImageFeatures2(detector, image) 14 | assert Evision.Detail.ImageFeatures == features.__struct__ 15 | end 16 | 17 | test "cast from Evision.AffineFeature.t()", %{image: image} do 18 | detector = Evision.AKAZE.create(nil) 19 | affine_detector = Evision.AffineFeature.create(detector) 20 | features = Evision.Detail.computeImageFeatures2(affine_detector, image) 21 | assert Evision.Detail.ImageFeatures == features.__struct__ 22 | end 23 | 24 | test "cast from Evision.BRISK.t()", %{image: image} do 25 | detector = Evision.BRISK.create(thresh: 30, octaves: 3) 26 | features = Evision.Detail.computeImageFeatures2(detector, image) 27 | assert Evision.Detail.ImageFeatures == features.__struct__ 28 | end 29 | 30 | test "cast from Evision.KAZE.t()", %{image: image} do 31 | detector = Evision.KAZE.create(nil) 32 | features = Evision.Detail.computeImageFeatures2(detector, image) 33 | assert Evision.Detail.ImageFeatures == features.__struct__ 34 | end 35 | 36 | test "cast from Evision.ORB.t()", %{image: image, opts: opts} do 37 | detector = Evision.ORB.create(opts) 38 | features = Evision.Detail.computeImageFeatures2(detector, image) 39 | assert Evision.Detail.ImageFeatures == features.__struct__ 40 | end 41 | end 42 | 43 | describe "implicit cast to Evision.Features2D okay but not implemented detectAndCompute" do 44 | test "cast from Evision.AgastFeatureDetector.t()", %{image: image} do 45 | detector = Evision.AgastFeatureDetector.create(nil) 46 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 47 | assert msg =~ "The function/feature is not implemented" 48 | end 49 | 50 | test "cast from Evision.FastFeatureDetector.t()", %{image: image} do 51 | detector = Evision.FastFeatureDetector.create(nil) 52 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 53 | assert msg =~ "The function/feature is not implemented" 54 | end 55 | 56 | test "cast from Evision.GFTTDetector.t()", %{image: image} do 57 | detector = Evision.GFTTDetector.create(nil) 58 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 59 | assert msg =~ "The function/feature is not implemented" 60 | end 61 | 62 | test "cast from Evision.MSER.t()", %{image: image} do 63 | detector = Evision.MSER.create(nil) 64 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 65 | assert msg =~ "The function/feature is not implemented" 66 | end 67 | 68 | test "cast from Evision.SimpleBlobDetector.t()", %{image: image} do 69 | detector = Evision.SimpleBlobDetector.create(nil) 70 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 71 | assert msg =~ "The function/feature is not implemented" 72 | end 73 | end 74 | 75 | describe "allowing default values for features 2D" do 76 | test "findCirclesGrid, colour image" do 77 | img = Evision.imread(Path.join([__DIR__, "testdata", "test-circle-grid.jpg"])) 78 | pattern_size = {12, 8} 79 | corners = Evision.findCirclesGrid(img, pattern_size) 80 | assert is_struct(corners, Evision.Mat) 81 | end 82 | 83 | test "findCirclesGrid, gray image" do 84 | img = Evision.imread(Path.join([__DIR__, "testdata", "test-circle-grid.jpg"])) 85 | gray = Evision.cvtColor(img, Evision.Constant.cv_COLOR_BGR2GRAY()) 86 | pattern_size = {12, 8} 87 | corners = Evision.findCirclesGrid(gray, pattern_size) 88 | assert is_struct(corners, Evision.Mat) 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /test/evision_keypoint_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.KeyPoint.Test do 2 | use ExUnit.Case 3 | 4 | test "Keypoints" do 5 | kp = Evision.KeyPoint.convert([{23.3, 23.3}]) 6 | assert Enum.count(kp) == 1 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/evision_xfeatures2d_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.XFeatures2D.Test do 2 | use ExUnit.Case 3 | 4 | setup do 5 | %Evision.Mat{} = image = Evision.imread(Path.join([__DIR__, "testdata", "back.jpg"])) 6 | %{image: image} 7 | end 8 | 9 | describe "implicit cast to Evision.XFeatures2D okay" do 10 | test "from Evision.XFeatures2D.BEBLID.t()", %{image: image} do 11 | detector = Evision.XFeatures2D.BEBLID.create(0.5) 12 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 13 | assert msg =~ "The function/feature is not implemented" 14 | end 15 | 16 | test "from Evision.XFeatures2D.BoostDesc.t()", %{image: image} do 17 | detector = Evision.XFeatures2D.BoostDesc.create() 18 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 19 | assert msg =~ "The function/feature is not implemented" 20 | end 21 | 22 | test "from Evision.XFeatures2D.BriefDescriptorExtractor.t()", %{image: image} do 23 | detector = Evision.XFeatures2D.BriefDescriptorExtractor.create() 24 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 25 | assert msg =~ "The function/feature is not implemented" 26 | end 27 | 28 | test "from Evision.XFeatures2D.DAISY.t()", %{image: image} do 29 | detector = Evision.XFeatures2D.DAISY.create() 30 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 31 | assert msg =~ "The function/feature is not implemented" 32 | end 33 | 34 | test "from Evision.XFeatures2D.FREAK.t()", %{image: image} do 35 | detector = Evision.XFeatures2D.FREAK.create() 36 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 37 | assert msg =~ "The function/feature is not implemented" 38 | end 39 | 40 | test "from Evision.XFeatures2D.LATCH.t()", %{image: image} do 41 | detector = Evision.XFeatures2D.LATCH.create() 42 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 43 | assert msg =~ "The function/feature is not implemented" 44 | end 45 | 46 | test "from Evision.XFeatures2D.LUCID.t()", %{image: image} do 47 | detector = Evision.XFeatures2D.LUCID.create() 48 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 49 | assert msg =~ "The function/feature is not implemented" 50 | end 51 | 52 | test "from Evision.XFeatures2D.MSDDetector.t()", %{image: image} do 53 | detector = Evision.XFeatures2D.MSDDetector.create() 54 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 55 | assert msg =~ "The function/feature is not implemented" 56 | end 57 | 58 | test "from Evision.XFeatures2D.StarDetector.t()", %{image: image} do 59 | detector = Evision.XFeatures2D.StarDetector.create() 60 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 61 | assert msg =~ "The function/feature is not implemented" 62 | end 63 | 64 | test "from Evision.XFeatures2D.TBMR.t()", %{image: image} do 65 | detector = Evision.XFeatures2D.TBMR.create() 66 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 67 | assert msg =~ "The function/feature is not implemented" 68 | end 69 | 70 | test "from Evision.XFeatures2D.TEBLID.t()", %{image: image} do 71 | detector = Evision.XFeatures2D.TEBLID.create(1.0) 72 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 73 | assert msg =~ "The function/feature is not implemented" 74 | end 75 | 76 | test "from Evision.XFeatures2D.VGG.t()", %{image: image} do 77 | detector = Evision.XFeatures2D.VGG.create() 78 | {:error, msg} = Evision.Detail.computeImageFeatures2(detector, image) 79 | assert msg =~ "The function/feature is not implemented" 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /test/ml_dtree_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.ML.DTrees.Test do 2 | use ExUnit.Case 3 | 4 | @moduletag timeout: 120_000 5 | 6 | @tag :ml 7 | @tag :require_downloading 8 | test "decision tree" do 9 | {features, labels} = Scidata.Wine.download() 10 | categories = Enum.count(Enum.uniq(labels)) 11 | features = Evision.Mat.from_nx(Nx.tensor(features, type: :f32, backend: Evision.Backend)) 12 | labels = Evision.Mat.from_nx(Nx.tensor(labels, type: :s32, backend: Evision.Backend)) 13 | 14 | dataset = Evision.ML.TrainData.create(features, Evision.Constant.cv_ROW_SAMPLE(), labels) 15 | dataset = Evision.ML.TrainData.setTrainTestSplitRatio(dataset, 0.9, shuffle: true) 16 | 17 | dtree = 18 | Evision.ML.DTrees.create() 19 | |> Evision.ML.DTrees.setMaxDepth(7) 20 | |> Evision.ML.DTrees.setMaxCategories(categories) 21 | |> Evision.ML.DTrees.setCVFolds(0) 22 | 23 | # train 24 | Evision.ML.DTrees.train(dtree, dataset) 25 | 26 | # calculate test error 27 | {test_error, _results} = Evision.ML.DTrees.calcError(dtree, dataset, true) 28 | 29 | # save to file 30 | dtree_binary = Path.join([__DIR__, "testdata", "dtree.bin"]) 31 | File.rm(dtree_binary) 32 | Evision.ML.DTrees.save(dtree, dtree_binary) 33 | 34 | # load from file 35 | dtree2 = Evision.ML.DTrees.load(dtree_binary) 36 | File.rm(dtree_binary) 37 | {test_error_2, _results} = Evision.ML.DTrees.calcError(dtree2, dataset, true) 38 | assert test_error == test_error_2 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/ml_rtree_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.ML.RTrees.Test do 2 | use ExUnit.Case 3 | 4 | @moduletag timeout: 120_000 5 | 6 | @tag :ml 7 | @tag :require_downloading 8 | test "random forest" do 9 | {features, labels} = Scidata.Wine.download() 10 | categories = Enum.count(Enum.uniq(labels)) 11 | features = Evision.Mat.from_nx(Nx.tensor(features, type: :f32, backend: Evision.Backend)) 12 | labels = Evision.Mat.from_nx(Nx.tensor(labels, type: :s32, backend: Evision.Backend)) 13 | 14 | dataset = Evision.ML.TrainData.create(features, Evision.Constant.cv_ROW_SAMPLE(), labels) 15 | dataset = Evision.ML.TrainData.setTrainTestSplitRatio(dataset, 0.8, shuffle: true) 16 | 17 | rtree = 18 | Evision.ML.RTrees.create() 19 | |> Evision.ML.RTrees.setMaxDepth(10) 20 | |> Evision.ML.RTrees.setMaxCategories(categories) 21 | |> Evision.ML.RTrees.setCVFolds(1) 22 | |> Evision.ML.RTrees.setTermCriteria({Evision.Constant.cv_MAX_ITER(), 10, 0.00005}) 23 | 24 | # train 25 | Evision.ML.RTrees.train(rtree, dataset) 26 | 27 | # calculate test error 28 | {test_error, _results} = Evision.ML.RTrees.calcError(rtree, dataset, true) 29 | 30 | # save to file 31 | rtree_binary = Path.join([__DIR__, "testdata", "rtree.bin"]) 32 | File.rm(rtree_binary) 33 | Evision.ML.RTrees.save(rtree, rtree_binary) 34 | 35 | # load from file 36 | rtree2 = Evision.ML.RTrees.load(rtree_binary) 37 | File.rm(rtree_binary) 38 | {test_error_2, _results} = Evision.ML.RTrees.calcError(rtree2, dataset, true) 39 | assert test_error == test_error_2 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/ml_svm_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.ML.SVM.Test do 2 | use ExUnit.Case 3 | alias Evision.Mat 4 | alias Evision.ML.SVM 5 | 6 | @moduletag timeout: 120_000 7 | 8 | @tag :ml 9 | test "svm" do 10 | labels = [1, -1, -1, -1] 11 | training_data = [[501, 10], [255, 10], [501, 255], [10, 501]] 12 | 13 | %Mat{} = 14 | labels_mat = 15 | Mat.from_binary( 16 | Enum.into(labels, <<>>, fn d -> <> end), 17 | {:s, 32}, 18 | 4, 19 | 1, 20 | 1 21 | ) 22 | 23 | %Mat{} = 24 | training_data_mat = 25 | Mat.from_binary( 26 | Enum.into(List.flatten(training_data), <<>>, fn d -> <> end), 27 | {:f, 32}, 28 | 4, 29 | 2, 30 | 1 31 | ) 32 | 33 | svm = SVM.create() 34 | svm = SVM.setType(svm, Evision.Constant.cv_C_SVC()) 35 | svm = SVM.setKernel(svm, Evision.Constant.cv_LINEAR()) 36 | svm = SVM.setTermCriteria(svm, {Evision.Constant.cv_MAX_ITER(), 100, 0.000001}) 37 | assert true = SVM.train(svm, training_data_mat, Evision.Constant.cv_ROW_SAMPLE(), labels_mat) 38 | assert true = SVM.isTrained(svm) 39 | 40 | %Mat{shape: {rows, cols}} = sv = SVM.getUncompressedSupportVectors(svm) 41 | sv_binary = Mat.to_binary(sv) 42 | float_bytes = 4 43 | 44 | support_vector = 45 | for i <- Range.new(rows - 1, 0, -1), reduce: [] do 46 | support_vector -> 47 | current_vector = 48 | for j <- Range.new(cols - 1, 0, -1), reduce: [] do 49 | vec -> 50 | <> = 51 | :binary.part(sv_binary, (i * cols + j) * float_bytes, 4) 52 | 53 | [trunc(float_data) | vec] 54 | end 55 | 56 | [current_vector | support_vector] 57 | end 58 | 59 | assert [[501, 10], [255, 10], [501, 255]] == support_vector 60 | 61 | green = [0, 255, 0] 62 | blue = [255, 0, 0] 63 | width = 512 64 | height = 512 65 | 66 | response_data = 67 | for x <- Range.new(width - 1, 0, -1), y <- Range.new(height - 1, 0, -1), reduce: [] do 68 | acc -> 69 | %Mat{} = 70 | sample = 71 | Mat.from_binary( 72 | <>, 73 | {:f, 32}, 74 | 1, 75 | 2, 76 | 1 77 | ) 78 | 79 | {_, %Mat{shape: {1, 1}} = response_mat} = SVM.predict(svm, sample) 80 | <> = Mat.to_binary(response_mat) 81 | response = trunc(response) 82 | assert response == 1 or response == -1 83 | 84 | case response do 85 | 1 -> 86 | [green | acc] 87 | 88 | -1 -> 89 | [blue | acc] 90 | end 91 | end 92 | 93 | response_data = response_data |> List.flatten() |> IO.iodata_to_binary() 94 | 95 | mat = Evision.imread(Path.join([__DIR__, "testdata", "svm_test.png"])) 96 | expected = Evision.Mat.to_binary(mat) 97 | assert expected == response_data 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /test/orb_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.ORB.Test do 2 | use ExUnit.Case 3 | 4 | @moduletag timeout: 120_000 5 | 6 | test "detect keypoints in an image" do 7 | img = %Evision.Mat{} = Evision.imread(Path.join([__DIR__, "testdata", "pca_test.jpg"])) 8 | 9 | orb = %Evision.ORB{} = Evision.ORB.create() 10 | kp = Evision.ORB.detect(orb, img) 11 | assert Enum.count(kp) != 0 12 | {kp, _des} = Evision.ORB.compute(orb, img, kp) 13 | assert Enum.count(kp) != 0 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/photo_hdr_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.Photo.HDR.Test do 2 | use ExUnit.Case 3 | 4 | alias Evision.CalibrateDebevec 5 | 6 | @moduletag timeout: 120_000 7 | 8 | @tag :photo 9 | @tag :require_downloading 10 | @download_file_1 "https://raw.githubusercontent.com/opencv/opencv_extra/4.x/testdata/cv/hdr/exposures/list.txt" 11 | @download_file_2 "https://raw.githubusercontent.com/opencv/opencv_extra/4.x/testdata/cv/hdr/exposures/" 12 | test "High Dynamic Range Imaging" do 13 | exposure_filenames = 14 | 0..15 15 | |> Enum.map(&Integer.to_string(&1)) 16 | |> Enum.map(&String.pad_leading(&1, 2, "0")) 17 | |> Enum.map(&("memorial" <> &1 <> ".png")) 18 | 19 | exposure_file_urls = Enum.map(exposure_filenames, &(@download_file_2 <> &1)) 20 | 21 | exposure_file_save_paths = 22 | exposure_filenames 23 | |> Enum.map(&Path.join([__DIR__, "testdata", "photo_hdr_test", &1])) 24 | 25 | assert true = 26 | exposure_file_urls 27 | |> Enum.zip(exposure_file_save_paths) 28 | |> Enum.map(fn {url, save_as} -> Evision.TestHelper.download!(url, save_as) end) 29 | |> Enum.all?(&(:ok = &1)) 30 | 31 | list_txt_file = Path.join([__DIR__, "testdata", "photo_hdr_test", "list.txt"]) 32 | 33 | assert :ok = Evision.TestHelper.download!(@download_file_1, list_txt_file) 34 | 35 | # load exposure sequences 36 | exposure_sequences = 37 | list_txt_file 38 | |> File.read!() 39 | |> String.split("\n") 40 | |> Enum.reject(&(String.length(&1) == 0)) 41 | # remove "\r" for Windows 42 | |> Enum.map(&String.replace(&1, "\r", "")) 43 | |> Enum.map(&String.split(&1, " ")) 44 | |> Enum.map(&List.to_tuple(&1)) 45 | |> Enum.map(fn {image_filename, times} -> 46 | mat = Evision.imread(Path.join([__DIR__, "testdata", "photo_hdr_test", image_filename])) 47 | {val, ""} = Float.parse(times) 48 | {mat, 1 / val} 49 | end) 50 | 51 | images = 52 | exposure_sequences 53 | |> Enum.map(&elem(&1, 0)) 54 | 55 | times = 56 | exposure_sequences 57 | |> Enum.map(&elem(&1, 1)) 58 | |> Enum.into(<<>>, fn d -> <> end) 59 | |> Evision.Mat.from_binary_by_shape({:f, 32}, {1, Enum.count(images)}) 60 | 61 | %CalibrateDebevec{} = calibrate = Evision.createCalibrateDebevec() 62 | response = Evision.CalibrateDebevec.process(calibrate, images, times) 63 | 64 | merge_debevec = Evision.createMergeDebevec() 65 | hdr = Evision.MergeDebevec.process(merge_debevec, images, times, response: response) 66 | 67 | tonemap = Evision.createTonemap(gamma: 2.2) 68 | ldr = Evision.Tonemap.process(tonemap, hdr) 69 | 70 | merge_mertens = Evision.createMergeMertens() 71 | fusion = Evision.MergeMertens.process(merge_mertens, images) 72 | 73 | output_fusion_file = Path.join([__DIR__, "testdata", "photo_hdr_test", "fusion.png"]) 74 | 75 | fusion 76 | |> Evision.Mat.to_nx(Nx.BinaryBackend) 77 | |> Nx.multiply(255) 78 | |> Nx.clip(0, 255) 79 | |> Nx.as_type({:u, 8}) 80 | |> Evision.Mat.from_nx_2d() 81 | |> then(&Evision.imwrite(output_fusion_file, &1)) 82 | 83 | output_ldr_file = Path.join([__DIR__, "testdata", "photo_hdr_test", "ldr.png"]) 84 | f32_shape = Evision.Mat.shape(ldr) 85 | nan = <<0, 0, 192, 255>> 86 | positive_inf = <<0, 0, 128, 127>> 87 | negative_inf = <<0, 0, 128, 255>> 88 | 89 | ldr 90 | |> Evision.Mat.to_binary() 91 | |> Evision.TestHelper.chunk_binary(4) 92 | |> Enum.map(fn f32 -> 93 | case f32 do 94 | ^nan -> 95 | <<0, 0, 0, 0>> 96 | 97 | ^positive_inf -> 98 | <<0, 0, 0, 0>> 99 | 100 | ^negative_inf -> 101 | <<0, 0, 0, 0>> 102 | 103 | # legal value 104 | _ -> 105 | f32 106 | end 107 | end) 108 | |> IO.iodata_to_binary() 109 | |> Nx.from_binary({:f, 32}) 110 | |> Nx.reshape(f32_shape) 111 | |> Nx.multiply(255) 112 | |> Nx.clip(0, 255) 113 | |> Nx.as_type({:u, 8}) 114 | |> Evision.Mat.from_nx_2d() 115 | |> then(&Evision.imwrite(output_ldr_file, &1)) 116 | 117 | output_hdr_file = Path.join([__DIR__, "testdata", "photo_hdr_test", "hdr.hdr"]) 118 | Evision.imwrite(output_hdr_file, hdr) 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /test/qr_detector_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.QRCodeDetector.Test do 2 | use ExUnit.Case 3 | 4 | @moduletag timeout: 120_000 5 | 6 | test "detect and decide QRCode in an image" do 7 | qr = Evision.QRCodeDetector.qrCodeDetector() 8 | img = Evision.imread(Path.join([__DIR__, "testdata", "qr_detector_test.png"]), flags: 0) 9 | {text, points, straight_qrcode} = Evision.QRCodeDetector.detectAndDecode(qr, img) 10 | assert text == "https://github.com/cocoa-xu/evision" 11 | 12 | points_nx = 13 | Nx.tensor( 14 | [ 15 | [ 16 | [34.0, 34.0], 17 | [265.0, 34.0], 18 | [265.0, 265.0], 19 | [34.0, 265.0] 20 | ] 21 | ], 22 | type: :f32 23 | ) 24 | 25 | points_out = Evision.Mat.to_nx(points, Nx.BinaryBackend) 26 | assert 1 == Nx.to_number(Nx.all_close(points_nx, points_out)) 27 | 28 | bin = Evision.Mat.to_binary(straight_qrcode) 29 | expected = File.read!(Path.join([__DIR__, "testdata", "straight_qrcode.bin"])) 30 | assert bin == expected 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/qr_encoder_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.QRCodeEncoder.Test do 2 | use ExUnit.Case 3 | 4 | @moduletag timeout: 120_000 5 | 6 | test "Encode and decode QRCode" do 7 | string_to_encode = "This is a string" 8 | 9 | {^string_to_encode, _, _} = 10 | Evision.QRCodeEncoder.encode(Evision.QRCodeEncoder.create(), string_to_encode) 11 | |> Evision.resize({300, 300}, interpolation: Evision.Constant.cv_INTER_AREA()) 12 | |> then( 13 | &Evision.QRCodeDetector.detectAndDecode(Evision.QRCodeDetector.qrCodeDetector(), &1) 14 | ) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.TestHelper do 2 | def download!(url, save_as, overwrite \\ false) 3 | 4 | def download!(url, save_as, false) do 5 | unless File.exists?(save_as) do 6 | download!(url, save_as, true) 7 | end 8 | 9 | :ok 10 | end 11 | 12 | def download!(url, save_as, true) do 13 | http_opts = [] 14 | opts = [body_format: :binary] 15 | arg = {url, []} 16 | 17 | if proxy = System.get_env("HTTP_PROXY") || System.get_env("http_proxy") do 18 | %{host: host, port: port} = URI.parse(proxy) 19 | 20 | :httpc.set_options([{:proxy, {{String.to_charlist(host), port}, []}}]) 21 | end 22 | 23 | if proxy = System.get_env("HTTPS_PROXY") || System.get_env("https_proxy") do 24 | %{host: host, port: port} = URI.parse(proxy) 25 | :httpc.set_options([{:https_proxy, {{String.to_charlist(host), port}, []}}]) 26 | end 27 | 28 | body = 29 | case :httpc.request(:get, arg, http_opts, opts) do 30 | {:ok, {{_, 200, _}, _, body}} -> 31 | body 32 | 33 | {:error, reason} -> 34 | raise inspect(reason) 35 | end 36 | 37 | File.write!(save_as, body) 38 | end 39 | 40 | @doc """ 41 | This function chunks binary data by every requested `chunk_size` 42 | 43 | To make it more general, this function allows the length of the last chunk 44 | to be less than the request `chunk_size`. 45 | 46 | For example, if you have a 7-byte binary data, and you'd like to chunk it by every 47 | 4 bytes, then this function will return two chunks with the first gives you the 48 | byte 0 to 3, and the second one gives byte 4 to 6. 49 | """ 50 | def chunk_binary(binary, chunk_size) when is_binary(binary) do 51 | total_bytes = byte_size(binary) 52 | full_chunks = div(total_bytes, chunk_size) 53 | 54 | chunks = 55 | if full_chunks > 0 do 56 | for i <- 0..(full_chunks - 1), reduce: [] do 57 | acc -> [:binary.part(binary, chunk_size * i, chunk_size) | acc] 58 | end 59 | else 60 | [] 61 | end 62 | 63 | remaining = rem(total_bytes, chunk_size) 64 | 65 | chunks = 66 | if remaining > 0 do 67 | [:binary.part(binary, chunk_size * full_chunks, remaining) | chunks] 68 | else 69 | chunks 70 | end 71 | 72 | Enum.reverse(chunks) 73 | end 74 | end 75 | 76 | compiled_modules = Evision.enabled_modules() 77 | 78 | ExUnit.configure( 79 | exclude: [ 80 | # exclude all tests that require downloading test data by default 81 | require_downloading: true, 82 | # exclude all tests that require ffmpeg by default 83 | # at the moment, this excludes video tests by default 84 | # as video module can compile without decoding capability 85 | # and there is perhaps no way to test input from a camera 86 | # (could set up a virtual camera, but let's leave that for now) 87 | require_ffmpeg: true, 88 | require_cuda: true, 89 | dnn: !compiled_modules.dnn, 90 | ml: !compiled_modules.ml, 91 | photo: !compiled_modules.photo, 92 | video: !compiled_modules.video 93 | ] 94 | ) 95 | 96 | {:ok, _} = Application.ensure_all_started(:inets) 97 | {:ok, _} = Application.ensure_all_started(:ssl) 98 | Application.put_env(:evision, :progress_bar_enabled, false) 99 | ExUnit.start() 100 | -------------------------------------------------------------------------------- /test/testdata/1023_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/1023_6.png -------------------------------------------------------------------------------- /test/testdata/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/back.jpg -------------------------------------------------------------------------------- /test/testdata/color_checker.etf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/color_checker.etf -------------------------------------------------------------------------------- /test/testdata/dnn_detection_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/dnn_detection_test.jpg -------------------------------------------------------------------------------- /test/testdata/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/dog.jpg -------------------------------------------------------------------------------- /test/testdata/front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/front.jpg -------------------------------------------------------------------------------- /test/testdata/imreadmulti_test.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/imreadmulti_test.tiff -------------------------------------------------------------------------------- /test/testdata/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/models/.gitkeep -------------------------------------------------------------------------------- /test/testdata/models/coco_names.txt: -------------------------------------------------------------------------------- 1 | person 2 | bicycle 3 | car 4 | motorcycle 5 | airplane 6 | bus 7 | train 8 | truck 9 | boat 10 | traffic 11 | fire 12 | 13 | stop 14 | parking 15 | bench 16 | bird 17 | cat 18 | dog 19 | horse 20 | sheep 21 | cow 22 | elephant 23 | bear 24 | zebra 25 | giraffe 26 | 27 | backpack 28 | umbrella 29 | 30 | 31 | handbag 32 | tie 33 | suitcase 34 | frisbee 35 | skis 36 | snowboard 37 | sports 38 | kite 39 | baseball 40 | baseball 41 | skateboard 42 | surfboard 43 | tennis 44 | bottle 45 | 46 | wine 47 | cup 48 | fork 49 | knife 50 | spoon 51 | bowl 52 | banana 53 | apple 54 | sandwich 55 | orange 56 | broccoli 57 | carrot 58 | hot 59 | pizza 60 | donut 61 | cake 62 | chair 63 | couch 64 | potted 65 | bed 66 | 67 | dining 68 | 69 | 70 | toilet 71 | 72 | tv 73 | laptop 74 | mouse 75 | remote 76 | keyboard 77 | cell 78 | microwave 79 | oven 80 | toaster 81 | sink 82 | refrigerator 83 | 84 | book 85 | clock 86 | vase 87 | scissors 88 | teddy 89 | hair 90 | toothbrush 91 | -------------------------------------------------------------------------------- /test/testdata/pca_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/pca_test.jpg -------------------------------------------------------------------------------- /test/testdata/photo_hdr_test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/photo_hdr_test/.gitkeep -------------------------------------------------------------------------------- /test/testdata/photo_hdr_test/list.txt: -------------------------------------------------------------------------------- 1 | memorial00.png 0.03125 2 | memorial01.png 0.0625 3 | memorial02.png 0.125 4 | memorial03.png 0.25 5 | memorial04.png 0.5 6 | memorial05.png 1 7 | memorial06.png 2 8 | memorial07.png 4 9 | memorial08.png 8 10 | memorial09.png 16 11 | memorial10.png 32 12 | memorial11.png 64 13 | memorial12.png 128 14 | memorial13.png 256 15 | memorial14.png 512 16 | memorial15.png 1024 17 | -------------------------------------------------------------------------------- /test/testdata/qr_detector_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/qr_detector_test.png -------------------------------------------------------------------------------- /test/testdata/straight_qrcode.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/straight_qrcode.bin -------------------------------------------------------------------------------- /test/testdata/sudoku_puzzle.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/sudoku_puzzle.webp -------------------------------------------------------------------------------- /test/testdata/svm_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/svm_test.png -------------------------------------------------------------------------------- /test/testdata/test-circle-grid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/test-circle-grid.jpg -------------------------------------------------------------------------------- /test/testdata/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/test.jpg -------------------------------------------------------------------------------- /test/testdata/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/test.png -------------------------------------------------------------------------------- /test/testdata/videocapture_test.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/videocapture_test.mp4 -------------------------------------------------------------------------------- /test/testdata/warp_perspective.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/warp_perspective.png -------------------------------------------------------------------------------- /test/testdata/warp_polar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-xu/evision/b6e3343fa36597875cdf5063dda47f10ec307fb0/test/testdata/warp_polar.png -------------------------------------------------------------------------------- /test/videocapture_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.VideoCapture.Test do 2 | use ExUnit.Case 3 | 4 | @moduletag timeout: 120_000 5 | 6 | @tag :video 7 | @tag :require_ffmpeg 8 | test "open a video file and read one frame" do 9 | video = 10 | Evision.VideoCapture.videoCapture(Path.join([__DIR__, "testdata", "videocapture_test.mp4"])) 11 | 12 | %Evision.VideoCapture{ 13 | isOpened: true, 14 | frame_height: 1080.0, 15 | frame_width: 1920.0 16 | } = video 17 | 18 | %Evision.Mat{shape: {1080, 1920, 3}} = Evision.VideoCapture.read(video) 19 | 20 | video = Evision.VideoCapture.release(video) 21 | false = Evision.VideoCapture.read(video) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/videowriter_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Evision.VideoWriter.Test do 2 | use ExUnit.Case 3 | 4 | @moduletag timeout: 120_000 5 | 6 | defmodule WriteVideo do 7 | def given(input_video_file, output_video_file, output_fps, output_seconds) do 8 | reader = Evision.VideoCapture.videoCapture(input_video_file) 9 | # mp4v 10 | fourcc = Evision.VideoWriter.fourcc(109, 112, 52, 118) 11 | 12 | height = 13 | Evision.VideoCapture.get(reader, Evision.Constant.cv_CAP_PROP_FRAME_HEIGHT()) |> trunc() 14 | 15 | width = 16 | Evision.VideoCapture.get(reader, Evision.Constant.cv_CAP_PROP_FRAME_WIDTH()) |> trunc() 17 | 18 | writer = 19 | Evision.VideoWriter.videoWriter( 20 | output_video_file, 21 | fourcc, 22 | output_fps / 1, 23 | {width, height} 24 | ) 25 | 26 | assert true == Evision.VideoWriter.isOpened(writer) 27 | frame = Evision.VideoCapture.read(reader) 28 | Evision.VideoCapture.release(reader) 29 | encode(frame, writer, output_fps * output_seconds) 30 | 31 | # verify 32 | reader = Evision.VideoCapture.videoCapture(output_video_file) 33 | 34 | ^output_fps = 35 | Evision.VideoCapture.get(reader, Evision.Constant.cv_CAP_PROP_FPS()) |> trunc() 36 | 37 | ^height = 38 | Evision.VideoCapture.get(reader, Evision.Constant.cv_CAP_PROP_FRAME_HEIGHT()) |> trunc() 39 | 40 | ^width = 41 | Evision.VideoCapture.get(reader, Evision.Constant.cv_CAP_PROP_FRAME_WIDTH()) |> trunc() 42 | 43 | w_frames_count = 44 | Evision.VideoCapture.get(reader, Evision.Constant.cv_CAP_PROP_FRAME_COUNT()) 45 | 46 | Evision.VideoCapture.release(reader) 47 | 48 | assert w_frames_count == output_fps * output_seconds 49 | end 50 | 51 | defp encode(_, writer, 0) do 52 | Evision.VideoWriter.release(writer) 53 | end 54 | 55 | defp encode(frame, writer, frames_count) do 56 | writer = Evision.VideoWriter.write(writer, frame) 57 | encode(frame, writer, frames_count - 1) 58 | end 59 | end 60 | 61 | @tag :video 62 | @tag :require_ffmpeg 63 | test "open a video file, read one frame and write a video@30FPS, duration 2 seconds" do 64 | input_video_file = Path.join([__DIR__, "testdata", "videocapture_test.mp4"]) 65 | output_video_file = Path.join([__DIR__, "testdata", "videowriter_test.mp4"]) 66 | WriteVideo.given(input_video_file, output_video_file, 30, 2) 67 | File.rm!(output_video_file) 68 | end 69 | end 70 | --------------------------------------------------------------------------------