cv 命名空间
所有 OpenCV 类与函数均位于 cv 命名空间中。使用 cv:: 前缀或 using namespace cv; 访问。若外部名称与 STL 或其他库冲突,请使用显式命名空间限定符解决冲突。
#include "opencv2/core.hpp"
using namespace cv;
Mat H = findHomography(points1, points2, RANSAC, 5);
// 名称冲突时显式限定
Mat a(100, 100, CV_32F);
cv::log(a, a);
自动内存管理
cv::Mat 等数据结构带有析构函数,通过引用计数管理底层缓冲区:复制 Mat 只增加引用计数而不复制数据;引用计数归零时才释放缓冲区。clone() 创建完整拷贝。用户自定义类型可用类似 std::shared_ptr 的 cv::Ptr(推荐 makePtr<T>)。
// 创建 8MB 大矩阵
Mat A(1000, 1000, CV_64F);
// 仅为同一矩阵创建新头文件;瞬时操作,与矩阵大小无关
Mat B = A;
// 为 A 的第 3 行创建头文件;同样不复制数据
Mat C = B.row(3);
// 真正的数据拷贝
Mat D = B.clone();
// 将 B 第 5 行拷入 C(即 A 的第 5 行 → A 的第 3 行)
B.row(5).copyTo(C);
// A 与 D 共享数据;修改 A 后 B、C 引用的仍是旧版本
A = D;
// B 变为空矩阵(不再引用缓冲区)
B.release();
// 最终深拷贝 C;大矩阵不再被引用而释放
C = C.clone();
输出数据自动分配
函数通常会根据输入数组的大小与类型自动分配或重新分配输出数组。核心技术是 Mat::create:若数组已具备所需大小与类型则不做任何事,否则先释放旧数据再分配新缓冲区。循环中帧分辨率不变时,数组只在首次分配。例外:mixChannels、RNG::fill 等需提前分配。
VideoCapture cap(0);
Mat frame, edges;
namedWindow("edges", WINDOW_AUTOSIZE);
for (;;) {
cap >> frame; // 分辨率已知 → frame 自动分配
cvtColor(frame, edges, COLOR_BGR2GRAY); // edges 自动分配
GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
Canny(edges, edges, 0, 30, 3);
imshow("edges", edges);
if (waitKey(30) >= 0) break;
}
饱和运算
图像像素常以 8/16 位紧凑编码,运算结果可能超出取值范围。OpenCV 使用饱和运算裁剪到合法范围(如 0..255),避免回绕造成视觉伪影。公式:I(x,y) = min(max(round(r), 0), 255)。32 位整数结果不应用饱和。C++ 中通过 cv::saturate_cast<> 实现,SIMD 指令(paddusb、packuswb)保证一致行为。
I.at<uchar>(y, x) = saturate_cast<uchar>(r);
// 等价于:I(x,y) = min(max(round(r), 0), 255)
// 8 位有符号、16 位有符号/无符号类型同理
固定像素类型与有限模板
为控制编译时间、代码体积并便于 Python/Java 等绑定,OpenCV 基于多态与运行时分发,仅在像素访问、saturate_cast、cv::Ptr 等场景使用轻量模板。数组元素必须属于固定基本类型集合(8/16/32/64 位整数、浮点、布尔),或由同类型元素组成的多通道数组(最多 128 通道)。5.0 新增 CV_16BF、CV_32U、CV_64U、CV_64S、CV_Bool,并支持 0D/1D 数组。完整列表见数据类型页。
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
#define CV_16F 7
#define CV_16BF 8 // 5.0 新增
#define CV_Bool 9 // 5.0 新增
#define CV_64U 10 // 5.0 新增
#define CV_64S 11 // 5.0 新增
#define CV_32U 12 // 5.0 新增
Mat mtx(3, 3, CV_32F); // 3×3 浮点矩阵
Mat cmtx(10, 1, CV_64FC2); // 10×1 双通道
Mat img(Size(1920, 1080), CV_8UC3); // 彩色图像
InputArray 与 OutputArray
许多函数既可接受 cv::Mat,也可接受 std::vector<>(如点集)、cv::Matx<>(如 3×3 单应矩阵)等。为避免 API 重复,OpenCV 引入代理类:InputArray 用于只读输入,OutputArray 用于输出。通常无需关心这些中间类型,也无需显式声明其变量,直接传入 Mat、vector、Matx、Vec 或 Scalar 即可。可选数组传入 cv::noArray()。
// 函数签名使用代理类型
void someFunc(InputArray src, OutputArray dst);
// 实际调用可传入多种容器
someFunc(mat, result);
someFunc(vector<Point2f>{...}, homography);
错误处理
OpenCV 使用异常表示关键错误。cv::Exception 派生自 std::exception,可用标准 C++ 机制优雅处理。常用抛出方式:CV_Error(errcode, description)、CV_Error_(errcode, (printf格式))、CV_Assert(condition);性能关键代码用仅在 Debug 配置保留的 CV_DbgAssert。输入格式正确但算法无法成功时(如优化未收敛),通常返回特殊错误码(多为布尔)。由于自动内存管理,出错时中间缓冲区自动释放,只需 try/catch。
try {
// 调用 OpenCV
} catch (const cv::Exception& e) {
std::cout << "exception caught: " << e.what() << std::endl;
}
// 抛出示例
CV_Error(Error::StsBadArg, "unsupported size");
CV_Assert(src.type() == CV_32F);
多线程与可重入
当前 OpenCV 实现完全可重入:不同线程可调用同一函数,或调用不同类实例的同一方法。同一 Mat 也可在不同线程中使用,因为引用计数操作采用架构相关的原子指令。注意:对同一块像素数据的并发写入仍需要应用层同步。
// 引用计数为原子操作 → 跨线程共享 Mat 头文件是安全的
// 多线程各自处理不同 ROI 时通常无需额外加锁
Universal Intrinsics(跨平台 SIMD)
5.0 升级到 Universal Intrinsics 2.0,用统一的内在函数接口编写一次 SIMD 代码,自动映射到 SSE/AVX/NEON/SVE/RISC-V 等指令集。图像 warp 等热点路径由此获得 10% 到 300%+ 的加速。应用层通常无需直接使用该接口,但了解它有助于理解性能特征。另有 HAL(硬件加速层)供芯片厂商注入定制实现。
// 应用代码无需改动,编译期自动选择最优路径
// 后端:SIMD (SSE/AVX/NEON/SVE/RISC-V)、OpenCL、CUDA、Vulkan