OpenCV入门学习-核心功能(一)

环境:

  • win11 x64
  • OpenCV 4.9.0
  • VS 2022 (v143)
  • C++ (ISO C++ 14 标准)

1. Mat

1.1. 数据结构与内存管理

Mat 是OpenCV的核心图像容器类,采用自动内存管理机制,无需手动分配/释放内存。其结构分为两部分:

  • 矩阵头(Header)
    固定大小(约数十字节),存储元信息:
    • 矩阵维度(rows, cols, channels)
    • 数据类型(如 CV_8UC3
    • 存储布局(step:每行的字节数)
    • 引用计数器
    • 数据指针地址
  • 数据块(Data)
    动态内存区域,存储实际像素值,采用引用计数机制管理:
    • 浅拷贝时多个 Mat 对象共享数据块
    • 最后一个引用对象销毁时自动释放内存

1.2. 深浅拷贝对比

1
2
3
cv::Mat A = cv::imread("dog.jpg", cv::IMREAD_COLOR);
cv::Mat B = A; // 浅拷贝(仅复制Header)
cv::Mat C = A.clone(); // 深拷贝(完整数据副本)
操作类型 内存开销 数据独立性 适用场景
浅拷贝 极小 共享数据 快速创建视图/ROI
深拷贝 完全独立 需独立修改像素的场景

📌 关键机制

  • 修改浅拷贝对象的像素会直接影响原对象
  • 使用 clone()copyTo() 进行显式深拷贝

1.3. 色彩空间与存储布局

  • 默认色彩空间:BGR(非 RGB ),与图像显示系统兼容
  • 通道顺序:对于3通道矩阵,像素按 B-G-R 顺序存储
  • 内存布局:连续存储(可通过 isContinuous() 判断),行优先存储

1.4. 构造函数全解析

1.4.1. 默认构造函数(延迟初始化)

1
2
cv::Mat matEmpty; // 创建空矩阵(Header无数据指针)
matEmpty = cv::imread("image.jpg"); // 延迟加载时分配内存

1.4.2. 指定尺寸与类型

1
2
3
// 创建480x640的3通道图像(BGR格式)
// Mat(int rows, int cols, int type);
cv::Mat mat(480, 640, CV_8UC3);

1.4.3. 初始化值构造函数

1
2
3
// 创建3x3蓝色矩阵(B=255, G=0, R=0)
// Mat(int rows, int cols, int type, const cv::Scalar& s);
cv::Mat matInit(3, 3, CV_8UC3, cv::Scalar(255, 0, 0));

1.4.4. 外部数据共享

1
2
float externalData[6] = {1,2,3,4,5,6};
cv::Mat matExt(2, 3, CV_32FC1, externalData); // 共享内存

1.4.5. ROI构造函数

1
2
cv::Rect roiArea(10, 10, 100, 100); // (x,y,width,height)
cv::Mat roi(img, roiArea); // 浅拷贝原图的ROI区域

1.4.6. 特殊矩阵生成

1
2
cv::Mat zeros = cv::Mat::zeros(3, 3, CV_32F); // 3x3浮点零矩阵
cv::Mat eye = cv::Mat::eye(3, 3, CV_8UC1); // 3x3单位矩阵

1.5. 数据访问与输出

1
2
3
4
5
6
7
// 正确输出矩阵内容(需指定格式)
cv::Mat intMatrix = (cv::Mat_<int>(2,2) << 1,2,3,4);
std::cout << "Matrix:\n" << cv::format(intMatrix, cv::Formatter::FMT_C) << std::endl;

// 输出结果:
// [1, 2;
// 3, 4]

⚠️ 注意事项

  • 使用 at<T>(row,col) 访问元素时需确保类型匹配(如 CV_8UC3 对应 cv::Vec3b
  • 多通道矩阵的 cv::Scalar 初始化顺序为 (B,G,R)
  • 外部数据构造的 cv::Mat 对象不管理原数据内存,需手动维护生命周期

1.6. 其他操作

  1. 多维矩阵支持
    cv::Mat 可处理N维数据,通过 dims 参数指定维度:

    1
    2
    3
    // Mat(int ndims, const int* sizes, int type, const cv::Scalar& s);
    int sizes[] = {3, 4, 5}; // 3维:3x4x5
    cv::Mat ndMat(3, sizes, CV_8UC1, cv::Scalar::all(0));
  2. 内存连续性优化
    使用 create() 方法重新分配连续内存:

    1
    mat.create(rows, cols, type); // 重置矩阵尺寸/类型
  3. 表达式计算优化
    OpenCV重载了矩阵运算符( + , * 等),自动避免中间变量拷贝:

    1
    2
    cv::Mat C = A.mul(B) + 0.5; // 对应元素相乘再加 0.5
    // ps: 矩阵乘法时直接用 *

1.7. 补充 CV_ 数据类型

OpenCV 中的 CV_ 数据结构(数据类型)是通过预定义宏(CV_<位数><类型>C<通道数>)表示的矩阵数据类型,主要用于定义 cv::Mat 或图像的数据类型。常见类型如下:

1.7.1. 命名规则

格式:CV_<bit_depth>(S|U|F)C<通道数>

  • bit_depth:数据位数(如 8, 16, 32, 64)。
  • S|U|F
    • U:无符号整数(Unsigned,如 uchar)。
    • S:有符号整数(Signed,如 int)。
    • F:浮点数(Float,如 floatdouble)。
  • C<通道数>:通道数(1-4,更高通道数需要自定义类型)。

1.7.2. 常见数据类型

数据类型 说明 C++ 类型等效 典型用途
CV_8UC1 8 位无符号单通道 uchar 灰度图像
CV_8UC3 8 位无符号三通道 cv::Vec3b BGR 彩色图像(默认格式)
CV_8UC4 8 位无符号四通道 cv::Vec4b 带透明度(BGRA)的图像
CV_16UC1 16 位无符号单通道 ushort 深度图像(如深度传感器)
CV_16SC1 16 位有符号单通道 short 特定算法的中间结果
CV_32SC1 32 位有符号单通道 int 像素索引或整数计算
CV_32FC1 32 位浮点单通道 float 图像处理中间结果(如边缘检测)
CV_32FC3 32 位浮点三通道 cv::Vec3f 浮点彩色数据(如 HDR)
CV_64FC1 64 位双精度浮点单通道 double 高精度计算(如几何变换)

1.7.3. 特殊类型与限制

  • 通道数限制: 默认支持 1~4 通道。超过 4 通道需自定义,如通过 CV_MAKETYPE 宏创建:

    1
    #define CV_8UC(n) CV_MAKETYPE(CV_8U, n)  // 例如 CV_8UC(5) 表示 5 通道
  • 默认图像类型: OpenCV 读取图像的默认格式为 CV_8UC3(BGR 三通道彩色)。

  • 浮点图像范围CV_32FCV_64F 类型的数据范围通常为 [0.0, 1.0][0, 255],需根据算法做归一化。

1.7.4. 类型转换与创建

  • 创建指定类型的 cv::Mat 矩阵

    1
    2
    cv::Mat img_float(480, 640, CV_32FC3);  // 创建一个 32 位浮点三通道矩阵
    cv::Mat img_gray(100, 100, CV_8UC1, cv::Scalar(0)); // 创建单通道灰度图
  • 类型转换: 使用 cv::Mat::convertTo()cv::cvtColor()

    1
    2
    cv::Mat img_uint8;
    img_float.convertTo(img_uint8, CV_8UC3, 255.0); // 将浮点图像转为 8UC3

1.7.5. 示例

1
2
3
4
5
6
7
8
9
10
11
#include <opencv2/opencv.hpp>

int main() {
// 创建一个 3 通道 8UC3 的红色图像
cv::Mat red_image(300, 300, CV_8UC3, cv::Scalar(0, 0, 255));

// 创建一个单通道 32 位浮点矩阵
cv::Mat float_mat(100, 100, CV_32FC1, cv::Scalar(0.5f));

return 0;
}

1.7.6. 总结

CV_ 数据类型定义了矩阵中元素的存储格式,选择时需考虑以下因素:

  1. 数据范围(如 8 位足够表示 0-255 的像素)。
  2. 是否需要浮点运算(如几何变换需要高精度)。
  3. 通道数需求(如彩色图像需 3 通道)。

2. 图像扫描、查找表与性能优化

通过一个实际的颜色空间缩减示例,快速掌握 OpenCV 中图像处理的三大核心技能:图像扫描方法查找表(LUT)的应用以及算法性能测量

2.1. 图像矩阵的内存存储

在 OpenCV 中,图像数据以 Mat 对象存储,其内存布局与颜色通道紧密相关:

  1. 灰度图像:每个像素对应一个 uchar 值(0-255),按行连续存储。
  2. 彩色图像(BGR):每个像素包含3个通道(蓝、绿、红),每个通道一个 uchar 值。
    注意:OpenCV 默认使用 BGR 顺序而非 RGB !

内存中的存储方式

  • 按行连续存储:像素按行优先顺序排列,每行(row)称为一个 “高度”维度。
  • 多通道数据:每个像素的通道值连续存储。
    • 例如,BGR 图像的像素存储顺序为 [B0, G0, R0, B1, G1, R1, ...]
  • 可能的填充字节 (Padding)
    • 某些情况下,每行末尾可能有对齐填充(用 step 属性表示实际每行的字节数)。

通过 Mat::isContinuous() 可判断矩阵是否连续存储。若连续,可将图像视为一维数组处理,提升遍历效率。

2.2. 颜色空间缩减示例

假设我们需要将图像颜色值从 256 级缩减到更少的级别(例如每 10 个值合并为一个)。直接计算公式为:
\[\nonumber I_{\text{new}} = \lfloor \frac{I_{\text{old}}}{10} \rfloor \times 10 \]

但频繁的除法和乘法会拖慢性能。此时,查找表(Lookup Table, LUT)可预先计算所有可能的输入值对应的输出值,后续直接替换,大幅提升效率。

2.2.1. 查找表生成代码

1
2
3
4
int divideWith = 10;
uchar table[256];
for (int i = 0; i < 256; ++i)
table[i] = (uchar)(divideWith * (i/divideWith)); // 自动向下取整

2.3. 图像扫描方法对比

2.3.1. 高效方法:指针访问

通过指针直接操作内存,适合连续存储的图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cv::Mat& ScanImageWithPointer(cv::Mat& image, const uchar* table) {
CV_Assert(image.depth() == CV_8U); // 仅处理8位图像
int channels = image.channels();
int rows = image.rows;
int cols = image.cols * channels;

if (image.isContinuous()) {
cols *= rows;
rows = 1;
}

for (int i = 0; i < rows; ++i) {
uchar* p = image.ptr<uchar>(i);
for (int j = 0; j < cols; ++j)
p[j] = table[p[j]];
}
return image;
}

2.3.2. 安全方法:迭代器

使用 OpenCV 迭代器自动处理行间隙,代码更安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cv::Mat& ScanImageWithIterator(cv::Mat& image, const uchar* table) {
CV_Assert(image.depth() == CV_8U);
if (image.channels() == 1) {
cv::MatIterator_<uchar> it = image.begin<uchar>();
cv::MatIterator_<uchar> end = image.end<uchar>();
for (; it != end; ++it)
*it = table[*it];
} else if (image.channels() == 3) {
cv::MatIterator_<cv::Vec3b> it = image.begin<cv::Vec3b>();
cv::MatIterator_<cv::Vec3b> end = image.end<cv::Vec3b>();
for (; it != end; ++it) {
(*it)[0] = table[(*it)[0]]; // B通道
(*it)[1] = table[(*it)[1]]; // G通道
(*it)[2] = table[(*it)[2]]; // R通道
}
}
return image;
}

2.3.3. 动态地址计算:at 方法

适用于随机访问,但全图扫描时性能较差。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cv::Mat& ScanImageWithAt(cv::Mat& image, const uchar* table) {
CV_Assert(image.depth() == CV_8U);
if (image.channels() == 1) {
for (int i = 0; i < image.rows; ++i)
for (int j = 0; j < image.cols; ++j)
image.at<uchar>(i, j) = table[image.at<uchar>(i, j)];
} else if (image.channels() == 3) {
cv::Mat_<cv::Vec3b> _I = image;
for (int i = 0; i < image.rows; ++i)
for (int j = 0; j < image.cols; ++j) {
_I(i, j)[0] = table[_I(i, j)[0]]; // B通道
_I(i, j)[1] = table[_I(i, j)[1]]; // G通道
_I(i, j)[2] = table[_I(i, j)[2]]; // R通道
}
image = _I;
}
return image;
}

2.4. 终极优化:OpenCV 内置 LUT 函数

使用 cv::LUT() 函数,底层优化更高效,支持多线程。

void cv::LUT (InputArray src, InputArray lut, OutputArray dst)

1
2
3
4
5
6
7
8
9
cv::Mat ApplyLUT(cv::Mat& image, const uchar* table) {
cv::Mat lookupTable(1, 256, CV_8U);
uchar* p = lookupTable.ptr();
for (int i = 0; i < 256; ++i) p[i] = table[i];

cv::Mat result;
cv::LUT(image, lookupTable, result);
return result;
}

2.5. 性能对比

通过 cv::getTickCount()cv::getTickFrequency() 测量时间:

1
2
3
4
double t = (double)cv::getTickCount();
// 执行扫描操作
t = ((double)cv::getTickCount() - t) / cv::getTickFrequency();
std::cout << "耗时(秒): " << t << std::endl;
方法 平均耗时(毫秒)
指针访问 79.47
迭代器 83.72
动态地址计算(at) 93.78
LUT函数 32.58

结论

  1. 优先使用 OpenCV 内置函数(如 LUT()),性能最佳。
  2. 需要自定义遍历时,指针访问最快,迭代器更安全。
  3. 避免在循环中使用 at 方法进行全图扫描。

2.6. 实战建议

  • 连续存储优化:若图像连续(isContinuous() == true),可将其视为一维数组处理。
  • 多通道处理:注意 BGR 顺序,遍历时需分别操作每个通道。
  • 调试技巧:在 Debug模 式下,at 方法会检查越界访问,适合快速定位错误。

3. 图像处理中的掩膜操作

图像处理是计算机视觉的核心技术之一,而 OpenCV 作为最流行的开源库,为开发者提供了丰富的工具。本节将介绍 OpenCV 中的掩膜操作(Mask Operations),以图像对比度增强为例,理解其原理与实现。

3.1. 什么是掩膜操作?

掩膜操作通过一个称为 内核(Kernel)掩膜(Mask) 的小矩阵,对图像中的每个像素进行加权计算,从而调整其值。这种操作本质上是将当前像素及其邻域像素的值按权重相加,常用于图像滤波、边缘检测、锐化等任务。

例如,对比度增强的公式为:
\[ \nonumber I(i,j) = 5 \cdot I(i,j) - [I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)] \]

对应的内核矩阵为:
\[ \nonumber M = \begin{bmatrix} 0 & -1 & 0 \\ -1 & 5 & -1 \\ 0 & -1 & 0 \end{bmatrix} \]
中心像素权重为 5,上下左右像素权重为 -1,其余为 0 。通过该内核,可以突出中心像素,增强图像细节。

3.2. 手动实现掩膜操作

3.2.1. 关键步骤

  1. 边界处理:图像边缘的像素缺少邻域,通常直接设为0。
  2. 遍历像素:使用指针逐行访问像素,避免直接修改原图。
  3. 加权计算:根据内核公式重新计算每个像素的值。

3.2.2. 代码示例(简化版)

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 Sharpen(const cv::Mat& input, cv::Mat& output) {

CV_Assert(input.depth() == CV_8U); // 确保输入图像为8位图像

const int channels = input.channels();
output.create(input.size(), input.type()); // 创建与输入图像大小和类型相同的输出图像

for (int row = 1; row < input.rows - 1; row++) {

const uchar* prev = input.ptr<uchar>(row - 1); // 获取上一行的指针
const uchar* curr = input.ptr<uchar>(row); // 获取当前行的指针
const uchar* next = input.ptr<uchar>(row + 1); // 获取下一行的指针
uchar* out = output.ptr<uchar>(row); // 获取输出图像当前行的指针

for (int col = channels; col < channels * (input.cols - 1); col++) {
// 应用公式:5*当前像素 - 上下左右像素
out[col] = cv::saturate_cast<uchar>(
5 * curr[col] - (curr[col - channels] + curr[col + channels] + prev[col] + next[col])
);
}
}

// 边界置0
output.row(0).setTo(cv::Scalar(0));
output.row(output.rows - 1).setTo(cv::Scalar(0));
output.col(0).setTo(cv::Scalar(0));
output.col(output.cols - 1).setTo(cv::Scalar(0));
}

cv::saturate_cast

在图像处理方面,无论是加是减,乘除,都可能会超出一个像素灰度值的范围(0~255),saturate_cast 函数的作用即是:当运算完之后,结果为负,则转为 0,结果超出 255,则为 255。

3.3. 使用OpenCV内置函数 filter2D

手动实现掩膜操作虽然直观,但代码冗长且效率较低。OpenCV 提供了 filter2D 函数,可直接应用内核矩阵,代码更简洁且性能更优。

3.3.1. 代码示例

1
2
3
4
5
6
7
8
9
10
cv::Mat kernel = (cv::Mat_<char>(3, 3) << 
0, -1, 0,
-1, 5, -1,
0, -1, 0
);
cv::Mat result;
// void filter2D( InputArray src, OutputArray dst, int ddepth,
// InputArray kernel, Point anchor = Point(-1,-1), double delta = 0,
// int borderType = BORDER_DEFAULT );
cv::filter2D(input, result, input.depth(), kernel);

3.3.2. 优势

  • 高效性:内置函数经过优化,速度更快(测试中手动实现需 31ms,filter2D 仅需 13ms)。
  • 灵活性:支持自定义内核中心、边界处理模式(如填充、镜像等)。

3.4. 性能对比与适用场景

方法 优点 缺点
手动实现 理解底层原理,灵活调试 代码复杂,性能较低
filter2D 简洁高效,支持高级功能 需熟悉内核矩阵的构建

推荐场景

  • 学习阶段:建议手动实现,深入理解像素操作原理。
  • 实际项目:优先使用 filter2D ,提升效率并减少代码量。

3.5. 总结

掩膜操作是图像处理的基础技术,OpenCV 提供了灵活的实现方式。通过对比手动实现与内置函数,开发者可以根据需求选择最佳方案。掌握这一技术后,可进一步探索高斯模糊、边缘检测(如 Sobel 算子)等进阶应用。

动手尝试

  1. 修改内核矩阵,观察图像变化(例如锐化、模糊)。
  2. 对比不同边界处理模式的效果(如 BORDER_REFLECTBORDER_CONSTANT )。

4. 图像基础操作

介绍图像处理的基础操作,涵盖图像加载、像素操作、内存管理及可视化等核心内容。

4.1. 图像输入输出

4.1.1. 加载图像

使用 cv::imread 加载图像时,默认读取为 BGR 三通道格式。若需灰度图,需显式指定参数:

1
2
cv::Mat img = cv::imread("image.jpg"); // 三通道BGR图像
cv::Mat gray_img = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE); // 单通道灰度图

