OpenCv图像处理之图像视频摄像头读取和保存

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

经过上一节的学习,我们了解到opencv处理的图片其实就是对矩阵进行操作,使用cv::imread()对图片进行读取并返回一个矩阵,来看一个例子:

#include <iostream>
#include <opencv2/highgui.hpp>

using namespace std;

int main() {
	//imread(const String &filedir,int flags)
    cv::Mat img = cv::imread("D:/null.jpg", 1);
    //另一种判空的写法:img.data == NULL;
    if (img.empty()) {
        cout << "open the file failed!" << endl;
    }
    cv::Mat gray_img = cv::imread("D:/cat.jpg", 0);
    if (!gray_img.empty()) {
        cv::imshow("gray_img", gray_img);
        cv::waitKey(0);
    }
    cv::Mat nochange_img = cv::imread("D:/cat.jpg", -1);
    if (!nochange_img.empty()) {
        cv::imshow("no_chang", nochange_img);
        cv::waitKey(0);
    }
    return 0;
}

效果显示
open the file failed!
在这里插入图片描述在这里插入图片描述
上述代码展示了读取图片的三种不同的状态,这个状态由flags参数决定,flags=1(>0)表示函数需要返回三通道图像,若图片不是三通道则强制转成3通道.flags=0表示函数返回单通道图像,若图片是多通道图片则强制转成单通道.flags=-1(<0)表示不对图像进行通道转换处理.imread()支持图像格式:jpg,jpeg,dib,bmp,png,pbm,ppm,sr,ras,tiff,exr,jp2

使用cv::imwrite()存储图片

上面我们学习了如何读取指定路径下的图片,接下来来看一下如何将处理过的图片进行指定存储.

#include <iostream>
#include <opencv2/highgui.hpp>

using namespace std;
using namespace cv;

int main() {

    Mat mat = imread("D:/cat.jpg", 1);
    if (!mat.empty()) {
        //Rect(x,y,width,height)
        Mat roi(mat, Rect(200, 200, 400, 500));
        //bool imwrite(const String &filedir,InputArray img)
        //imwrite("D:/cat_roi_write.jpg", roi);
        //imwrite("D:/cat_roi_write.bmp", roi);
        bool is_write = imwrite("D:/cat_roi_write.png", roi);
        cout << "write is succeed?:" << is_write << endl;
    } else {
        cerr << "openfile is failed" << endl;
    }
    return 0;
}

