Repository: conan-io/conan-center-index

Issue:
[package] opencv/4.5.1: does not have dnn support
<!-- 
  Please don't forget to update the issue title.
  Include all applicable information to help us reproduce your problem.
-->

### Package and Environment Details (include every applicable attribute)
  * Package Name/Version: **opencv/4.5.1**
  * Operating System+version: **Linux Ubuntu 20.04**
  * Compiler+version: **GCC 9.3.0**
  * Conan version: **conan 1.32.1**
  * Python version: **Python 3.8.5**


### Conan profile (output of `conan profile show default` or `conan profile show <profile>` if custom profile is in use)
```
[settings]
arch=x86_64
arch_build=x86_64
build_type=Debug
compiler=gcc
compiler.libcxx=libstdc++11
compiler.version=9.3
os=Linux
os_build=Linux
[options]
*:fPIC=True
opencv*:contrib=True
[build_requires]
[env]

```


### Steps to reproduce (Include if Applicable)
conan install opencv/4.5.1@ -o opencv*:contrib=True --build missing



### Logs (Include/Attach if Applicable)
<details><summary>Click to expand log</summary>

```
not relevant - module is missing; build is ok
```

</details>



Discussion:
Previous version opencv/4.3.0@conan/stable contained the DNN module with default options
OpenCV/4.3.0@conan/stable is deprecated and wasn't part of Conan Center Index. I'll check it. Thanks for reporting
DNN is only used with protobuf, but protobuf is not included as requirement. It deserves an update. 
> DNN is only used with protobuf, but protobuf is not included as requirement. It deserves an update.

Yes I recognized too, I guessed this will be requested soon :) I think it is a little bit more complicated, because of the indirect `protobuf` dependency. The officially used version is not in CCI, if I remember right (too old). And there is a optional `recompile` option for protoc files, not sure if it would be useful for a standard recipe installation...

`protobuf` is enabled by default in OpenCV's CMakeLists.txt, therefore it should be the same in the recipe?
We can add "with_protobuf" flag with default value True in order to have the same behavior as OpenCV defaults
I played with this for a few days now. Not so easy, because:
* OpenCV typically uses it's own copy of `protobuf`, which is an older version. I'm sure we don't want to use this. 
* If we want to use an external (and newer)  `protobuf` library, we need to recompile the `proto` files
* Therefore, we need a `protoc` compiler, for native build this if fine, but not for cross building. Currently we have no protobuf-compiler recipe, which can be used as build requirement.
* `protobuf` itself has some trouble for cross building, at least for Android, where a library `log` should be linked.
* Maybe more yet unknown problems...

I have a branch with experimental code for this. It compiles (but no DNN functions tested here, only building!) for Linux build machine and Linux and Android hosts right now. I will continue testing for Macos build machine and Macos, Android, iOS host machines too, because this is my current works setup. Windows I have no chance to test. 

Here is my branch: https://github.com/andioz/conan-center-index/tree/opencv4x/dnn

@uilianries @ericLemanissier @madebr and other experts in Conan, OpenCV and Protobuf: I need your help! Currently my branch is not ready for a PR, I would say. I need your input for cleaning up and making things right. I hacked a lot to make it work, but I'm sure there is a better way to make it work. Please check out my branch and let us discuss how to make it clean. I have touched 3 recipes:
* `opencv` of course - adding dnn, but need new requirement `protobuf` AND in case of cross compiling a build requirement `protobuf-compiler`, which doesn't exist yet
* `protobuf` - disabling compiler build because of missing log library linkage
* `protobuf-compiler` - a POC for a new recipe which installs the prebuild protoc packages for all versions supported by the `protobuf` recipe.

I'm currently using the "old" single profile strategy. If changes are required to make everything work with the "new" dual profile  strategy, I would prefer to make it work with both scenarios. 

Hope to CU there!
There is no protobuf-compiler package and never will be because protoc is part of the protobuf package. IIRC you can also add the same requirement as a regular dependency and as a build_requirement at the same time (disclaimer: I have never done that)

To make it easier to provide feedback I would suggest that you open a pull request as a draft 
> IIRC you can also add the same requirement as a regular dependency and as a build_requirement at the same time.

I'm sure I tried this first, but if I remember right the build requirement is not used in this case.

Resolving patch:
diff --git a/recipes/opencv/4.x/conanfile.py b/recipes/opencv/4.x/conanfile.py
--- a/recipes/opencv/4.x/conanfile.py
+++ b/recipes/opencv/4.x/conanfile.py
@@ -32,7 +32,8 @@ class OpenCVConan(ConanFile):
         "with_quirc": [True, False],
         "with_cuda": [True, False],
         "with_cublas": [True, False],
-        "with_cufft": [True, False]
+        "with_cufft": [True, False],
+        "dnn": [True, False]
     }
     default_options = {
         "shared": False,
@@ -52,7 +53,8 @@ class OpenCVConan(ConanFile):
         "with_quirc": True,
         "with_cuda": False,
         "with_cublas": False,
-        "with_cufft": False
+        "with_cufft": False,
+        "dnn": True
     }
 
     short_paths = True
