Files

163 lines
8.1 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.
/*
* ============================================================
* 实验 1:图像读取与保存
* 知识点:cv::Mat、cv::imread、cv::imwrite
* ============================================================
*
* 【实验目标】
* 1. 理解 cv::Mat —— OpenCV 中存储图像的核心数据类型
* 2. 学会用 cv::imread 从文件中读取图像
* 3. 学会用 cv::imwrite 将图像保存到文件
* 4. 养成检查图像是否读取成功的习惯
*
* 【生活化比喻】
* 把 cv::Mat 想象成一张"棋盘格纸":
* - 每个格子是一个"像素"(就是图像上的一个小点)
* - 格子里填的数字代表颜色(比如 0=黑, 255=白)
* - 整张纸就是一幅图像,OpenCV 把它存在计算机内存里
*
* cv::imread 就像是"用扫描仪把一张打印好的照片扫进电脑"
* cv::imwrite 就像是"用打印机把电脑里的图打印出来"
*/
// 包含 OpenCV 核心头文件
// opencv.hpp 是一个"总头文件",包含了 OpenCV 所有常用模块
// 这是最方便的写法,学习阶段推荐使用
#include <opencv2/opencv.hpp>
// 包含标准输入输出流,用于打印信息到终端
#include <iostream>
// main 函数 —— 所有 C++ 程序的入口点
// argc 是命令行参数个数,argv 是参数内容数组
// 在本实验中我们不使用命令行参数,所以参数名可以省略
int main()
{
// ==========================================
// 第一部分:生成一张测试图片
// ==========================================
// 为什么先生成一张图片而不是直接读取?
// 因为我们要确保每个同学都能运行成功,不依赖任何外部图片文件
// 等学会读取之后,你可以把自己的照片放到这个文件夹里来测试!
// cv::Mat(行数, 列数, 类型, 颜色值) —— 创建一个矩阵/图像
// - 480:图像高度 480 像素(行数),也就是垂直方向有 480 个点
// - 640:图像宽度 640 像素(列数),也就是水平方向有 640 个点
// - CV_8UC3:每个像素用 8 位无符号整数 × 3 个通道表示
// - 8U = 8-bit Unsigned(取值范围 0~255)
// - C3 = 3 个通道(Channel),也就是 B(蓝)、G(绿)、R(红)
// - 所以每个像素需要 3 个字节来存储颜色信息
// - cv::Scalar(255, 255, 255):Scalar 是一个四元组 (B, G, R, Alpha)
// - 这里 (255,255,255) 就是纯白色(B=255, G=255, R=255)
// 注意 OpenCV 的颜色顺序是 BGR,不是我们常见的 RGB!
// 这是历史原因造成的,记住就好:OpenCV 里永远是 BGR 顺序
std::cout << "[步骤 1] 正在生成一张 640x480 的彩色测试图片..." << std::endl;
cv::Mat canvas(480, 640, CV_8UC3, cv::Scalar(50, 50, 50)); // 深灰色背景
// 在画布上画一些彩色图形,让图片看起来有趣
// cv::circle(图像, 圆心, 半径, 颜色, 线宽) —— 画圆
// 圆心用 cv::Point(x, y) 表示,注意 x 是水平方向(列),y 是垂直方向(行)
// 线宽 = -1 表示填充整个圆(实心圆)
cv::circle(canvas, cv::Point(150, 120), 80, cv::Scalar(255, 0, 0), -1); // 蓝色实心圆(B=255)
cv::circle(canvas, cv::Point(320, 120), 80, cv::Scalar(0, 255, 0), -1); // 绿色实心圆(G=255)
cv::circle(canvas, cv::Point(490, 120), 80, cv::Scalar(0, 0, 255), -1); // 红色实心圆(R=255)
// cv::rectangle(图像, 左上角, 右下角, 颜色, 线宽) —— 画矩形
// 线宽 = 3 表示空心矩形,边框宽度为 3 像素
cv::rectangle(canvas, cv::Point(100, 280), cv::Point(540, 440),
cv::Scalar(0, 255, 255), 3); // 黄色空心矩形(B=0, G=255, R=255)
// cv::putText(图像, 文字, 起始位置, 字体, 字号, 颜色, 线宽) —— 写文字
cv::putText(canvas, "OpenCV Experiment 1", cv::Point(130, 350),
cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(255, 255, 255), 2); // 白色文字
// 将生成的图片保存为文件
// cv::imwrite(文件路径, 图像矩阵) —— 把内存中的图像写入磁盘
// 返回值是 bool 类型:true 表示保存成功,false 表示失败
// 文件格式由扩展名决定,这里 .jpg 表示用 JPEG 格式压缩保存
bool write_ok = cv::imwrite("test_image.jpg", canvas);
if (write_ok)
{
std::cout << " -> 测试图片已保存为 test_image.jpg" << std::endl;
}
else
{
std::cerr << " [错误] 保存测试图片失败!" << std::endl;
return -1; // 返回 -1 表示程序异常退出
}
// ==========================================
// 第二部分:读取图片
// ==========================================
std::cout << "[步骤 2] 正在读取 test_image.jpg..." << std::endl;
// cv::imread(文件路径, 读取模式) —— 从磁盘读取图像到内存
// 返回值是一个 cv::Mat 对象,包含了图像的像素数据
//
// 读取模式(第二个参数):
// cv::IMREAD_COLOR = 1 :始终读取为 3 通道彩色图(默认值)
// cv::IMREAD_GRAYSCALE = 0 :始终读取为单通道灰度图
// cv::IMREAD_UNCHANGED = -1 :保持原样(包括 Alpha 透明通道)
//
// 这里使用 cv::IMREAD_COLOR 确保读进来是彩色图像
cv::Mat image = cv::imread("test_image.jpg", cv::IMREAD_COLOR);
// 【极其重要】检查图像是否读取成功!
// image.empty() 返回 true 表示 Mat 对象中没有数据
// 常见失败原因:
// 1. 文件名写错了(大小写、拼写错误)
// 2. 文件不在程序运行的当前目录下
// 3. 文件损坏或格式不支持
// 养成好习惯:每次 imread 之后都要检查 empty()!
if (image.empty())
{
std::cerr << "[错误] 无法读取图像!请检查:" << std::endl;
std::cerr << " 1. test_image.jpg 是否在当前目录下?" << std::endl;
std::cerr << " 2. 文件名是否正确(注意大小写)?" << std::endl;
return -1; // 返回 -1 表示程序因错误退出
}
// 打印图像的基本信息
// image.rows —— 图像高度(行数,垂直像素数)
// image.cols —— 图像宽度(列数,水平像素数)
// image.channels() —— 通道数(彩色=3,灰度=1)
// image.type() —— 像素数据类型编号
std::cout << " -> 读取成功!图像信息:" << std::endl;
std::cout << " 宽度 (cols): " << image.cols << " 像素" << std::endl;
std::cout << " 高度 (rows): " << image.rows << " 像素" << std::endl;
std::cout << " 通道数: " << image.channels() << " (3=彩色, 1=灰度)" << std::endl;
// ==========================================
// 第三部分:保存读到的图片(验证读取无误)
// ==========================================
std::cout << "[步骤 3] 正在保存为 output.jpg..." << std::endl;
// 把读到的图像原封不动地保存为新文件
// 这样可以验证"读取 → 保存"整个流程是否正常
bool save_ok = cv::imwrite("output.jpg", image);
if (save_ok)
{
std::cout << " -> 图片已成功保存为 output.jpg" << std::endl;
std::cout << " -> 请在 Windows 资源管理器中打开该文件查看结果!" << std::endl;
}
else
{
std::cerr << " [错误] 保存图片失败!请检查磁盘空间和写入权限。" << std::endl;
return -1;
}
// ==========================================
// 总结
// ==========================================
std::cout << std::endl;
std::cout << "===== 实验 1 完成 =====" << std::endl;
std::cout << "你学会了:" << std::endl;
std::cout << " 1. cv::Mat 是 OpenCV 存储图像的数据类型" << std::endl;
std::cout << " 2. cv::imread() 从文件读取图像" << std::endl;
std::cout << " 3. cv::imwrite() 将图像写入文件" << std::endl;
std::cout << " 4. 读取后一定要检查 empty()" << std::endl;
// return 0 表示程序正常结束
return 0;
}