十六、OpenCV中的图像文件处理

文章目录

    • 一、图像的载入与保存
      • [1.1 使用cv::imread()读取图片](#1.1 使用cv::imread()读取图片)
      • [1.2 使用cv::imwrite()保存图像](#1.2 使用cv::imwrite()保存图像)
    • 二、图片的编码与解码
      • [2.1 使用cv::imencode()压缩图像](#2.1 使用cv::imencode()压缩图像)
      • [2.2 使用cv::imdecode()解码文件](#2.2 使用cv::imdecode()解码文件)
    • 三、视频的处理
      • [3.1 使用cv::VideoCapture对象读取视频流](#3.1 使用cv::VideoCapture对象读取视频流)
      • [3.2 使用cv::VideoWriter对象写入视频](#3.2 使用cv::VideoWriter对象写入视频)

一、图像的载入与保存

1.1 使用cv::imread()读取图片

cv::imread() 是 OpenCV 中最常用的函数之一,用于从文件中读取图像数据,并返回一个 cv::Mat 对象(即图像矩阵)。它的功能相当于"加载一张图片到内存"。

函数原型:

cpp 复制代码
cv::Mat cv::imread(const std::string& filename, int flags = cv::IMREAD_COLOR);

参数说明:

常用标志(flags):

返回值:

返回一个 cv::Mat 对象,包含图像数据。如果加载失败(例如路径错误、文件不存在或格式不支持),返回一个 空矩阵(mat.empty() == true)。

基本示例:

cpp 复制代码
#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 等),并可通过参数控制压缩质量。

函数原型:

cpp 复制代码
bool cv::imwrite(const std::string& filename, 
                 cv::InputArray img, 
                 const std::vector<int>& params = std::vector<int>());

参数说明:

返回值:

  • 成功保存返回 true
  • 保存失败返回 false(可能是路径不存在或格式不支持)

支持的文件格式:

基本示例:

cpp 复制代码
#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;
    else
        cout << "图像保存失败!" << endl;

    return 0;
}

带参数保存(控制压缩率或质量)

JPEG(控制质量):

cpp 复制代码
std::vector<int> params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(90); // 质量: 0~100,越高越清晰,文件越大

imwrite("high_quality.jpg", img, params);

PNG(控制压缩级别):

cpp 复制代码
std::vector<int> params;
params.push_back(cv::IMWRITE_PNG_COMPRESSION);
params.push_back(3);  // 压缩级别: 0~9,越高压缩越强(越慢)

imwrite("compressed.png", img, params);

WebP(控制压缩率):

cpp 复制代码
std::vector<int> params;
params.push_back(cv::IMWRITE_WEBP_QUALITY);
params.push_back(80); // 0~100

imwrite("image.webp", img, params);

常见搭配参数总结表:

二、图片的编码与解码

2.1 使用cv::imencode()压缩图像

它不直接保存文件,而是把图像编码成 内存中的字节流(如 JPG、PNG 等格式的二进制数据)。

这种方式常用于:网络传输、 数据库存储、Base64 编码或摄像头实时推流等场景。

函数原型:

cpp 复制代码
bool cv::imencode(const std::string& ext,
                  cv::InputArray img,
                  std::vector<uchar>& buf,
                  const std::vector<int>& params = std::vector<int>());

参数说明:

返回值:

  • 返回 true 表示编码成功
  • 返回 false 表示失败(例如格式不支持或图像为空)

基本示例:

cpp 复制代码
#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() 再把字节流还原成图像

这两个函数通常搭配使用,在 网络传输、数据库存储、内存压缩处理 等场景中非常常见。

函数原型:

cpp 复制代码
cv::Mat cv::imdecode(cv::InputArray buf, int flags);

参数说明:

常见的 flags 取值:

返回值:

返回一个 cv::Mat 类型的图像矩阵。如果解码失败(如数据无效、格式错误),返回一个 空矩阵(mat.empty() == true)。

基本示例:

cpp 复制代码
#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() 使用(常见场景)

cpp 复制代码
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 摄像头)

类定义:

cpp 复制代码
class cv::VideoCapture

这是一个封装视频流读取的类,支持多种后端(如 V4L2、DirectShow、FFmpeg 等)。

常用构造与打开方式:

打开摄像头

cpp 复制代码
cv::VideoCapture cap(0);  // 打开默认摄像头(编号0)

打开视频文件

cpp 复制代码
cv::VideoCapture cap("test.mp4");

延后打开:

cpp 复制代码
cv::VideoCapture cap;
cap.open(0);  // 之后再打开

常用成员函数:

常见属性(get / set):

示例一:打开摄像头并显示实时画面

cpp 复制代码
#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;
}

示例二:读取视频文件

cpp 复制代码
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();

示例三:设置摄像头参数

cpp 复制代码
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 摄像头

cpp 复制代码
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(函数的另一个重要区别是附加的通道参数。当使用的设备本身就有多个传感头时(例如多个成像设备)。多传感头在被设计用于立体成像的设备中非常常见。

cpp 复制代码
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 等);
  • 设置帧率、分辨率、通道数等参数。

构造函数原型:

cpp 复制代码
cv::VideoWriter::VideoWriter(
    const std::string& filename,
    int fourcc,
    double fps,
    cv::Size frameSize,
    bool isColor = true
);

或者

cpp 复制代码
cv::VideoWriter::VideoWriter(
    const cv::String& filename,
    int apiPreference,
    int fourcc,
    double fps,
    cv::Size frameSize,
    bool isColor = true
);

参数说明:

常见编码格式(fourcc):

常用方法:

示例 1:保存摄像头视频到文件

cpp 复制代码
#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:将图片序列保存为视频

cpp 复制代码
#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;
}
相关推荐
B站计算机毕业设计之家3 小时前
计算机视觉:python手写数字识别系统 手写数字检测 CNN算法 卷积神经网络 OpenCV和Keras模型 大数据毕业设计(建议收藏)✅
python·神经网络·opencv·计算机视觉·cnn·手写数字·数字识别
wolfseek3 小时前
opencv模版匹配
c++·人工智能·opencv·计算机视觉
B站计算机毕业设计之家3 小时前
Python手势识别检测系统 基于MediaPipe的改进SSD算法 opencv+mediapipe 深度学习 大数据 (建议收藏)✅
python·深度学习·opencv·计算机视觉·1024程序员节
B站计算机毕业设计之家7 小时前
计算机视觉python口罩实时检测识别系统 YOLOv8模型 PyTorch 和PySide6界面 opencv (建议收藏)✅
python·深度学习·opencv·计算机视觉·cnn·1024程序员节
却道天凉_好个秋9 小时前
OpenCV(十四):绘制直线
人工智能·opencv·计算机视觉
B站计算机毕业设计之家10 小时前
计算机视觉:YOLO实现目标识别+目标跟踪技术 pyqt界面 OpenCV 计算机视觉 深度学习 计算机(建议收藏)✅
python·opencv·yolo·计算机视觉·目标跟踪·口罩识别
渡我白衣11 小时前
AI 应用层革命(一)——软件的终结与智能体的崛起
人工智能·opencv·机器学习·语言模型·数据挖掘·人机交互·集成学习
新手村领路人1 天前
python opencv gpu加速 cmake msvc cuda编译问题和设置
开发语言·python·opencv
TechNomad1 天前
十七、OpenCV中HighGUI模块的介绍和使用
opencv