4.1.2. 保存图像

图像保存格式由文件扩展名决定:

1
cv::imwrite("output.png", img); // 保存为PNG格式

注意:若需从内存读写图像,使用 cv::imdecodecv::imencode

4.2. 像素操作

4.2.1. 访问像素值

  • 单通道灰度图(8UC1):

    1
    uchar intensity = img.at<uchar>(y, x); // 注意坐标顺序为(y, x)
  • 三通道 BGR 图像(8UC3):

    1
    2
    3
    4
    cv::Vec3b bgr_pixel = img.at<cv::Vec3b>(y, x);
    uchar blue = bgr_pixel[0]; // B通道
    uchar green = bgr_pixel[1]; // G通道
    uchar red = bgr_pixel[2]; // R通道

4.2.2. 修改像素值

1
2
img.at<uchar>(y, x) = 128; // 将灰度图像素设为128
img.at<cv::Vec3b>(y, x) = cv::Vec3b(255, 0, 0); // 设为蓝色

4.3. 内存管理与引用计数

4.3.1. 数据共享机制

cv::Mat 通过引用计数管理内存,多个实例可共享同一数据块:

1
2
3
std::vector<cv::Point3f> points;
// ...填充数据
cv::Mat points_mat = cv::Mat(points).reshape(1); // 共享数据,不复制