@@ -134,6 +136,8 @@ def requirements(self):
             self.requires("quirc/1.1")
         if self.options.get_safe("with_gtk"):
             self.requires("gtk/system")
+        if self.options.dnn:
+            self.requires("protobuf/3.15.5")
 
     def source(self):
         tools.get(**self.conan_data["sources"][self.version][0])
@@ -158,6 +162,26 @@ def _patch_opencv(self):
         tools.replace_in_file(os.path.join(self._source_subfolder, "CMakeLists.txt"), "ANDROID OR NOT UNIX", "FALSE")
         tools.replace_in_file(os.path.join(self._source_subfolder, "modules", "imgcodecs", "CMakeLists.txt"), "JASPER_", "Jasper_")
 
+        if self.options.dnn:
+            find_protobuf = os.path.join(self._source_subfolder, "cmake", "OpenCVFindProtobuf.cmake")
+            # variables generated by protobuf recipe have all lowercase prefixes
+            tools.replace_in_file(find_protobuf,
+                                  'find_package(Protobuf QUIET)',
+                                  '''find_package(Protobuf QUIET)
+            if(NOT DEFINED Protobuf_LIBRARIES)
+              set(Protobuf_LIBRARIES ${protobuf_LIBRARIES})
+            endif()
+            if(NOT DEFINED Protobuf_LIBRARY)
+              set(Protobuf_LIBRARY ${protobuf_LIBS})
+            endif()
+            if(NOT DEFINED Protobuf_INCLUDE_DIR)
+              set(Protobuf_INCLUDE_DIR ${protobuf_INCLUDE_DIR})
+            endif()''')
+            # in 'if' block, get_target_property() produces an error
+            if tools.Version(self.version) >= "4.4.0":
+                tools.replace_in_file(find_protobuf,
+                                      'if(TARGET "${Protobuf_LIBRARIES}")',
+                                      'if(FALSE)  # patch: disable if(TARGET "${Protobuf_LIBRARIES}")')
 
     def _configure_cmake(self):
         if self._cmake:
@@ -231,7 +255,6 @@ def _configure_cmake(self):
         self._cmake.definitions["WITH_OPENNI2"] = False
         self._cmake.definitions["WITH_OPENVX"] = False
         self._cmake.definitions["WITH_PLAIDML"] = False
-        self._cmake.definitions["WITH_PROTOBUF"] = False
         self._cmake.definitions["WITH_PVAPI"] = False
         self._cmake.definitions["WITH_QT"] = False
         self._cmake.definitions["WITH_QUIRC"] = False
@@ -260,6 +283,11 @@ def _configure_cmake(self):
         self._cmake.definitions["WITH_MSMF_DXVA"] = self.settings.compiler == "Visual Studio"
         self._cmake.definitions["OPENCV_MODULES_PUBLIC"] = "opencv"
 
+        self._cmake.definitions["WITH_PROTOBUF"] = self.options.dnn
+        if self.options.dnn:
+            self._cmake.definitions["PROTOBUF_UPDATE_FILES"] = True
+            self._cmake.definitions["BUILD_opencv_dnn"] = True
+
         if self.options.contrib:
             self._cmake.definitions['OPENCV_EXTRA_MODULES_PATH'] = os.path.join(self.build_folder, self._contrib_folder, 'modules')
         self._cmake.definitions['BUILD_opencv_freetype'] = self.options.get_safe("contrib_freetype", False)
@@ -369,6 +397,9 @@ def quirc():
         def gtk():
             return ["gtk::gtk"] if self.options.get_safe("with_gtk") else []
 
+        def protobuf():
+            return ["protobuf::protobuf"] if self.options.dnn else []
+
         def freetype():
             return ["freetype::freetype"] if self.options.get_safe("contrib_freetype") else []
 
@@ -390,6 +421,11 @@ def xfeatures2d():
             {"target": "opencv_stitching",  "lib": "stitching",  "requires": ["opencv_core", "opencv_flann", "opencv_imgproc", "opencv_features2d", "opencv_calib3d"] + xfeatures2d() + eigen()},
             {"target": "opencv_video",      "lib": "video",      "requires": ["opencv_core", "opencv_flann", "opencv_imgproc", "opencv_features2d", "opencv_calib3d"] + eigen()},
         ]
+        if self.options.dnn:
+            opencv_components.extend([
+                {"target": "opencv_dnn", "lib": "dnn", "requires": ["opencv_core", "opencv_imgproc"] + protobuf()}
+            ])
+
         if self.options.contrib:
             opencv_components.extend([
                 {"target": "opencv_intensity_transform", "lib": "intensity_transform", "requires": ["opencv_core", "opencv_imgproc"] + eigen()},


Test patch: