PKG Add opencv-python, ffmpeg and libwebp (#2305)
* Add opencv-python * Update comment * Add JPEG, PNG, WEBP, ZLIB support * Add tests for image processing * Add more core modules * Disable opencl * Replace lena with baboon and add more tests * Add file system support * Add ffmpeg * Add more tests * Disable pthread in ffmpeg * Disable canonical input processing mode in node test * Update changelog * Remove import test * Allow more time in the first test * Split out libwebp * Fix node test * Use a seperate CI job for opencv-python * Fix generator * Update changelog * Remove protobuf package * Try to fix CI workspace conflict * Fix CI * Use another CI job for generating unified packages.json * Try to fix CI * Fix CI again * Disable verbose build * Prevent from building opencv-python twice * Persist only build artifacts * Sepearate Cmake args into a script * Try to reuse build packages job * Fix CI * Fix typo * Fix merge conflict * Use large resource class for package build * Do not upload unwanted artifacts * Do not upload unwanted artifacts
|
@ -111,78 +111,13 @@ jobs:
|
|||
- store_artifacts:
|
||||
path: /root/repo/packages/build-logs
|
||||
|
||||
build-packages-no-numpy-dependents:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- attach_workspace:
|
||||
at: .
|
||||
|
||||
- restore_cache:
|
||||
keys:
|
||||
- -pkg1-v20220303-{{ checksum "Makefile.envs" }}
|
||||
- -pkg1-v20220303
|
||||
|
||||
- run:
|
||||
name: build packages
|
||||
no_output_timeout: 30m
|
||||
command: |
|
||||
source pyodide_env.sh
|
||||
|
||||
# Set mtime for EM_CONFIG to avoid ccache cache misses
|
||||
touch -m -d '1 Jan 2021 12:00' emsdk/emsdk/.emscripten
|
||||
|
||||
ccache -z
|
||||
PYODIDE_PACKAGES='*, no-numpy-dependents' make -C packages
|
||||
ccache -s
|
||||
environment:
|
||||
PYODIDE_JOBS: 5
|
||||
|
||||
- run:
|
||||
name: check-size
|
||||
command: ls -lh dist/
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- /root/.ccache
|
||||
key: -pkg1-v20220303-{{ checksum "Makefile.envs" }}
|
||||
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./packages
|
||||
- ./dist
|
||||
|
||||
- run:
|
||||
name: Zip build directory
|
||||
command: |
|
||||
tar cjf pyodide-build.tar.gz dist
|
||||
tar cjf build-logs.tar.gz packages/build-logs
|
||||
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- .
|
||||
|
||||
- store_artifacts:
|
||||
path: /root/repo/dist/
|
||||
|
||||
- store_artifacts:
|
||||
path: /root/repo/pyodide-build.tar.gz
|
||||
|
||||
- store_artifacts:
|
||||
path: /root/repo/packages/build-logs
|
||||
|
||||
- store_artifacts:
|
||||
path: /root/repo/build-logs.tar.gz
|
||||
|
||||
build-packages:
|
||||
parameters:
|
||||
packages:
|
||||
description: The packages to be built.
|
||||
type: string
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
|
@ -196,7 +131,7 @@ jobs:
|
|||
|
||||
- run:
|
||||
name: build packages
|
||||
no_output_timeout: 30m
|
||||
no_output_timeout: 60m
|
||||
command: |
|
||||
source pyodide_env.sh
|
||||
|
||||
|
@ -204,7 +139,7 @@ jobs:
|
|||
touch -m -d '1 Jan 2021 12:00' emsdk/emsdk/.emscripten
|
||||
|
||||
ccache -z
|
||||
PYODIDE_PACKAGES='*' make -C packages
|
||||
PYODIDE_PACKAGES='<< parameters.packages >>' make -C packages
|
||||
ccache -s
|
||||
environment:
|
||||
PYODIDE_JOBS: 5
|
||||
|
@ -221,24 +156,12 @@ jobs:
|
|||
- run:
|
||||
name: Zip build directory
|
||||
command: |
|
||||
tar cjf pyodide-build.tar.gz dist
|
||||
tar --exclude="node_modules" -cjf pyodide-build.tar.gz dist
|
||||
tar cjf build-logs.tar.gz packages/build-logs
|
||||
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./packages/.artifacts
|
||||
- ./dist
|
||||
|
||||
- store_artifacts:
|
||||
path: /root/repo/dist/
|
||||
|
||||
- store_artifacts:
|
||||
path: /root/repo/pyodide-build.tar.gz
|
||||
|
||||
- store_artifacts:
|
||||
path: /root/repo/packages/build-logs
|
||||
|
||||
- store_artifacts:
|
||||
path: /root/repo/build-logs.tar.gz
|
||||
|
||||
|
@ -421,19 +344,68 @@ workflows:
|
|||
tags:
|
||||
only: /.*/
|
||||
|
||||
- build-packages-no-numpy-dependents:
|
||||
- build-packages:
|
||||
name: build-packages-no-numpy-dependents
|
||||
packages: "*,no-numpy-dependents"
|
||||
requires:
|
||||
- build-core
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
post-steps:
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./packages
|
||||
- ./dist
|
||||
|
||||
- build-packages:
|
||||
name: build-packages-opencv-python
|
||||
packages: opencv-python
|
||||
requires:
|
||||
- build-packages-no-numpy-dependents
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
post-steps:
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./packages/opencv-python/build
|
||||
- ./packages/opencv-python/dist
|
||||
- ./packages/build-logs/opencv*
|
||||
- ./dist/opencv*
|
||||
|
||||
- build-packages:
|
||||
name: build-packages-numpy-dependents
|
||||
packages: "*,!opencv-python"
|
||||
requires:
|
||||
- build-packages-no-numpy-dependents
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
post-steps:
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./packages
|
||||
- ./dist
|
||||
|
||||
- build-packages:
|
||||
name: build-packages
|
||||
packages: "*"
|
||||
requires:
|
||||
- build-packages-numpy-dependents
|
||||
- build-packages-opencv-python
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
post-steps:
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./packages/.artifacts
|
||||
- ./dist
|
||||
|
||||
- test-main:
|
||||
name: test-core-chrome
|
||||
|
|
12
conftest.py
|
@ -385,12 +385,16 @@ class NodeWrapper(SeleniumWrapper):
|
|||
browser = "node"
|
||||
|
||||
def init_node(self):
|
||||
self.p = pexpect.spawn(
|
||||
f"node --expose-gc --experimental-wasm-bigint ./src/test-js/node_test_driver.js {self.base_url}",
|
||||
timeout=60,
|
||||
)
|
||||
self.p = pexpect.spawn("/bin/bash", timeout=60)
|
||||
self.p.setecho(False)
|
||||
self.p.delaybeforesend = None
|
||||
# disable canonical input processing mode to allow sending longer lines
|
||||
# See: https://pexpect.readthedocs.io/en/stable/api/pexpect.html#pexpect.spawn.send
|
||||
self.p.sendline("stty -icanon")
|
||||
self.p.sendline(
|
||||
f"node --expose-gc --experimental-wasm-bigint ./src/test-js/node_test_driver.js {self.base_url}",
|
||||
)
|
||||
|
||||
try:
|
||||
self.p.expect_exact("READY!!")
|
||||
except pexpect.exceptions.EOF:
|
||||
|
|
|
@ -26,6 +26,8 @@ substitutions:
|
|||
`pyodide.runPython(code, { globals : some_dict})`;
|
||||
{pr}`2391`
|
||||
|
||||
- New packages: opencv-python v4.5.5.64 {pr}`2305`, ffmpeg {pr}`2305`, libwebp {pr}`2305`
|
||||
|
||||
## Version 0.20.0
|
||||
|
||||
[See the release notes for a summary.](https://blog.pyodide.org/posts/0.20-release/)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package:
|
||||
name: ffmpeg
|
||||
version: "4.4.1"
|
||||
|
||||
source:
|
||||
url: https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n4.4.1.tar.gz
|
||||
sha256: 82b43cc67296bcd01a59ae6b327cdb50121d3a9e35f41a30de1edd71bb4a6666
|
||||
extract_dir: FFmpeg-n4.4.1
|
||||
build:
|
||||
library: true
|
||||
script: |
|
||||
emconfigure ./configure \
|
||||
--extra-cflags="-fPIC" \
|
||||
--disable-x86asm \
|
||||
--disable-inline-asm \
|
||||
--disable-doc \
|
||||
--disable-stripping \
|
||||
--disable-programs \
|
||||
--disable-pthreads \
|
||||
--nm="$PYODIDE_ROOT/emsdk/emsdk/upstream/bin/llvm-nm -g" \
|
||||
--ar=emar --cc=emcc --cxx=em++ --objcc=emcc --dep-cc=emcc --ranlib=emranlib \
|
||||
--prefix=./lib
|
||||
|
||||
emmake make -j${PYODIDE_JOBS:-3}
|
||||
emmake make install
|
|
@ -0,0 +1,14 @@
|
|||
package:
|
||||
name: libwebp
|
||||
version: 1.2.2
|
||||
|
||||
source:
|
||||
url: https://github.com/webmproject/libwebp/archive/refs/tags/v1.2.2.tar.gz
|
||||
sha256: 51e9297aadb7d9eb99129fe0050f53a11fcce38a0848fb2b0389e385ad93695e
|
||||
|
||||
build:
|
||||
library: true
|
||||
script: |
|
||||
mkdir build && cd build && emcmake cmake -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_INSTALL_PREFIX=./lib ../
|
||||
emmake make -j ${PYODIDE_JOBS:-3}
|
||||
emmake make install
|
|
@ -0,0 +1,31 @@
|
|||
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE)
|
||||
set(OPENCV_PYTHON_SKIP_LINKER_EXCLUDE_LIBS TRUE)
|
||||
set(CMAKE_SKIP_COMPATIBILITY_TESTS 1)
|
||||
set(CMAKE_SIZEOF_CHAR 1)
|
||||
set(CMAKE_SIZEOF_UNSIGNED_SHORT 2)
|
||||
set(CMAKE_SIZEOF_SHORT 2)
|
||||
set(CMAKE_SIZEOF_INT 4)
|
||||
set(CMAKE_SIZEOF_UNSIGNED_LONG 4)
|
||||
set(CMAKE_SIZEOF_UNSIGNED_INT 4)
|
||||
set(CMAKE_SIZEOF_LONG 4)
|
||||
set(CMAKE_SIZEOF_VOID_P 4)
|
||||
set(CMAKE_SIZEOF_FLOAT 4)
|
||||
set(CMAKE_SIZEOF_DOUBLE 8)
|
||||
set(CMAKE_C_SIZEOF_DATA_PTR 4)
|
||||
set(CMAKE_CXX_SIZEOF_DATA_PTR 4)
|
||||
set(CMAKE_HAVE_LIMITS_H 1)
|
||||
set(CMAKE_HAVE_UNISTD_H 1)
|
||||
set(CMAKE_HAVE_PTHREAD_H 1)
|
||||
set(CMAKE_HAVE_SYS_PRCTL_H 1)
|
||||
set(CMAKE_WORDS_BIGENDIAN 0)
|
||||
set(CMAKE_DL_LIBS)
|
||||
set(CMAKE_RANLIB echo)
|
||||
|
||||
# Force filesystem support, it is disabled in Emscripten platform (needs fix)
|
||||
# https://github.com/opencv/opencv/blob/17234f82d025e3bbfbf611089637e5aa2038e7b8/modules/core/include/opencv2/core/utils/filesystem.private.hpp#L10
|
||||
add_definitions(-DOPENCV_HAVE_FILESYSTEM_SUPPORT=1)
|
||||
|
||||
if ("${EMSCRIPTEN_ROOT_PATH}" STREQUAL "")
|
||||
set(EMSCRIPTEN_ROOT_PATH "$ENV{EMSCRIPTEN}")
|
||||
endif()
|
||||
list(APPEND CMAKE_MODULE_PATH "${EMSCRIPTEN_ROOT_PATH}/cmake/Modules")
|
|
@ -0,0 +1,218 @@
|
|||
# ----------------------------------------------------------------------------
|
||||
# Detect 3rd-party image IO libraries
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# We want to use emscripten-ported version of ZLIB, LIBJPEG, LIBPNG.
|
||||
# However, OpenCV tries to find them in system paths.
|
||||
# Let's deceive OpenCV and pretend we have them.
|
||||
|
||||
set(HAVE_JPEG YES)
|
||||
set(HAVE_PNG YES)
|
||||
set(ZLIB_FOUND YES)
|
||||
|
||||
# --- libtiff (optional, should be searched after zlib and libjpeg) ---
|
||||
if(WITH_TIFF)
|
||||
if(BUILD_TIFF)
|
||||
ocv_clear_vars(TIFF_FOUND)
|
||||
else()
|
||||
ocv_clear_internal_cache_vars(TIFF_LIBRARY TIFF_INCLUDE_DIR)
|
||||
include(FindTIFF)
|
||||
if(TIFF_FOUND)
|
||||
ocv_parse_header("${TIFF_INCLUDE_DIR}/tiff.h" TIFF_VERSION_LINES TIFF_VERSION_CLASSIC TIFF_VERSION_BIG TIFF_VERSION TIFF_BIGTIFF_VERSION)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TIFF_FOUND)
|
||||
ocv_clear_vars(TIFF_LIBRARY TIFF_LIBRARIES TIFF_INCLUDE_DIR)
|
||||
|
||||
set(TIFF_LIBRARY libtiff CACHE INTERNAL "")
|
||||
set(TIFF_LIBRARIES ${TIFF_LIBRARY})
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libtiff")
|
||||
set(TIFF_INCLUDE_DIR "${${TIFF_LIBRARY}_SOURCE_DIR}" "${${TIFF_LIBRARY}_BINARY_DIR}" CACHE INTERNAL "")
|
||||
ocv_parse_header("${${TIFF_LIBRARY}_SOURCE_DIR}/tiff.h" TIFF_VERSION_LINES TIFF_VERSION_CLASSIC TIFF_VERSION_BIG TIFF_VERSION TIFF_BIGTIFF_VERSION)
|
||||
endif()
|
||||
|
||||
if(TIFF_VERSION_CLASSIC AND NOT TIFF_VERSION)
|
||||
set(TIFF_VERSION ${TIFF_VERSION_CLASSIC})
|
||||
endif()
|
||||
|
||||
if(TIFF_BIGTIFF_VERSION AND NOT TIFF_VERSION_BIG)
|
||||
set(TIFF_VERSION_BIG ${TIFF_BIGTIFF_VERSION})
|
||||
endif()
|
||||
|
||||
if(NOT TIFF_VERSION_STRING AND TIFF_INCLUDE_DIR)
|
||||
list(GET TIFF_INCLUDE_DIR 0 _TIFF_INCLUDE_DIR)
|
||||
if(EXISTS "${_TIFF_INCLUDE_DIR}/tiffvers.h")
|
||||
file(STRINGS "${_TIFF_INCLUDE_DIR}/tiffvers.h" tiff_version_str REGEX "^#define[\t ]+TIFFLIB_VERSION_STR[\t ]+\"LIBTIFF, Version .*")
|
||||
string(REGEX REPLACE "^#define[\t ]+TIFFLIB_VERSION_STR[\t ]+\"LIBTIFF, Version +([^ \\n]*).*" "\\1" TIFF_VERSION_STRING "${tiff_version_str}")
|
||||
unset(tiff_version_str)
|
||||
endif()
|
||||
unset(_TIFF_INCLUDE_DIR)
|
||||
endif()
|
||||
|
||||
set(HAVE_TIFF YES)
|
||||
endif()
|
||||
|
||||
# --- libwebp (optional) ---
|
||||
|
||||
if(WITH_WEBP)
|
||||
if(BUILD_WEBP)
|
||||
ocv_clear_vars(WEBP_FOUND WEBP_LIBRARY WEBP_LIBRARIES WEBP_INCLUDE_DIR)
|
||||
else()
|
||||
ocv_clear_internal_cache_vars(WEBP_LIBRARY WEBP_INCLUDE_DIR)
|
||||
include(cmake/OpenCVFindWebP.cmake)
|
||||
if(WEBP_FOUND)
|
||||
set(HAVE_WEBP 1)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- Add libwebp to 3rdparty/libwebp and compile it if not available ---
|
||||
if(WITH_WEBP AND NOT WEBP_FOUND
|
||||
AND (NOT ANDROID OR HAVE_CPUFEATURES)
|
||||
)
|
||||
ocv_clear_vars(WEBP_LIBRARY WEBP_INCLUDE_DIR)
|
||||
set(WEBP_LIBRARY libwebp CACHE INTERNAL "")
|
||||
set(WEBP_LIBRARIES ${WEBP_LIBRARY})
|
||||
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libwebp")
|
||||
set(WEBP_INCLUDE_DIR "${${WEBP_LIBRARY}_SOURCE_DIR}/src" CACHE INTERNAL "")
|
||||
set(HAVE_WEBP 1)
|
||||
endif()
|
||||
|
||||
if(NOT WEBP_VERSION AND WEBP_INCLUDE_DIR)
|
||||
ocv_clear_vars(ENC_MAJ_VERSION ENC_MIN_VERSION ENC_REV_VERSION)
|
||||
if(EXISTS "${WEBP_INCLUDE_DIR}/enc/vp8enci.h")
|
||||
ocv_parse_header("${WEBP_INCLUDE_DIR}/enc/vp8enci.h" WEBP_VERSION_LINES ENC_MAJ_VERSION ENC_MIN_VERSION ENC_REV_VERSION)
|
||||
set(WEBP_VERSION "${ENC_MAJ_VERSION}.${ENC_MIN_VERSION}.${ENC_REV_VERSION}")
|
||||
elseif(EXISTS "${WEBP_INCLUDE_DIR}/webp/encode.h")
|
||||
file(STRINGS "${WEBP_INCLUDE_DIR}/webp/encode.h" WEBP_ENCODER_ABI_VERSION REGEX "#define[ \t]+WEBP_ENCODER_ABI_VERSION[ \t]+([x0-9a-f]+)" )
|
||||
if(WEBP_ENCODER_ABI_VERSION MATCHES "#define[ \t]+WEBP_ENCODER_ABI_VERSION[ \t]+([x0-9a-f]+)")
|
||||
set(WEBP_ENCODER_ABI_VERSION "${CMAKE_MATCH_1}")
|
||||
set(WEBP_VERSION "encoder: ${WEBP_ENCODER_ABI_VERSION}")
|
||||
else()
|
||||
unset(WEBP_ENCODER_ABI_VERSION)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- libopenjp2 (optional, check before libjasper) ---
|
||||
if(WITH_OPENJPEG)
|
||||
if(BUILD_OPENJPEG)
|
||||
ocv_clear_vars(OpenJPEG_FOUND)
|
||||
else()
|
||||
find_package(OpenJPEG QUIET)
|
||||
endif()
|
||||
|
||||
if(NOT OpenJPEG_FOUND OR OPENJPEG_MAJOR_VERSION LESS 2)
|
||||
ocv_clear_vars(OPENJPEG_MAJOR_VERSION OPENJPEG_MINOR_VERSION OPENJPEG_BUILD_VERSION OPENJPEG_LIBRARIES OPENJPEG_INCLUDE_DIRS)
|
||||
message(STATUS "Could NOT find OpenJPEG (minimal suitable version: 2.0, "
|
||||
"recommended version >= 2.3.1). OpenJPEG will be built from sources")
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/openjpeg")
|
||||
if(OCV_CAN_BUILD_OPENJPEG)
|
||||
set(HAVE_OPENJPEG YES)
|
||||
message(STATUS "OpenJPEG libraries will be built from sources: ${OPENJPEG_LIBRARIES} "
|
||||
"(version \"${OPENJPEG_VERSION}\")")
|
||||
else()
|
||||
set(HAVE_OPENJPEG NO)
|
||||
message(STATUS "OpenJPEG libraries can't be built from sources. System requirements are not fulfilled.")
|
||||
endif()
|
||||
else()
|
||||
set(HAVE_OPENJPEG YES)
|
||||
message(STATUS "Found system OpenJPEG: ${OPENJPEG_LIBRARIES} "
|
||||
"(found version \"${OPENJPEG_VERSION}\")")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- libjasper (optional, should be searched after libjpeg) ---
|
||||
if(WITH_JASPER AND NOT HAVE_OPENJPEG)
|
||||
if(BUILD_JASPER)
|
||||
ocv_clear_vars(JASPER_FOUND)
|
||||
else()
|
||||
include(FindJasper)
|
||||
endif()
|
||||
|
||||
if(NOT JASPER_FOUND)
|
||||
ocv_clear_vars(JASPER_LIBRARY JASPER_LIBRARIES JASPER_INCLUDE_DIR)
|
||||
|
||||
set(JASPER_LIBRARY libjasper CACHE INTERNAL "")
|
||||
set(JASPER_LIBRARIES ${JASPER_LIBRARY})
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libjasper")
|
||||
set(JASPER_INCLUDE_DIR "${${JASPER_LIBRARY}_SOURCE_DIR}" CACHE INTERNAL "")
|
||||
endif()
|
||||
|
||||
set(HAVE_JASPER YES)
|
||||
|
||||
if(NOT JASPER_VERSION_STRING)
|
||||
ocv_parse_header2(JASPER "${JASPER_INCLUDE_DIR}/jasper/jas_config.h" JAS_VERSION "")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- OpenEXR (optional) ---
|
||||
if(WITH_OPENEXR)
|
||||
ocv_clear_vars(HAVE_OPENEXR)
|
||||
if(NOT BUILD_OPENEXR)
|
||||
ocv_clear_internal_cache_vars(OPENEXR_INCLUDE_PATHS OPENEXR_LIBRARIES OPENEXR_ILMIMF_LIBRARY OPENEXR_VERSION)
|
||||
include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindOpenEXR.cmake")
|
||||
endif()
|
||||
|
||||
if(OPENEXR_FOUND)
|
||||
set(HAVE_OPENEXR YES)
|
||||
else()
|
||||
ocv_clear_vars(OPENEXR_INCLUDE_PATHS OPENEXR_LIBRARIES OPENEXR_ILMIMF_LIBRARY OPENEXR_VERSION)
|
||||
|
||||
set(OPENEXR_LIBRARIES IlmImf)
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/openexr")
|
||||
if(OPENEXR_VERSION) # check via TARGET doesn't work
|
||||
set(BUILD_OPENEXR ON)
|
||||
set(HAVE_OPENEXR YES)
|
||||
set(BUILD_OPENEXR ON)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- GDAL (optional) ---
|
||||
if(WITH_GDAL)
|
||||
find_package(GDAL QUIET)
|
||||
|
||||
if(NOT GDAL_FOUND)
|
||||
set(HAVE_GDAL NO)
|
||||
ocv_clear_vars(GDAL_VERSION GDAL_LIBRARIES)
|
||||
else()
|
||||
set(HAVE_GDAL YES)
|
||||
ocv_include_directories(${GDAL_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_GDCM)
|
||||
find_package(GDCM QUIET)
|
||||
if(NOT GDCM_FOUND)
|
||||
set(HAVE_GDCM NO)
|
||||
ocv_clear_vars(GDCM_VERSION GDCM_LIBRARIES)
|
||||
else()
|
||||
set(HAVE_GDCM YES)
|
||||
# include(${GDCM_USE_FILE})
|
||||
set(GDCM_LIBRARIES gdcmMSFF) # GDCM does not set this variable for some reason
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_IMGCODEC_HDR)
|
||||
set(HAVE_IMGCODEC_HDR ON)
|
||||
elseif(DEFINED WITH_IMGCODEC_HDR)
|
||||
set(HAVE_IMGCODEC_HDR OFF)
|
||||
endif()
|
||||
if(WITH_IMGCODEC_SUNRASTER)
|
||||
set(HAVE_IMGCODEC_SUNRASTER ON)
|
||||
elseif(DEFINED WITH_IMGCODEC_SUNRASTER)
|
||||
set(HAVE_IMGCODEC_SUNRASTER OFF)
|
||||
endif()
|
||||
if(WITH_IMGCODEC_PXM)
|
||||
set(HAVE_IMGCODEC_PXM ON)
|
||||
elseif(DEFINED WITH_IMGCODEC_PXM)
|
||||
set(HAVE_IMGCODEC_PXM OFF)
|
||||
endif()
|
||||
if(WITH_IMGCODEC_PFM)
|
||||
set(HAVE_IMGCODEC_PFM ON)
|
||||
elseif(DEFINED WITH_IMGCODEC_PFM)
|
||||
set(HAVE_IMGCODEC_PFM OFF)
|
||||
endif()
|
|
@ -0,0 +1,90 @@
|
|||
#!/bin/bash
|
||||
|
||||
export CMAKE_ARGS=" \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$PYODIDE_ROOT/packages/opencv-python/cmake/Config.cmake \
|
||||
-DPYTHON3_INCLUDE_PATH=$PYTHONINCLUDE \
|
||||
-DPYTHON3_LIBRARY=$PYTHONINCLUDE/../libpython$PYMAJOR.$PYMINOR.a \
|
||||
-DPYTHON3_VERSION_MAJOR=$PYMAJOR \
|
||||
-DPYTHON3_VERSION_MINOR=$PYMINOR \
|
||||
-DPYTHON3_NUMPY_INCLUDE_DIRS=$NUMPY_INCLUDE_DIR \
|
||||
\
|
||||
-DWITH_ADE=ON \
|
||||
-DWITH_JPEG=ON \
|
||||
-DWITH_PNG=ON \
|
||||
-DWITH_WEBP=ON \
|
||||
-DBUILD_WEBP=OFF \
|
||||
-DWEBP_INCLUDE_DIR=$LIBWEBP_ROOT/include \
|
||||
-DWEBP_LIBRARY=$LIBWEBP_ROOT/lib/libwebp.a \
|
||||
\
|
||||
-DBUILD_opencv_python3=ON \
|
||||
-DBUILD_opencv_world=ON \
|
||||
-DBUILD_opencv_imgcodecs=ON \
|
||||
-DBUILD_opencv_videoio=ON \
|
||||
-DBUILD_opencv_gapi=ON \
|
||||
-DBUILD_opencv_photo=ON \
|
||||
-DBUILD_opencv_stitching=ON \
|
||||
-DBUILD_opencv_highgui=ON \
|
||||
-DBUILD_opencv_features2d=ON \
|
||||
-DBUILD_opencv_flann=ON \
|
||||
-DBUILD_opencv_calib3d=ON \
|
||||
-DBUILD_opencv_dnn=ON \
|
||||
-DBUILD_opencv_ml=ON \
|
||||
-DBUILD_opencv_objdetect=ON \
|
||||
-DWITH_OPENCL=OFF \
|
||||
-DOPENCV_DNN_OPENCL=OFF \
|
||||
-DWITH_PROTOBUF=ON \
|
||||
-DWITH_FFMPEG=ON \
|
||||
\
|
||||
-DPYTHON3_EXECUTABLE=python \
|
||||
-DPYTHON3_LIMITED_API=ON \
|
||||
-DPYTHON_DEFAULT_EXECUTABLE=python \
|
||||
-DENABLE_PIC=FALSE \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCPU_BASELINE='' \
|
||||
-DCPU_DISPATCH='' \
|
||||
-DCV_TRACE=OFF \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DWITH_1394=OFF \
|
||||
-DWITH_VTK=OFF \
|
||||
-DWITH_EIGEN=OFF \
|
||||
-DWITH_GSTREAMER=OFF \
|
||||
-DWITH_GTK=OFF \
|
||||
-DWITH_GTK_2_X=OFF \
|
||||
-DWITH_QT=OFF \
|
||||
-DWITH_IPP=OFF \
|
||||
-DWITH_JASPER=OFF \
|
||||
-DWITH_OPENJPEG=OFF \
|
||||
-DWITH_OPENEXR=OFF \
|
||||
-DWITH_OPENGL=OFF \
|
||||
-DWITH_OPENVX=OFF \
|
||||
-DWITH_OPENNI=OFF \
|
||||
-DWITH_OPENNI2=OFF \
|
||||
-DWITH_TBB=OFF \
|
||||
-DWITH_TIFF=OFF \
|
||||
-DWITH_V4L=OFF \
|
||||
-DWITH_OPENCL_SVM=OFF \
|
||||
-DWITH_OPENCLAMDFFT=OFF \
|
||||
-DWITH_OPENCLAMDBLAS=OFF \
|
||||
-DWITH_GPHOTO2=OFF \
|
||||
-DWITH_LAPACK=OFF \
|
||||
-DWITH_ITT=OFF \
|
||||
-DWITH_QUIRC=OFF \
|
||||
-DBUILD_ZLIB=OFF \
|
||||
-DBUILD_opencv_apps=OFF \
|
||||
-DBUILD_opencv_shape=OFF \
|
||||
-DBUILD_opencv_videostab=OFF \
|
||||
-DBUILD_opencv_superres=OFF \
|
||||
-DBUILD_opencv_java=OFF \
|
||||
-DBUILD_opencv_js=OFF \
|
||||
-DBUILD_opencv_python2=OFF \
|
||||
-DBUILD_EXAMPLES=OFF \
|
||||
-DBUILD_PACKAGE=OFF \
|
||||
-DBUILD_TESTS=OFF \
|
||||
-DBUILD_PERF_TESTS=OFF \
|
||||
-DBUILD_DOCS=OFF \
|
||||
-DWITH_PTHREADS_PF=OFF \
|
||||
-DCV_ENABLE_INTRINSICS=OFF \
|
||||
-DBUILD_WASM_INTRIN_TESTS=OFF \
|
||||
-DCMAKE_INSTALL_PREFIX=../cmake-install \
|
||||
-DCMAKE_VERBOSE_MAKEFILE=ON \
|
||||
"
|
|
@ -0,0 +1,49 @@
|
|||
# --- FFMPEG ---
|
||||
|
||||
set(HAVE_FFMPEG TRUE)
|
||||
set(FFMPEG_FOUND TRUE)
|
||||
|
||||
set(FFMPEG_ROOT_PATH "$ENV{FFMPEG_ROOT}")
|
||||
set(FFMPEG_INCLUDE_DIRS "${FFMPEG_ROOT_PATH}/include")
|
||||
set(FFMPEG_LIBRARIES
|
||||
"${FFMPEG_ROOT_PATH}/lib/libavcodec.a"
|
||||
"${FFMPEG_ROOT_PATH}/lib/libavformat.a"
|
||||
"${FFMPEG_ROOT_PATH}/lib/libavutil.a"
|
||||
"${FFMPEG_ROOT_PATH}/lib/libswscale.a"
|
||||
"${FFMPEG_ROOT_PATH}/lib/libswresample.a"
|
||||
)
|
||||
|
||||
ocv_add_external_target(ffmpeg "${FFMPEG_INCLUDE_DIRS}" "${FFMPEG_LIBRARIES}" "HAVE_FFMPEG")
|
||||
|
||||
set(__builtin_defines "")
|
||||
set(__builtin_include_dirs "")
|
||||
set(__builtin_libs "")
|
||||
set(__plugin_defines "")
|
||||
set(__plugin_include_dirs "")
|
||||
set(__plugin_libs "")
|
||||
if(HAVE_OPENCL)
|
||||
set(__opencl_dirs "")
|
||||
if(OPENCL_INCLUDE_DIRS)
|
||||
set(__opencl_dirs "${OPENCL_INCLUDE_DIRS}")
|
||||
elseif(OPENCL_INCLUDE_DIR)
|
||||
set(__opencl_dirs "${OPENCL_INCLUDE_DIR}")
|
||||
else()
|
||||
set(__opencl_dirs "${OpenCV_SOURCE_DIR}/3rdparty/include/opencl/1.2")
|
||||
endif()
|
||||
# extra dependencies for buildin code (OpenCL dir is required for extensions like cl_d3d11.h)
|
||||
# buildin HAVE_OPENCL is already defined through cvconfig.h
|
||||
list(APPEND __builtin_include_dirs "${__opencl_dirs}")
|
||||
|
||||
# extra dependencies for
|
||||
list(APPEND __plugin_defines "HAVE_OPENCL")
|
||||
list(APPEND __plugin_include_dirs "${__opencl_dirs}")
|
||||
endif()
|
||||
|
||||
# TODO: libva, d3d11
|
||||
|
||||
if(__builtin_include_dirs OR __builtin_include_defines OR __builtin_include_libs)
|
||||
ocv_add_external_target(ffmpeg.builtin_deps "${__builtin_include_dirs}" "${__builtin_include_libs}" "${__builtin_defines}")
|
||||
endif()
|
||||
if(VIDEOIO_ENABLE_PLUGINS AND __plugin_include_dirs OR __plugin_include_defines OR __plugin_include_libs)
|
||||
ocv_add_external_target(ffmpeg.plugin_deps "${__plugin_include_dirs}" "${__plugin_include_libs}" "${__plugin_defines}")
|
||||
endif()
|
|
@ -0,0 +1,65 @@
|
|||
package:
|
||||
name: opencv-python
|
||||
version: 4.5.5.64
|
||||
about:
|
||||
home: https://github.com/skvark/opencv-python
|
||||
PyPI: https://pypi.org/project/opencv-python
|
||||
summary: Wrapper package for OpenCV python bindings.
|
||||
license: MIT
|
||||
source:
|
||||
url: https://files.pythonhosted.org/packages/3c/61/ee4496192ed27f657532fdf0d814b05b9787e7fc5122ed3ca57282bae69c/opencv-python-4.5.5.64.tar.gz
|
||||
sha256: f65de0446a330c3b773cd04ba10345d8ce1b15dcac3f49770204e37602d0b3f7
|
||||
extras:
|
||||
- [cmake/OpenCVFindLibsGrfmt.cmake, opencv/cmake/OpenCVFindLibsGrfmt.cmake]
|
||||
- [
|
||||
cmake/detect_ffmpeg.cmake,
|
||||
opencv/modules/videoio/cmake/detect_ffmpeg.cmake,
|
||||
]
|
||||
patches:
|
||||
- patches/0001-Enable-file-system.patch
|
||||
|
||||
requirements:
|
||||
run:
|
||||
- numpy
|
||||
- ffmpeg
|
||||
- libwebp
|
||||
build:
|
||||
cxxflags: |
|
||||
-fPIC
|
||||
-s USE_ZLIB=1
|
||||
-s USE_LIBJPEG=1
|
||||
-s USE_LIBPNG=1
|
||||
-s SIDE_MODULE=1
|
||||
ldflags: |
|
||||
-ljpeg
|
||||
-lz
|
||||
-lpng
|
||||
|
||||
# Note on CMAKE_ARGS:
|
||||
# CMake args are adopted from OpenCV.js (https://github.com/opencv/opencv/blob/4.x/platforms/js/build_js.py)
|
||||
# But we support more of modules than OpenCV.js.
|
||||
#
|
||||
# Note on CMAKE_TOOLCHAIN_FILE:
|
||||
# We don't want to use toolchain file provided by Emscripten,
|
||||
# because our build script hijack gcc, c++, ... and replace it with emcc, em++, ..., instead of calling them directly.
|
||||
#
|
||||
# List of OpenCV modules can be found at: https://docs.opencv.org/4.x/
|
||||
# Build configs can be found at: https://docs.opencv.org/4.x/db/d05/tutorial_config_reference.html
|
||||
|
||||
script: |
|
||||
pip install scikit-build
|
||||
# TODO: remove this line after version update (https://github.com/opencv/opencv-python/issues/648)
|
||||
sed -i "s/cmake_install_dir=cmake_install_reldir/_cmake_install_dir=cmake_install_reldir/" setup.py
|
||||
|
||||
# export VERBOSE=1
|
||||
|
||||
export NUMPY_INCLUDE_DIR="$HOSTINSTALLDIR/lib/python$PYMAJOR.$PYMINOR/site-packages/numpy/core/include/"
|
||||
export EMSCRIPTEN="$PYODIDE_ROOT/emsdk/emsdk/upstream/emscripten/"
|
||||
export FFMPEG_ROOT="$PYODIDE_ROOT/packages/ffmpeg/build/ffmpeg-4.4.1/lib"
|
||||
export LIBWEBP_ROOT="$PYODIDE_ROOT/packages/libwebp/build/libwebp-1.2.2/build/lib"
|
||||
|
||||
source $PYODIDE_ROOT/packages/opencv-python/cmake/build_args.sh
|
||||
|
||||
test:
|
||||
imports:
|
||||
- cv2
|
|
@ -0,0 +1,75 @@
|
|||
From c7e9f892204ce1e47774fe21790195e2cbd7f2b3 Mon Sep 17 00:00:00 2001
|
||||
From: ryanking13 <def6488@gmail.com>
|
||||
Date: Tue, 29 Mar 2022 01:58:40 +0000
|
||||
Subject: [PATCH] Enable file system
|
||||
|
||||
---
|
||||
.../include/opencv2/core/utils/plugin_loader.private.hpp | 8 ++++----
|
||||
modules/core/src/utils/filesystem.cpp | 4 ++--
|
||||
2 files changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/modules/core/include/opencv2/core/utils/plugin_loader.private.hpp b/modules/core/include/opencv2/core/utils/plugin_loader.private.hpp
|
||||
index d6390fc74a..c089309443 100644
|
||||
--- a/opencv/modules/core/include/opencv2/core/utils/plugin_loader.private.hpp
|
||||
+++ b/opencv/modules/core/include/opencv2/core/utils/plugin_loader.private.hpp
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
-#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__)
|
||||
+#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
@@ -65,7 +65,7 @@ void* getSymbol_(LibHandle_t h, const char* symbolName)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return (void*)GetProcAddress(h, symbolName);
|
||||
-#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__)
|
||||
+#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__)
|
||||
return dlsym(h, symbolName);
|
||||
#endif
|
||||
}
|
||||
@@ -79,7 +79,7 @@ LibHandle_t libraryLoad_(const FileSystemPath_t& filename)
|
||||
# else
|
||||
return LoadLibraryW(filename.c_str());
|
||||
#endif
|
||||
-#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__)
|
||||
+#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__)
|
||||
void* handle = dlopen(filename.c_str(), RTLD_NOW);
|
||||
CV_LOG_IF_DEBUG(NULL, !handle, "dlopen() error: " << dlerror());
|
||||
return handle;
|
||||
@@ -91,7 +91,7 @@ void libraryRelease_(LibHandle_t h)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
FreeLibrary(h);
|
||||
-#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__)
|
||||
+#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__)
|
||||
dlclose(h);
|
||||
#endif
|
||||
}
|
||||
diff --git a/modules/core/src/utils/filesystem.cpp b/modules/core/src/utils/filesystem.cpp
|
||||
index 663ec311e4..3ef4408d2d 100644
|
||||
--- a/opencv/modules/core/src/utils/filesystem.cpp
|
||||
+++ b/opencv/modules/core/src/utils/filesystem.cpp
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <errno.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
-#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__
|
||||
+#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __EMSCRIPTEN__
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -343,7 +343,7 @@ private:
|
||||
Impl& operator=(const Impl&); // disabled
|
||||
};
|
||||
|
||||
-#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__
|
||||
+#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __EMSCRIPTEN__
|
||||
|
||||
struct FileLock::Impl
|
||||
{
|
||||
--
|
||||
2.35.1
|
||||
|
After Width: | Height: | Size: 181 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 182 KiB |
After Width: | Height: | Size: 265 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 282 B |
After Width: | Height: | Size: 272 KiB |
After Width: | Height: | Size: 255 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 226 KiB |
|
@ -0,0 +1,551 @@
|
|||
import base64
|
||||
import pathlib
|
||||
|
||||
from pyodide_build.testing import run_in_pyodide
|
||||
|
||||
REFERENCE_IMAGES_PATH = pathlib.Path(__file__).parent / "reference-images"
|
||||
|
||||
|
||||
def compare_with_reference_image(selenium, reference_image, var="img", grayscale=True):
|
||||
reference_image_encoded = base64.b64encode(reference_image.read_bytes())
|
||||
grayscale = "cv.IMREAD_GRAYSCALE" if grayscale else "cv.IMREAD_COLOR"
|
||||
match_ratio = selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
DIFF_THRESHOLD = 2
|
||||
arr = np.frombuffer(base64.b64decode({reference_image_encoded!r}), np.uint8)
|
||||
ref_data = cv.imdecode(arr, {grayscale})
|
||||
|
||||
pixels_match = np.count_nonzero(np.abs({var}.astype(np.int16) - ref_data.astype(np.int16)) <= DIFF_THRESHOLD)
|
||||
pixels_total = ref_data.size
|
||||
float(pixels_match / pixels_total)
|
||||
"""
|
||||
)
|
||||
|
||||
# Due to some randomness in the result, we allow a small difference
|
||||
return match_ratio > 0.95
|
||||
|
||||
|
||||
def test_import(selenium):
|
||||
selenium.set_script_timeout(60)
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
"""
|
||||
import cv2
|
||||
cv2.__version__
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
@run_in_pyodide(packages=["opencv-python", "numpy"])
|
||||
def test_image_extensions():
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
shape = (16, 16, 3)
|
||||
img = np.zeros(shape, np.uint8)
|
||||
|
||||
extensions = {
|
||||
"bmp": b"BM6\x03",
|
||||
"jpg": b"\xff\xd8\xff\xe0",
|
||||
"jpeg": b"\xff\xd8\xff\xe0",
|
||||
"png": b"\x89PNG",
|
||||
"webp": b"RIFF",
|
||||
}
|
||||
|
||||
for ext, signature in extensions.items():
|
||||
result, buf = cv.imencode(f".{ext}", img)
|
||||
assert result
|
||||
assert bytes(buf[:4]) == signature
|
||||
|
||||
|
||||
@run_in_pyodide(packages=["opencv-python", "numpy"])
|
||||
def test_io():
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
shape = (16, 16, 3)
|
||||
img = np.zeros(shape, np.uint8)
|
||||
|
||||
filename = "test.bmp"
|
||||
cv.imwrite(filename, img)
|
||||
img_ = cv.imread(filename)
|
||||
assert img_.shape == img.shape
|
||||
|
||||
|
||||
@run_in_pyodide(packages=["opencv-python", "numpy"])
|
||||
def test_drawing():
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
width = 100
|
||||
height = 100
|
||||
shape = (width, height, 3)
|
||||
img = np.zeros(shape, np.uint8)
|
||||
|
||||
cv.line(img, (0, 0), (width - 1, 0), (255, 0, 0), 5)
|
||||
cv.line(img, (0, 0), (0, height - 1), (0, 0, 255), 5)
|
||||
cv.rectangle(img, (0, 0), (width // 2, height // 2), (0, 255, 0), 2)
|
||||
cv.circle(img, (0, 0), radius=width // 2, color=(255, 0, 0))
|
||||
cv.putText(img, "Hello Pyodide", (0, 0), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
|
||||
|
||||
|
||||
@run_in_pyodide(packages=["opencv-python", "numpy"])
|
||||
def test_pixel_access():
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
shape = (16, 16, 3)
|
||||
img = np.zeros(shape, np.uint8)
|
||||
|
||||
img[5, 5] = [1, 2, 3]
|
||||
assert list(img[5, 5]) == [1, 2, 3]
|
||||
|
||||
b, g, r = cv.split(img)
|
||||
img_ = cv.merge([b, g, r])
|
||||
assert (img == img_).all()
|
||||
|
||||
|
||||
@run_in_pyodide(packages=["opencv-python", "numpy"])
|
||||
def test_image_processing():
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
# Masking
|
||||
img = np.random.randint(0, 255, size=500)
|
||||
lower = np.array([0])
|
||||
upper = np.array([200])
|
||||
mask = cv.inRange(img, lower, upper)
|
||||
res = cv.bitwise_and(img, img, mask=mask)
|
||||
assert not (res > 200).any()
|
||||
|
||||
|
||||
def test_edge_detection(selenium):
|
||||
original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "baboon.png").read_bytes())
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
|
||||
src = cv.imdecode(src, cv.IMREAD_COLOR)
|
||||
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
|
||||
sobel = cv.Sobel(gray, cv.CV_8U, 1, 0, 3)
|
||||
laplacian = cv.Laplacian(gray, cv.CV_8U, ksize=3)
|
||||
canny = cv.Canny(src, 100, 255)
|
||||
None
|
||||
"""
|
||||
)
|
||||
|
||||
assert compare_with_reference_image(
|
||||
selenium, REFERENCE_IMAGES_PATH / "baboon_sobel.png", "sobel"
|
||||
)
|
||||
assert compare_with_reference_image(
|
||||
selenium, REFERENCE_IMAGES_PATH / "baboon_laplacian.png", "laplacian"
|
||||
)
|
||||
assert compare_with_reference_image(
|
||||
selenium, REFERENCE_IMAGES_PATH / "baboon_canny.png", "canny"
|
||||
)
|
||||
|
||||
|
||||
def test_photo_decolor(selenium):
|
||||
original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "baboon.png").read_bytes())
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
|
||||
src = cv.imdecode(src, cv.IMREAD_COLOR)
|
||||
grayscale, color_boost = cv.decolor(src)
|
||||
None
|
||||
"""
|
||||
)
|
||||
|
||||
assert compare_with_reference_image(
|
||||
selenium, REFERENCE_IMAGES_PATH / "baboon_decolor_grayscale.png", "grayscale"
|
||||
)
|
||||
assert compare_with_reference_image(
|
||||
selenium,
|
||||
REFERENCE_IMAGES_PATH / "baboon_decolor_color_boost.png",
|
||||
"color_boost",
|
||||
grayscale=False,
|
||||
)
|
||||
|
||||
|
||||
def test_stitch(selenium):
|
||||
original_img_left = base64.b64encode(
|
||||
(REFERENCE_IMAGES_PATH / "mountain1.png").read_bytes()
|
||||
)
|
||||
original_img_right = base64.b64encode(
|
||||
(REFERENCE_IMAGES_PATH / "mountain2.png").read_bytes()
|
||||
)
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
left = np.frombuffer(base64.b64decode({original_img_left!r}), np.uint8)
|
||||
left = cv.imdecode(left, cv.IMREAD_COLOR)
|
||||
right = np.frombuffer(base64.b64decode({original_img_right!r}), np.uint8)
|
||||
right = cv.imdecode(right, cv.IMREAD_COLOR)
|
||||
stitcher = cv.Stitcher.create(cv.Stitcher_PANORAMA)
|
||||
status, panorama = stitcher.stitch([left, right])
|
||||
|
||||
# It seems that the result is not always the same due to the randomness, so check the status and size instead
|
||||
assert status == cv.Stitcher_OK
|
||||
assert panorama.shape[0] >= max(left.shape[0], right.shape[0])
|
||||
assert panorama.shape[1] >= max(left.shape[1], right.shape[1])
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_video_optical_flow(selenium):
|
||||
original_img = base64.b64encode(
|
||||
(REFERENCE_IMAGES_PATH / "traffic.mp4").read_bytes()
|
||||
)
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
src = base64.b64decode({original_img!r})
|
||||
|
||||
video_path = "video.mp4"
|
||||
with open(video_path, "wb") as f:
|
||||
f.write(src)
|
||||
|
||||
cap = cv.VideoCapture(video_path)
|
||||
assert cap.isOpened()
|
||||
|
||||
# params for ShiTomasi corner detection
|
||||
feature_params = dict( maxCorners = 100,
|
||||
qualityLevel = 0.3,
|
||||
minDistance = 7,
|
||||
blockSize = 7 )
|
||||
# Parameters for lucas kanade optical flow
|
||||
lk_params = dict( winSize = (15, 15),
|
||||
maxLevel = 2,
|
||||
criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))
|
||||
|
||||
# Take first frame and find corners in it
|
||||
ret, old_frame = cap.read()
|
||||
assert ret
|
||||
|
||||
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
|
||||
p0 = cv.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
|
||||
# Create a mask image for drawing purposes
|
||||
mask = np.zeros_like(old_frame)
|
||||
while(1):
|
||||
ret, frame = cap.read()
|
||||
if not ret:
|
||||
break
|
||||
frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
|
||||
# calculate optical flow
|
||||
p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
|
||||
# Select good points
|
||||
if p1 is not None:
|
||||
good_new = p1[st==1]
|
||||
good_old = p0[st==1]
|
||||
# draw the tracks
|
||||
for i, (new, old) in enumerate(zip(good_new, good_old)):
|
||||
a, b = new.ravel()
|
||||
c, d = old.ravel()
|
||||
mask = cv.line(mask, (int(a), int(b)), (int(c), int(d)), [0, 0, 255], 2)
|
||||
frame = cv.circle(frame, (int(a), int(b)), 5, [255, 0, 0], -1)
|
||||
img = cv.add(frame, mask)
|
||||
# Now update the previous frame and previous points
|
||||
old_gray = frame_gray.copy()
|
||||
p0 = good_new.reshape(-1, 1, 2)
|
||||
|
||||
optical_flow = img
|
||||
None
|
||||
"""
|
||||
)
|
||||
|
||||
assert compare_with_reference_image(
|
||||
selenium,
|
||||
REFERENCE_IMAGES_PATH / "traffic_optical_flow.png",
|
||||
"optical_flow",
|
||||
grayscale=False,
|
||||
)
|
||||
|
||||
|
||||
def test_flann_sift(selenium):
|
||||
original_img_src1 = base64.b64encode(
|
||||
(REFERENCE_IMAGES_PATH / "box.png").read_bytes()
|
||||
)
|
||||
original_img_src2 = base64.b64encode(
|
||||
(REFERENCE_IMAGES_PATH / "box_in_scene.png").read_bytes()
|
||||
)
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
src1 = np.frombuffer(base64.b64decode({original_img_src1!r}), np.uint8)
|
||||
src1 = cv.imdecode(src1, cv.IMREAD_GRAYSCALE)
|
||||
src2 = np.frombuffer(base64.b64decode({original_img_src2!r}), np.uint8)
|
||||
src2 = cv.imdecode(src2, cv.IMREAD_GRAYSCALE)
|
||||
|
||||
#-- Step 1: Detect the keypoints using SIFT Detector, compute the descriptors
|
||||
detector = cv.SIFT_create()
|
||||
keypoints1, descriptors1 = detector.detectAndCompute(src1, None)
|
||||
keypoints2, descriptors2 = detector.detectAndCompute(src2, None)
|
||||
|
||||
#-- Step 2: Matching descriptor vectors with a FLANN based matcher
|
||||
matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED)
|
||||
knn_matches = matcher.knnMatch(descriptors1, descriptors2, 2)
|
||||
|
||||
#-- Filter matches using the Lowe's ratio test
|
||||
ratio_thresh = 0.3
|
||||
good_matches = []
|
||||
for m,n in knn_matches:
|
||||
if m.distance < ratio_thresh * n.distance:
|
||||
good_matches.append(m)
|
||||
|
||||
#-- Draw matches
|
||||
matches = np.empty((max(src1.shape[0], src2.shape[0]), src1.shape[1]+src2.shape[1], 3), dtype=np.uint8)
|
||||
cv.drawMatches(src1, keypoints1, src2, keypoints2, good_matches, matches, matchColor=[255, 0, 0], flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
|
||||
|
||||
sift_result = cv.cvtColor(matches, cv.COLOR_BGR2GRAY)
|
||||
None
|
||||
"""
|
||||
)
|
||||
|
||||
assert compare_with_reference_image(
|
||||
selenium,
|
||||
REFERENCE_IMAGES_PATH / "box_sift.png",
|
||||
"sift_result",
|
||||
grayscale=True,
|
||||
)
|
||||
|
||||
|
||||
def test_dnn_mnist(selenium):
|
||||
"""
|
||||
Run tiny MNIST classification ONNX model
|
||||
Training script: https://github.com/ryanking13/torch-opencv-mnist
|
||||
"""
|
||||
|
||||
original_img = base64.b64encode(
|
||||
(REFERENCE_IMAGES_PATH / "mnist_2.png").read_bytes()
|
||||
)
|
||||
tf_model = base64.b64encode((REFERENCE_IMAGES_PATH / "mnist.onnx").read_bytes())
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
model_weights = base64.b64decode({tf_model!r})
|
||||
model_weights_path = './mnist.onnx'
|
||||
with open(model_weights_path, 'wb') as f:
|
||||
f.write(model_weights)
|
||||
|
||||
src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
|
||||
src = cv.imdecode(src, cv.IMREAD_GRAYSCALE)
|
||||
|
||||
net = cv.dnn.readNet(model_weights_path)
|
||||
blob = cv.dnn.blobFromImage(src, 1.0, (28, 28), (0, 0, 0), False, False)
|
||||
|
||||
net.setInput(blob)
|
||||
prob = net.forward()
|
||||
assert "output_0" in net.getLayerNames()
|
||||
assert np.argmax(prob) == 2
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_ml_pca(selenium):
|
||||
original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "pca.png").read_bytes())
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
from math import atan2, cos, sin, sqrt, pi
|
||||
|
||||
def drawAxis(img, p_, q_, colour, scale):
|
||||
p = list(p_)
|
||||
q = list(q_)
|
||||
|
||||
angle = atan2(p[1] - q[1], p[0] - q[0]) # angle in radians
|
||||
hypotenuse = sqrt((p[1] - q[1]) * (p[1] - q[1]) + (p[0] - q[0]) * (p[0] - q[0]))
|
||||
# Here we lengthen the arrow by a factor of scale
|
||||
q[0] = p[0] - scale * hypotenuse * cos(angle)
|
||||
q[1] = p[1] - scale * hypotenuse * sin(angle)
|
||||
cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
|
||||
# create the arrow hooks
|
||||
p[0] = q[0] + 9 * cos(angle + pi / 4)
|
||||
p[1] = q[1] + 9 * sin(angle + pi / 4)
|
||||
cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
|
||||
p[0] = q[0] + 9 * cos(angle - pi / 4)
|
||||
p[1] = q[1] + 9 * sin(angle - pi / 4)
|
||||
cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
|
||||
|
||||
def getOrientation(pts, img):
|
||||
|
||||
sz = len(pts)
|
||||
data_pts = np.empty((sz, 2), dtype=np.float64)
|
||||
for i in range(data_pts.shape[0]):
|
||||
data_pts[i,0] = pts[i,0,0]
|
||||
data_pts[i,1] = pts[i,0,1]
|
||||
# Perform PCA analysis
|
||||
mean = np.empty((0))
|
||||
mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)
|
||||
# Store the center of the object
|
||||
cntr = (int(mean[0,0]), int(mean[0,1]))
|
||||
|
||||
|
||||
cv.circle(img, cntr, 3, (255, 0, 255), 2)
|
||||
p1 = (cntr[0] + 0.02 * eigenvectors[0,0] * eigenvalues[0,0], cntr[1] + 0.02 * eigenvectors[0,1] * eigenvalues[0,0])
|
||||
p2 = (cntr[0] - 0.02 * eigenvectors[1,0] * eigenvalues[1,0], cntr[1] - 0.02 * eigenvectors[1,1] * eigenvalues[1,0])
|
||||
drawAxis(img, cntr, p1, (0, 255, 0), 1)
|
||||
drawAxis(img, cntr, p2, (255, 255, 0), 5)
|
||||
angle = atan2(eigenvectors[0,1], eigenvectors[0,0]) # orientation in radians
|
||||
|
||||
return angle
|
||||
|
||||
src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
|
||||
src = cv.imdecode(src, cv.IMREAD_COLOR)
|
||||
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
|
||||
|
||||
# Convert image to binary
|
||||
_, bw = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
|
||||
contours, _ = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
|
||||
for i, c in enumerate(contours):
|
||||
# Calculate the area of each contour
|
||||
area = cv.contourArea(c)
|
||||
# Ignore contours that are too small or too large
|
||||
if area < 1e2 or 1e5 < area:
|
||||
continue
|
||||
# Draw each contour only for visualisation purposes
|
||||
cv.drawContours(src, contours, i, (0, 0, 255), 2)
|
||||
# Find the orientation of each shape
|
||||
getOrientation(c, src)
|
||||
|
||||
pca_result = src
|
||||
None
|
||||
"""
|
||||
)
|
||||
|
||||
assert compare_with_reference_image(
|
||||
selenium,
|
||||
REFERENCE_IMAGES_PATH / "pca_result.png",
|
||||
"pca_result",
|
||||
grayscale=False,
|
||||
)
|
||||
|
||||
|
||||
def test_objdetect_face(selenium):
|
||||
original_img = base64.b64encode(
|
||||
(REFERENCE_IMAGES_PATH / "monalisa.png").read_bytes()
|
||||
)
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
|
||||
src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
|
||||
src = cv.imdecode(src, cv.IMREAD_COLOR)
|
||||
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
|
||||
gray = cv.equalizeHist(gray)
|
||||
|
||||
face_cascade = cv.CascadeClassifier()
|
||||
eyes_cascade = cv.CascadeClassifier()
|
||||
data_path = Path(cv.data.haarcascades)
|
||||
face_cascade.load(str(data_path / "haarcascade_frontalface_alt.xml"))
|
||||
eyes_cascade.load(str(data_path / "haarcascade_eye_tree_eyeglasses.xml"))
|
||||
|
||||
faces = face_cascade.detectMultiScale(gray)
|
||||
face_detected = src.copy()
|
||||
for (x,y,w,h) in faces:
|
||||
center = (x + w//2, y + h//2)
|
||||
face_detected = cv.ellipse(face_detected, center, (w//2, h//2), 0, 0, 360, (255, 0, 255), 4)
|
||||
faceROI = gray[y:y+h,x:x+w]
|
||||
eyes = eyes_cascade.detectMultiScale(faceROI)
|
||||
for (x2,y2,w2,h2) in eyes:
|
||||
eye_center = (x + x2 + w2//2, y + y2 + h2//2)
|
||||
radius = int(round((w2 + h2)*0.25))
|
||||
face_detected = cv.circle(face_detected, eye_center, radius, (255, 0, 0 ), 4)
|
||||
|
||||
None
|
||||
"""
|
||||
)
|
||||
|
||||
assert compare_with_reference_image(
|
||||
selenium,
|
||||
REFERENCE_IMAGES_PATH / "monalisa_facedetect.png",
|
||||
"face_detected",
|
||||
grayscale=False,
|
||||
)
|
||||
|
||||
|
||||
def test_feature2d_kaze(selenium):
|
||||
original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "baboon.png").read_bytes())
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
|
||||
src = cv.imdecode(src, cv.IMREAD_COLOR)
|
||||
|
||||
detector = cv.KAZE_create()
|
||||
keypoints = detector.detect(src)
|
||||
|
||||
kaze = cv.drawKeypoints(src, keypoints, None, color=(0, 0, 255), flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
|
||||
None
|
||||
"""
|
||||
)
|
||||
|
||||
assert compare_with_reference_image(
|
||||
selenium,
|
||||
REFERENCE_IMAGES_PATH / "baboon_kaze.png",
|
||||
"kaze",
|
||||
grayscale=False,
|
||||
)
|
||||
|
||||
|
||||
def test_calib3d_chessboard(selenium):
|
||||
original_img = base64.b64encode(
|
||||
(REFERENCE_IMAGES_PATH / "chessboard.png").read_bytes()
|
||||
)
|
||||
selenium.load_package("opencv-python")
|
||||
selenium.run(
|
||||
f"""
|
||||
import base64
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
|
||||
src = cv.imdecode(src, cv.IMREAD_COLOR)
|
||||
|
||||
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
|
||||
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
|
||||
ret, corners = cv.findChessboardCorners(gray, (9, 6), None)
|
||||
cv.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
|
||||
cv.drawChessboardCorners(gray, (9, 6), corners, ret)
|
||||
chessboard_corners = gray
|
||||
None
|
||||
"""
|
||||
)
|
||||
|
||||
assert compare_with_reference_image(
|
||||
selenium,
|
||||
REFERENCE_IMAGES_PATH / "chessboard_corners.png",
|
||||
"chessboard_corners",
|
||||
)
|
|
@ -206,13 +206,18 @@ def generate_dependency_graph(
|
|||
if "*" in packages:
|
||||
packages.discard("*")
|
||||
packages.update(
|
||||
str(x) for x in packages_dir.iterdir() if (x / "meta.yaml").is_file()
|
||||
str(x.name) for x in packages_dir.iterdir() if (x / "meta.yaml").is_file()
|
||||
)
|
||||
|
||||
no_numpy_dependents = "no-numpy-dependents" in packages
|
||||
if no_numpy_dependents:
|
||||
packages.discard("no-numpy-dependents")
|
||||
|
||||
packages_exclude = list(filter(lambda pkg: pkg.startswith("!"), packages))
|
||||
for pkg_exclude in packages_exclude:
|
||||
packages.discard(pkg_exclude)
|
||||
packages.discard(pkg_exclude[1:])
|
||||
|
||||
while packages:
|
||||
pkgname = packages.pop()
|
||||
|
||||
|
|
|
@ -494,6 +494,7 @@ def handle_command_generate_args(
|
|||
|
||||
if result:
|
||||
new_args.append(result)
|
||||
|
||||
return new_args
|
||||
|
||||
|
||||
|
@ -527,6 +528,19 @@ def handle_command(
|
|||
if args.pkgname == "scipy":
|
||||
scipy_fixes(new_args)
|
||||
|
||||
# FIXME: For some unknown reason,
|
||||
# opencv-python tries to link a same library (libopencv_world.a) multiple times,
|
||||
# which leads to 'duplicated symbols' error.
|
||||
if args.pkgname == "opencv-python":
|
||||
duplicated_lib = "libopencv_world.a"
|
||||
_new_args = []
|
||||
for arg in new_args:
|
||||
if duplicated_lib in arg and arg in _new_args:
|
||||
continue
|
||||
_new_args.append(arg)
|
||||
|
||||
new_args = _new_args
|
||||
|
||||
returncode = subprocess.run(new_args).returncode
|
||||
if returncode != 0:
|
||||
sys.exit(returncode)
|
||||
|
|