4.3.2. 显式复制数据

需独立操作数据时,使用 clone()copyTo()

1
2
3
cv::Mat img_clone = img.clone(); // 深拷贝
cv::Mat img_copy;
img.copyTo(img_copy); // 深拷贝(目标为空矩阵时)

4.4. 基本图像操作

4.4.1. 图像置黑

1
img = cv::Scalar(0); // 所有像素设为0(黑色)

4.4.2. 选择感兴趣区域(ROI)

1
2
cv::Rect roi(10, 10, 100, 100); // (x, y, width, height)
cv::Mat small_img = img(roi); // 提取ROI

4.4.3. 颜色空间转换

1
2
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY); // BGR转灰度

4.4.4. 图像类型转换

1
2
cv::Mat src_32f;
src.convertTo(src_32f, CV_32F); // 8UC1转32FC1

4.5. 图像可视化

4.5.1. 显示 8 位图像

1
2
3
cv::namedWindow("Display", cv::WINDOW_AUTOSIZE);
cv::imshow("Display", img);
cv::waitKey(0); // 等待按键

4.5.2. 显示浮点图像

需将 32F 类型归一化到 0~255 范围:

1
2
3
4
5
6
7
8
9
10
11
cv::Mat sobelx;
cv::Sobel(gray, sobelx, CV_32F, 1, 0); // 计算Sobel梯度

