diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7e48007 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +.gitattributes export-ignore +.github/ export-ignore +ci-docker.nix export-ignore + +CMakeLists.txt export-subst diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fca30aa --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,50 @@ +# DEV_HASH: $Format:%H$ + +cmake_minimum_required(VERSION 3.12) +cmake_policy(SET CMP0048 NEW) + +include(CMakePackageConfigHelpers) + + +project(opencv-swig VERSION 1.0.0 LANGUAGES NONE) + +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR CMake) +else() + set(DEF_INSTALL_CMAKE_DIR "lib/cmake/OpenCV-SWIG") + set(DEF_INSTALL_SWIGLIB_DIR "share/swig/any") +endif() + +set( + OpenCV-SWIG_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH + "Installation directory for CMake files" +) + +set( + OpenCV-SWIG_LIBDIR ${DEF_INSTALL_SWIGLIB_DIR} CACHE PATH + "Installation directory for SWIG library files" +) + +configure_package_config_file(OpenCV-SWIGConfig.cmake.in + "${PROJECT_BINARY_DIR}/OpenCV-SWIGConfig.cmake" + INSTALL_DESTINATION ${OpenCV-SWIG_CMAKE_DIR} + PATH_VARS OpenCV-SWIG_LIBDIR +) +write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/OpenCV-SWIGConfigVersion.cmake" + COMPATIBILITY SameMajorVersion + ARCH_INDEPENDENT +) + +install( + FILES + "${PROJECT_BINARY_DIR}/OpenCV-SWIGConfig.cmake" + "${PROJECT_BINARY_DIR}/OpenCV-SWIGConfigVersion.cmake" + DESTINATION ${OpenCV-SWIG_CMAKE_DIR} +) + +install( + DIRECTORY lib/ + DESTINATION ${OpenCV-SWIG_LIBDIR} + FILES_MATCHING PATTERN "*.i" +) diff --git a/OpenCV-SWIGConfig.cmake.in b/OpenCV-SWIGConfig.cmake.in new file mode 100644 index 0000000..03def5a --- /dev/null +++ b/OpenCV-SWIGConfig.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +set_and_check(OpenCV-SWIG_INCLUDE_DIRS "@PACKAGE_OpenCV-SWIG_LIBDIR@") + +check_required_components(OpenCV-SWIG) diff --git a/README.md b/README.md index 663301f..7402aed 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,15 @@ The OpenCV itself has a Python binding exposing its API, this library is not tha ## Hello, world! -The use case of this library is when someone is writing a C++ library, which uses OpenCV -types on its API, and desires to create a Python binding to that library. +The use case for this library is when someone writing a C++ library, which uses OpenCV +types on its public API, wants create a Python binding to that library. -Let's say you have built some C++ library `MyLib` having a `my_lib.hpp` header: +Let's suppose you have built a C++ library named `MyLib` having a `my_lib.hpp` header: ```C++ +#ifndef MY_LIB_HPP_INCLUDED +#define MY_LIB_HPP_INCLUDED + #include #include @@ -31,11 +34,13 @@ inline auto getImage() -> cv::Mat3b { return cv::Mat3b(3, 5); } + +#endif /* MY_LIB_HPP_INCLUDED */ ``` -To generate a `MyLib` Python binding, everything you need is a `my_lib.i` SWIG file as: +To generate a `MyLib` Python binding all you need is a `my_lib.i` SWIG file as: -``` +```swig %module my_lib %include @@ -48,25 +53,58 @@ To generate a `MyLib` Python binding, everything you need is a `my_lib.i` SWIG f %include "my_lib.hpp" ``` -and calling SWIG as in: - -```shell -swig -I -I -python -c++ my_lib.i +and a `CMakeLists.txt`: + +```cmake +cmake_minimum_required(VERSION 3.0) + +cmake_policy(SET CMP0074 NEW) +cmake_policy(SET CMP0078 NEW) +cmake_policy(SET CMP0086 NEW) + +project(MyLib) + +find_package(OpenCV-SWIG REQUIRED) +find_package(SWIG REQUIRED COMPONENTS python) +find_package(Boost REQUIRED) +find_package(OpenCV REQUIRED core) +find_package(Python REQUIRED COMPONENTS Interpreter Development) + +include(UseSWIG) + +set_property(SOURCE my_lib.i PROPERTY CPLUSPLUS ON) +swig_add_library(my_lib LANGUAGE python SOURCES my_lib.i my_lib.hpp) +set_property( + TARGET my_lib + PROPERTY SWIG_INCLUDE_DIRECTORIES + ${OpenCV-SWIG_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS} +) + +target_include_directories(my_lib + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${Python_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} +) + +target_link_libraries(my_lib + ${OpenCV_LIBRARIES} +) ``` -A `my_lib_wrap.cxx` and `my_lib.py` files will be generated containing the binding source -code. - -On a Linux system the `my_lib_wrap.cxx` can be compiled to a `_my_lib.so` (as expected by -the Python interpreter) with the command: +After calling: ```shell -g++ -shared -fpic $(pkg-config --cflags --libs python3) $(pkg-config --cflags --libs opencv4) my_lib_wrap.cxx -o _my_lib.so +mkdir build +cd build +cmake .. -DOpenCV-SWIG_ROOT= +make ``` -Notice that the pkg-config package names (`python3` and `opencv4`) can be slightly -different on your particular system. That will create a Python module `my_lib` on the -current directory, and the code bellow should work just fine: +A Python module `my_lib.py` will be created on the current directory, and the code bellow +should work just fine: ```Python import my_lib @@ -84,13 +122,43 @@ cv_img2 = my_lib.Mat3b.from_array(np_arr) ## Install -To install, just copy all files under the `lib` directory to the same directory as the -SWIG Python modules (e.g.:`/usr/share/swig/4.0.1/python`). Another option is to copy the -`lib` directory to another path, `/home/bla/swig_libs` for example, and call SWIG with a -`-I` argument. Following the above example: +To install, execute on the package root dir: +```shell +mkdir build +cd build +cmake .. -DCMAKE_INSTALL_PREFIX= ``` -swig -I/home/bla/swig_libs -c++ -python my_lib.i + +### Nix + +If using [Nix](https://nixos.org/), any Git commit can be used as `buildInputs` with: + +```text +opencv-swig = pkgs.callPackage ( + fetchTarball https://github.com/renatoGarcia/opencv-swig/archive/.tar.gz +) {}; +``` + +To built the [Hello, World!](#hello-world) example, a `shell.nix` as below will set a +correct environment: + +```nix +let + pkgs = import {}; + opencv-swig = pkgs.callPackage ( + fetchTarball https://github.com/renatoGarcia/opencv-swig/archive/v1.0.0.tar.gz + ) {}; + +in pkgs.mkShell { + buildInputs = [ + opencv-swig + pkgs.boost + pkgs.swig + pkgs.cmake + pkgs.pythonPackages.opencv + ]; +} ``` ## Supported OpenCV versions diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..40d38a1 --- /dev/null +++ b/default.nix @@ -0,0 +1,46 @@ +{ + stdenv + , fetchgit + , lib + , cmake +}: + +let + headerSrc = builtins.readFile ./CMakeLists.txt; + + versionNumbersRegex = ".*project[[:space:]]*\\(.*[[:space:]]VERSION[[:space:]]+([[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+).*\\).*"; + + versionNumbers = builtins.head (builtins.match versionNumbersRegex headerSrc); + + devHash_ = builtins.match ".*DEV_HASH: ([[:xdigit:]]+).*" headerSrc; + devHash = if devHash_ != null then builtins.head devHash_ else null; + + repo = builtins.fetchGit { + url = https://github.com/renatoGarcia/opencv-swig.git; + ref = "cmake_nix"; + rev = devHash; + }; + + version = + versionNumbers + + ( + if devHash != null + then "+dev." + toString repo.revCount + else "+local_repo" + ); + +in stdenv.mkDerivation { + pname = "opencv-swig"; + inherit version; + + src = ./.; + + nativeBuildInputs = [ cmake ]; + + meta = with lib; { + homepage = "https://github.com/renatoGarcia/opencv-swig"; + description = "A SWIG library for OpenCV types"; + license = licenses.bsd3; + maintainers = with maintainers; [ renatoGarcia ]; + }; +} diff --git a/hello_world/CMakeLists.txt b/hello_world/CMakeLists.txt new file mode 100644 index 0000000..d564893 --- /dev/null +++ b/hello_world/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.0) + +cmake_policy(SET CMP0074 NEW) +cmake_policy(SET CMP0078 NEW) +cmake_policy(SET CMP0086 NEW) + +project(MyLib) + +find_package(OpenCV-SWIG REQUIRED) +find_package(SWIG REQUIRED COMPONENTS python) +find_package(Boost REQUIRED) +find_package(OpenCV REQUIRED core) +find_package(Python REQUIRED COMPONENTS Interpreter Development) + +include(UseSWIG) + +set_property(SOURCE my_lib.i PROPERTY CPLUSPLUS ON) +swig_add_library(my_lib LANGUAGE python SOURCES my_lib.i my_lib.hpp) +set_property( + TARGET my_lib + PROPERTY SWIG_INCLUDE_DIRECTORIES + ${OpenCV-SWIG_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS} +) + +target_include_directories(my_lib + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${Python_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} +) + +target_link_libraries(my_lib + ${OpenCV_LIBRARIES} +) diff --git a/hello_world/my_lib.hpp b/hello_world/my_lib.hpp new file mode 100644 index 0000000..9c0b75a --- /dev/null +++ b/hello_world/my_lib.hpp @@ -0,0 +1,17 @@ +#ifndef MY_LIB_HPP_INCLUDED +#define MY_LIB_HPP_INCLUDED + +#include +#include + +inline auto moveTo(cv::Point const& p) -> void +{ + std::cout << "cv::Point moved" << std::endl; +} + +inline auto getImage() -> cv::Mat3b +{ + return cv::Mat3b(3, 5); +} + +#endif /* MY_LIB_HPP_INCLUDED */ diff --git a/hello_world/my_lib.i b/hello_world/my_lib.i new file mode 100644 index 0000000..6c8ac84 --- /dev/null +++ b/hello_world/my_lib.i @@ -0,0 +1,10 @@ +%module my_lib + +%include +%cv_instantiate_all_defaults + +%{ + #include "my_lib.hpp" +%} + +%include "my_lib.hpp" diff --git a/hello_world/shell.nix b/hello_world/shell.nix new file mode 100644 index 0000000..728cca4 --- /dev/null +++ b/hello_world/shell.nix @@ -0,0 +1,15 @@ +let + pkgs = import {}; + opencv-swig = pkgs.callPackage ( + fetchTarball https://github.com/renatoGarcia/opencv-swig/archive/v1.0.0.tar.gz + ) {}; + +in pkgs.mkShell { + buildInputs = [ + opencv-swig + pkgs.boost + pkgs.swig + pkgs.cmake + pkgs.pythonPackages.opencv + ]; +}