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_MSEC | 0 | 视频文件的当前位置(以毫秒为单位)或视频捕获时间戳 |
CV_CAP_PROP_POS_FRAMES | 1 | 基于0的索引将被解码/捕获下一帧 |
CV_CAP_PROP_POS_AVI_RATIO | 2 | 视频文件相对位置:0 - 电影的开始,电影的1 - 结束 |
CV_CAP_PROP_FRAME_WIDTH | 3 | 视频里每一帧的宽 |
CV_CAP_PROP_FRAME_HEIGHT | 4 | 视频里每一帧的高 |
CV_CAP_PROP_FPS | 5 | 视频的帧速 |
CV_CAP_PROP_FOURCC | 6 | 4个字符表示的视频编码器格式 |
CV_CAP_PROP_FRAME_COUNT | 7 | 视频的帧数 |
CV_CAP_PROP_FORMAT | 8 | byretrieve()返回的Mat对象的格式 |
CV_CAP_PROP_MODE | 9 | 指示当前捕获模式的后端特定值 |
CV_CAP_PROP_BRIGHTNESS | 10 | 图像的亮度(仅适用于相机) |
CV_CAP_PROP_CONTRAST | 11 | 图像对比度(仅适用于相机) |
CV_CAP_PROP_SATURATION | 12 | 图像的饱和度(仅适用于相机) |
CV_CAP_PROP_HUE | 13 | 图像的色相(仅适用于相机) |
CV_CAP_PROP_GAIN | 14 | 图像的增益(仅适用于相机) |
CV_CAP_PROP_EXPOSURE | 15 | 曝光(仅适用于相机) |
CV_CAP_PROP_CONVERT_RGB | 16 | 表示图像是否应转换为RGB的布尔标志 |
CV_CAP_PROP_WHITE_BALANCE | 17 | 目前不支持 |
CV_CAP_PROP_RECTIFICATION | 18 | 立体摄像机的整流标志(注意:只有当前支持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;
}