diff --git a/ci/constants.py b/ci/constants.py index df8f302d2a..2d3cac804a 100644 --- a/ci/constants.py +++ b/ci/constants.py @@ -41,6 +41,10 @@ class TargetPython(Enum): 'x3dh', 'pynacl', 'doubleratchet', + # The opencv recipe fails to pass travis tests due to the long processing + # when building it and the lack of console output, so, it's only broken + # for travis, see: https://github.com/kivy/python-for-android/pull/1661 + 'opencv', 'omemo', # requires `libpq-dev` system dependency e.g. for `pg_config` binary 'psycopg2', @@ -74,6 +78,10 @@ class TargetPython(Enum): 'icu', # https://github.com/kivy/python-for-android/issues/1354 'kivent_core', 'kivent_cymunk', 'kivent_particles', 'kivent_polygen', + # The opencv recipe fails to pass travis tests due to the long processing + # when building it and the lack of console output, so, it's only broken + # for travis, see: https://github.com/kivy/python-for-android/pull/1661 + 'opencv', # requires `libpq-dev` system dependency e.g. for `pg_config` binary 'psycopg2', 'netifaces', diff --git a/pythonforandroid/recipes/opencv/__init__.py b/pythonforandroid/recipes/opencv/__init__.py index 169aa88c5e..6932bc225c 100644 --- a/pythonforandroid/recipes/opencv/__init__.py +++ b/pythonforandroid/recipes/opencv/__init__.py @@ -1,4 +1,4 @@ -import os +from os.path import join import sh from pythonforandroid.recipe import NDKRecipe from pythonforandroid.toolchain import ( @@ -9,9 +9,35 @@ class OpenCVRecipe(NDKRecipe): + ''' + .. versionchanged:: 0.7.1 + rewrote recipe to support the python bindings (cv2.so) and enable the + build of most of the libraries of the opencv's package, so we can + process images, videos, objects, photos... + ''' version = '4.0.1' url = 'https://github.com/opencv/opencv/archive/{version}.zip' depends = ['numpy'] + patches = ['patches/p4a_build.patch'] + generated_libraries = [ + 'libopencv_features2d.so', + 'libopencv_imgproc.so', + 'libopencv_stitching.so', + 'libopencv_calib3d.so', + 'libopencv_flann.so', + 'libopencv_ml.so', + 'libopencv_videoio.so', + 'libopencv_core.so', + 'libopencv_highgui.so', + 'libopencv_objdetect.so', + 'libopencv_video.so', + 'libopencv_dnn.so', + 'libopencv_imgcodecs.so', + 'libopencv_photo.so' + ] + + def get_lib_dir(self, arch): + return join(self.get_build_dir(arch.arch), 'build', 'lib', arch.arch) def get_recipe_env(self, arch): env = super(OpenCVRecipe, self).get_recipe_env(arch) @@ -19,33 +45,91 @@ def get_recipe_env(self, arch): env['ANDROID_SDK'] = self.ctx.sdk_dir return env - def should_build(self, arch): - return True - def build_arch(self, arch): - build_dir = os.path.join(self.get_build_dir(arch.arch), 'build') + build_dir = join(self.get_build_dir(arch.arch), 'build') shprint(sh.mkdir, '-p', build_dir) with current_directory(build_dir): env = self.get_recipe_env(arch) + + python_major = self.ctx.python_recipe.version[0] + python_include_root = self.ctx.python_recipe.include_root(arch.arch) + python_site_packages = self.ctx.get_site_packages_dir() + python_link_root = self.ctx.python_recipe.link_root(arch.arch) + python_link_version = self.ctx.python_recipe.major_minor_version_string + if 'python3' in self.ctx.python_recipe.name: + python_link_version += 'm' + python_library = join(python_link_root, + 'libpython{}.so'.format(python_link_version)) + python_include_numpy = join(python_site_packages, + 'numpy', 'core', 'include') + shprint(sh.cmake, + '-DP4A=ON', '-DANDROID_ABI={}'.format(arch.arch), - '-DCMAKE_TOOLCHAIN_FILE={}/build/cmake/android.toolchain.cmake'.format(self.ctx.ndk_dir), - '-DPYTHON_NUMPY_INCLUDE_DIR={}/numpy/core/include'.format(self.ctx.get_site_packages_dir()), + '-DANDROID_STANDALONE_TOOLCHAIN={}'.format(self.ctx.ndk_dir), + '-DANDROID_NATIVE_API_LEVEL={}'.format(self.ctx.ndk_api), '-DANDROID_EXECUTABLE={}/tools/android'.format(env['ANDROID_SDK']), - '-DBUILD_TESTS=OFF', '-DBUILD_PERF_TESTS=OFF', '-DENABLE_TESTING=OFF', - '-DBUILD_EXAMPLES=OFF', '-DBUILD_ANDROID_EXAMPLES=OFF', - '-DBUILD_opencv_imgproc=OFF', '-DBUILD_opencv_flann=OFF', - '-DBUILD_opencv_python3=ON', + + '-DCMAKE_TOOLCHAIN_FILE={}'.format( + join(self.ctx.ndk_dir, 'build', 'cmake', + 'android.toolchain.cmake')), + # Make the linkage with our python library, otherwise we + # will get dlopen error when trying to import cv2's module. + '-DCMAKE_SHARED_LINKER_FLAGS=-L{path} -lpython{version}'.format( + path=python_link_root, + version=python_link_version), + '-DBUILD_WITH_STANDALONE_TOOLCHAIN=ON', - '-DPYTHON_PACKAGES_PATH={}'.format(self.ctx.get_site_packages_dir()), - '-DANDROID_STANDALONE_TOOLCHAIN={}'.format(self.ctx.ndk_dir), - '-DANDROID_NATIVE_API_LEVEL={}'.format(self.ctx.android_api), + # Force to build as shared libraries the cv2's dependant + # libs or we will not be able to link with our python + '-DBUILD_SHARED_LIBS=ON', + '-DBUILD_STATIC_LIBS=OFF', + + # Disable some opencv's features + '-DBUILD_opencv_java=OFF', + '-DBUILD_opencv_java_bindings_generator=OFF', + # '-DBUILD_opencv_highgui=OFF', + # '-DBUILD_opencv_imgproc=OFF', + # '-DBUILD_opencv_flann=OFF', + '-DBUILD_TESTS=OFF', + '-DBUILD_PERF_TESTS=OFF', + '-DENABLE_TESTING=OFF', + '-DBUILD_EXAMPLES=OFF', + '-DBUILD_ANDROID_EXAMPLES=OFF', + + # Force to only build our version of python + '-DBUILD_OPENCV_PYTHON{major}=ON'.format(major=python_major), + '-DBUILD_OPENCV_PYTHON{major}=OFF'.format( + major='2' if python_major == '3' else '3'), + + # Force to install the `cv2.so` library directly into + # python's site packages (otherwise the cv2's loader fails + # on finding the cv2.so library) + '-DOPENCV_SKIP_PYTHON_LOADER=ON', + '-DOPENCV_PYTHON{major}_INSTALL_PATH={site_packages}'.format( + major=python_major, site_packages=python_site_packages), + + # Define python's paths for: exe, lib, includes, numpy... + '-DPYTHON_DEFAULT_EXECUTABLE={}'.format(self.ctx.hostpython), + '-DPYTHON{major}_EXECUTABLE={host_python}'.format( + major=python_major, host_python=self.ctx.hostpython), + '-DPYTHON{major}_INCLUDE_PATH={include_path}'.format( + major=python_major, include_path=python_include_root), + '-DPYTHON{major}_LIBRARIES={python_lib}'.format( + major=python_major, python_lib=python_library), + '-DPYTHON{major}_NUMPY_INCLUDE_DIRS={numpy_include}'.format( + major=python_major, numpy_include=python_include_numpy), + '-DPYTHON{major}_PACKAGES_PATH={site_packages}'.format( + major=python_major, site_packages=python_site_packages), + self.get_build_dir(arch.arch), _env=env) - shprint(sh.make, '-j', str(cpu_count())) + shprint(sh.make, '-j' + str(cpu_count()), 'opencv_python' + python_major) + # Install python bindings (cv2.so) shprint(sh.cmake, '-DCOMPONENT=python', '-P', './cmake_install.cmake') - sh.cp('-a', sh.glob('./lib/{}/lib*.a'.format(arch.arch)), self.ctx.get_libs_dir(arch.arch)) - self.ctx.get_libs_dir(arch.arch) + # Copy third party shared libs that we need in our final apk + sh.cp('-a', sh.glob('./lib/{}/lib*.so'.format(arch.arch)), + self.ctx.get_libs_dir(arch.arch)) recipe = OpenCVRecipe() diff --git a/pythonforandroid/recipes/opencv/patches/p4a_build.patch b/pythonforandroid/recipes/opencv/patches/p4a_build.patch new file mode 100644 index 0000000000..fd60c01d38 --- /dev/null +++ b/pythonforandroid/recipes/opencv/patches/p4a_build.patch @@ -0,0 +1,33 @@ +This patch allow that the opencv's build command correctly detects our version +of python, so we can successfully build the python bindings (cv2.so) +--- opencv-4.0.1/cmake/OpenCVDetectPython.cmake.orig 2018-12-22 08:03:30.000000000 +0100 ++++ opencv-4.0.1/cmake/OpenCVDetectPython.cmake 2019-01-31 11:33:10.896502978 +0100 +@@ -175,7 +175,7 @@ if(NOT ${found}) + endif() + endif() + +- if(NOT ANDROID AND NOT IOS) ++ if(P4A OR NOT ANDROID AND NOT IOS) + if(CMAKE_HOST_UNIX) + execute_process(COMMAND ${_executable} -c "from distutils.sysconfig import *; print(get_python_lib())" + RESULT_VARIABLE _cvpy_process +@@ -244,7 +244,7 @@ if(NOT ${found}) + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif() + endif() +- endif(NOT ANDROID AND NOT IOS) ++ endif(P4A OR NOT ANDROID AND NOT IOS) + endif() + + # Export return values +--- opencv-4.0.1/modules/python/CMakeLists.txt.orig 2018-12-22 08:03:30.000000000 +0100 ++++ opencv-4.0.1/modules/python/CMakeLists.txt 2019-01-31 11:47:17.100494908 +0100 +@@ -3,7 +3,7 @@ + # ---------------------------------------------------------------------------- + if(DEFINED OPENCV_INITIAL_PASS) # OpenCV build + +-if(ANDROID OR APPLE_FRAMEWORK OR WINRT) ++if(ANDROID AND NOT P4A OR APPLE_FRAMEWORK OR WINRT) + ocv_module_disable_(python2) + ocv_module_disable_(python3) + return()