OpenCV

Download

Install

下载 opencv-3.4.11-vc14_vc15.exe 文件,双击安装(其实只是一个解压过程)。

有的教程中会建议将解压后的 OpenCV 下的某些路径添加到系统环境变量中;这里换一种方法,仅修改 VS 的工程属性,无需添加系统环境变量。

VS2017 配置

参考OpenCV 4.1.0 + Visual Studio 2019 开发环境搭建 超级简单

准备文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- <sln> 解决方案文件夹
- x64
- Debug // 将 opencv\build\x64\vc15\bin 路径下的 3 个.dll 文件拷贝到这里
- <proj>.exe
- opencv_world3411.dll
- opencv_world3411d.dll
- opencv_ffmpeg3411_64.dll
- Release
- <proj>.exe
- opencv_world3411.dll
- opencv_world3411d.dll
- opencv_ffmpeg3411_64.dll
- <proj> 项目文件夹
- Debug
- include
- opencv2 // 从 opencv\build\include 路径下将整个 opencv2 文件夹拷贝到这里
- lib // 将 opencv\build\x64\vc15\lib 路径下的两个.lib 文件拷贝到这里
- opencv_world3411.lib
- opencv_world3411d.lib
- src
- main.cpp
- x64
- Debug
- Debug

配置

  • LIB
    解决方案资源管理器 右击项目名 ->属性 -> 链接器 -> 常规 -> 附加库目录 : 将 lib 文件夹路径添加到这里,或者直接用相对路径表示,添加lib 即可.
    解决方案资源管理器 右击项目名 ->属性 -> 链接器 -> 输入 -> 附加依赖项: 将 lib 文件名添加到这里, 例如 opencv_world3411d.lib.

  • INCLUDE
    解决方案资源管理器 右击项目名 ->属性 ->C/C++-> 常规 -> 附加包含目录 : 将 include 文件夹路径添加到这里,或者直接用相对路径表示,添加include 即可.

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
#include"opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main()
{
cout << "OpenCV Test:" << endl;
/*********************
* 这个图片需要自己准备,放在 images 文件夹下
* 注意这里的 "./" 表示相对路径,指相对于工程文件 <demo_proj.vcxproj> 的路径
*********************/
Mat img = imread("./images/demo.jpg"); //
imshow("demo", img);// 显示图片 6 秒
waitKey(6000);
return 0;
}

在图片中写入中文字符

使用 OpenCV 自带的 cv::putText() 函数往图片里写入中文字符时无法正常显示,而是显示为 “???”.

要解决这个问题, 在 Windows 平台上可以调用 Windows API 里的 HDC 系列函数来完成字符绘制,封装后提供的接口为 cv::putTextZH().

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "cv_puttextzh.h"

int main() {
cv::Mat image = cv::Mat(500, 500,CV_8UC3, cv::Scalar(144, 238, 144));
cv::putTextZH(
image,
" 你好!OpenCV",
cv::Point(image.cols/3, image.rows / 2),
CV_RGB(255, 255, 0),
30
);
cv::imshow(" 中文图窗 ", image);
cv::waitKey(0);

return 0;
}

1
2
3
4
5
> ls
CMakeLists.txt main.cpp cv_puttextzh.h
> mkdir build
> cd build
> cmake .. -A x64 -DOpenCV_DIR=D:/OpenCV/opencv/build/x64/vc15/lib

如果是其他平台,在 OpenCV 显示中文 | 知乎 中也有解决方案,但需要结合 freetype 和 harfbuzz 重新编译 opencv_contrib.

常用颜色 BGR 值

cv_color_def.h >folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// OpenCV 常用颜色 BGR 值
#ifndef CV_COLOR_DEF_H
#define CV_COLOR_DEF_H
#include <opencv2/core/types.hpp>
#define CV_COLOR_RED cv::Scalar(0,0,255) // 纯红
#define CV_COLOR_GREEN cv::Scalar(0,255,0) // 纯绿
#define CV_COLOR_BLUE cv::Scalar(255,0,0) // 纯蓝

