17

What is the best way to build C++ code that uses the OpenCV library using Bazel? I.e., what would the BUILD rules look like?

How should be the WORKSPACE and BUILD files look like in order to compile the following code using bazel:


#include "opencv2/opencv.hpp"
#include "iostream"

int main(int, char**) {
  using namespace cv;
  VideoCapture cap(0);
  Mat save_img; cap >> save_img;
  if(save_img.empty())
  {
    std::cerr << "ERROR >> Something is wrong with camera..." << std::endl;
  }
  imwrite("test.jpg", save_img);
  return 0;
}

Keivan
  • 1,300
  • 1
  • 16
  • 29
John Zhang
  • 518
  • 1
  • 5
  • 12

5 Answers5

33

There are a couple of options. The easiest way is probably to install locally in the way the OpenCV site recommends:

git clone https://github.com/Itseez/opencv.git
cd opencv/
mkdir build install
cd build
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/path/to/opencv/install ..
make install

Then add the following to your WORKSPACE file:

new_local_repository(
    name = "opencv",
    path = "/path/to/opencv/install",
    build_file = "opencv.BUILD",
)

Create opencv.BUILD in the same directory as WORKSPACE with the following:

cc_library(
    name = "opencv",
    srcs = glob(["lib/*.so*"]),
    hdrs = glob(["include/**/*.hpp"]),
    includes = ["include"],
    visibility = ["//visibility:public"], 
    linkstatic = 1,
)

Then your code can depend on @opencv//:opencv to link in the .so's under lib/ and reference the headers under include/.

However, this isn't very portable. If you want a portable solution (and you're feeling ambitious), you could add the OpenCV git repo to your workspace and download & build it. Something like:

# WORKSPACE
new_git_repository(
    name = "opencv",
    remote = "https://github.com/Itseez/opencv.git",
    build_file = "opencv.BUILD",
    tag = "3.1.0",
)

And make opencv.BUILD something like:

cc_library(
    name = "core",
    visibility = ["//visibility:public"],
    srcs = glob(["modules/core/src/**/*.cpp"]),
    hdrs = glob([
        "modules/core/src/**/*.hpp", 
        "modules/core/include/**/*.hpp"]
    ) + [":module-includes"],
)

genrule(
    name = "module-includes",
    cmd = "echo '#define HAVE_OPENCV_CORE' > $@",
    outs = ["opencv2/opencv_modules.hpp"],
)

...

Then your code could depend on more specific targets, e.g., @opencv//:core.

As a third option, you declare both cmake and OpenCV in your WORKSPACE file and use a genrule to run cmake on OpenCV from within Bazel.

Rick Smith
  • 9,031
  • 15
  • 81
  • 85
