/* * ============================================================ * 实验 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 #include 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(row, col) —— 访问/修改单个像素值 // .at 中的 uchar = unsigned char = 8比特无符号整数 // 因为 CV_8UC1 的每个像素就是 uchar 类型 gradient.at(row, col) = static_cast(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; }