#define CV_COLOR_DARKGRAY cv::Scalar(169,169,169) // 深灰色
#define CV_COLOR_DARKRED cv::Scalar(0,0,139) // 深红色
#define CV_COLOR_ORANGERED cv::Scalar(0,69,255) // 橙红色

#define CV_COLOR_CHOCOLATE cv::Scalar(30,105,210) // 巧克力
#define CV_COLOR_GOLD cv::Scalar(10,215,255) // 金色
#define CV_COLOR_YELLOW cv::Scalar(0,255,255) // 纯黄色

#define CV_COLOR_OLIVE cv::Scalar(0,128,128) // 橄榄色
#define CV_COLOR_LIGHTGREEN cv::Scalar(144,238,144) // 浅绿色
#define CV_COLOR_DARKCYAN cv::Scalar(139,139,0) // 深青色


#define CV_COLOR_SKYBLUE cv::Scalar(230,216,173) // 天蓝色
#define CV_COLOR_INDIGO cv::Scalar(130,0,75) // 藏青色
#define CV_COLOR_PURPLE cv::Scalar(128,0,128) // 紫色

#define CV_COLOR_PINK cv::Scalar(203,192,255) // 粉色
#define CV_COLOR_DEEPPINK cv::Scalar(147,20,255) // 深粉色
#define CV_COLOR_VIOLET cv::Scalar(238,130,238) // 紫罗兰

#endif // CV_COLOR_DEF_H

Trackbar 滑动条

TrackbarDemo.h >folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <opencv2/opencv.hpp>
#include <opencv2/core/utils/logger.hpp>
//SimpleBlobDetector
cv::SimpleBlobDetector::Params pBLOBDetector;
cv::Mat src;
int iThStep = 10;
int iMinth = 10;
int iMaxth = 200;
int iMinBt = 10;
int iMinar = 10;
int iMaxar = 1500;
int iMinCir = 0;
int iMinIne = 0;
int iMinCon = 0;

void detect(int, void*)
{
pBLOBDetector.thresholdStep = iThStep;
pBLOBDetector.minThreshold = iMinth;
pBLOBDetector.maxThreshold = iMaxth;
pBLOBDetector.minRepeatability = 2;
pBLOBDetector.minDistBetweenBlobs = iMinBt;
pBLOBDetector.filterByColor = true;
pBLOBDetector.blobColor = 0;
// 斑点面积
pBLOBDetector.filterByArea = true;
pBLOBDetector.minArea = iMinar;
pBLOBDetector.maxArea = iMaxar;
// 斑点圆度
pBLOBDetector.filterByCircularity = true;
pBLOBDetector.minCircularity = iMinCir * 0.01;
pBLOBDetector.maxCircularity = (float)3.40282e+038;
// 斑点惯性率
pBLOBDetector.filterByInertia = true;
pBLOBDetector.minInertiaRatio = iMinIne * 0.01;
pBLOBDetector.maxInertiaRatio = (float)3.40282e+038;
// 斑点凸度
pBLOBDetector.filterByConvexity = true;
pBLOBDetector.minConvexity = iMinCon * 0.01;
pBLOBDetector.maxConvexity = (float)3.40282e+038;
//* 用参数创建对象
cv::Ptr<cv::SimpleBlobDetector> blob = cv::SimpleBlobDetector::create(pBLOBDetector);
//Ptr<SimpleBlobDetector> blob=SimpleBlobDetector::create();// 默认参数创建
//*blob 检测
std::vector<cv::KeyPoint> key_points;
//Mat dst;
//cvtColor(src, dst, COLOR_RGB2GRAY);

blob->detect(src, key_points);
cv::Mat outImg;
//src.copyTo(outImg);

// 绘制结果
cv::drawKeypoints(src, key_points, outImg, cv::Scalar(0, 0, 255));
cv::imshow("blob", outImg);
}

