Repository: conan-io/conan-center-index

Issue:
[request] libCVD
### Package Name/Version

libcvd/2.3.1

### Webpage

https://github.com/edrosten/libcvd

### Source code

https://github.com/edrosten/libcvd

### Description of the library/tool

libcvd - efficient and easy to use C++ computer vision library.


Discussion:
Used as a vendored library in #22346.

Resolving patch:
diff --git a/recipes/libcvd/all/conanfile.py b/recipes/libcvd/all/conanfile.py
new file mode 100644
--- /dev/null
+++ b/recipes/libcvd/all/conanfile.py
@@ -0,0 +1,177 @@
+import os
+
+from conan import ConanFile
+from conan.errors import ConanInvalidConfiguration
+from conan.tools.build import check_min_cppstd
+from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
+from conan.tools.files import copy, get, save, rmdir, replace_in_file
+from conan.tools.scm import Version
+
+required_conan_version = ">=1.53.0"
+
+
+class LibCVDConan(ConanFile):
+    name = "libcvd"
+    description = "libCVD - efficient and easy-to-use C++ computer vision library"
+    license = "BSD-2-Clause"
+    url = "https://github.com/conan-io/conan-center-index"
+    homepage = "https://github.com/edrosten/libcvd"
+    topics = ("computer-vision",)
+
+    package_type = "library"
+    settings = "os", "arch", "compiler", "build_type"
+    options = {
+        "shared": [True, False],
+        "fPIC": [True, False],
+        "with_libjpeg": [False, "libjpeg", "libjpeg-turbo", "mozjpeg"],
+        "with_libpng": [True, False],
+        "with_libtiff": [True, False],
+        "with_ffmpeg": [True, False],
+        "with_libdc1394": [True, False],
+        "with_opengl": [True, False],
+    }
+    default_options = {
+        "shared": False,
+        "fPIC": True,
+        "with_libjpeg": "libjpeg",
+        "with_libpng": True,
+        "with_libtiff": True,
+        # Build without video support by default
+        "with_ffmpeg": False,
+        "with_libdc1394": False,
+        "with_opengl": False,
+    }
+
+    @property
+    def _min_cppstd(self):
+        return 17
+
+    @property
+    def _compilers_minimum_version(self):
+        return {
+            "gcc": "7",
+            "clang": "5",
+            "apple-clang": "10",
+            "msvc": "191",
+            "Visual Studio": "15",
+        }
+
+    def config_options(self):
+        if self.settings.os == "Windows":
+            del self.options.fPIC
+            # OpenGL is always used on Windows
+            del self.options.with_opengl
+
+    def configure(self):
+        if self.options.shared:
+            self.options.rm_safe("fPIC")
+
+    def layout(self):
+        cmake_layout(self, src_folder="src")
+
+    def requirements(self):
+        # https://github.com/edrosten/libcvd/blob/main/cmake/CVDFindAllDeps.cmake
+        if self.options.with_ffmpeg:
+            # FFMPEG v5.x+ are not supported
+            self.requires("ffmpeg/4.4.4", transitive_libs=True)
+        if self.options.with_libdc1394:
+            self.requires("libdc1394/2.2.7")
+            # FIXME: libidc1394 seems to be missing raw1394 dependency
+            # test_package fails with "undefined reference to `raw1394_new_handle'" etc
+        if self.options.with_libjpeg == "libjpeg-turbo":
+            self.requires("libjpeg-turbo/3.0.2")
+        elif self.options.with_libjpeg == "libjpeg":
+            self.requires("libjpeg/9e")
+        elif self.options.with_libjpeg == "mozjpeg":
+            self.requires("mozjpeg/4.1.5")
+        if self.options.with_libpng:
+            self.requires("libpng/[>=1.6 <2]")
+        if self.options.with_libtiff:
+            self.requires("libtiff/4.6.0")
+        if self.options.get_safe("with_opengl", True):
+            # https://github.com/edrosten/libcvd/blob/RELEASE_2_5_0/cvd/videodisplay.h#L18-L20
+            if self.settings.os in ["Linux", "FreeBSD"]:
+                self.requires("libglvnd/1.7.0", transitive_headers=True, transitive_libs=True)
+                self.requires("xorg/system", transitive_headers=True, transitive_libs=True)
+            else:
+                self.requires("opengl/system", transitive_headers=True, transitive_libs=True)
+        # TODO: https://github.com/ankurhanda/TooN
+        # https://github.com/edrosten/libcvd/blob/RELEASE_2_5_0/cvd/canny.h#L4
+        # self.requires("toon/3.1.1", transitive_headers=True, transitive_libs=True)
+
+    def validate(self):
+        if self.settings.compiler.cppstd:
+            check_min_cppstd(self, self._min_cppstd)
+        minimum_version = self._compilers_minimum_version.get(str(self.settings.compiler), False)
+        if minimum_version and Version(self.settings.compiler.version) < minimum_version:
+            raise ConanInvalidConfiguration(
+                f"{self.ref} requires C++{self._min_cppstd}, which your compiler does not support."
+            )
+
+    def source(self):
+        get(self, **self.conan_data["sources"][self.version], strip_root=True)
+
+    def generate(self):
+        tc = CMakeToolchain(self)
+        tc.variables["CVD_ENABLE_TESTS"] = False
+        tc.variables["CVD_ENABLE_PROGS"] = False
+        tc.variables["CVD_ENABLE_EXAMPLES"] = False
+        tc.variables["CMAKE_DISABLE_FIND_PACKAGE_ffmpeg"] = not self.options.with_ffmpeg
+        tc.variables["CMAKE_DISABLE_FIND_PACKAGE_libdc1394"] = not self.options.with_libdc1394
+        tc.variables["CMAKE_DISABLE_FIND_PACKAGE_JPEG"] = not self.options.with_libjpeg
+        tc.variables["CMAKE_DISABLE_FIND_PACKAGE_PNG"] = not self.options.with_libpng
+        tc.variables["CMAKE_DISABLE_FIND_PACKAGE_TIFF"] = not self.options.with_libtiff
+        tc.variables["CMAKE_DISABLE_FIND_PACKAGE_OpenGL"] = not self.options.get_safe("with_opengl", True)
+        tc.variables["CMAKE_DISABLE_FIND_PACKAGE_X11"] = not self.options.get_safe("with_opengl", True)
+        tc.variables["CMAKE_DISABLE_FIND_PACKAGE_TooN"] = True
+        tc.variables["CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS"] = True
+        tc.cache_variables["CMAKE_POLICY_DEFAULT_CMP0077"] = "NEW"
+        tc.generate()
+
+        deps = CMakeDeps(self)
+        deps.set_property("ffmpeg", "cmake_file_name", "CVD_FFMPEG")
+        deps.set_property("libdc1394", "cmake_file_name", "CVD_dc1394v2")
+        deps.set_property("toon", "cmake_file_name", "CVD_TooN")
+        deps.generate()
+
+    def _patch_sources(self):
+        # Use deps from Conan
+        save(self, os.path.join(self.source_folder, "cmake", "CVDFindFFMPEG.cmake"),
+             "find_package(CVD_FFMPEG REQUIRED)\n" if self.options.with_ffmpeg else "")
+        save(self, os.path.join(self.source_folder, "cmake", "CVDFinddc1394v2.cmake"),
+             "find_package(CVD_dc1394v2 REQUIRED)\n" if self.options.with_libdc1394 else "")
+        save(self, os.path.join(self.source_folder, "cmake", "CVDFindTooN.cmake"),
+            "set(CVD_TooN_FOUND FALSE)\n")
+
+        # For debugging
+        save(self, os.path.join(self.source_folder, "cmake", "CVDFindAllDeps.cmake"),
+             '\nmessage(INFO "CVD_DEP_LIBS: ${CVD_DEP_LIBS}")\n', append=True)
+
+        # Install DLL
+        replace_in_file(self, os.path.join(self.source_folder, "CMakeLists.txt"),
+                        "install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib)",
+                        "install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin)")
+
+
+    def build(self):
+        self._patch_sources()
+        cmake = CMake(self)
+        cmake.configure()
+        cmake.build()
+
+    def package(self):
+        copy(self, "LICENSE", self.source_folder, os.path.join(self.package_folder, "licenses"))
+        cmake = CMake(self)
+        cmake.install()
+        rmdir(self, os.path.join(self.package_folder, "cmake"))
+
+    def package_info(self):
+        self.cpp_info.set_property("cmake_file_name", "CVD")
+        self.cpp_info.set_property("cmake_target_name", "cvd")
+        self.cpp_info.set_property("cmake_find_mode", "both")
+
+        postfix = "_debug" if self.settings.build_type == "Debug" else ""
+        self.cpp_info.libs = ["cvd" + postfix]
+
+        if self.settings.os == "Windows":
+            self.cpp_info.system_libs.append("ws2_32")


