十六、OpenCV中的图像文件处理
文章目录
- 一、图像的载入与保存
- 1.1 使用cv::imread()读取图片
- 1.2 使用cv::imwrite()保存图像
- 二、图片的编码与解码
- 2.1 使用cv::imencode()压缩图像
- 2.2 使用cv::imdecode()解码文件
- 三、视频的处理
- 3.1 使用cv::VideoCapture对象读取视频流
- 3.2 使用cv::VideoWriter对象写入视频
一、图像的载入与保存
1.1 使用cv::imread()读取图片
cv::imread() 是 OpenCV 中最常用的函数之一,用于从文件中读取图像数据,并返回一个 cv::Mat 对象(即图像矩阵)。它的功能相当于“加载一张图片到内存”。
函数原型:
cv::Mat cv::imread(const std::string& filename, int flags = cv::IMREAD_COLOR);
参数说明:

常用标志(flags):

返回值:
返回一个 cv::Mat 对象,包含图像数据。如果加载失败(例如路径错误、文件不存在或格式不支持),返回一个 空矩阵(mat.empty() == true)。
基本示例:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;int main() {// 1. 从文件读取图像Mat img = imread("example.jpg", IMREAD_COLOR);// 2. 检查是否成功读取if (img.empty()) {cout << "读取图像失败,请检查路径或文件名!" << endl;return -1;}// 3. 显示图像imshow("显示图片", img);// 4. 等待用户按键waitKey(0);return 0;
}
不同模式下的效果:

1.2 使用cv::imwrite()保存图像
cv::imwrite() 是 OpenCV 中与 cv::imread() 相对的函数,用于将图像数据写入到文件(保存图片)。它支持多种图像格式(如 PNG、JPG、BMP、TIFF 等),并可通过参数控制压缩质量。
函数原型:
bool cv::imwrite(const std::string& filename, cv::InputArray img, const std::vector<int>& params = std::vector<int>());
参数说明:

返回值:
- 成功保存返回 true
- 保存失败返回 false(可能是路径不存在或格式不支持)
支持的文件格式:

基本示例:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;int main() {// 1. 读取一张图片Mat img = imread("example.jpg");if (img.empty()) {cout << "读取图像失败!" << endl;return -1;}// 2. 保存为不同格式bool ok1 = imwrite("output.jpg", img); // 默认JPEG格式bool ok2 = imwrite("output.png", img); // PNG格式// 3. 检查保存结果if (ok1 && ok2)cout << "图像保存成功!" << endl;elsecout << "图像保存失败!" << endl;return 0;
}
带参数保存(控制压缩率或质量)
JPEG(控制质量):
std::vector<int> params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(90); // 质量: 0~100,越高越清晰,文件越大imwrite("high_quality.jpg", img, params);
PNG(控制压缩级别):
std::vector<int> params;
params.push_back(cv::IMWRITE_PNG_COMPRESSION);
params.push_back(3); // 压缩级别: 0~9,越高压缩越强(越慢)imwrite("compressed.png", img, params);
WebP(控制压缩率):
std::vector<int> params;
params.push_back(cv::IMWRITE_WEBP_QUALITY);
params.push_back(80); // 0~100imwrite("image.webp", img, params);
常见搭配参数总结表:

二、图片的编码与解码
2.1 使用cv::imencode()压缩图像
它不直接保存文件,而是把图像编码成 内存中的字节流(如 JPG、PNG 等格式的二进制数据)。
这种方式常用于:网络传输、 数据库存储、Base64 编码或摄像头实时推流等场景。
函数原型:
bool cv::imencode(const std::string& ext,cv::InputArray img,std::vector<uchar>& buf,const std::vector<int>& params = std::vector<int>());
参数说明:

返回值:
- 返回 true 表示编码成功
- 返回 false 表示失败(例如格式不支持或图像为空)
基本示例:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;int main() {// 1. 读取图像Mat img = imread("example.jpg");if (img.empty()) {cout << "图像读取失败!" << endl;return -1;}// 2. 编码为 JPG 格式字节流vector<uchar> buffer;bool ok = imencode(".jpg", img, buffer);if (!ok) {cout << "编码失败!" << endl;return -1;}cout << "编码成功!编码后字节数:" << buffer.size() << endl;// 3. 将编码后的数据写入文件(模拟 imwrite)FILE* fp = fopen("output_from_encode.jpg", "wb");fwrite(buffer.data(), 1, buffer.size(), fp);fclose(fp);cout << "文件已保存!" << endl;return 0;
}
2.2 使用cv::imdecode()解码文件
就是从内存中的图像字节数据恢复(解码)为 OpenCV 的 cv::Mat 图像对象。
简而言之:
- cv::imencode() 把图像变成字节流
- cv::imdecode() 再把字节流还原成图像
这两个函数通常搭配使用,在 网络传输、数据库存储、内存压缩处理 等场景中非常常见。
函数原型:
cv::Mat cv::imdecode(cv::InputArray buf, int flags);
参数说明:

常见的 flags 取值:

返回值:
返回一个 cv::Mat 类型的图像矩阵。如果解码失败(如数据无效、格式错误),返回一个 空矩阵(mat.empty() == true)。
基本示例:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;int main() {// 1. 读取文件到内存FILE* fp = fopen("example.jpg", "rb");if (!fp) {cout << "文件打开失败!" << endl;return -1;}fseek(fp, 0, SEEK_END);long size = ftell(fp);rewind(fp);vector<uchar> buffer(size);fread(buffer.data(), 1, size, fp);fclose(fp);// 2. 从内存数据解码为图像Mat img = imdecode(buffer, IMREAD_COLOR);if (img.empty()) {cout << "图像解码失败!" << endl;return -1;}// 3. 显示图像imshow("解码图像", img);waitKey(0);return 0;
}
配合 imencode() 使用(常见场景)
Mat src = imread("input.png");
vector<uchar> buf;// 编码为内存中的 JPEG 数据
imencode(".jpg", src, buf);// 再从内存解码回 Mat
Mat decoded = imdecode(buf, IMREAD_COLOR);imshow("原图", src);
imshow("解码后图", decoded);
waitKey(0);
三、视频的处理
3.1 使用cv::VideoCapture对象读取视频流
cv::VideoCapture 是 OpenCV 中一个非常核心的类,用于视频输入(Video Input)。
它可以从以下来源捕获视频帧:
- 摄像头(如 USB 摄像头、笔记本内置摄像头)
- 视频文件(如 .mp4, .avi 等)
- 网络流媒体(如 RTSP、HTTP、IP 摄像头)
类定义:
class cv::VideoCapture
这是一个封装视频流读取的类,支持多种后端(如 V4L2、DirectShow、FFmpeg 等)。
常用构造与打开方式:
打开摄像头
cv::VideoCapture cap(0); // 打开默认摄像头(编号0)
打开视频文件
cv::VideoCapture cap("test.mp4");
延后打开:
cv::VideoCapture cap;
cap.open(0); // 之后再打开
常用成员函数:

常见属性(get / set):