void main()
{
//cv::Mat src;
src = cv::imread("C:/Users/siyou/Desktop/af8aed46ec80b30b72c45cad6c3b9cd6.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cout << "Cannot load image" << std::endl;
return;
}
cv::imshow("src", src);
cv::namedWindow("Detectwindow", cv::WINDOW_NORMAL);
cv::createTrackbar(" 最小圆度 ", "Detectwindow", &iMinCir, 100, detect);
cv::createTrackbar(" 最小惯性率 ", "Detectwindow", &iMinIne, 100, detect);
cv::createTrackbar(" 最大凸度 ", "Detectwindow", &iMinCon, 100, detect);
cv::createTrackbar(" 阈值步距 ", "Detectwindow", &iThStep, 100, detect);
cv::createTrackbar(" 最小阈值 ", "Detectwindow", &iMinth, 255, detect);
cv::createTrackbar(" 最大阈值 ", "Detectwindow", &iMaxth, 255, detect);
cv::createTrackbar(" 最小距离 ", "Detectwindow", &iMinBt, 255, detect);
cv::createTrackbar(" 最小面积 ", "Detectwindow", &iMinar, 1000, detect);
cv::createTrackbar(" 最大面积 ", "Detectwindow", &iMaxar, 5000, detect);
detect(0, 0);
cv::waitKey(0);
}

OpenCV Contrib

在 OpenCV 中有部分模块 (modules) 通常没有稳定的 API, 并且未经充分测试, 因此, 它们不应该作为 OpenCV 正式发行版的一部分发布.
这些模块被单独开发, 并首先在 opencv_contrib 存储库中发布. 直至模块成熟并普及时, 才将其移至中央 OpenCV 存储库.

下载

要使用 opencv_contrib 必须将其与 OpenCV 主体部分联合编译, 因此需要下载具有相同版本号的源码. 这里统一使用 3.4.11.
从以下地址下载 OpenCVopencv_contrib的源码包, 如果 github 下载太慢可以尝试 gitee 极速下载. 将源码包解压到统一目录下,分别命名为 sourcesopencv_contrib. 并新建 build 文件夹.

1
2
3
4
- <OpenCV_Contrib>
- sources # opencv-3.4.11.zip 解压并重命名得到
- opencv_contrib # opencv_contrib-3.4.11.zip 解压得到
- build # 新建空文件夹

编译安装

打开CMake-Gui, 配置如下:

  • Where is the source code: <OpenCV_Contrib>/sources
  • Where to build the binaries: <OpenCV_Contrib>/build
    点击一次 Configure
    修改配置:
  • BUILD_opencv_world [√]
  • OPENCV_ENABLE_NONFREE [√]
  • OPENCV_EXTRA_MODULES_PATH: <OpenCV_Contrib>/opencv_contrib/modules
    并再次点击 Configure.
    之后 CMake 会从网上下载部分文件, 但往往因为网速问题而下载失败,输出红色 warning,此时可以按照提示去查看 <OpenCV_Contrib>/build/CMakeDownloadLog.txt, 其中记录了 CMake 试图从某些网址下载文件放到<OpenCV_Contrib>/sources/.cache/ 相应路径下. 以如下片段为例.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#do_copy "face_landmark_model.dat" "7505c44ca4eb54b4ab1e4777cb96ac05" "https://raw.githubusercontent.com/opencv/opencv_3rdparty/8afa57abc8229d611c4937165d20e2a2d9fc5a12/face_landmark_model.dat" "D:/OpenCV/opencv/build_contrib/testdata/cv/face/"