kris
  • 23,024
  • 10
  • 70
  • 79
  • 1
    Thanks so much! I went with the easiest option (the first one). Some minor corrections if anyone is using OpenCV 3.1.0: cc_library( name = "opencv", srcs = glob([ "lib/*.so*" ]), hdrs = glob([ "include/**/*.hpp" ]), includes = [ "include" ], visibility = [ "//visibility:public" ], linkstatic = 1, ) – John Zhang Jan 30 '16 at 15:15
  • @kristina the second solution is not portable neither. there is a lot of missing files that are generated when running cmake, not only `opencv_modules.hpp` – Ghilas BELHADJ Feb 14 '17 at 10:15
  • @Ghilas I think you are missing what I meant by "portable:" if your build works for you, you can hand it to your coworker and it'll work on their machine, too. If the build doesn't expose the artifacts you need, it's not a portability problem. – kris Feb 14 '17 at 14:56
  • @kristina The code in `opencv.BUILD` don't work as is, even if my code only needs `@opencv/:core`, that's what I meant. I've found a more complete code [here](https://github.com/hatstand/symmetrical-octo-fiesta/blob/28714db06cfbb4574efa00d395facbb3e53024b6/BUILD.opencv) that works. Now the only thing that I'm looking for is how to expose the JNI functions of OpenCV as I use the library from Java too. Any Idea ? – Ghilas BELHADJ Feb 14 '17 at 15:13
  • @kristina on mac I'm getting a linker error like this. Any idea? ```Undefined symbols for architecture x86_64: "cv::Mat::deallocate()", referenced from ...``` – Keven Wang Nov 29 '17 at 02:55
  • @kristina. I follow your advice and build a project. But got a very wired error: The building process finish normally. but when I try to run the program. I have this error: bazel-bin/main/hello-world: error while loading shared libraries: libhdf5.so.10: cannot open shared object file: No such file or directory. I don't even use opencv in my code, just add opencv add deps. here is my code, just a very simple helloworld program. can you take a look? https://github.com/scotthuang1989/tools_study/tree/master/bazel/opencv_app_bazel – scott huang Dec 24 '17 at 06:33
  • This cmake command works: `cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..` – sizzle Aug 28 '18 at 16:04
  • I had some of the errors above and it seems that's its related to probably: opencv versions or different machine OSs. Given that, it is important that we check the contents and routes we are defining in the `WORKSPACE` and `opencv.BUILD` files. For example, as I'm writing this comment in 2020 in a machine running macOS, opencv's srcs are `.dylib`, different from `.so`, and some of the headers have `.h` extension not only `.hpp`, and so on. So one recommendation would be to check every route in the `install` folder and see if everything is matching our definitions. – gonzarodriguezt Mar 10 '20 at 20:04
  • the second solution is not explained well, and it doesn't work. – Zaikun Xu Jun 25 '20 at 09:07
  • "Prefer `http_archive` to `git_repository` and `new_git_repository`. The reasons are: Git repository rules depend on system `git(1)` whereas the HTTP downloader is built into Bazel and has no system dependencies, `http_archive` supports a list of `urls` as mirrors, and `git_repository` supports only a single `remote`. `http_archive` works with the repository cache, but not `git_repository` " [Best Practices](https://docs.bazel.build/versions/main/external.html) – user0221441 Aug 03 '21 at 11:56
7

Here is an up2date solution that works with the current set of bazel(v3.1.0). In this little project i wanted to build a C++ program that depends on the newest openCV release (4.3.0), but only on a selected set of modules (core,highgui,imgcodecs,imgproc).

No local installed openCV required, bazel loads the needed files from github (although it works even when there is an old version of openCV installed):

Content of /WORKSPACE file:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

all_content = """filegroup(name = "all", srcs = glob(["**"]), visibility = ["//visibility:public"])"""

http_archive(
    name = "opencv",
    build_file_content = all_content,
    strip_prefix = "opencv-4.3.0",
    urls = ["https://github.com/opencv/opencv/archive/4.3.0.zip"],
)

http_archive(
    name = "rules_foreign_cc",
    strip_prefix = "rules_foreign_cc-master",
    url = "https://github.com/bazelbuild/rules_foreign_cc/archive/master.zip",
)

load("@rules_foreign_cc//:workspace_definitions.bzl", "rules_foreign_cc_dependencies")

rules_foreign_cc_dependencies()

Content of /BUILD file:

load("@rules_foreign_cc//tools/build_defs:cmake.bzl", "cmake_external")

cmake_external(
    name = "opencv",
    cmake_options = [
        "-GNinja",
        "-DBUILD_LIST=core,highgui,imgcodecs,imgproc",
    ],
    lib_source = "@opencv//:all",
    make_commands = [
        "ninja",
        "ninja install",
    ],
    out_include_dir = "include/opencv4",
    shared_libraries = [
        "libopencv_core.so",
        "libopencv_highgui.so",
        "libopencv_imgcodecs.so",
        "libopencv_imgproc.so",
    ],
    visibility = ["//visibility:public"],
)

And finally, your target that depends on opencv, in my case a file /opencv/BUILD:

cc_binary(
    name = "opencv",
    srcs = ["opencv.cpp"],
    data = [
      "LinuxLogo.jpg",
      "WindowsLogo.jpg",
    ],
    deps = ["//:opencv"],
)

If you want to try it out, here is the rest: blackliner/automata

git clone https://github.com/blackliner/automata.git
cd automata
bazel build ...
  • Tried to use it under Windows 10 with CMake 3.17.2 building for Visual Studio 2019 Win64 (i.e. `cmake_options=[-G"Visual Studio 16 2019" -A"Win64"`], make_commands = ["MSBuild.exe INSTALL.vcxproj"]). Does not work with PowerShell. Switched to MSys2 shell - does also not work - I guess this was tested using Linux? – Vertexwahn May 19 '20 at 19:41
  • Ok, wait, CMake? This is about using Bazel instead of CMake. What exactly did you try to do? And yes, i used Ubuntu 18.04 LTS. – Florian Berchtold May 21 '20 at 15:13
  • `cmake_external` calls CMake. I used Bazel 3.1.0 to test it under Windows 10. But cmake_external works currently not for Powershell since it uses bash scripts. Even on msys2 shell I run into problems... – Vertexwahn May 21 '20 at 16:21
3

I succeed with @kristina's first option.

  1. Install opencv:

    git clone https://github.com/Itseez/opencv.git
    
    cd opencv/
    
    mkdir build install
    
    cd build
    
    cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
    
    make install
    
  2. Change WORKSPACE file (at tensorflow/WORKSPACE cloned from github)

    new_local_repository(
    
    name = "opencv",
    
    path = "/usr/local",
    
    build_file = "opencv.BUILD",
    
    )
    
  3. Make opencv.BUILD file at the same place as WORKSPACE file:

    cc_library(
    
    name = "opencv",
    
    srcs = glob(["lib/*.so*"]),
    
    hdrs = glob(["include/**/*.hpp"]),
    
    includes = ["include"],
    
    visibility = ["//visibility:public"], 
    
    linkstatic = 1,
    
    )
    
  4. You may have to config the opencv libs path:

a. Make sure you have /etc/ld.so.conf.d/opencv.conf file with content:

    /usr/local/lib

b. Run the command:

    sudo ldconfig -v
thor
  • 21,418
  • 31
  • 87
  • 173
C. Ha
  • 31
  • 1
  • How do I include int my BUILD file? – Pototo Apr 18 '17 at 02:36
  • Make sure to get the .h files too. Not sure how this worked for others. This is what my build looked like: package(default_visibility = ["//visibility:public"]) cc_library( name = "opencv", srcs = glob(["lib/*.so*"]), hdrs = glob([ "include/opencv2/**/*.h", "include/opencv2/**/*.hpp", ]), strip_include_prefix = "include", linkstatic = 1, visibility = ["//visibility:public"], ) – tdeegan Feb 15 '18 at 19:26
  • Humm, when I do this i'm still getting tons on undefined reference/ In my deps, I added @opencv. if I do @opencv//:core bazel cries with not defined – Nam Vu Jan 10 '20 at 18:34
1

This is what I did for OpenCV 2.4.13.2, core/ only. This approach goes from the opencv source, which is adapted from the accepted answer above by @kristina.

The first thing is to add the http_archive for the opencv 2.4 release:

# OpenCV 2.4.13.2
new_http_archive(
    name = "opencv2",
    url = "https://github.com/opencv/opencv/archive/2.4.13.2.zip",
    build_file = "third_party/opencv2.BUILD",
    strip_prefix = "opencv-2.4.13.2",
)

And then, add the file third_party/opencv2.BUILD as:

cc_library(
    name = "dynamicuda",
    hdrs = glob([
        "modules/dynamicuda/include/**/*.hpp",
    ]),
    includes = [
        "modules/dynamicuda/include"
    ],
)

cc_library(
    name = "core",
    visibility = ["//visibility:public"],
    srcs = glob(["modules/core/src/**/*.cpp"]),
    hdrs = glob([
        "modules/core/src/**/*.hpp",
        "modules/core/include/**/*.hpp",
     ]) + [
        ":module_includes",
        ":cvconfig",
        ":version_string",
    ],
    copts = [
        "-Imodules/dynamicuda/include",
    ],
    # Note that opencv core requires zlib and pthread to build.
    linkopts = ["-pthread", "-lz"],
    includes = [
        "modules/core/include",
    ],
    deps = [
        ":dynamicuda",
    ],
)

genrule(
    name = "module_includes",
    cmd = "echo '#define HAVE_OPENCV_CORE' > $@",
    outs = ["opencv2/opencv_modules.hpp"],
)

genrule(
    name = "cvconfig",
    outs = ["cvconfig.h"],
    cmd = """
cat > $@ <<"EOF"
// JPEG-2000
#define HAVE_JASPER

// IJG JPEG
#define HAVE_JPEG

// PNG
#define HAVE_PNG

// TIFF
#define HAVE_TIFF

// Compile for 'real' NVIDIA GPU architectures
#define CUDA_ARCH_BIN ""

// NVIDIA GPU features are used
#define CUDA_ARCH_FEATURES ""

// Compile for 'virtual' NVIDIA PTX architectures
#define CUDA_ARCH_PTX ""
EOF"""
)

genrule(
    name = "version_string",
    outs = ["version_string.inc"],
    cmd = """
cat > $@ <<"EOF"
"\\n"
)

Note that I did not put anything in the version_string.inc. It is just a C++ string literal which does not affect the functionality of OpenCV. If you are really interested in this file see this example.

After this you should be able to add target with dependencies on @opencv2//:core.

BreakDS
  • 498
  • 4
  • 13
0

Here's a simple demo of OpenCV & C++ built with Bazel: https://github.com/jcju/opencv_bazel_win

You may set up the OpenCV path in WORKSPACE and run:

bazel run //src:main
JC Ju
  • 189
  • 2
  • 6
  • 1
    I tested it on Window 10 x64+ Powershell + Bazel 3.1.0 - `linkstatic = 1` has no effect. I can compile my OpenCV code, but when running it complains about mission OpenCV DLLs. – Vertexwahn May 21 '20 at 13:26
  • If it shows missing of DLLs, the most convenient way is to add opencv into Windows Environment Variables. For example: open Environment Variables list -> Edit "PATH"-> add [C:\opencv]/install/[x64]/[vc14]/bin; replace the opencv path and VS version with your settings. – JC Ju Mar 25 '21 at 18:45