示例一:打开摄像头并显示实时画面
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;int main() {VideoCapture cap(0); // 打开默认摄像头if (!cap.isOpened()) {cout << "无法打开摄像头!" << endl;return -1;}Mat frame;while (true) {cap >> frame; // 读取一帧if (frame.empty()) break;imshow("Camera", frame);if (waitKey(10) == 27) // 按ESC退出break;}cap.release();destroyAllWindows();return 0;
}
示例二:读取视频文件
VideoCapture cap("video.mp4");
if (!cap.isOpened()) {cout << "无法打开视频文件!" << endl;return -1;
}Mat frame;
while (true) {if (!cap.read(frame)) break; // 读取失败(到结尾)imshow("Video", frame);if (waitKey(25) == 27) break;
}cap.release();
destroyAllWindows();
示例三:设置摄像头参数
cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720);
cap.set(cv::CAP_PROP_FPS, 30);cout << "Width: " << cap.get(cv::CAP_PROP_FRAME_WIDTH) << endl;
cout << "Height: " << cap.get(cv::CAP_PROP_FRAME_HEIGHT) << endl;
示例四:打开网络流或 IP 摄像头
VideoCapture cap("rtsp://user:password@192.168.1.100:554/stream1");
if (!cap.isOpened()) {cout << "无法连接 RTSP 视频流!" << endl;return -1;
}Mat frame;
while (cap.read(frame)) {imshow("RTSP Stream", frame);if (waitKey(1) == 27) break;
}
grab() / retrieve() 示例(提高性能)
cv::VideoCapture::grab()函数将当前可以使用的图像数据拷贝至用户看不到的一个内部缓存区。那么为什么我们希望OpenCV将图像顺放在一个我们无法触及的地方呢?原因是抓取的图像顿是未处理的,grab函数只是设计用于将原始图像数据尽快获取到电脑(一般是从相机)。之所以将捕获(grab)和恢复(retrieve)分开进行而不是像 cv::videoCaptrue::readO函数那样同时进行,是有许多原因的。多相机的数据读取是最常见的一种情况(例如立体成像)。在这种情况下,需要尽可能缩短多个相机获取的图像之间的时间差(对于立体成像的理想情况是完全同步获取)。
因此,先完成数据的抓取,将数据安全放入缓存区之后再进行解码恢复,这样做非常有意义。同cv::VideoCapture::read()函数一样,cv::VideoCapture::grab()只在抓取成功时返回true。一旦成功抓取到图像,你便可以调用cv::videoCapture::etrieveO函数用于处理必要的图像解码,内存分配与拷贝,最终以cv::Mat形式的序列将图像顿返回给你。 cv::VideoCapture::retrrieveO函数与cv::VideoCapture::readO函数非常相似,不同之处在于,它处理的是内部缓存区数据而不是cv::videoCapture::grabO所拷贝的图像。cv::VideoCapture::ead和cv::VideoCapture::retrieve(函数的另一个重要区别是附加的通道参数。当使用的设备本身就有多个传感头时(例如多个成像设备)。多传感头在被设计用于立体成像的设备中非常常见。
VideoCapture cap(0);
Mat frame;while (true) {cap.grab(); // 抓取帧(不解码)cap.retrieve(frame); // 再取出帧imshow("Camera", frame);if (waitKey(10) == 27) break;
}
当不需要每帧都处理时(例如只取关键帧),grab() 可以减少 CPU 解码负担。
3.2 使用cv::VideoWriter对象写入视频
cv::VideoWriter 用于:
- 将一系列图像帧保存为视频文件;
- 控制输出视频的编码方式(如 MJPG、H264 等);
- 设置帧率、分辨率、通道数等参数。
构造函数原型:
cv::VideoWriter::VideoWriter(const std::string& filename,int fourcc,double fps,cv::Size frameSize,bool isColor = true
);
或者
cv::VideoWriter::VideoWriter(const cv::String& filename,int apiPreference,int fourcc,double fps,cv::Size frameSize,bool isColor = true
);
参数说明:

常见编码格式(fourcc):

常用方法:

示例 1:保存摄像头视频到文件
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;int main() {// 打开摄像头VideoCapture cap(0);if (!cap.isOpened()) {cout << "无法打开摄像头!" << endl;return -1;}// 获取视频帧尺寸int width = (int)cap.get(CAP_PROP_FRAME_WIDTH);int height = (int)cap.get(CAP_PROP_FRAME_HEIGHT);Size frameSize(width, height);// 创建VideoWriter对象VideoWriter writer("output.avi",VideoWriter::fourcc('M','J','P','G'),30.0,frameSize,true);if (!writer.isOpened()) {cout << "无法打开视频文件用于写入!" << endl;return -1;}Mat frame;cout << "正在录制,按 ESC 退出..." << endl;while (true) {cap >> frame;if (frame.empty()) break;writer.write(frame); // 写入帧imshow("Recording", frame);if (waitKey(10) == 27) break; // 按 ESC 退出}writer.release();cap.release();cout << "视频保存完成!" << endl;return 0;
}
示例 2:将图片序列保存为视频
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;int main() {Size size(640, 480);VideoWriter writer("sequence.avi",VideoWriter::fourcc('M','J','P','G'),20,size);if (!writer.isOpened()) {cout << "视频文件无法打开!" << endl;return -1;}for (int i = 0; i < 100; ++i) {Mat frame(size, CV_8UC3, Scalar(255, 255 - i*2, 50 + i));putText(frame, "Frame: " + to_string(i),Point(50, 200), FONT_HERSHEY_SIMPLEX, 1.5, Scalar(0,0,0), 2);writer.write(frame);}writer.release();cout << "视频保存成功!" << endl;return 0;
}