效果显示
write is succeed?:1
在这里插入图片描述
png是无损图片
jpg是压缩成的有损图片
bmp是不压缩的无损图片,大小较大
注意并不是所有Mat对象都能存储为图片,一般来说只有8U类型的单通道和3通道矩阵可以进行图像存储,若需要保存成16U类型的图片,则只能使用png,jpeg 2000,tiff格式进行存储.
为了将其他格式的矩阵保存为图片,opencv提供了Mat::convertTo(),从v::cvtColor()能够将矩阵转换为能够保存的类型格式,若指定目录下已经存在要保存的文件,则会对其进行覆盖.(其中cvtColor()是cv类的成员函数,需要声明头文件#include<opencv2/opencv.hpp>)

使用cv::VideoCapture::open()读取视频

VideoCapture既可以从视频文件读取图像,也可以从摄像头中读取图像.使用VideoCapture::open()打开.

#include <iostream>
#include <opencv2/highgui.hpp>

using namespace std;
using namespace cv;

int main() {
    VideoCapture capture;
    Mat frame;
    frame = capture.open("D:/test_video.mp4");
    if (!capture.isOpened()) {
        cout << "this video can not open" << endl;
        return -1;
    } else {
        cout << "video is successful open=" << capture.isOpened() << endl;
    }
    //窗口大小自适应,灵活调整,写在cv::imshow()之前,防止图片尺寸太大imshow显示不全,注意名字和imshow相同,不然可能会显示两个窗口
    namedWindow("show_frame", WINDOW_AUTOSIZE);
    while (capture.read(frame)) {
        imshow("show_frame", frame);
        //每一帧的等待时间,数字越小读取越快
        waitKey(1);
    }
    //释放视频流,手动调用虚析构函数,open()和VideoCapture的析构函数会自动调用,故可以不手动释放
    capture.release();
    return 0;
}

打印是否打开成功的结果:video is successful open=1
调用本地摄像头很容易,将capture.open("D:/test_video.mp4");换成capture.open(0);即可.

使用cv::VideoWriter::write()存储视频

使用opencv存储视频流,需要在初始化时设置一系列参数文件名,编解码器,帧率,宽度,高度等等
CV_FOURCC可以获取的编码格式
CV_FOURCC(‘P’, ‘I’, ‘M’, ‘1’) = MPEG-1 code
CV_FOURCC(‘M’, ‘J’, ‘P’, ‘G’) = motion-jpeg codec
CV_FOURCC(‘M’, ‘P’, ‘4’, ‘2’) = MPEG-4.2 codec
CV_FOURCC(‘D’, ‘I’, ‘V’, ‘3’) = MPEG-4.3 codec
CV_FOURCC(‘D’, ‘I’, ‘V’, ‘X’) = MPEG-4 codec
CV_FOURCC(‘U’, ‘2’, ‘6’, ‘3’) = H263 codec
CV_FOURCC(‘I’, ‘2’, ‘6’, ‘3’) = H263I codec
CV_FOURCC(‘F’, ‘L’, ‘V’, ‘1’) = FLV1 codec

MPEG-1是为CD光盘介质定制的视频和音频压缩格式.
Motion JPEG是一种视频压缩格式,其中每一帧图像都分别使用JPEG编码.
MPEG-4利用很窄的带宽,通过帧重建技术,压缩和传输数据,求出以最少的数据获得最佳的图像质量.

VideoWriter::open()函数原型CV_WRAP virtual bool open(const String& filename, int fourcc, double fps, Size frameSize, bool isColor = true);

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>

using namespace std;
using namespace cv;

int main() {
    VideoCapture capture;
    capture.open("D:/test_video.mp4");
    if (!capture.isOpened()) {
        cout << "can not open the video" << endl;
    }
    double fps = capture.get(CAP_PROP_FPS);
    int width = int(capture.get(CAP_PROP_FRAME_WIDTH));
    int height = int(capture.get(CAP_PROP_FRAME_HEIGHT));
    int fourcc_type = VideoWriter::fourcc('M', 'P', '4', '2');
    VideoWriter videoWriter;
    videoWriter.open("D:/write_video.avi", fourcc_type, fps,
                     Size(width, height), true);
    if (!videoWriter.isOpened()) {
        cout << "video_writer can not open" << endl;
        return -1;
    }
    Mat frame;
    while (capture.read(frame)) {
        //videoWriter << frame;这种写法也是正确的,使用VideoWriter的输出流进行写入
        videoWriter.write(frame);

    }
    return 0;
}
}

效果显示
在这里插入图片描述

使用cv::VideoCapture读取摄像头

#include <iostream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <thread>

using namespace std;
using namespace cv;
thread::id main_thread_id = this_thread::get_id();

void open_video(VideoCapture &capture_usb, const string &filename, Mat &frame) {
    capture_usb.open(filename);
    if (!capture_usb.isOpened()) {
        cerr << "url not exit" << endl;
    }
    if (this_thread::get_id() == main_thread_id) {
        cout << "this is the main thread" << endl;
    } else {
        cout << "this is not the main thread!" << endl;
    }
    capture_usb.set(CAP_PROP_FOURCC, VideoWriter::fourcc('M', 'J', 'P', 'G'));
    capture_usb.set(CAP_PROP_FRAME_HEIGHT, 500);
    capture_usb.set(CAP_PROP_FRAME_WIDTH, 500);
    capture_usb.set(CAP_PROP_FPS, 30);
    namedWindow("capture", WINDOW_AUTOSIZE);
    while (capture_usb.read(frame)) {
        imshow("capture", frame);
        waitKey(1000);
    }
}

void open_video1(VideoCapture &capture_usb, const string &filename, Mat &frame) {
    capture_usb.open(filename);
    if (!capture_usb.isOpened()) {
        cerr << "url not exit" << endl;
    }
    if (this_thread::get_id() == main_thread_id) {
        cout << "this is the main thread" << endl;
    } else {
        cout << "this is not the main thread!" << endl;
    }
    //设置采集格式
    capture_usb.set(CAP_PROP_FOURCC, VideoWriter::fourcc('M', 'J', 'P', 'G'));
    //设置分辨率
    capture_usb.set(CAP_PROP_FRAME_HEIGHT, 500);
    capture_usb.set(CAP_PROP_FRAME_WIDTH, 500);
    //设置每秒读取的图片数量
    capture_usb.set(CAP_PROP_FPS, 30);
    namedWindow("capture1", WINDOW_AUTOSIZE);
    while (capture_usb.read(frame)) {
        imshow("capture1", frame);
        waitKey(1000);
    }
}