double min_val, max_val;
cv::minMaxLoc(sobelx, &min_val, &max_val); // 获取极值

cv::Mat draw;
sobelx.convertTo(draw, CV_8U, 255.0/(max_val - min_val), -min_val*255.0/(max_val - min_val));
cv::imshow("Sobel", draw);
cv::waitKey(0);
cv::destroyWindow("Sobel");

4.6. 总结

介绍了 OpenCV 中图像处理的基础操作,包括加载/保存、像素访问、内存管理和可视化。可以进一步结合官方文档探索高级功能(如滤波、特征检测等)。


5. 使用cv::addWeighted实现图像线性混合

在计算机视觉中,图像混合是一项基础且实用的技术。通过调整权重参数,可以实现两张图片的平滑过渡效果,例如幻灯片切换或视频转场。本节介绍如何使用 cv::addWeighted 函数实现图像的线性混合。

5.1. 理论背景

线性混合的数学表达式为:
\[\nonumber g(x) = (1 - \alpha) \cdot f_0(x) + \alpha \cdot f_1(x) \]
其中,\(\alpha\) 的取值范围为 \([0, 1]\)。当 \(\alpha\) 从 0 逐渐增加到 1 时,输出图像会从 \(f_0\) 平滑过渡到 \(f_1\)

5.2. 代码实现