Test patch:
diff --git a/recipes/libcvd/all/test_package/CMakeLists.txt b/recipes/libcvd/all/test_package/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/recipes/libcvd/all/test_package/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_package LANGUAGES CXX)
+
+find_package(CVD REQUIRED MODULE)
+
+add_executable(${PROJECT_NAME} test_package.cpp)
+target_link_libraries(${PROJECT_NAME} PRIVATE ${CVD_LIBRARIES})
+target_include_directories(${PROJECT_NAME} PRIVATE ${CVD_INCLUDE_DIRS})
+target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
diff --git a/recipes/libcvd/all/test_package/conanfile.py b/recipes/libcvd/all/test_package/conanfile.py
new file mode 100644
--- /dev/null
+++ b/recipes/libcvd/all/test_package/conanfile.py
@@ -0,0 +1,26 @@
+from conan import ConanFile
+from conan.tools.build import can_run
+from conan.tools.cmake import cmake_layout, CMake
+import os
+
+
+class TestPackageConan(ConanFile):
+    settings = "os", "arch", "compiler", "build_type"
+    generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv"
+    test_type = "explicit"
+
+    def layout(self):
+        cmake_layout(self)
+
+    def requirements(self):
+        self.requires(self.tested_reference_str)
+
+    def build(self):
+        cmake = CMake(self)
+        cmake.configure()
+        cmake.build()
+
+    def test(self):
+        if can_run(self):
+            bin_path = os.path.join(self.cpp.build.bindir, "test_package")
+            self.run(bin_path, env="conanrun")
diff --git a/recipes/libcvd/all/test_package/test_package.cpp b/recipes/libcvd/all/test_package/test_package.cpp
new file mode 100644
--- /dev/null
+++ b/recipes/libcvd/all/test_package/test_package.cpp
@@ -0,0 +1,85 @@
+// https://github.com/edrosten/libcvd/blob/RELEASE_2_5_0/examples/distance_transform.cc
+
+// Copyright (c) 2005--2013, The Authors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include <cvd/distance_transform.h>
+#include <cvd/image_io.h>
+
+#include <algorithm>
+#include <random>
+
+using CVD::byte;
+using CVD::euclidean_distance_transform_sq;
+using CVD::Image;
+using CVD::ImageRef;
+using CVD::img_save;
+using CVD::Rgb;
+using std::max_element;
+using std::mt19937;
+using std::uniform_int_distribution;
+
+int main()
+{
+	//Create a blank image.
+	Image<byte> im(ImageRef(128, 128), 0);
+
+	mt19937 engine;
+	uniform_int_distribution<int> rand_x(0, im.size().x - 1);
+	uniform_int_distribution<int> rand_y(0, im.size().y - 1);
+
+	//Scatter down 7 points at random.
+	for(int i = 1; i < 8; i++)
+		im[rand_y(engine)][rand_x(engine)] = i;
+
+	Image<int> dt(im.size());
+	Image<ImageRef> inverse_dt(im.size());
+
+	//Perform the distance transform
+	euclidean_distance_transform_sq(im, dt, inverse_dt);
+
+	//Create an output which is the distance transfom of the input,
+	//but coloured according to which pixel is closest.
+	int largest_distance = *max_element(dt.begin(), dt.end());
+
+	Image<Rgb<byte>> out(im.size());
+
+	for(int y = 0; y < im.size().y; y++)
+		for(int x = 0; x < im.size().x; x++)
+		{
+			int c = floor(sqrt(dt[y][x] * 1.0 / largest_distance) * 255 + .5);
+
+			Rgb<byte> r(0, 0, 0);
+			if(im[inverse_dt[y][x]] & 1)
+				r.red = c;
+			if(im[inverse_dt[y][x]] & 2)
+				r.green = c;
+			if(im[inverse_dt[y][x]] & 4)
+				r.blue = c;
+
+			out[y][x] = r;
+		}
+
+	img_save(out, "distance_transform_result.png");
+}