Skip to content

Commit 522a062

Browse files
archit120vpisarev
andauthored
Merge pull request #2547 from archit120:julia-phase1
Julia Bindings GSoC - phase1 * Julia Phase 1 first commit * a few fixes to compile julia bindings on mac * Readme changes * Allow usage before installation and update README * Add CxxWrap installation and fix Mac build bug * CMake fixes and test refactoring * add tests, fix trailing whitespace, disable array-vec conversion * Fix trailing whiteline and warning * Add module documentation and fix tests in CMake * Change copyright block, CMake variable name * copy => copy_if_different * fix status dump and return lines * Remove Julia_Found outside init.cmake * change indentation Co-authored-by: Vadim Pisarevsky <[email protected]>
1 parent 39ced2a commit 522a062

30 files changed

+2507
-0
lines changed

modules/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,5 @@ $ cmake -D OPENCV_EXTRA_MODULES_PATH=<opencv_contrib>/modules -D BUILD_opencv_<r
7575
- **xobjdetect**: Boosted 2D Object Detection -- Uses a Waldboost cascade and local binary patterns computed as integral features for 2D object detection.
7676

7777
- **xphoto**: Extra Computational Photography -- Additional photo processing algorithms: Color balance / Denoising / Inpainting.
78+
79+
- **julia**: Julia language wrappers with samples and tests.