以下是使用 C++ 和 OpenCV 实现图像混合的完整代码:

#include <opencv2/opencv.hpp>  
#include <iostream>  

int main() {  
    double alpha = 0.5;  
    double beta, user_input;  

    // 读取两张图片  
    cv::Mat src1 = cv::imread(cv::samples::findFile("LinuxLogo.jpg"));  
    cv::Mat src2 = cv::imread(cv::samples::findFile("WindowsLogo.jpg"));  

    // 检查图片是否加载成功  
    if (src1.empty()) {  
        std::cout << "错误:无法加载src1" << std::endl;  
        return -1;  
    }  
    if (src2.empty()) {  
        std::cout << "错误:无法加载src2" << std::endl;  
        return -1;  
    }  

    // 获取用户输入的alpha值  
    std::cout << "请输入alpha值(0.0-1.0): ";  
    std::cin >> user_input;  
    if (user_input >= 0 && user_input <= 1) {  
        alpha = user_input;  
    }  

    beta = 1.0 - alpha;  
    cv::Mat dst;  

    // 执行线性混合
    // void addWeighted(InputArray src1, double alpha, InputArray src2,
    //                  double beta, double gamma, OutputArray dst,
    //                  int //dtype = -1);
    cv::addWeighted(src1, alpha, src2, beta, 0.0, dst);  

    // 显示结果  
    cv::imshow("混合结果", dst);  
    cv::waitKey(0);  

    return 0;  
}  

