CMake、OpenCV 和单元测试

在这里插入图片描述我写了很多关于 CMake 的文章,如果你感兴趣,可以点击以下链接阅读:

  • CMake VS Make
  • CMake:在构建世界掀起风暴
  • 现代 CMake 使用技巧
  • CMake 交叉编译
  • CMake 生成器已开启

我们将继续对 CMake 的探索,这篇文章技术性高,且易于实操,我们会练习将 CMake 用于一个实际的项目。我将使用 OpenCV 得到的 CMake 标志显示。听起来就很有意思,话不多说,让我们开始吧。

我们身处何方?

C++ 世界已准备向 C++ 20 跃进。现代 CMake 日趋成熟,在windows 上获取 OpenCV 也只是一些 vcpkg 命令的问题。由于 vcpkg 总是从源代码处编译所有内容,所以与安装器不同,避免了很多兼容性问题。

首先可以参考链接中非常实用的安装说明,安装 vcpkg。在 OpenCV 的编译过程中,将构建和安装以下软件包:

  • libjpeg-turbo[core]:x86-windows -> 2.0.5
  • liblzma[core]:x86-windows -> 5.2.5#2
  • libpng[core]:x86-windows -> 1.6.37#13
  • libwebp[core,nearlossless,simd,unicode]:x86-windows -> 1.1.0#1
  • opencv[core,dnn,jpeg,opengl,png,tiff,webp]:x86-windows -> 4.3.0
  • opencv4[core,dnn,jpeg,opengl,png,tiff,webp]:x86-windows -> 4.3.0#4
  • opengl[core]:x86-windows -> 0.0#8
  • protobuf[core]:x86-windows -> 3.14.0
  • tiff[core]:x86-windows -> 4.1.0
  • zlib[core]:x86-windows -> 1.2.11#9

使用 vcpkg 编译OpenCV 需要一些时间,因为上面的所有依赖项也将从源代码处编译。因此,在 OpenCV 编译过程中要有耐心,具体有下面十个步骤:

  • Starting package 1/10: libjpeg-turbo:x86-windows
  • Starting package 2/10: liblzma:x86-windows
  • Starting package 3/10: zlib:x86-windows
  • Starting package 4/10: libpng:x86-windows
  • Starting package 5/10: libwebp:x86-windows
  • Starting package 6/10: opengl:x86-windows
  • Starting package 7/10: protobuf:x86-windows
  • Starting package 8/10: tiff:x86-windows
  • Starting package 9/10: opencv4:x86-windows
  • Starting package 10/10: opencv:x86-windows

使用 vcpkg 完成 OpenCV 编译后,你将在 \vcpkg\installed\x86-windows\share\opencv\OpenCVConfig.cmake. 中看到 OpenCVConfig.cmake。 此文件包含 OpenCV CMake 选项,用于外部项目。让我们从这个 OpenCV CMake 配置文件的开头开始,构建一个 CMakeLists.txt 文件:

cmake_minimum_required (VERSION 3.8)
project (“CMakeTriangles”)set (CMAKE_TOOLCHAIN_FILE “D:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake”)set (OpenCV_DIR"D:/Tools/vcpkg/installed/x86-windows/share/opencv")
find_package(OpenCV REQUIRED)

#Add source to this project’s executable.add_executable (CMakeTriangles “CMakeTriangles.cpp”)target_link_libraries (CMakeTriangles ${OpenCV_LIBS})# TODO: Add tests and install targets if needed.

在系统中使用硬编码路径获取 CMakeLists.txt 文件,是绝对不可取的,我们稍后会给出建议的方案。现在让我们看看如何使用 OpenCV 制作 CMake 标志。以下是 CMakeTriangles.cpp 的代码:

#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc.hpp>#include
using namespace cv;using namespace std;
int main(){ Mat image = Mat::zeros(400, 600, CV_8UC3); image.setTo(Scalar(255, 255, 255)); fillConvexPoly(image, vector{ Point(200, 300), Point(400, 300), Point(300, 50) }, Scalar(128, 128, 128)); fillConvexPoly(image, vector{ Point(200, 300), Point(300, 175), Point(300, 50) }, Scalar(255, 0, 0)); fillConvexPoly(image, vector{ Point(400, 300), Point(305, 255), Point(305, 50) }, Scalar(0, 0, 255)); fillConvexPoly(image, vector{ Point(205, 300), Point(260, 240), Point(395, 300) }, Scalar(0, 255, 0)); imshow(“CMake Trianges!!”, image); waitKey(0); return 0;}

在这里插入图片描述
考虑到 CMakeLists.txt 文件和 CMakeTriangles.cpp 文件,我们可以很容易地运行编译:

cmake -S. -BBuild -A “Win32” cd Build && cmake --build .