#missing "D:/OpenCV/opencv/build_contrib/testdata/cv/face//face_landmark_model.dat"
#check_md5 "D:/OpenCV/opencv/sources/.cache/data/7505c44ca4eb54b4ab1e4777cb96ac05-face_landmark_model.dat"
#mismatch_md5 "D:/OpenCV/opencv/sources/.cache/data/7505c44ca4eb54b4ab1e4777cb96ac05-face_landmark_model.dat" "cbe7f803f749dbe38cf6445036349412"
#delete "D:/OpenCV/opencv/sources/.cache/data/7505c44ca4eb54b4ab1e4777cb96ac05-face_landmark_model.dat"
#cmake_download "D:/OpenCV/opencv/sources/.cache/data/7505c44ca4eb54b4ab1e4777cb96ac05-face_landmark_model.dat" "https://raw.githubusercontent.com/opencv/opencv_3rdparty/8afa57abc8229d611c4937165d20e2a2d9fc5a12/face_landmark_model.dat"
#try 1
# timeout on name lookup is not supported
# Trying 185.199.108.133:443...
# TCP_NODELAY set
# connect to 185.199.108.133 port 443 failed: Timed out
# Failed to connect to raw.githubusercontent.com port 443: Timed out
# Closing connection 0
#

在该段日志中,CMake 试图从 “https://raw.githubusercontent.com/opencv/opencv_3rdparty/8afa57abc8229d611c4937165d20e2a2d9fc5a12/face_landmark_model.dat" 下载 “face_landmark_model.dat” 文件放到 “D:/OpenCV/opencv/build_contrib/testdata/cv/face/“ 路径下.
但是因为下载失败,<...>/cv/face/"路径下没有找到该文件. 并且去 D:/OpenCV/opencv/sources/.cache/data/路径下查看, 发现其中有一个命名为 7505c44ca4eb54b4ab1e4777cb96ac05-face_landmark_model.dat 的空文件,大小为 0Kb. 因为是空的,所以在接下来的 md5 校验中不匹配.CMake delete 了该文件, 又尝试去网络上下载,但由于网络超时, 下载失败.

通过分析以上日志, 可以得出解决办法:

  1. 手动从以上路径下载相应文件,重命名后放到 <OpenCV_Contrib>/sources/.cache/ 相应路径下,放到该路径下的文件名应当类似于7505c44ca4eb54b4ab1e4777cb96ac05-face_landmark_model.dat, 文件名前是一段 md5 校验码.
  2. 或者: 直接下载文件复制到 <OpenCV_Contrib>/build/testdata/cv/face//face_landmark_model.dat, 此时文件名前不用加 md5 码.

所有文件手动下载后,再次点击Configure,应当不再输出红色的 warning 信息, 否则还应再次检查日志文件.

点击 Generate 生成 VS2017 解决方案, 点击 Open Project 打开. 在 VS2017 工具栏选择: 生成 -> 批生成 ,勾选ALL_BUILD Debug,ALL_BUILD Release, INSTALL Debug, INSTALL Release 四项, 点击 生成 . 等待编译并安装, 之后在build/install/ 路径下会生成相应的库文件用于调用.

Reference

InputArray/OutputArray 形参类型说明

在写 OpenCV C++ 程序时经常会看到其内置函数的形参是 cv::InputArray,cv::OutputArray 等类型; 如果仅仅是调用这些函数, 直接传入 cv::Mat,std::vector<cv::Mat> 等类型的实参即可.
因为 OpenCV 定义的这些类型可以方便地看出形参是输入参数还是输出参数, 看起来比 const cv::Mat&,cv::Mat& 要专业些, 所以有些自己写的函数也想用这种类型, 本项目就简单介绍一下这些类型如何使用.

cv::InputArray

cv::InputArray是输入参数, 一般可以对标 const cv::Mat& 类型, 在函数定义中使用的时候可以通过 cv::_InputArray::getMat() 获取到真正的 cv::Mat. 如下函数默认实参为cv::noArray(), 如果没有显式地传入有效实参, 则src.empty()true.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void test_InputArray(cv::InputArray src = cv::noArray())
{
CV_Assert(src.kind()==cv::_InputArray::MAT);
cv::Mat src_mat;
if(src.empty())
{
src_mat = cv::Mat::eye(100,100,CV_8UC1)*255;
}
else{
src_mat = src.getMat();
}
//do something...
}

