163 lines
8.1 KiB
C++
163 lines
8.1 KiB
C++
|
|
/*
|
|||
|
|
* ============================================================
|
|||
|
|
* 实验 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;
|
|||
|
|
}
|