现在让我们从 CMakeLists.txt 文件中删除硬编码的值

cmake_minimum_required (VERSION 3.8)
project (“CMakeTriangles”)
find_package(OpenCV REQUIRED)
#Add source to this project’s executable.add_executable (CMakeTriangles “CMakeTriangles.cpp”)target_link_libraries (CMakeTriangles ${OpenCV_LIBS})
#TODO: Add tests and install targets if needed.

有了这个新的 CMakeLists.txt 文件,项目编译也相应有了些改变:

cmake -S. -BBuild -A “Win32” -DOpenCV_DIR=/vcpkg/installed/x86-windows/share/opencv -DCMAKE_TOOLCHAIN_FILE= /vcpkg/scripts/buildsystems/vcpkg.cmakecd Build && cmake --build .

这里 是相对于已安装的 vcpkg 文件夹的。使用这种方法,你将能在构建文件夹中找到运行程序所需的所有 DLL.
在这里插入图片描述

你现在可以双击这个可执行文件,显示如上图。由于 vcpkg、CMake 和OpenCV 都是跨平台的工具,Windows、Linux 和 Mac 都可以支持,因此我们能够按照上面的步骤编译并运行 CMake triangles 映像。

我们该如何添加测试?

作为架构师,我支持在开发周期中尽早添加单元测试。一种测试驱动的方法,是在编写代码之前就添加测试,这种方法可能适用于某些项目,但我反对软件开发中的任何“过度狂热”。让我们重构我们的单片代码,使其更易于单元测试(读者注意:建议在重构之前添加测试)。经过紧张地重构,我们的项目现在被拆分了 CMakeTrianglesLib.cpp、CMakeTriangles.h 和CMakeTriangles.cpp。
其中 CMakeTriangles.h 是:

#pragma once
#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc.hpp>#include
using namespace cv;using namespace std;#include
Mat CreateImageWithBackground(int rows, int cols, int type, Scalar background);
void CreateConvexPolygonsOnImage(Mat image, vector<vector> points, vector backgrounds);
void DisplayWindowWithTitle(Mat image, string title);

CMakeTriangleslib.cpp 是:

#include “CMakeTriangles.h”
Mat CreateImageWithBackground(int rows, int cols, int type, Scalar background){ Mat image = Mat::zeros(rows, cols, type); image.setTo(background); return image;}
void CreateConvexPolygonsOnImage(Mat image, vector<vector> points, vector backgrounds){ if (points.size() != backgrounds.size()) return; // No change in the image
for (auto i = 0UL; i < points.size(); ++i) { fillConvexPoly(image, points[i], backgrounds[i]); }}
void DisplayWindowWithTitle(Mat image, string title){ imshow(title, image);}

CMakeTriangles.cpp 是:

#include “CMakeTriangles.h”
int main(){ auto image = CreateImageWithBackground(400, 600, CV_8UC3, Scalar(255, 255, 255));
CreateConvexPolygonsOnImage(image, { { Point(200, 300), Point(400, 300), Point(300, 50) }, // Grey inner triangle { Point(200, 300), Point(300, 175), Point(300, 50) }, // Blue left triangle { Point(400, 300), Point(305, 255), Point(305, 50) }, // Red right triangle { Point(205, 300), Point(260, 240), Point(395, 300)} }, // Green lower triangle // Grey, Blue Red Green { Scalar(128, 128, 128), Scalar(255, 0, 0), Scalar(0, 0, 255), Scalar(0, 255, 0) });

DisplayWindowWithTitle(image, “CMake Triangles Refactored!!”); waitKey(0); return 0;}

CMakeLists.txt 变成:

cmake_minimum_required (VERSION 3.8)project (“CMakeTrianglesRefactored”)
find_package(OpenCV REQUIRED)
add_library (CMakeTrianglesLib “CMakeTrianglesLib.cpp” “CMakeTriangles.h”)target_include_directories(CMakeTrianglesLib PRIVATE ${OpenCV_INCLUDE_DIRS})
add_executable (CMakeTriangles “CMakeTriangles.cpp”)
target_link_libraries (CMakeTriangles CMakeTrianglesLib ${OpenCV_LIBS})

编译现在生成一个额外的库文件,如下所示:

在这里插入图片描述
测试我们的库代码,我们希望能被广泛使用? –使用谷歌测试(GTest)。使用 vcpkg 安装 GTest,会有下列提示:

The package gtest:x86-windows provides CMake targets: find_package(GTest CONFIG REQUIRED) target_link_libraries(main PRIVATE GTest::gmock GTest::gtest GTest::gmock_main GTest::gtest_main)

我们已经编写了一些测试代码。创建文件 CMakeTrianglesLibTest.cpp 并加入下列代码:

#include “gtest/gtest.h”#include “CMakeTriangles.h”
TEST(testCMakeTriangleLib, Given_Rows_Cols_Type_BG_When_CreateImageWithBackground_Call_Then_Return_OK){ const int r = 300; const int c = 600; auto image = CreateImageWithBackground(r, c, CV_8UC3, Scalar(255, 255, 255)); EXPECT_EQ(c, image.cols); EXPECT_EQ(r, image.rows);}

这个测试代码解释了如何使用 GTest 将所有内容与 CMake 结合起来编写OpenCV 单元测试。最后的 CMakeLists.txt 文件如下所示:

cmake_minimum_required (VERSION 3.8)project (“CMakeTrianglesRefactored”)
find_package(OpenCV REQUIRED)
add_library (CMakeTrianglesLib “CMakeTrianglesLib.cpp” “CMakeTriangles.h”)target_include_directories(CMakeTrianglesLib PRIVATE ${OpenCV_INCLUDE_DIRS})
add_executable (CMakeTriangles “CMakeTriangles.cpp”)
target_link_libraries (CMakeTriangles CMakeTrianglesLib ${OpenCV_LIBS})
find_package(GTest CONFIG REQUIRED)add_executable(CMakeTrianglesLibTest “CMakeTrianglesLibTest.cpp”)target_link_libraries(CMakeTrianglesLibTest PRIVATE CMakeTrianglesLib ${OpenCV_LIBS} GTest::gtest GTest::gtest_main)

最后,你就能成功构建整个项目,并获得:
在这里插入图片描述
你也可以现在执行这些测试,并获得相应结果:

在这里插入图片描述

结论

这是一篇很长的文章,我希望我可以带大家了解 CMake 和 OpenCV 编译,如何使用 GTest 编写 OpenCV 单元测试,并使用 CMake 完成所有工作。我知道 OpenCV ts 模块,但它只是 OpenCV 的内部模块,因此建议不要修补源代码以将它改为公开可见。使用 GTest 测试 OpenCV,使用 CMake 简化跨平台构建。我强烈推荐选择 VCPkg 作为包管理器。

编写这篇文章让我觉得乐趣无穷,希望也能将这种快乐传递给大家!

点击了解 Incredibuild 加速 CI/CD 的解决方案,并获取试用 License!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/418720.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何解决 C/C++ 编译器优化导致的编译BUG(程序崩溃)支援VC++/CLANG/GCC

本文仅适用于&#xff0c;有愿意、爱捣鼓的童靴。 因编译器优化导致编译BUG&#xff0c;即DEBUG下面无故障稳定工作&#xff0c;但RELESE下程序会在特定函数位置上崩溃。 这要求 C/C 开发人员拥有最基本的素质&#xff0c;需要能够承受&#xff0c;逐行审视编译器输出的目标平…

获取当前数据 上下移动

点击按钮 上下移动 当前数据 代码 // 出国境管理 登记备案人员列表 <template><a-row><a-col span"24"><a-card :class"style[a-table-wrapper]"><!-- 出国境 登记备案人员列表 --><a-table:rowKey"records >…

【Java】查看class文件的jdk编译版本的两种方式

一、使用文本编辑工具EditPlus 使用EditPlus打开该class文件&#xff0c;字符集选择16进制&#xff08;Hex viewer&#xff09;。 仅看第一行数据&#xff0c;前面8个字节CA FE BA BE是固定的。 之后4个字节00 00 是次版本。 次版本后面的4个字节00 34 就是jdk版本。 jdk版本…

Java代码块

Java代码块 普通代码块 普通代码块在对象创建时执行&#xff0c;创建一个对象就会执行一次&#xff0c;可把构造函数中的冗余代码放到普通代码块中 public class Test {public void method() {// 普通代码块{int x 10;System.out.println(x);}public method(){}} }普通代码块…

使用mininet快速入门ONOS路由交换技术与原理-路由篇

上篇文章 《使用mininet快速入门ONOS路由交换技术与原理-交换篇》 使用mininet搭建了一个简单的网络拓扑&#xff0c;并实现了同一交换机下同网段多主机的通信&#xff0c;其中涉及到的通信知识主要以二层mac地址通信为主。 但在芸芸网络的世界中&#xff0c;主机间的通信除了…

Education Codeforces Round 162(Div.2) A~E

A.Moving Chips (思维) 题意&#xff1a; 给一个长度为 n n n的数组 a a a&#xff0c; a i 1 a_i1 ai​1或者 a i 0 a_i0 ai​0&#xff0c;现在可以选择一个 1 1 1&#xff0c;然后将其与左侧最近的 0 0 0交换。询问使得所有的 1 1 1连在一起&#xff0c;中间没有 0 0 0…

Vue+SpringBoot打造不良邮件过滤系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统用户模块2.2 收件箱模块2.3 发件箱模块2.4 垃圾箱模块2.5 回收站模块2.6 邮箱过滤设置模块 三、实体类设计3.1 系统用户3.2 邮件3.3 其他实体 四、系统展示五、核心代码5.1 查询收件箱档案5.2 查询回收站档案5.3 新…

js 面试 1判断变量是否是数组 2 检测数据类型方法

1 是否是数组 1) typeof 检测数据类型运算符 优点&#xff1a;使用简单 缺点&#xff1a;只能检测基本类型&#xff08;除null外&#xff09; console.log(typeof(10)) //Number console.log(typeof(false)) //boolean console.log(typeof(hello)) //string console.log(typeof…

LeetCode 刷题 [C++] 第236题.二叉树的最近公共祖先

题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以…

【网络编程】理解客户端和服务器并使用Java提供的api实现回显服务器

目录 一、网络编程 二、客户端和服务器 三、客户端和服务器的交互模式 四、TCP 和 UDP UDP socket api 的使用 1、DatagramSoket 2、DatagramPacket TCP socket api 的使用 1、ServerSocket 2、Socket 一、网络编程 本质上就是学习传输层给应用层提供的 api&#x…

MySQL之事务详解

华子目录 什么是事务银行转账案例方式1方式2具体操作 事务的四大特性并发事务问题脏读不可重复读幻读 事务的隔离级别查看事务隔离级别设置事务隔离级别 session与global的区别 什么是事务 事务&#xff08;transaction&#xff09;&#xff0c;一个最小的不可再分的工作单元&…

实例:NX二次开发抽取平面以及标准柱面中心线

一、概述 最近体验许多外挂&#xff0c;包括胡波外挂、星空外挂及模圣等都有抽取面的中心线&#xff0c;由于刚刚学习&#xff0c;我尝试看看能不能做出来&#xff0c;本博客代码没有封装函数&#xff0c;代码有待改进&#xff0c;但基本可以实现相应的功能。 二、案例实现的功…

Sora 原理与技术实战笔记一

b 站视频合集 【AIX组队学习】Sora原理与技术实战&#xff1a;Sora技术路径详解 Sora 技术报告&#xff08;OpenAI&#xff09; huggingsd 文生图视频系列的一个开源项目 最强视频生成模型Sora相关技术解析 https://github.com/lichao-sun/SoraReview 惊艳效果&#xff1a; 长…

Ps:路径面板

Ps菜单&#xff1a;窗口/路径 Window/Paths “路径”面板 Paths Panel提供了一系列功能&#xff0c;使用户能够创建、编辑、保存和利用路径。 ◆ ◆ ◆ 路径分类 在“路径”面板上的路径可分为五大类。 常规路径 Saved Path 也称“已保存的路径”&#xff0c;指的是已经存储在…

Python进阶学习:Pandas--DataFrame--如何把几列数据合并成新的一列

Python进阶学习&#xff1a;Pandas–DataFrame–如何把几列数据合并成新的一列 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1…

SpringMVC的配置2种(本质上还是一样的,实现的接口不同)

第一种SpringInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer 看第一种配置 package com.xxx.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class SpringInitConfig ext…

减少页面加载时间:提升用户体验的关键

✨✨ 祝屏幕前的您天天开心&#xff0c;每天都有好运相伴。我们一起加油&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一、为什么页面加载时间重要&#xff1f; 二、如何减少页面加载时间&#xff1f; …

Google发布Genie硬杠Sora:通过大量无监督视频训练最终生成可交互虚拟世界

前言 Sora 问世才不到两个星期&#xff0c;谷歌的世界模型也来了&#xff0c;能力看似更强大(嗯&#xff0c;看似)&#xff1a;它生成的虚拟世界自主可控 第一部分 首个基础世界模型Genie 1.1 Genie是什么 Genie是第一个以无监督方式从未标记的互联网视频中训练的生成式交互…

UDP数据报套接字编程入门

目录 1.TCP和UDP的特点及区别 1.1TCP的特点 1.2UDP的特点 1.3区别 2.UDP Socket的api的介绍 2.1DatagramSocket API 2.2DatagramPacket API 3.回显客户端与服务器 3.1回显服务器 3.1.1UdpEchoServer类的创建 3.1.2服务器的运行方法start() 3.1.3main部分 3.1.4.完整…

nginx反向代理之缓存 客户端IP透传 负载均衡

一 缓存功能 缓存功能可以加速访问&#xff0c;如果没有缓存关闭后端服务器后&#xff0c;图片将无法访问&#xff0c;缓存功能默认关闭&#xff0c;需要开启。 相关选项&#xff1a; ​ proxy_cache zone_name | off; 默认off #指明调用的缓存&#xff0c;或关闭缓存机制;C…