2.概述
算法原理
OpenCV(Open Source Computer Vision Library)是一个开源的机器视觉和机器学习软件库。1999年由英特尔公司发起,旨在为计算机视觉与人工智能领域提供通用函数库。
1.1 OpenCV的基本数据类型
OpenCV中的数据类型可分为三大类:
- 基本数据类型:直接由C++数据类型组装而来,包括向量、矩阵、点、矩形、尺寸等
- 助手对象:表示更抽象的概念,如垃圾收集指针类、范围对象等
- 大型数组类型 :典型代表是
cv::Mat类,用于表示任意维度的数组
1.2 核心数据类型
| 类名 | 用途 | 示例 |
|---|---|---|
cv::Vec |
固定长度的向量 | cv::Vec3f 表示3通道浮点向量 |
cv::Point |
点类容器 | cv::Point2i, cv::Point3f |
cv::Scalar |
四维双精度向量 | 用于表示颜色(B,G,R,Alpha) |
cv::Size |
尺寸类 | cv::Size2f |
cv::Rect |
矩形类 | (x, y, width, height) |
cv::RotatedRect |
有向矩形 | (center, size, angle) |
cv::Mat |
N维稠密数组 | 图像表示的核心类 |
1.2.1 数据类型概述
OpenCV设计了许多种数据类型,让计算机视觉和机器学习任务变得更加简单、直观。本节介绍OpenCV中的三类主要数据类型,以及操作这些数据的基本函数。
OpenCV中的数据类型可分为三大类:
-
基本数据类型:该类型直接由C++的数据类型(int或float等)组装而来,包括简单的向量和矩阵,以及简单的几何表示,如点、矩形和尺寸等。
-
助手对象:这些对象表示更抽象的概念,如垃圾收集指针类等,用于表示切片的范围对象,以及对某些算法终止条件的抽象等。
-
大型数组类型:该类型包含数组和其他常见的基本数据类型。大型数组类型的典型代表是cv::Mat类,用于表示包括任意基本元素的任意维度的数组。cv::Mat类的一个专门用途就是表示图像。
1.2.2 cv::Vec类
1.2.2.1基本概念
cv::Vec类可用来表示固定长度的向量(又称为固定向量类),是一个模板类,常使用[]来访问Vec向量成员,主要用来存储数值向量。除cv::Vec类外,还有固定维度的向量类cv::Matx。
cv::Matx类可用来处理固定大小的小维度矩阵运算。在计算机视觉中,有许多2×2、3×3和少量4×4的矩阵,以及用于各类变换的矩阵,都可以使用cv::Matx类来表示。值得注意的是,无论cv::Vec类,还是cv::Matx类,在编译时都需要知道变量的长度,这使得计算变得十分高效。
1.2.2.2 用法
- cv::Vec可以定义任意类型的向量:
cpp
cv::Vec<double, 3> myVector; //定义一个存放3个double型变量的向量
cv::Vec3d v3d(x0, x1, x2);
- cv::Mat类的基本运算
cpp
// 矩阵基本运算
m0 + m1, m0 - m1 // 矩阵加减法
s * m0, m0 * s // 矩阵与数相乘
m0.mul(m1) // 矩阵对应元素相乘
m0 * m1 // 矩阵相乘
m0.inv() // 矩阵求逆
m0.t() // 矩阵转置
m0 > m1, m0 == m1 // 逐元素比较
- cv::Vec类也可以使用以下预定义的类型:
cpp
- typedef cv::Vec<uchar, 2> Vec2b;
typedef cv::Vec<uchar, 3> Vec3b;
typedef cv::Vec<uchar, 4> Vec4b;
typedef cv::Vec<short, 2> Vec2s;
typedef cv::Vec<short, 3> Vec3s;
typedef cv::Vec<short, 4> Vec4s;
typedef cv::Vec<int, 2> Vec2i;
typedef cv::Vec<int, 3> Vec3i;
typedef cv::Vec<int, 4> Vec4i;
typedef cv::Vec<float, 2> Vec2f;
typedef cv::Vec<float, 3> Vec3f;
typedef cv::Vec<float, 4> Vec4f;
typedef cv::Vec<float, 6> Vec6f;
typedef cv::Vec<double, 2> Vec2d;
typedef cv::Vec<double, 3> Vec3d;
typedef cv::Vec<double, 4> Vec4d;
typedef cv::Vec<double, 6> Vec6d;
- 支持的运算如下:
cpp
v1 = v2 + v3
v1 = v2 - v3
v1 = v2 * scale
v1 = scale * v2
v1 = -v2
v1 += v2
v1 -= v2, v1 != v2
norm(v1) (euclidean norm)
1.2.2.3 示例代码
cpp
#include "stdafx.h"
#include "opencv.hpp"
using namespace std;
int main()
{
//定义与初始化
cv::Vec<int,3> myVec;
for (int i = 0; i < 3; i++)
myVec[i] = i;
cv::Vec3i v3i(0, 1, 2);
cv::Vec2d v2d(1.2, 2.4);
cv::Vec2d v2d_1(v2d);
//访问下标
cout << " myVec[0] = " << myVec[0] << endl;
cout << " myVec[1] = " << myVec[1] << endl;
cout << " myVec[2] = " << myVec[2] << endl << endl;
cout << " v3i[0] = " << v3i[0] << endl;
cout << " v3i[1] = " << v3i[1] << endl;
cout << " v3i[2] = " << v3i[2] << endl << endl;
cout << " v2d[0] = " << v2d[0] << endl;
cout << " v2d[1] = " << v2d[1] << endl << endl;
cout << " v2d_1(0) = " << v2d_1(0) << endl; //[]与()都能访问
cout << " v2d_1(1) = " << v2d_1(1) << endl << endl;
//计算
cv::Vec3f v1(1, 0, 0);
cv::Vec3f v2(1, 1, 0);
cv::Vec3f v3;
cout << " v1 = " << v1 << endl;
cout << " v2 = " << v2 << endl;
cout << " v1·v2 = " << v1.dot(v2) << endl; //点积
cout << " v1×v2 = " << v1.cross(v2) << endl; //叉积
cout << " v1 + v2 = " << v1 + v2 << endl; //加
cout << " v1 - v2 = " << v1 - v2 << endl; //减
cout << " v1 * 2 = " << v1*2 << endl; //乘标量
cout << " (v1 == v2) = " << (v1 == v2) << endl;
cout << " (v1 != v2) = " << (v1 != v2) << endl;
cout << " (v1+=v2) = " << (v1 += v2) << endl << endl;
system("PAUSE");
return 0;
}
cv:Vec类的初始化与基本运算
2.基本图像操作
2.1 颜色空间转换
RGB空间 :基于笛卡儿坐标系的三原色模型,OpenCV默认通道顺序为BGR
HSI空间 (色调、饱和度、亮度):
H={θ,B⩽G360−θ,B>GH=\begin{cases} \theta, & B \leqslant G \\ 360-\theta, & B > G \end{cases}H={θ,360−θ,B⩽GB>G
S=1−3(R+G+B)[min(R,G,B)]S=1-\frac{3}{(R+G+B)}[\min(R,G,B)]S=1−(R+G+B)3[min(R,G,B)]
I=13(R+G+B)I=\frac{1}{3}(R+G+B)I=31(R+G+B)
灰度空间 :
Y=0.299×R+0.587×G+0.114×BY=0.299\times R+0.587\times G+0.114\times BY=0.299×R+0.587×G+0.114×B
2.2 仿射变换
仿射变换的一般形式:
xy1\]=\[vw1\]\[t11t120t21t220t31t321\]\\begin{bmatrix} x \\\\ y \\\\ 1 \\end{bmatrix} = \\begin{bmatrix} v \\\\ w \\\\ 1 \\end{bmatrix} \\begin{bmatrix} t_{11} \& t_{12} \& 0 \\\\ t_{21} \& t_{22} \& 0 \\\\ t_{31} \& t_{32} \& 1 \\end{bmatrix} xy1 = vw1 t11t21t31t12t22t32001
| 变换类型 | 变换矩阵 |
|------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| 恒等变换 | \[100010\]\\begin{bmatrix} 1 \& 0 \& 0 \\\\ 0 \& 1 \& 0 \\end{bmatrix}\[100100\] |
| 尺度变换 | \[Cx000Cy0\]\\begin{bmatrix} C_x \& 0 \& 0 \\\\ 0 \& C_y \& 0 \\end{bmatrix}\[Cx00Cy00\] |
| 旋转变换 | \[cosθ−sinθsinθcosθ\]\\begin{bmatrix} \\cos\\theta \& -\\sin\\theta \\\\ \\sin\\theta \& \\cos\\theta \\end{bmatrix}\[cosθsinθ−sinθcosθ\] |
| 平移变换 | \[10tx01ty\]\\begin{bmatrix} 1 \& 0 \& t_x \\\\ 0 \& 1 \& t_y \\end{bmatrix}\[1001txty\] |
### 2.3 直方图均衡化
变换函数:
sk=T(rk)=∑j=0knjn,k=0,1,2,⋯ ,L−1s_k = T(r_k) = \\sum_{j=0}\^{k}\\frac{n_j}{n}, \\quad k=0,1,2,\\cdots,L-1sk=T(rk)=j=0∑knnj,k=0,1,2,⋯,L−1
### 2.4 示例代码
**示例代码2-1 读取、显示和存储图像**
```cpp
#include
只有 αi>0\alpha_i > 0αi>0 的样本(支持向量)决定分类超平面。
8.4 核方法
通过核函数将数据映射到高维空间实现非线性分类:
K(x,z)=⟨ϕ(x),ϕ(z)⟩K(x, z) = \langle \phi(x), \phi(z) \rangleK(x,z)=⟨ϕ(x),ϕ(z)⟩
常用核函数:
| 核函数 | 公式 | 参数 |
|---|---|---|
| 线性核 | K(x,z)=xTzK(x,z) = x^TzK(x,z)=xTz | 无 |
| 多项式核 | K(x,z)=(γxTz+c)dK(x,z) = (\gamma x^Tz + c)^dK(x,z)=(γxTz+c)d | γ,c,d\gamma, c, dγ,c,d |
| RBF核 | K(x,z)=exp(−γ∣x−z∣2)K(x,z) = \exp(-\gamma|x-z|^2)K(x,z)=exp(−γ∣x−z∣2) | γ\gammaγ |
| Sigmoid核 | K(x,z)=tanh(γxTz+c)K(x,z) = \tanh(\gamma x^Tz + c)K(x,z)=tanh(γxTz+c) | γ,c\gamma, cγ,c |
8.5 SVR回归
minw,b12∥w∥2+C∑i=1m(ξi+ξi∗)\min_{w,b} \frac{1}{2}\|w\|^2 + C\sum_{i=1}^{m}(\xi_i + \xi_i^*)w,bmin21∥w∥2+Ci=1∑m(ξi+ξi∗)
s.t.y(i)−(wTx(i)+b)⩽ε+ξi\text{s.t.} \quad y^{(i)} - (w^Tx^{(i)}+b) \leqslant \varepsilon + \xi_is.t.y(i)−(wTx(i)+b)⩽ε+ξi
8.6 OpenCV实现
cv::ml::SVM类:
cpp
// 创建SVM
Ptr<SVM> svm = SVM::create();
// SVM类型
svm->setType(SVM::C_SVC); // C-SVM分类
// SVM::NU_SVC // v-SVM分类
// SVM::ONE_CLASS // 单类分类
// SVM::EPS_SVR // ε-SVR回归
// SVM::NU_SVR // v-SVR回归
// 核函数
svm->setKernel(SVM::RBF); // RBF核
// SVM::LINEAR // 线性核
// SVM::POLY // 多项式核
// SVM::SIGMOID // Sigmoid核
// 参数
svm->setC(10); // 惩罚参数C
svm->setGamma(0.5); // RBF/Poly/Sigmoid参数
svm->setDegree(3); // 多项式核度数
svm->setP(0.1); // ε-SVR参数
svm->setNu(0.5); // v-SVM参数
// 训练
svm->train(trainData, ROW_SAMPLE, trainLabels);
// 自动参数优化
svm->trainAuto(trainData, 10); // 10折交叉验证
// 预测
Mat response;
svm->predict(testData, response);
// 获取支持向量
Mat supportVectors = svm->getSupportVectors();
8.7 示例代码
示例代码1 HOG特征+SVM手写数字分类
cpp
#include <opencv2/ml.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
using namespace cv;
using namespace cv::ml;
// 偏斜校正函数
Mat deskew(const Mat& img) {
Moments m = moments(img);
if (m.m00 == 0) return img;
double skew = m.mu11 / m.mu02;
Mat warpMat = (Mat_<float>(2, 3) <<
1, skew, -0.5 * skew * img.rows,
0, 1, 0);
Mat result;
warpAffine(img, result, warpMat, img.size());
return result;
}
// HOG特征计算
vector<float> computeHOG(const Mat& img) {
HOGDescriptor hog(Size(20, 20), Size(8, 8), Size(4, 4),
Size(4, 4), 9);
vector<float> descriptors;
hog.compute(img, descriptors);
return descriptors;
}
int main() {
// 读取并预处理图像
Mat bigImg = imread("digits.png", 0);
Mat labels;
vector<Mat> trainDigits, testDigits;
// ... 数据准备代码 ...
// 计算HOG特征
Mat trainData, trainLabels;
for (size_t i = 0; i < trainDigits.size(); i++) {
Mat deskewed = deskew(trainDigits[i]);
vector<float> hog = computeHOG(deskewed);
trainData.push_back(Mat(hog).t());
trainLabels.push_back(label);
}
// 创建并训练SVM
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::RBF);
svm->setC(10);
svm->setGamma(0.05);
// 使用auto训练优化参数
// svm->trainAuto(trainData, 10);
svm->train(trainData, ROW_SAMPLE, trainLabels);
// 测试
Mat testData;
for (size_t i = 0; i < testDigits.size(); i++) {
Mat deskewed = deskew(testDigits[i]);
vector<float> hog = computeHOG(deskewed);
testData.push_back(Mat(hog).t());
}
Mat response;
svm->predict(testData, response);
float accuracy = countNonZero(response == testLabels) /
(float)testLabels.rows;
cout << "测试精度: " << accuracy << endl;
return 0;
}
9.神经网络
9.1 神经元模型(感知器)
输入:x∈Rnx \in \mathbb{R}^nx∈Rn
输出:hθ(x)=g(z)=g(θTx+b)h_\theta(x) = g(z) = g(\theta^Tx + b)hθ(x)=g(z)=g(θTx+b)
9.2 激活函数
| 激活函数 | 公式 |
|---|---|
| Sigmoid | 11+e−z\frac{1}{1+e^{-z}}1+e−z1 |
| Tanh | ez−e−zez+e−z\frac{e^z - e^{-z}}{e^z + e^{-z}}ez+e−zez−e−z |
| ReLU | max(0,z)\max(0, z)max(0,z) |
9.3 多层感知器结构
- 输入层:接收特征向量
- 隐藏层:提取特征
- 输出层:输出预测结果
9.4 前向传播
a(l)=g(z(l))=g(W(l−1)a(l−1)+b(l))a^{(l)} = g(z^{(l)}) = g(W^{(l-1)}a^{(l-1)} + b^{(l)})a(l)=g(z(l))=g(W(l−1)a(l−1)+b(l))
9.5 代价函数
J(θ)=L(θ)+λR(θ)J(\theta) = \mathcal{L}(\theta) + \lambda R(\theta)J(θ)=L(θ)+λR(θ)
交叉熵损失:
L(θ)=−1N∑i=1N∑k=1K[yk(i)log(y^k(i))+(1−yk(i))log(1−y^k(i))]\mathcal{L}(\theta) = -\frac{1}{N}\sum_{i=1}^{N}\sum_{k=1}^{K}[y_k^{(i)}\log(\hat{y}_k^{(i)}) + (1-y_k^{(i)})\log(1-\hat{y}_k^{(i)})]L(θ)=−N1i=1∑Nk=1∑K[yk(i)log(y^k(i))+(1−yk(i))log(1−y^k(i))]
L2正则项:
λR(θ)=λ2N∑l=1L−1∑i=1sl∑j=1sl+1(θji(l))2\lambda R(\theta) = \frac{\lambda}{2N}\sum_{l=1}^{L-1}\sum_{i=1}^{s_l}\sum_{j=1}^{s_{l+1}}(\theta_{ji}^{(l)})^2λR(θ)=2Nλl=1∑L−1i=1∑slj=1∑sl+1(θji(l))2
9.6 梯度下降法
参数更新:
θ:=θ−α∂J(θ)∂θ\theta := \theta - \alpha \frac{\partial J(\theta)}{\partial \theta}θ:=θ−α∂θ∂J(θ)
9.7 反向传播算法(BP)
1. 前向传播计算各层输出
2. 计算输出层误差:δ(L)=(a(L)−y)⊙g′(z(L))\delta^{(L)} = (a^{(L)} - y) \odot g'(z^{(L)})δ(L)=(a(L)−y)⊙g′(z(L))
3. 反向递推:δ(l)=(W(l))Tδ(l+1)⊙g′(z(l))\delta^{(l)} = (W^{(l)})^T\delta^{(l+1)} \odot g'(z^{(l)})δ(l)=(W(l))Tδ(l+1)⊙g′(z(l))
4. 计算梯度并更新权重
9.8 OpenCV实现
cv::ml::ANN_MLP类:
cpp
// 创建MLP
Ptr<ANN_MLP> mlp = ANN_MLP::create();
// 设置网络结构 [输入层, 隐藏层1, ..., 输出层]
Mat layerSizes = (Mat_<int>(1, 4) << 784, 100, 50, 10);
mlp->setLayerSizes(layerSizes);
// 设置激活函数
mlp->setActivationFunction(ANN_MLP::SIGMOID_SYM, 0.6, 1);
// ANN_MLP::IDENTITY
// ANN_MLP::RELU
// ANN_MLP::LEAKYRELU
// 设置训练方法
mlp->setTrainMethod(ANN_MLP::RPROP); // 默认
// ANN_MLP::BACKPROP
// ANN_MLP::ANNEAL
// RPROP参数
mlp->setRpropDW0(0.1); // 初始步长
mlp->setRpropDWMin(1e-7); // 最小步长
mlp->setRpropDWMax(50.0); // 最大步长
// BP参数
mlp->setBackpropWeightScale(0.1); // 权重梯度
mlp->setBackpropMomentumScale(0.1); // 冲量
// 终止条件
mlp->setTermCriteria(TermCriteria(
TermCriteria::MAX_ITER + TermCriteria::EPS,
1000, 0.01));
// 训练
mlp->train(trainData, ROW_SAMPLE, trainLabels);
// 预测
Mat response;
mlp->predict(testData, response);
9.9 示例代码
示例代码1 MLP手写数字分类
cpp
#include <opencv2/ml.hpp>
#include <opencv2/core.hpp>
using namespace cv;
using namespace cv::ml;
int main() {
// MNIST数据读取
// ... mnistReader代码 ...
// 准备训练数据(784维输入,10维输出)
Mat trainData, trainLabels;
Mat testData, testLabels;
// ... 数据准备 ...
// 创建MLP
Ptr<ANN_MLP> mlp = ANN_MLP::create();
// 设置网络结构 [输入, 隐藏层, 输出]
Mat layerSizes = (Mat_<int>(1, 3) << 784, 300, 10);
mlp->setLayerSizes(layerSizes);
// 设置激活函数
mlp->setActivationFunction(ANN_MLP::SIGMOID_SYM, 0.6, 1);
// 设置训练方法
mlp->setTrainMethod(ANN_MLP::RPROP);
mlp->setRpropDW0(0.1);
mlp->setRpropDWMin(1e-7);
mlp->setRpropDWMax(50.0);
// 设置终止条件
mlp->setTermCriteria(TermCriteria(
TermCriteria::MAX_ITER + TermCriteria::EPS,
1000, 0.01));
// 训练
mlp->train(trainData, ROW_SAMPLE, trainLabels);
// 测试
Mat response;
mlp->predict(testData, response);
// 计算精度
int correct = 0;
for (int i = 0; i < response.rows; i++) {
Point maxLoc;
minMaxLoc(response.row(i), 0, 0, 0, &maxLoc);
int predicted = maxLoc.x;
int actual = testLabels.at<int>(i);
if (predicted == actual) correct++;
}
cout << "MLP测试精度: " << (float)correct / response.rows << endl;
return 0;
}
9.10 应用提示
| 网络结构示例 | 说明 |
|---|---|
| [784, 10, 10] | 单隐藏层,10个神经元 |
| [784, 100, 10] | 单隐藏层,100个神经元 |
| [784, 300, 10] | 单隐藏层,300个神经元 |
| [784, 100, 50, 10] | 双隐藏层 |
训练时间与精度随网络复杂度增加而增加。
10.深度神经网络
10.1 卷积神经网络(CNN)
CNN是深度学习的基石,比全连接网络更适合图像处理。
CNN基本结构:
bash
输入 → Conv → ReLU → Pooling → ... → Conv → ReLU →
Pooling → FC → ReLU → FC → Softmax → 输出
10.2 卷积层
二维卷积 :
s(i,j)=(I∗K)(i,j)=∑m∑nI(m,n)K(i−m,j−n)s(i,j) = (I*K)(i,j) = \sum_m \sum_n I(m,n)K(i-m,j-n)s(i,j)=(I∗K)(i,j)=m∑n∑I(m,n)K(i−m,j−n)
填充(Padding) :在输入周围填充像素维持尺寸
步幅(Stride):滤波器移动的间隔
输出尺寸公式 :
OW=W+2P−FWS+1OW = \frac{W + 2P - FW}{S} + 1OW=SW+2P−FW+1
OH=H+2P−FHS+1OH = \frac{H + 2P - FH}{S} + 1OH=SH+2P−FH+1
10.3 池化层
- 最大池化:取区域最大值
- 平均池化 :取区域平均值
作用:减少特征图尺寸,降低计算量,增加平移不变性。
10.4 Softmax层
用于多分类:
Softmax(zi)=ezi∑jezj\text{Softmax}(z_i) = \frac{e^{z_i}}{\sum_j e^{z_j}}Softmax(zi)=∑jezjezi
10.5 OpenCV DNN模块
cv::dnn::Net类:
cpp
#include <opencv2/dnn.hpp>
using namespace cv;
using namespace cv::dnn;
// 加载模型
Net net = readNetFromCaffe(prototxt, caffeModel); // Caffe
// Net net = readNetFromTensorflow(model); // TensorFlow
// Net net = readNetFromTorch(model); // Torch
// Net net = readNetFromONNX(model); // ONNX
// 设置后端和目标
net.setPreferableBackend(DNN_BACKEND_OPENCV);
net.setPreferableTarget(DNN_TARGET_CPU);
// DNN_TARGET_OPENCL // GPU
// DNN_TARGET_OPENCL_FP16 // FPGA
// 读取图像并预处理
Mat img = imread("test.jpg");
Mat blob = blobFromImage(img, 1.0/255, Size(224, 224),
Scalar(0,0,0), false, false);
// 前向传播
net.setInput(blob);
Mat prob = net.forward();
// 获取预测结果
Point classIdPoint;
double confidence;
minMaxLoc(prob.reshape(1, 1), 0, &confidence, 0, &classIdPoint);
int classId = classIdPoint.x;
10.6 应用示例类型
10.6.1 图像分类(GoogLeNet)
cpp
// 加载GoogLeNet模型
Net net = readNetFromCaffe("deploy.prototxt",
"caffenet_train_iter_10000.caffemodel");
Mat img = imread("test.jpg");
Mat blob = blobFromImage(img, 1.0f/255, Size(227, 227));
net.setInput(blob);
Mat prob = net.forward();
// 读取类别标签
vector<String> classNames = {...};
int classId;
minMaxLoc(prob, nullptr, nullptr, nullptr, &classIdPoint);
classId = classIdPoint.x;
cout << "预测类别: " << classNames[classId] << endl;
10.6.2 目标检测(YOLOv4)
cpp
// 加载YOLOv4模型
Net net = readNetFromDarknet("yolov4.cfg", "yolov4.weights");
Mat img = imread("test.jpg");
Mat blob = blobFromImage(img, 1/255.0, Size(416, 416));
net.setInput(blob);
vector<Mat> outputs;
net.forward(outputs, net.getUnconnectedOutLayersNames());
// 解析检测结果
for (auto& output : outputs) {
for (int i = 0; i < output.rows; i++) {
Mat detection = output.row(i);
float* data = (float*)detection.data;
float confidence = data[4];
// ... 解析边界框 ...
}
}
10.6.3 实例分割(Mask R-CNN)
cpp
// 加载Mask R-CNN模型
Net net = readNetFromTensorflow("mask_rcnn_inception_v2_coco.pb",
"mask_rcnn_inception_v2_coco.pbtxt");
Mat img = imread("test.jpg");
Mat blob = blobFromImage(img, 1.0f, Size(800, 800));
net.setInput(blob);
vector<String> outNames = {"detection_out_final", "mask_detect_out"};
vector<Mat> outputs;
net.forward(outputs, outNames);
// 解析分割结果
Mat dets = outputs[0]; // 检测结果
Mat masks = outputs[1]; // 分割掩码
10.7 应用提示
1. 模型格式 :支持Caffe、TensorFlow、Torch、PyTorch、Darknet和ONNX
2. GPU加速 :从OpenCV 4.1.0开始支持CUDA加速
3. 自定义层 :支持通过registerCustomLayer添加自定义层
4. 预处理:注意图像尺寸归一化和通道顺序(BGR→RGB)
11 附录:常用函数速查表
11.1 数据类型转换
| 操作 | 函数 |
|---|---|
| RGB转灰度 | cvtColor(img, gray, COLOR_BGR2GRAY) |
| RGB转HSV | cvtColor(img, hsv, COLOR_BGR2HSV) |
| 图像归一化 | normalize(img, img, 0, 255, NORM_MINMAX) |
11.2 数据集划分
cpp
Ptr<TrainData> data = TrainData::loadFromCSV(...);
data->setTrainTestSplitRatio(0.8); // 80%训练, 20%测试
Mat trainSamples = data->getTrainingSamples();
Mat trainLabels = data->getTrainingResponses();
Mat testSamples = data->getTestSamples();
Mat testLabels = data->getTestResponses();
11.3 模型持久化
cpp
// 保存
model->save("model.xml");
// 加载
Ptr<ModelType> model = Algorithm::load<ModelType>("model.xml");