Files

152 lines
7.3 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* ============================================================
* 实验 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;
}