feat: init OpenCV C++ training course with 8 experiments and detailed tutorial
This commit is contained in:
139
exp3_gaussian_blur/main.cpp
Normal file
139
exp3_gaussian_blur/main.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* ============================================================
|
||||
* 实验 3:高斯模糊 (Gaussian Blur)
|
||||
* 知识点:cv::GaussianBlur
|
||||
* ============================================================
|
||||
*
|
||||
* 【实验目标】
|
||||
* 1. 理解"模糊"本质上是什么(图像怎么变模糊的)
|
||||
* 2. 掌握 cv::GaussianBlur 函数的参数含义
|
||||
* 3. 观察不同卷积核大小对模糊效果的影响
|
||||
*
|
||||
* 【生活化比喻】
|
||||
* 高斯模糊就像透过一块"毛玻璃"或"近视眼镜"看东西:
|
||||
* - 原来的图像每个像素都很清晰(边界分明)
|
||||
* - 模糊后,每个像素和它周围的像素颜色混在一起了
|
||||
* - 核(kernel)越大,相当于毛玻璃越厚,混的范围越大,越模糊
|
||||
*
|
||||
* 为什么叫"高斯"?
|
||||
* - 高斯(Gauss)是一位伟大的数学家
|
||||
* - 他发现了一种"钟形曲线"(正态分布),用来描述自然界的很多现象
|
||||
* - 在图像上,就是用这个钟形曲线来决定"周围的像素各占多大权重"
|
||||
* - 距离中心越近的像素权重越大(理所当然嘛,最近的邻居影响最大)
|
||||
*
|
||||
* 在 RoboMaster 中为什么需要模糊?
|
||||
* - 消除图像噪声(摄像头传感器本身的噪点)
|
||||
* - 让后续的边缘检测、颜色提取更稳定
|
||||
* - 像是"先把细节捋平,再找关键特征"
|
||||
*
|
||||
* 【函数详解】
|
||||
* cv::GaussianBlur(src, dst, ksize, sigmaX, sigmaY, borderType)
|
||||
*
|
||||
* src :输入图像(可以是一张有明显边缘的图)
|
||||
* dst :输出图像(大小和类型与输入相同)
|
||||
* ksize :高斯核的大小,必须是奇数!(如 (3,3), (5,5), (15,15))
|
||||
* 为什么是奇数?因为要有一个"中心像素",核以它为中心对称展开
|
||||
* sigmaX :X 方向上的高斯标准差(控制左右方向的模糊程度)
|
||||
* 如果设为 0,OpenCV 会根据 ksize 自动帮你算一个合适的值
|
||||
* sigmaY :Y 方向上的高斯标准差(控制上下方向的模糊程度)
|
||||
* 如果设为 0,等于 sigmaX
|
||||
* borderType :边界处理方式(可选参数,默认即可)
|
||||
*/
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << "[步骤 1] 正在生成测试图片(含清晰边缘和纹理)..." << std::endl;
|
||||
|
||||
// 创建一张 400×400 的白色画布
|
||||
cv::Mat original(400, 400, CV_8UC3, cv::Scalar(255, 255, 255));
|
||||
|
||||
// 画各种形状 —— 这些形状有清晰的边缘,模糊后效果明显
|
||||
// 蓝色实心圆
|
||||
cv::circle(original, cv::Point(100, 100), 60, cv::Scalar(255, 0, 0), -1);
|
||||
// 绿色矩形框
|
||||
cv::rectangle(original, cv::Point(200, 50), cv::Point(350, 150),
|
||||
cv::Scalar(0, 200, 0), 3);
|
||||
// 红色三角形(用 line 画三条线)
|
||||
cv::line(original, cv::Point(50, 320), cv::Point(200, 200),
|
||||
cv::Scalar(0, 0, 255), 5);
|
||||
cv::line(original, cv::Point(200, 200), cv::Point(350, 320),
|
||||
cv::Scalar(0, 0, 255), 5);
|
||||
cv::line(original, cv::Point(350, 320), cv::Point(50, 320),
|
||||
cv::Scalar(0, 0, 255), 5);
|
||||
|
||||
// 写文字(文字边缘也是测试模糊的好素材)
|
||||
cv::putText(original, "OpenCV", cv::Point(80, 370),
|
||||
cv::FONT_HERSHEY_SIMPLEX, 1.2, cv::Scalar(0, 0, 0), 3);
|
||||
|
||||
// 画一些散点(模拟噪声,模糊后会被消除)
|
||||
for (int i = 0; i < 300; i++)
|
||||
{
|
||||
int x = rand() % 400; // 随机 X 坐标(0~399)
|
||||
int y = rand() % 400; // 随机 Y 坐标(0~399)
|
||||
// 每个点画一个 2×2 的小方块(因为单个像素可能看不清)
|
||||
cv::rectangle(original, cv::Point(x, y), cv::Point(x + 2, y + 2),
|
||||
cv::Scalar(0, 0, 0), -1);
|
||||
}
|
||||
|
||||
cv::imwrite("original.jpg", original);
|
||||
std::cout << " -> 原始图片已保存为 original.jpg(注意看上面的散点噪声)" << std::endl;
|
||||
|
||||
// ==========================================
|
||||
// 核心实验:对比不同模糊程度
|
||||
// ==========================================
|
||||
|
||||
// --- 模糊程度 1:轻微模糊(ksize = 3×3)---
|
||||
// 3×3 的核非常小,只混合当前像素和它最近的 8 个邻居
|
||||
// 效果:能消除最细小的噪点,但基本保留图像结构
|
||||
std::cout << "[步骤 2] 应用轻微高斯模糊 (ksize = 3x3)..." << std::endl;
|
||||
cv::Mat blur_light;
|
||||
cv::GaussianBlur(original, blur_light, cv::Size(3, 3), 0, 0);
|
||||
cv::imwrite("blur_3x3.jpg", blur_light);
|
||||
std::cout << " -> 已保存为 blur_3x3.jpg(轻微模糊,细节基本保留)" << std::endl;
|
||||
|
||||
// --- 模糊程度 2:中度模糊(ksize = 9×9)---
|
||||
// 9×9 的核覆盖了 81 个像素的范围
|
||||
// 效果:散点噪声明显消失,边缘变得柔和
|
||||
std::cout << "[步骤 3] 应用中度高模糊 (ksize = 9x9)..." << std::endl;
|
||||
cv::Mat blur_medium;
|
||||
cv::GaussianBlur(original, blur_medium, cv::Size(9, 9), 0, 0);
|
||||
cv::imwrite("blur_9x9.jpg", blur_medium);
|
||||
std::cout << " -> 已保存为 blur_9x9.jpg(中度模糊,噪声消失,边缘变软)" << std::endl;
|
||||
|
||||
// --- 模糊程度 3:重度模糊(ksize = 21×21)---
|
||||
// 21×21 的核非常大,覆盖了 441 个像素的范围
|
||||
// 效果:图像变得严重模糊,细节几乎全部丢失
|
||||
// 这在实际应用中通常不会用这么大的核,这里纯粹为了演示
|
||||
std::cout << "[步骤 4] 应用重度高斯模糊 (ksize = 21x21)..." << std::endl;
|
||||
cv::Mat blur_heavy;
|
||||
cv::GaussianBlur(original, blur_heavy, cv::Size(21, 21), 0, 0);
|
||||
cv::imwrite("blur_21x21.jpg", blur_heavy);
|
||||
std::cout << " -> 已保存为 blur_21x21.jpg(重度模糊,近乎一片朦胧)" << std::endl;
|
||||
|
||||
// --- 模糊程度 4:X/Y 方向不同模糊程度 ---
|
||||
// 前面三个例子 sigmaX 和 sigmaY 都设为 0(自动计算)
|
||||
// 这里我们手动指定不同方向的标准差:X 方向模糊大,Y 方向模糊小
|
||||
std::cout << "[步骤 5] 应用各向异性模糊 (sigmaX=15, sigmaY=3)..." << std::endl;
|
||||
cv::Mat blur_aniso;
|
||||
cv::GaussianBlur(original, blur_aniso, cv::Size(0, 0), 15, 3);
|
||||
// 注意:当 ksize 设为 (0,0) 时,OpenCV 会根据 sigma 自动计算合适的核大小
|
||||
// sigmaX=15(水平方向大模糊)比 sigmaY=3(垂直方向小模糊)大很多
|
||||
// 所以效果是:水平方向被"抹平"得很厉害,垂直方向相对清晰
|
||||
cv::imwrite("blur_anisotropic.jpg", blur_aniso);
|
||||
std::cout << " -> 已保存为 blur_anisotropic.jpg(水平方向很糊,垂直方向较清晰)" << std::endl;
|
||||
|
||||
// 总结
|
||||
std::cout << std::endl;
|
||||
std::cout << "===== 实验 3 完成 =====" << std::endl;
|
||||
std::cout << "你学会了:" << std::endl;
|
||||
std::cout << " 1. GaussianBlur 用高斯分布对像素进行加权平均" << std::endl;
|
||||
std::cout << " 2. ksize 越大,模糊范围越大(必须是奇数!)" << std::endl;
|
||||
std::cout << " 3. sigma 控制模糊的标准差(0 表示自动计算)" << std::endl;
|
||||
std::cout << " 4. 模糊是后续边缘检测、颜色提取的重要预处理步骤" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user