5.3. 关键代码解析

  1. 加载图片
    • 使用 cv::imread 读取图片,并通过 cv::samples::findFile 确保路径正确。
  2. 输入验证
    • 用户输入的 \(\alpha\) 值通过 std::cin 获取,并限制在 [0, 1] 范围内。
  3. 混合计算
    • cv::addWeighted 函数的参数依次为:
      • src1:第一张输入图像。
      • alpha:第一张图像的权重。
      • src2:第二张输入图像。
      • beta:第二张图像的权重(通常为 1 - alpha )。
      • gamma:标量偏移量(此处设为0)。
      • dst:输出图像。

5.4. 注意事项

  1. 图片尺寸与类型
    • 输入的两张图像必须具有相同的尺寸(宽度和高度)和数据类型,否则会导致运行时错误。
  2. 扩展应用
    • 此技术可用于创建动态效果,如视频淡入淡出、AR 场景叠加等。

5.5. 总结

通过 cv::addWeighted 函数,OpenCV 为图像混合提供了一种简洁高效的实现方式。掌握这一技术后,可以进一步探索更复杂的图像处理任务,如图像融合、遮罩处理等。

OpenCV入门学习-核心功能(二)

6. 参考资料

[1] OpenCV 官方文档

-------------本文结束 感谢您的阅读-------------