twistedfall / opencv-rust

Rust bindings for OpenCV 3 & 4
MIT License
2k stars 162 forks source link

contrib depends on undeclared symbols on windows #94

Closed amedeedaboville closed 4 years ago

amedeedaboville commented 4 years ago

Hi, thanks a lot for opencv-rust. It's made me able to use opencv in rust, and on windows no less.

I'm on windows, (nightly-x86_64-pc-windows-msvc), using the contrib feature along with buildtime-bindgen for aruco marker detection. I've installed opencv with vcpkg, and have installed the [contrib] sub-package there.

Non-contrib features work fine. But using a contrib feature (aruco), when I compile my example on windows (even in release mode) I get

note: libopencv-68ebf1f28df599f0.rlib(aruco.o) : error LNK2019: unresolved external symbol "void __cdecl cv::aruco::_drawPlanarBoardImpl(class cv::aruco::Board *,class cv::Size_<int>,class cv::_Output

and a number of other missing symbols (some of them are cv::face::FacemarkLBF::BBox::BBox, which were mentioned in issue #75, but I believe are part of a bigger issue detailled here)

I think what's happening is that these symbols are not meant to be exported(at least on windows). On windows, symbols are not exported by default, and one needs to add __declspec(dllexport) to ones functions for them to be exported.

Apparently, this is what the CV_EXPORTS_W macro does:

#if (defined WIN32 || defined _WIN32 || defined WINCE) && defined CVAPI_EXPORTS
# define CV_EXPORTS __declspec(dllexport)
#else
# define CV_EXPORTS
#endif

Now, for the aruco wrapper, aruco.hpp contains:

CV_EXPORTS_W void drawPlanarBoard(const Ptr<Board> &board, Size outSize, OutputArray img,
                                  int marginSize = 0, int borderBits = 1);
void _drawPlanarBoardImpl(Board *board, Size outSize, OutputArray img,
                          int marginSize = 0, int borderBits = 1);

And the second function is not CV_EXPORTS_W.

The same thing is happening in the face module, where if we look at https://github.com/opencv/opencv_contrib/blob/master/modules/face/include/opencv2/face/facemarkLBF.hpp#L96 the BBox class is not exported.

I read issue #75, and OP there seems to have resolved it by installing precompiled binary files, which is curious as to why the symbols are exported there. I'm wondering if they were cross compiled with gcc? You mentioned in that issue that the symbols are there on linux, but there is a platform difference here where they aren't there on windows by default.

I'm trying to work around this by having vcpkg use the CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS flag, which is discouraged, but I haven't filtered the flag down in the right place yet.

Does this make sense or do you think I am wrong somewhere? I think in the future, things like https://github.com/twistedfall/opencv-rust/blob/af4759f937865473acb7fac870a09ea710f440b3/src/opencv/hub/aruco.rs#L92 shouldn't use non EXPORT_W symbols.

For now, a workaround (I'm not familiar with opencv-rust enough yet to know if this is possible) could be to only link to these functions on non-windows with #[cfg!(not(target_os = "windows"))]

Thanks.

twistedfall commented 4 years ago

Thank you for such a thorough investigation! It sounds like you’re onto something there. I will be able l check it in more detail some time tomorrow. For now you might want to try the clang branch. It has a completely rewritten binding generator, and it uses libclang instead of python script. And it only exports symbols marked as such (with some exceptions). So you'll need to have llvm/clang installed and maybe manually point clang_sys crate to it. I haven’t been able to make on work on windows yet, but you might have more luck.

twistedfall commented 4 years ago

Should be fixed in v0.30.0. Please note that this version comes with the totally rewritten binding generator and you will need llvm installed instead of python to be able to correctly regenerate bindings. Please see the changelog and readme for more details.

amedeedaboville commented 4 years ago

Wow, amazing, thanks a lot! That binding generator looks like quite a piece work! Thanks for doing this complex task of binding opencv to Rust.