modules/julia/CMakeLists.txt

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
if(NOT HAVE_JULIA MATCHES "YES")
2+
message(STATUS "Julia not found. Not compiling Julia Bindings. ${HAVE_JULIA}")
3+
ocv_module_disable(julia)
4+
elseif(NOT PYTHON3_EXECUTABLE)
5+
# message(WARNING "Python 3 required for Julia bindings...disabling")
6+
# ocv_module_disable(julia)
7+
endif()
8+
9+
# WARN_MIXED_PRECISION
10+
macro(WARN_MIXED_PRECISION COMPILER_BITNESS JULIA_BITNESS)
11+
set(MSG "Your compiler is ${COMPILER_BITNESS}-bit")
12+
set(MSG "${MSG} but your version of Julia is ${JULIA_BITNESS}-bit.")
13+
set(MSG "${MSG} To build Julia bindings, please switch to a ${JULIA_BITNESS}-bit compiler.")
14+
message(WARNING ${MSG})
15+
endmacro()
16+
17+
18+
math(EXPR ARCH "${CMAKE_SIZEOF_VOID_P} * 8")
19+
if (${ARCH} EQUAL 32 AND ${Julia_WORD_SIZE} MATCHES "64")
20+
warn_mixed_precision("32" "64")
21+
ocv_module_disable(julia)
22+
return()
23+
elseif (${ARCH} EQUAL 64 AND NOT ${Julia_WORD_SIZE} MATCHES "64")
24+
warn_mixed_precision("64" "32")
25+
ocv_module_disable(julia)
26+
return()
27+
endif()
28+
29+
if(NOT JlCxx_DIR)
30+
execute_process(
31+
COMMAND "${Julia_EXECUTABLE}" --startup-file=no -e "using CxxWrap; print(CxxWrap.CxxWrapCore.prefix_path())"
32+
OUTPUT_VARIABLE JlCxx_DIR
33+
)
34+
endif()
35+
36+
if(JlCxx_DIR)
37+
if(EXISTS ${JlCxx_DIR}/JlCxxConfig.cmake)
38+
else()
39+
message(STATUS "JlCxx found but not source build - disabling Julia module")
40+
ocv_module_disable(julia)
41+
endif()
42+
endif()
43+
44+
find_package(JlCxx QUIET)
45+
46+
if(NOT JlCxx_FOUND)
47+
message(STATUS "JlCxx not found")
48+
ocv_module_disable(julia)
49+
return()
50+
else()
51+
message(STATUS "JlCxx_DIR: ${JlCxx_DIR}")
52+
endif()
53+
54+
set(JlCxx_DIR "${JlCxx_DIR}" CACHE STRING ADVANCED)
55+
set(HAVE_JULIA "YES" CACHE STRING ADVANCED)
56+
57+
58+
set(the_description "The Julia bindings")
59+
ocv_add_module(
60+
julia
61+
opencv_core
62+
opencv_imgproc
63+
opencv_imgcodecs
64+
opencv_objdetect
65+
opencv_highgui
66+
opencv_features2d
67+
opencv_videoio
68+
)
69+
70+
ocv_glob_module_sources()
71+
ocv_module_include_directories()
72+
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wmissing-prototypes -Wmissing-declarations)
73+
74+
ocv_add_library(${the_module} SHARED ${OPENCV_MODULE_${the_module}_HEADERS} ${OPENCV_MODULE_${the_module}_SOURCES})
75+
76+
if(NOT JULIA_PKG_INSTALL_PATH)
77+
set(JULIA_PKG_INSTALL_PATH ${CMAKE_BINARY_DIR})
78+
endif()
79+
set(JULIA_PKG_INSTALL_PATH_HOOK ${JULIA_PKG_INSTALL_PATH} CACHE STRING "" FORCE)
80+
mark_as_advanced(JULIA_PKG_INSTALL_PATH_HOOK)
81+
82+
83+
message(STATUS "Installing ${the_module} bindings at ${JULIA_PKG_INSTALL_PATH}")
84+
85+
86+
install(DIRECTORY package/ DESTINATION ${JULIA_PKG_INSTALL_PATH})
87+
install(TARGETS ${the_module} LIBRARY DESTINATION ${JULIA_PKG_INSTALL_PATH}/OpenCV/src/lib)
88+
89+
90+
if(JULIA_PKG_INSTALL_ENV)
91+
set(JULIA_PKG_EXECS "Pkg.activate(${JULIA_PKG_INSTALL_ENV});${JULIA_PKG_EXECS}")
92+
endif()
93+
94+
95+
set(JULIA_COMMAND "\"${Julia_EXECUTABLE}\" -e \"using Pkg; ${JULIA_PKG_EXECS}\"")
96+
# message(STATUS "Installing Julia bindings using ${JULIA_COMMAND}")
97+
98+
99+
if(NOT INSTALL_CREATE_DISTRIB)
100+
install(CODE "
101+
set(JULIA_PKG_PATH \"${JULIA_PKG_INSTALL_PATH}/OpenCV\")
102+
execute_process(COMMAND \"${Julia_EXECUTABLE}\" ${CMAKE_CURRENT_LIST_DIR}/package/install_package.jl \${JULIA_PKG_PATH} OUTPUT_VARIABLE JULIA_INSTALL_OUT)
103+
104+
105+
# set(JULIA_PKG_EXECS \"Pkg.develop(PackageSpec(path=\\\\\\\"${JULIA_PKG_INSTALL_PATH}/OpenCV\\\\\\\"));\")
106+
107+
# if(${JULIA_PKG_INSTALL_ENV})
108+
# set(JULIA_PKG_EXECS \"Pkg.activate(${JULIA_PKG_INSTALL_ENV});\${JULIA_PKG_EXECS}\")
109+
# endif()
110+
# set(JULIA_COMMAND \"\\\"using Pkg; \${JULIA_PKG_EXECS}\\\"\")
111+
112+
# message(STATUS \"Installing Julia bindings using \${JULIA_COMMAND}\")
113+
114+
# execute_process(COMMAND \"${Julia_EXECUTABLE}\" -e \${JULIA_COMMAND} OUTPUT_VARIABLE JULIA_INSTALL_OUT)
115+
116+
message(STATUS \"Install output: \${JULIA_INSTALL_OUT}\")
117+
")
118+
endif()
119+
120+
# set(JULIA_COMMAND \"\"${Julia_EXECUTABLE}\" -e \"using Pkg; \${JULIA_PKG_EXECS}\"\")
121+
122+
# ocv_create_module()
123+
124+
#ocv_add_accuracy_tests()
125+
#ocv_add_perf_tests()
126+
#ocv_add_samples()
127+
128+
message(STATUS ${OPENCV_MODULE_${the_module}_DEPS_TO_LINK})
129+
130+
ocv_target_link_libraries(${the_module} PUBLIC ${OPENCV_MODULE_${the_module}_DEPS_TO_LINK}
131+
INTERFACE ${OPENCV_MODULE_${the_module}_DEPS_TO_LINK}
132+
)
133+
ocv_target_link_libraries(${the_module} PUBLIC ${OPENCV_MODULE_${the_module}_DEPS_EXT}
134+
INTERFACE ${OPENCV_MODULE_${the_module}_DEPS_EXT}
135+
)
136+
ocv_target_link_libraries(${the_module} PRIVATE ${OPENCV_LINKER_LIBS} ${OPENCV_HAL_LINKER_LIBS} ${IPP_LIBS} ${ARGN})
137+
138+
ocv_target_link_libraries(${the_module} JlCxx::cxxwrap_julia)
139+
ocv_target_link_libraries(${the_module} JlCxx::cxxwrap_julia_stl)
140+
141+
# targets# opencv_julia_sources --> opencv_julia
142+
143+
add_custom_command(TARGET ${the_module}
144+
POST_BUILD
145+
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/package/OpenCV ${CMAKE_BINARY_DIR}/OpenCV
146+
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${the_module}> ${CMAKE_BINARY_DIR}/OpenCV/src/lib/libopencv_julia
147+
COMMENT "Copying over julia package"
148+
)
149+
150+
151+
if (BUILD_TESTS)
152+
add_subdirectory(test)
153+
endif()

modules/julia/LICENSE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The code is a part of OpenCV and is distributed under Apache 2 license.
2+
See https://www.apache.org/licenses/LICENSE-2.0
3+
Copyright (C) 2020 by Archit Rungta.

modules/julia/README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
OpenCV Julia Bindings
2+
============================
3+
This module contains some limited functionality that allows OpenCV functions be used from Julia. Upon installation the binding files are automatically registered with Julia's package manager like any normal package.
4+
5+
This module requires Julia 1.4 and the CxxWrap.jl 0.10.
6+
7+
CxxWrap Installation
8+
----
9+
Installation of CxxWrap is like any other Julia Package. Just start the REPL. Hit `]` and then type `add CxxWrap`.
10+
11+
```
12+
$ julia
13+
...
14+
julia> ]
15+
pkg> add CxxWrap
16+
```
17+
18+
For now, Julia module is only compatible with Ubuntu and MacOS. Also, you must use a source build of [libcxxwrap-julia](https://github.com/JuliaInterop/libcxxwrap-julia). Follow the link for instructions on how to do that.
19+
20+
Build
21+
-----
22+
The Julia module is fully integrated into the OpenCV build system. While compiling add this to your command line `-DWITH_JULIA=ON`. If cmake finds a Julia executable available on the host system while configuring OpenCV, it will attempt to generate Julia wrappers for all OpenCV modules. If cmake is having trouble finding your Julia installation, you can explicitly point it to the Julia executable by defining the `Julia_EXECUTABLE` variable. For example:
23+
24+
cmake -DWITH_JULIA=ON -DJulia_EXECUTABLE=/home/user/julia-1.4.1/bin ..
25+
26+
If you prefer using the gui version of cmake (cmake-gui), you can use the *Add Entry* option in the GUI to manually add the *path* variable `Julia_EXECUTABLE`.
27+
28+
29+
Install
30+
-------
31+
By default the Julia package is installed in `CMAKE_BINARY_DIR`, you can change this by setting the `JULIA_PKG_INSTALL_PATH` cmake variable. The package is automatically registered with the Julia package manager.
32+
33+
Run
34+
---
35+
36+
In order to use the bindings, simply type
37+
```bash
38+
$ julia
39+
...
40+
julia> using OpenCV
41+
```
42+
43+
Note that this works only if you called `make install`. To run the wrapper package without making the installation target you must first set the environment variable `JULIA_LOAD_PATH` to the directory containing the OpenCV package. For example if in the build directory
44+
```bash
45+
$ export JULIA_LOAD_PATH=$PWD/OpenCV
46+
$ julia
47+
...
48+
julia> using OpenCV
49+
```
50+
51+
The Julia package does not export any symbols so all functions/structs/constants must be prefixed with OpenCV
52+
```Julia
53+
using OpenCV
54+
const cv = OpenCV
55+
img = cv.imread('cameraman.tif');
56+
57+
cv.imshow("window name", img)
58+
59+
cv.waitKey(Int32(0))
60+
```
61+
62+
Finally, because Julia does not support OOP paradigm some changes had to be made. To access functions like `obj.function(ARGS)` you should instead use `function(obj, ARGS)`. The below example of reading frames from a VideoCapture should make it more clear.
63+
64+
```Julia
65+
cap = OpenCV.VideoCapture(Int32(0))
66+
ret, img = OpenCV.read(cap)
67+
```
68+
69+
Instead of calling `cap.read()`, we called `OpenCV.read(cap)`
70+
71+
Another change is that all integer and float constants might need to prefixed with appropriate type constructor. This is needed because OpenCV functions accept 32-bit integers/floats but integer and float constants in Julia are sized based on the whether Julia is running in 64bit or 32bit mode.
72+
73+
------------------------------------------------------------------
74+
75+
76+
Usage
77+
=========
78+
This section has some more information about how to use the Julia bindings.
79+
80+
The function signatures are identical to Python bindings except the previously mentioned OOP exception. All functions that will accept a Mat/numpy array in C++/Python signatures will instead accept `OpenCV.InputArray` type in the Julia functions. `OpenCV.InputArray` is a union type between `CxxMat` and `AbstractArray{T, 3}`. As such, you can pass any arrays generated by any Julia function directly into OpenCV. If the AbstractArray is strided and has appropriate strides, the bindings will try to directly pass the memory region to OpenCV functions. However, if that's not possible then the array will first be copied to a `DenseArray` and then passed to OpenCV. The previously mentioned `CxxMat` is a black-box pointer type and should never be needed by users.
81+
82+
The output arrays of all OpenCV functions are of the type `OpenCV.Mat{T}`. Currently, all array input and output is restricted to 3D only (2D Mat and an additional dimension for color channels). The `OpenCV.Mat` type inherits from `AbstractArray{T, 3}` and can be directly passed to any Julia function that accepts AbstractArray types. It internally maintains a pointer to the original C++ `Mat` type to make sure that the memory is never deallocated. However, if you copy the `OpenCV.Mat` type object then the pointer is not copied and array moves to a Julia owned memory space.
83+
84+
All other types map directly to the corresponding types on C++. Unlike Python, `Point`, `Size`, `Rect` etc are represented not as tuples but as appropriate objects like `OpenCV.Point{Float32}` and so on. However, `Scalar` types are a tuple of numbers where the tuple has a size of 1-4.

0 commit comments

Comments
 (0)