cv::InputArrayOfArrays

cv::InputArrayOfArrays实际上就是 cv::InputArray, 但既然类型名为InputArrayOfArrays, 一般用于表示const std::vector<cv::Mat>&, 可以通过getMatVector() 获取到实际的数据.

cv::OutputArray

cv::OutputArray是输出参数, 该类型的变量并没有默认分配内存空间, 需要在函数定义中显式地申请空间.
以下函数默认实参为 cv::noArray(), 如果用户实际上不需要该数据, 则可以使用该实参, 在函数定义中通过cv::_OutputArray::needed() 判断是否需要, 然后执行相应的操作.

1
2
3
4
5
6
7
8
void test_OutputArray(cv::OutputArray dst = cv::noArray())
{
if(dst.needed())
{
cv::Mat mat = cv::Mat::eye(50,50,CV_8UC1)*255;
dst.assign(mat);
}
}

以上函数定义中通过定义 cv::Mat mat 来分配了空间, 然后再通过 assign() 即可将其通过 dst 输出.
还有另一种分配空间的方式:create, 此时无需 assign 即可输出.

1
2
3
4
5
6
7
8
9
void test_OutputArray(cv::OutputArray dst = cv::noArray())
{
if(dst.needed())
{
dst.create(cv::Size(50,50),CV_8UC1);
cv::Mat mat = dst.getMat();
mat = cv::Mat::eye(50,50,CV_8UC1)*255;
}
}

cv::OutputArrayOfArrays

cv::OutputArrayOfArrays实际上就是 cv::OutputArray, 但既然类型名为OutputArrayOfArrays, 一般用于表示std::vector<cv::Mat>&.
assign 前要先通过 create 为其分配空间.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void test_OutputArrayofArrays(cv::OutputArrayOfArrays dst)
{
std::vector<cv::Mat> outImages;
outImages.push_back(cv::Mat::eye(500,500,CV_8UC1)*255);
outImages.push_back(cv::Mat::eye(600,600,CV_8UC1)*255);

// 以下三种实现任意一种都是可行的
#if 1
// 表示 dst 是 outImages.size() 行 1 列的 array, 其中每个元素都是 CV_8UC1 类型的 cv::Mat
dst.create((int)outImages.size(), 1, CV_8UC1);
#endif

#if 0
// 表示 dst 是 1 维的 array, 该维度长度是 outImages.size(),array 内每个元素都是 CV_8UC1 类型的 cv::Mat
int size = (int)outImages.size();
dst.create(1, &size, CV_8UC1);
#endif

#if 0
// 表示 dst 是 2 维的 array, 两个维度长度分别是 1 和 outImages.size(),array 内每个元素都是 CV_8UC1 类型的 cv::Mat
// 由于我们这里实际用的是 `std::vector<cv::Mat>` 类型的变量, 因此交换两个维度的长度也是可以的.
// 即 int size[2] = {1,(int)outImages.size()};
int size[2] = {(int)outImages.size(),1};
dst.create(2, size, CV_8UC1);
#endif

dst.assign(outImages);
}

分辨 cv::Matstd::vector<cv::Mat>

由于 cv::InputArraycv::InputArrayOfArrays实际上是同一种类型, 因此尽管我们约定前者表示 cv::Mat, 后者表示std::vector<cv::Mat>, 但这并不是强制的. 为了确保用户传入的实参符合预期, 可以用kind() 来获取实参的类型.

1
CV_Assert(src.kind()==cv::_InputArray::MAT);

Reference

作者

Luo Siyou

发布于

2023-01-03

更新于

2023-03-20

许可协议