feat: init OpenCV C++ training course with 8 experiments and detailed tutorial
This commit is contained in:
9
exp5_threshold/CMakeLists.txt
Normal file
9
exp5_threshold/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# 实验 5:图像阈值化 - CMake 构建配置
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(exp5_threshold)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
find_package(OpenCV REQUIRED)
|
||||
add_executable(exp5 main.cpp)
|
||||
target_link_libraries(exp5 ${OpenCV_LIBS})
|
||||
include_directories(${OpenCV_INCLUDE_DIRS})
|
||||
151
exp5_threshold/main.cpp
Normal file
151
exp5_threshold/main.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* ============================================================
|
||||
* 实验 5:图像阈值化 (Thresholding)
|
||||
* 知识点:cv::threshold
|
||||
* ============================================================
|
||||
*
|
||||
* 【实验目标】
|
||||
* 1. 理解"阈值化"是什么——把灰度图变成黑白图
|
||||
* 2. 掌握 cv::threshold 函数的各种阈值类型
|
||||
* 3. 理解阈值化在图像分割中的基础地位
|
||||
*
|
||||
* 【生活化比喻】
|
||||
* 阈值化就像是"考试及格线":
|
||||
* - 考试成绩 0~100 分
|
||||
* - 学校规定 60 分及格:≥ 60 算"通过",< 60 算"不通过"
|
||||
* - 这里的"60"就是阈值 (threshold)
|
||||
*
|
||||
* 应用到图像上:
|
||||
* - 每个像素的灰度值 0~255(相当于 0~100 分)
|
||||
* - 设定阈值 128(相当于 60 分)
|
||||
* - 灰度 ≥ 128 的像素 → 变白色(255,相当于"通过")
|
||||
* - 灰度 < 128 的像素 → 变黑色(0,相当于"不通过")
|
||||
* - 整张图就只剩黑白两色了!
|
||||
*
|
||||
* 为什么需要这个操作?
|
||||
* - 把"感兴趣的物体"从"背景"中分离出来
|
||||
* - 为后续的形态学操作(实验 6)做准备
|
||||
* - 很多算法(如轮廓检测)需要二值图像作为输入
|
||||
*
|
||||
* 【函数详解】
|
||||
* cv::threshold(src, dst, thresh, maxval, type)
|
||||
*
|
||||
* src :输入图像(必须是单通道灰度图)
|
||||
* dst :输出图像(和输入同样大小和类型)
|
||||
* thresh :阈值(就是那个"及格线")
|
||||
* maxval :最大值(当像素满足条件时,赋予它的新值,通常是 255)
|
||||
* type :阈值类型(决定"怎么比较"和"比较后怎么赋值")
|
||||
*
|
||||
* OpenCV 支持的阈值类型:
|
||||
* cv::THRESH_BINARY :>阈值→maxval,否则→0 (标准二值化)
|
||||
* cv::THRESH_BINARY_INV :>阈值→0,否则→maxval (反二值化)
|
||||
* cv::THRESH_TRUNC :>阈值→阈值,否则→原值不变 (截断)
|
||||
* cv::THRESH_TOZERO :>阈值→原值不变,否则→0 (置零)
|
||||
* cv::THRESH_TOZERO_INV :>阈值→0,否则→原值不变 (反置零)
|
||||
*/
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
// ==========================================
|
||||
// 第一部分:生成一张灰度渐变图
|
||||
// ==========================================
|
||||
std::cout << "[步骤 1] 正在生成灰度渐变测试图..." << std::endl;
|
||||
|
||||
// 创建一张 300(高)× 512(宽)的灰度图
|
||||
// CV_8UC1 表示:8位无符号整数、1通道(灰度)
|
||||
cv::Mat gradient(300, 512, CV_8UC1);
|
||||
|
||||
// 用手工方式生成从左到右的灰度渐变
|
||||
// 左边的像素灰度值为 0(全黑),右边为 255(全白),逐渐过渡
|
||||
for (int row = 0; row < gradient.rows; row++)
|
||||
{
|
||||
for (int col = 0; col < gradient.cols; col++)
|
||||
{
|
||||
// 灰度值 = 列索引 × 255 / 511(比例映射)
|
||||
// 这样 col=0 对应灰度 = 0,col=511 对应灰度 = 255
|
||||
// gradient.at<uchar>(row, col) —— 访问/修改单个像素值
|
||||
// .at<uchar> 中的 uchar = unsigned char = 8比特无符号整数
|
||||
// 因为 CV_8UC1 的每个像素就是 uchar 类型
|
||||
gradient.at<uchar>(row, col) = static_cast<uchar>(col * 255 / 511);
|
||||
}
|
||||
}
|
||||
|
||||
// 在渐变图上画几个标记,方便观察阈值效果
|
||||
// 画一条垂直线标记阈值 128 的位置
|
||||
cv::line(gradient, cv::Point(256, 0), cv::Point(256, 299),
|
||||
cv::Scalar(255), 1); // 在 x=256 处画白线(灰度 128 的位置)
|
||||
// 放一些文字
|
||||
cv::putText(gradient, "0", cv::Point(5, 150),
|
||||
cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(255));
|
||||
cv::putText(gradient, "128", cv::Point(240, 150),
|
||||
cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(255));
|
||||
cv::putText(gradient, "255", cv::Point(470, 150),
|
||||
cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0));
|
||||
|
||||
cv::imwrite("gradient.jpg", gradient);
|
||||
std::cout << " -> 原始渐变图已保存为 gradient.jpg" << std::endl;
|
||||
std::cout << " (从左到右灰度从 0 渐变到 255)" << std::endl;
|
||||
|
||||
// ==========================================
|
||||
// 第二部分:演示各种阈值类型
|
||||
// ==========================================
|
||||
// 统一使用阈值 128(中间值),maxval = 255(纯白)
|
||||
|
||||
// --- 类型 1:THRESH_BINARY(标准二值化)---
|
||||
// 逻辑:如果 像素值 > 128,则赋值为 255;否则赋值为 0
|
||||
// 效果:左边一半(<128)全黑,右边一半(>128)全白
|
||||
std::cout << "[步骤 2] THRESH_BINARY(标准二值化)..." << std::endl;
|
||||
cv::Mat thresh_binary;
|
||||
cv::threshold(gradient, thresh_binary, 128, 255, cv::THRESH_BINARY);
|
||||
cv::imwrite("thresh_binary.jpg", thresh_binary);
|
||||
std::cout << " -> 已保存为 thresh_binary.jpg" << std::endl;
|
||||
|
||||
// --- 类型 2:THRESH_BINARY_INV(反二值化)---
|
||||
// 逻辑:如果 像素值 > 128,则赋值为 0;否则赋值为 255
|
||||
// 效果:和上面完全相反——左边白,右边黑
|
||||
std::cout << "[步骤 3] THRESH_BINARY_INV(反二值化)..." << std::endl;
|
||||
cv::Mat thresh_binary_inv;
|
||||
cv::threshold(gradient, thresh_binary_inv, 128, 255, cv::THRESH_BINARY_INV);
|
||||
cv::imwrite("thresh_binary_inv.jpg", thresh_binary_inv);
|
||||
std::cout << " -> 已保存为 thresh_binary_inv.jpg" << std::endl;
|
||||
|
||||
// --- 类型 3:THRESH_TRUNC(截断)---
|
||||
// 逻辑:如果 像素值 > 128,则赋值为 128(被截断!);否则保持原值不变
|
||||
// 效果:左边(<128)保持渐变,右边(>128)全部变成 128(一片灰)
|
||||
std::cout << "[步骤 4] THRESH_TRUNC(截断)..." << std::endl;
|
||||
cv::Mat thresh_trunc;
|
||||
cv::threshold(gradient, thresh_trunc, 128, 255, cv::THRESH_TRUNC);
|
||||
cv::imwrite("thresh_trunc.jpg", thresh_trunc);
|
||||
std::cout << " -> 已保存为 thresh_trunc.jpg" << std::endl;
|
||||
|
||||
// --- 类型 4:THRESH_TOZERO(置零)---
|
||||
// 逻辑:如果 像素值 > 128,则保持原值不变;否则赋值为 0(变黑)
|
||||
// 效果:左边(<128)全黑,右边(>128)保持渐变
|
||||
std::cout << "[步骤 5] THRESH_TOZERO(置零)..." << std::endl;
|
||||
cv::Mat thresh_tozero;
|
||||
cv::threshold(gradient, thresh_tozero, 128, 255, cv::THRESH_TOZERO);
|
||||
cv::imwrite("thresh_tozero.jpg", thresh_tozero);
|
||||
std::cout << " -> 已保存为 thresh_tozero.jpg" << std::endl;
|
||||
|
||||
// --- 类型 5:THRESH_TOZERO_INV(反置零)---
|
||||
// 逻辑:如果 像素值 > 128,则赋值为 0(变黑);否则保持原值不变
|
||||
// 效果:左边(<128)保持渐变,右边(>128)全黑
|
||||
std::cout << "[步骤 6] THRESH_TOZERO_INV(反置零)..." << std::endl;
|
||||
cv::Mat thresh_tozero_inv;
|
||||
cv::threshold(gradient, thresh_tozero_inv, 128, 255, cv::THRESH_TOZERO_INV);
|
||||
cv::imwrite("thresh_tozero_inv.jpg", thresh_tozero_inv);
|
||||
std::cout << " -> 已保存为 thresh_tozero_inv.jpg" << std::endl;
|
||||
|
||||
// 总结
|
||||
std::cout << std::endl;
|
||||
std::cout << "===== 实验 5 完成 =====" << std::endl;
|
||||
std::cout << "你学会了:" << std::endl;
|
||||
std::cout << " 1. 阈值化就是把灰度图按某个界限分成两部分" << std::endl;
|
||||
std::cout << " 2. 有 5 种阈值类型,最常用的是 THRESH_BINARY" << std::endl;
|
||||
std::cout << " 3. 阈值化是图像分割的基础操作" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user