/* * ============================================================ * 实验 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 // 包含标准输入输出流,用于打印信息到终端 #include // 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; }