int main() {
    VideoCapture capture_usb, local_video_capture, capture_usb1, capture_usb2;
    Mat frame, frame_video, frame1, frame2;
    //url为读取的视频流地址(接口)
    const string filename = url;
    const string local_file = "D:/test_video.mp4";
    const string filename1 = url;
    thread thread_1(open_video, ref(capture_usb), filename, ref(frame));
    thread thread_2(open_video1, ref(capture_usb1), filename1, ref(frame));
    thread_2.join();
    thread_1.detach();

    for (;;) {
        if (!capture_usb.isOpened()) {
            break;
        }
        if (!capture_usb1.isOpened()) {
            break;
        }
       
    capture_usb.release();
    capture_usb1.release();
    return 0;
}

capture_usb.set()参数简介

参数功能
CV_CAP_PROP_POS_MSEC0视频文件的当前位置(以毫秒为单位)或视频捕获时间戳
CV_CAP_PROP_POS_FRAMES1基于0的索引将被解码/捕获下一帧
CV_CAP_PROP_POS_AVI_RATIO2视频文件相对位置:0 - 电影的开始,电影的1 - 结束
CV_CAP_PROP_FRAME_WIDTH3视频里每一帧的宽
CV_CAP_PROP_FRAME_HEIGHT4视频里每一帧的高
CV_CAP_PROP_FPS5视频的帧速
CV_CAP_PROP_FOURCC64个字符表示的视频编码器格式
CV_CAP_PROP_FRAME_COUNT7视频的帧数
CV_CAP_PROP_FORMAT8byretrieve()返回的Mat对象的格式
CV_CAP_PROP_MODE9指示当前捕获模式的后端特定值
CV_CAP_PROP_BRIGHTNESS10图像的亮度(仅适用于相机)
CV_CAP_PROP_CONTRAST11图像对比度(仅适用于相机)
CV_CAP_PROP_SATURATION12图像的饱和度(仅适用于相机)
CV_CAP_PROP_HUE13图像的色相(仅适用于相机)
CV_CAP_PROP_GAIN14图像的增益(仅适用于相机)
CV_CAP_PROP_EXPOSURE15曝光(仅适用于相机)
CV_CAP_PROP_CONVERT_RGB16表示图像是否应转换为RGB的布尔标志
CV_CAP_PROP_WHITE_BALANCE17目前不支持
CV_CAP_PROP_RECTIFICATION18立体摄像机的整流标志(注意:只有当前支持DC1394 v 2.x后端)

使用互斥量、锁、多线程进行数据读取和显示

#include <iostream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <thread>
#include <deque>
#include <mutex>
#include <Windows.h>
#include <ctime>
using namespace std;
using namespace cv;
thread::id main_thread_id = this_thread::get_id();
deque<cv::Mat> mat_deque;
mutex mut;

void open_video(VideoCapture &capture_usb, const string &filename, Mat &frame) {
    capture_usb.open(filename);
    if (!capture_usb.isOpened()) {
        cerr << "url not exit" << endl;
    }
    if (this_thread::get_id() == main_thread_id) {
        cout << "this is the main thread" << endl;
    } else {
        cout << "this is not the main thread!" << endl;
    }
    capture_usb.set(CAP_PROP_FOURCC, VideoWriter::fourcc('M', 'J', 'P', 'G'));
    capture_usb.set(CAP_PROP_FRAME_HEIGHT, 200);
    capture_usb.set(CAP_PROP_FRAME_WIDTH, 200);
    capture_usb.set(CAP_PROP_FPS, 300);
    capture_usb.set(CAP_PROP_BUFFERSIZE, 3);
    while (capture_usb.read(frame)) {
    	long start_time = clock();
        if (waitKey(20) >= 0) { break; }
        //创建线程锁锁住互斥量
        lock_guard<mutex> lk(mut);
        mat_deque.push_back(frame);
        long end_time = clock();
        cout<<"read_total_time="<<end_time-start_time<<endl;
    }
}

void get_video_data(Mat &frame) {
    while (true) {
        if (!mat_deque.empty()) {
        	long start_time = clock();
            unique_lock<mutex> lk(mut);
            frame = mat_deque.back();
            long end_time = clock();
            cout<<"get_total_time="<<end_time-start_time<<endl;
            //拿到数据之后解锁
            lk.unlock();
            if (waitKey(20) >= 0) { break; }
            imshow("capture", frame);
            waitKey(1000);
            mat_deque.pop_back();
        }
    }
}

int main() {
    VideoCapture capture_usb;
    Mat frame, frame_video, frame1, frame2;
    const string filename = url;
    const string filename1 = url;
    const string filename_123 = url;
    thread thread_1(open_video, ref(capture_usb), filename_123, ref(frame));
    thread thread3(get_video_data,ref(frame_video));
    thread_1.join();
    thread3.join();

    for (int i = 0; i < 5; ++i) {
        thisthread::sleepfor(2000ms);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值