C++学习笔记之CV基础篇读取写入及遍历的三种方法 9.27(1)

9 篇文章 0 订阅

OpenCV读写视频文件解析
纠正个误区,我见有人用OpenCV做多媒体开发,OpenCV这东西再强大,这方面也不行的,之所以把视频读取写入这部分做的强大一些,也是为了方便大家做视频处理的时候方便些,而且这部分也是基于vfw和ffmpeg二次开发的,功能还是很弱的。一定要记住一点,OpenCV是一个强大的计算机视觉库,而不是视频流编码器或者解码器。希望大家不要走入这个误区,可以把这部分简单单独看待。目前,OpenCV只支持avi的格式,而且生成的视频文件不能大于2GB,而且不能添加音频。如果你想突破这些限制,我建议你最好还是看看ffMpeg,而不是浪费时间在OpenCV上。不过也可以利用视频后期合成工具制作。进入重点VideoWriter类。这个类是highgui交互很重要的一个工具类,可以方便我们容易的将图片序列保存成视频文件。类内成员函数有构造函数,openisOpenedwrite(也可以用<<),使用还是很简单的。使用很简单,先调用构造函数确定文件的名称,格式,帧率,帧大小,是否彩色。其中格式作为第二个参数,OpenCV提供的格式是未经过压缩的,目前支持的格式如下:

CV_FOURCC('P', 'I', 'M', '1') = MPEG-1 codec
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

然后就<<不停的把image传进去就行啦,代码如下:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
 
using namespace cv;
 
void main()
{
	VideoCapture capture(0);
	VideoWriter writer("VideoTest.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25.0, Size(640, 480));
	Mat frame;
	
	while (capture.isOpened())
	{
		capture >> frame;
		writer << frame;
		imshow("video", frame);
		if (cvWaitKey(20) == 27)
		{
			break;
		}
	}
}

图像读写类

视频处理的是运动图像,而不是静止图像。视频资源可以是一个专用摄像机、网络摄像头、视频文件或图像文件序列。
在OpenCV 中,VideoCapture 类和 VideoWriter 类为视频处理中所涉及的捕获和记录任务提供了一个易用的C++API。

cv::VideoCapture类

1、对象的构造函数,如下面的例子:

cv::VideoCapture capture(“D:\Camera Road 01.avi”);

参数为const string&,即读入彩色图像,若设置为0则读取摄像头。

2、验证视频读入是否成功,如下:

if (!capture.isOpened())
{ std::cout << “Vidoe open failed!<< std::endl; return -1; }

3、验证完成后,就可以开始读取视频啦!

cv::Mat frame;capture >> frame;

VideoCapture对象的操作可以像流一样读入到Mat类型的对象(即图像)中。

cv::VideoWriter类

这个类是用来写入一个视频的,使用起来比capture麻烦一些。

构造函数

cv::VideoCapture(const string& path,int fourcc,double fps, Size framesize, bool isColor=true)

需要注意的是

fourcc,cv::VideoWriter::fourcc(char c1, char c2, char c3, char c4)

常用的格式有

·

 CV_FOURCC(‘P’,‘I’,‘M’,1) = MPEG-1 codec

· 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

图像读取

OpenCV支持bmp、jpg、png、tiff等常用图像格式的解析,所用函数为imread。imread函数原型如下:

Mat imread( const String& filename, int flags = IMREAD_COLOR );

其中,filename参数为图像文件的路径,可以是相对路径,也可以是绝对路径;flags参数为图像文件解析的方式,支持的方式有如下:

IMREAD_UNCHANGED:不对图像文件进行任何转换,直接读取;
IMREAD_GRAYSCALE:将任何图像均转换为灰度图像(单通道)进行读取;
IMREAD_COLOR:将任何图像均转为RGB彩色图像(三通道)进行读取;
IMREAD_ANYDEPTH:如果不设置这个参数,16/32位图像将会自动转为8位图像;
IMREAD_ANYCOLOR:将按照图像文件设定的颜色格式进行图像读取;
IMREAD_LOAD_GDAL:调用gdal库进行图像文件读取(可以简单地理解为读取TIFF图像文件)。
例如:

#include <opencv2/opencv.hpp>
#include

int main(int argc, char const *argv[])
{
    cv::Mat img = cv::imread("test.png", cv::IMREAD_GRAYSCALE);

    //宽度
    std::cout << "宽度: "<< img.cols << std::endl;
    //高度
    std::cout << "高度: " << img.rows << std::endl;
    //通道数
    std::cout << "通道数: " << img.channels() << std::endl;
    //深度
    //elemSize函数返回的是一个像素占用的字节数
    std::cout << "深度: " << img.elemSize() / img.channels() * 8 << std::endl;

    return 0;
}

图像显示

OpenCV的图像显示函数为imshow,函数原型如下:

void imshow(const String& winname, InputArray mat);

其中,winname参数表示显示图像窗口的名称(任意字符),mat参数表示需要显示的图像。对于这个函数,需要注意的是(特别是新手),imshow函数只支持8位灰度图像、8位彩色图像和32位灰度图像(像素值范围0-1),具体原因大家可以自行百度显示器灰度等级。同时还有,除了使用该函数之外,还需要使用waitKey函数使图像界面一直显示。例如:

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

int main(int argc, char const *argv[])
{
    cv::Mat img = cv::imread("test.png", cv::IMREAD_GRAYSCALE);
    cv::imshow("hello", img);
    cv::waitKey();

    return 0;
}

图像保存

imwrite函数用来保存图片,函数原型如下:

bool imwrite( const String& filename, InputArray img,
              const std::vector<int>& params = std::vector<int>());

其中,filename参数表示需要写入的文件名,必须要加上后缀,比如“123.png”;img参数表示Mat类型的图像数据;params参数表示为特定格式保存的参数编码,它有一个默认值std::vector(),所以一般情况下不用写。

同时还需要注意的是,并不是所有格式的Mat型数据都能被保存为图片,目前OpenCV主要只支持单通道和3通道的图像,并且此时要求其深度为8bit和16bit无符号。所以,其他一些数据类型是不支持的,比如说float型等。如果Mat类型数据的深度和通道数不满足上面的要求,则需要使用convertTo()函数和cvtColor()函数来进行转换。

例如:

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

int main(int argc, char const *argv[])
{
    cv::Mat img = cv::imread("test.png", cv::IMREAD_GRAYSCALE);
    cv::imwrite("test_copy.png", img);

    return 0;
}

图像遍历

图像遍历的几种方式
OpenCV的at成员函数
OpenCV提供了便利的访问图像数据的接口,at函数原型:

template<typename _Tp> _Tp& at(int row, int col);

其中,参数row为行号,参数col为列号;模板参数_Tp常用类型如下:

图像类型 _Tp参数
单通道灰度 unsigned char
三通道彩色(8位) cv::Vec3b
16位灰度 unsigned short
32位浮点型 float
双通道32位 cv::Vec2f
例如:

#include <opencv2/opencv.hpp>
#include

int main(int argc, char const *argv[])
{
    cv::Mat img = cv::imread("test.png", cv::IMREAD_GRAYSCALE);

    for (int r = 0; r < img.rows; ++r) {
        for (int c = 0; c < img.cols; ++c) {
            int data = img.at<unsigned char>(r, c);
            std::cout << data << std::endl;
        }
    }

  return 0;
}

数据缓存区指针

使用data属性获取数据缓存区指针,利用指针直接读取数据缓存区的内容。例如:

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

int main(int argc, char const *argv[])
{
    cv::Mat img = cv::imread("test.png", cv::IMREAD_GRAYSCALE);

    unsigned char * img_data = reinterpret_cast<unsigned char *>(img.data);
    for(int r = 0; r < img.rows; ++r) {
        for(int c = 0; c < img.cols; ++c) {
            std::cout << static_cast<float>(*img_data) << std::endl;
            ++img_data;
        }
    }

    return 0;
}

行首指针

除了data属性可以获取数据缓存区的指针,OpenCV还提供了ptr接口来直接获取每一行的行首指针。例如:

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

int main(int argc, char const *argv[])
{
    cv::Mat img = cv::imread("test.png", cv::IMREAD_GRAYSCALE);

    for(int r = 0; r < img.rows; ++r) {
        unsigned char * rdata = img.ptr<unsigned char>(r);
        for(int c = 0; c < img.cols; ++c) {
            std::cout << static_cast<float>(*rdata) << std::endl;
            ++rdata;
        }
    }

    return 0;
}

视频操作

视频读取

OpenCV中通过VideoCaptrue类对视频进行读取操作以及调用摄像头,该类的API如下:

//功能:创建一个VideoCapture类的实例,如果传入对应的参数,可以直接打开视频文件或者要调用的摄像头。
//参数: filename – 打开的视频文件名。
//device – 打开的视频捕获设备id ,如果只有一个摄像头可以填0,表示打开默认的摄像头

VideoCapture::VideoCapture();  
VideoCapture::VideoCapture(const string& filename);  
VideoCapture::VideoCapture(int device);  

//功能:打开一个视频文件或者打开一个捕获视频的设备(也就是摄像头)

bool VideoCapture::open(const string& filename);  
bool VideoCapture::open(int device);

//功能:判断视频读取或者摄像头调用是否成功,成功则返回true

bool VideoCapture::isOpened();

//功能:关闭视频文件或者摄像头

void VideoCapture::release();

//功能:读取视频内容

bool VideoCapture::grab();  //若没有视频帧被捕获,返回false
bool VideoCapture::retrieve(Mat& image, int channel=0);  //若没有视频帧被捕获,返回false
VideoCapture& VideoCapture::operator>>(Mat& image);  //若没有视频帧被捕获,返回空,即cv::Mat.empty()返回true
bool VideoCapture::read(Mat& image);  //若没有视频帧被捕获,返回false

//功能:获得视频有诸多属性,比如:帧率、总帧数、尺寸、格式等
//如果查询的视频属性是VideoCapture类不支持的,将会返回0

double VideoCapture::get(int propId);  

//功能:设置VideoCapture类的属性,设置成功返回ture,失败返回false
//参数:第一个是属性ID,第二个是该属性要设置的值

bool VideoCapture::set(int propertyId, double value);

其中,视频的属性主要包括:

属性值 属性含义

CV_CAP_PROP_POS_MSEC	以毫秒计算的当前的位置
CV_CAP_PROP_POS_FRAMES	以帧计算当前的位置
CV_CAP_PROP_POS_AVI_RATIO	视频的相对位置,0表示视频开始,1表示视频结束
CV_CAP_PROP_FRAME_WIDTH	帧宽度
CV_CAP_PROP_FRAME_HEIGHT	帧高度
CV_CAP_PROP_FPS	帧率
CV_CAP_PROP_FOURCC	4字符编码方式
CV_CAP_PROP_FRAME_COUNT	视频帧数
CV_CAP_PROP_FORMAT	视频格式
CV_CAP_PROP_MODE	
CV_CAP_PROP_BRIGHTNESS	亮度
CV_CAP_PROP_CONTRAST	对比度
CV_CAP_PROP_SATURATION	饱和度
CV_CAP_PROP_HUE	色调
CV_CAP_PROP_GAIN	增益
CV_CAP_PROP_EXPOSURE	曝光
CV_CAP_PROP_CONVERT_RGB	图像是否应转换为RGB的标志
CV_CAP_PROP_WHITE_BALANCE	白平衡
CV_CAP_PROP_RECTIFICATION	

例如:

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

int main(int argc, char const *argv[])
{
    cv::VideoCapture cap;
    cap.open("test.mp4");

    if (!cap.isOpened())
        return 0;

    int width = cap.get(CV_CAP_PROP_FRAME_WIDTH);             //帧宽度
    int height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);           //帧高度
    int totalFrames = cap.get(CV_CAP_PROP_FRAME_COUNT);       //总帧数
    int frameRate = cap.get(CV_CAP_PROP_FPS);                 //帧率 x frames/s

    std::cout << "视频宽度: " << width << std::endl;
    std::cout << "视频高度: " << height << std::endl;
    std::cout << "视频总帧数: " << totalFrames << std::endl;
    std::cout << "帧率: " << frameRate << std::endl;

    cv::Mat frame;
    while(1) {
        cap >> frame;
        if (frame.empty())
            break;
        cv::imshow("hello", frame);
        cv::waitKey(50);
    }
    cap.release();

    return 0;
}

在上面的API中还将到set方法,可以指定属性的值。比如,想要跳转到视频的中间的某一帧,就可以通过设置CV_CAP_PROP_POS_FRAMES属性来实现,即:

cap.set(CV_CAP_PROP_POS_FRAMES, 20); //跳转到第20帧

视频保存

OpenCV中通过VideoWriter类对视频进行读取操作以及调用摄像头,该类的API与VideoCapture类似,该类的主要API除了构造函数外,提供了open、IsOpen、release、write和重载操作符<<

其主要的API如下:

VideoWriter::VideoWriter(const string& filename, int fourcc,
                         double fps, Size frameSize, bool isColor=true);
bool VideoWriter::open(const string& filename, int fourcc,
                       double fps, Size frameSize, bool isColor=true);

其中,filename参数表示文件名,fourcc参数表示编码格式,fps参数表示视频帧率,frameSize表示视频的尺寸大小。

OpenCV支持的编码格式包括:

编码格式 编码格式含义

CV_FOURCC(‘P’,‘I’,‘M’,1)	MPEG-1
CV_FOURCC(‘M’,‘J’,‘P’,‘G’)	motion-jpeg
CV_FOURCC(‘M’, ‘P’,4,2)	MPEG-4.2
CV_FOURCC(‘D’, ‘I’, ‘V’,3)	MPEG-4.3
CV_FOURCC(‘D’, ‘I’, ‘V’, ‘X’)	MPEG-4
CV_FOURCC(‘U’,2,6,3)	H263
CV_FOURCC(‘I’,2,6,3)	H263I
CV_FOURCC(‘F’, ‘L’, ‘V’,1)	FLV1

值得注意的是:OpenCV里对视频的编码解码等支持并不是很良好,所以不要希望用这个类去实现摄像头图像的获取与转码,有兴趣的可以参考FFmpeg库。

例如:

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

int main(int argc, char const *argv[])
{
    cv::VideoCapture cap;
    cap.open("test.mp4");

    if (!cap.isOpened())
        return 0;

    int width = cap.get(CV_CAP_PROP_FRAME_WIDTH);             //帧宽度
    int height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);           //帧高度
    int totalFrames = cap.get(CV_CAP_PROP_FRAME_COUNT);       //总帧数
    int frameRate = cap.get(CV_CAP_PROP_FPS);                 //帧率 x frames/s

    std::cout << "视频宽度: " << width << std::endl;
    std::cout << "视频高度: " << height << std::endl;
    std::cout << "视频总帧数: " << totalFrames << std::endl;
    std::cout << "帧率: " << frameRate << std::endl;

    cv::VideoWriter wri;
    wri.open("test_copy.avi", CV_FOURCC('M', 'P', '4', '2'), frameRate, cv::Size(width, height));

    cv::Mat frame;
    while(1) {
        cap >> frame;
        if (frame.empty())
            break;
        wri << frame;
    }
    cap.release();
    wri.release();

    return 0;
}

使用VideoWriter,建议生成.avi后缀的视频。

在这里插入图片描述

opencv 遍历mat的三种方法

1. 通过 .at(i,j) 遍历

void colorReduce(Mat& image,int div)  
{  
    for(int i=0;i<image.rows;i++)  
    {  
        for(int j=0;j<image.cols;j++)  
        {  
            image.at<Vec3b>(i,j)[0]=image.at<Vec3b>(i,j)[0]/div*div+div/2;  
            image.at<Vec3b>(i,j)[1]=image.at<Vec3b>(i,j)[1]/div*div+div/2;  
            image.at<Vec3b>(i,j)[2]=image.at<Vec3b>(i,j)[2]/div*div+div/2;  
        }  
    }  
}  

image.at<uchar>(i,j):取出灰度图像中i行j列的点。

image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点,k=[0,1,2],分别代表B,G,R

uchar,Vec3b都是图像像素值的类型,在core里它是通过typedef Vec<T,N>来定义的,N代表元素的个数,T代表类型。

OpenCV定义了一个Mat的模板子类为Mat_,它重载了operator()让我们可以更方便的取图像上的点。

Mat m2(300, 500, CV_8UC3, Scalar(0, 0, 0));
Mat_<Vec3b> im = m2;
for (int i = 0; i < im.rows; i++) {
    for (int j = 0; j < im.cols; j++) {
        im(i, j)[0] = im(i, j)[0] + (uchar) (255 / 2);
        im(i, j)[1] = im(i, j)[1] + (uchar) 0;
        im(i, j)[2] = im(i, j)[2] + (uchar) (111 / 2);
    }
}

2. 通过行指针 .ptr(k)来遍历

void colorReduce(const Mat& image,Mat& outImage,int div)  
{  
    // 创建与原图像等尺寸的图像  
    outImage.create(image.size(),image.type());  
    int nr=image.rows;  
    // 将3通道转换为1通道  
    int nl=image.cols*image.channels();  
    for(int k=0;k<nr;k++)  
    {  
        // 每一行图像的指针  
        const uchar* inData=image.ptr<uchar>(k);  
        uchar* outData=outImage.ptr<uchar>(k);  
        for(int i=0;i<nl;i++)  
        {  
            outData[i]=inData[i]/div*div+div/2;  
        }  
    }  
} 

3. 通过迭代器Mat_iterator来遍历

void colorReduce(const Mat& image,Mat& outImage,int div)  
{  
    outImage.create(image.size(),image.type());
    // 获得迭代器  
    MatConstIterator_<Vec3b> it_in=image.begin<Vec3b>();  
    MatConstIterator_<Vec3b> itend_in=image.end<Vec3b>();  
    MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>();  
    MatIterator_<Vec3b> itend_out=outImage.end<Vec3b>();  
    while(it_in!=itend_in)  
    {  
        (*it_out)[0]=(*it_in)[0]/div*div+div/2;  
        (*it_out)[1]=(*it_in)[1]/div*div+div/2;  
        (*it_out)[2]=(*it_in)[2]/div*div+div/2;  
        it_in++;  
        it_out++;  
    }  
} 

最后关于srand(time(0))的解释:
srand(time(0)) ;//先设置种子
rand();//然后产生随机数
Srand是种下随机种子数,你每回种下的种子不一样,用Rand得到的随机数就不一样。为了每回种下一个不一样的种子,所以就选用Time(0),Time(0)是得到当前时时间值(因为每时每刻时间是不一样的了)。srand(time(0)) ;就是给这个算法一个启动种子,也就是算法的随机种子数,有这个数以后才可以产生随机数,

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

void main ( void )
{
    int i;
    
    srand(time(0));
    
    /* 输出 10 个随机数. */
    for (i = 0; i < 10; i++)
        printf( "%6d/n", rand());
}

输出结果: 22614 15772 30620 24913
7556 31659 27876 27229 25083 16564

#include "stdafx.h" #include"highgui.h" int main(int argc,char* argv[]) { cvNamedWindow( "avi"); CvCapture* capture = cvCreateFileCapture( "D:\\sample.avi"); IplImage* frame; while(1) { frame = cvQueryFrame(capture); if(!frame) break ; cvShowImage( "avi",frame); char c = cvWaitKey(33); if(c == 27) break; } cvReleaseCapture(&capture); cvDestroyWindow( "avi"); return 0; } 结构体 CvCapture CvCapture 是一个结构体,用来保存图像捕获所需要的信息。 opencv提供两种方式从外部捕获图像: 一种是从摄像头中, 一种是通过解码视频得到图像。 两种方式都必须从第一帧开始一帧一帧的按顺序获取,因此每获取一帧后都要保存相应的状态和参数。 比如从视频文件中获取,需要保存视频文件的文件名,相应的解码器类型,下一次如果要 获取将需要解码哪一帧等。 这些信息都保存在CvCapture结构中,每获取一帧后,这些信息 都将被更新,获取下一帧需要将新信息传给获取的 api接口 cvCreateFileCapture(char*name) 通过输入要读取的avi文件的路径,然后,该函数返回一个指向 CvCapture结构体的指针。 cvQueryFrame(capture) 输入一个CvCapture 类型的指针,该函数主要功能是将视频文件的下一帧加载到内存。与 cvLoadImage的不同之处是,该函数不重新分配内存空间。 C=cvWaitKey(33) 当前帧被显示后,等待 33毫秒。如果用户触发了一个按键, c会被设置成这个按键的 ASCII码,否则会被设置成 -1。 cvWaitKey(33) 在此处的另外一个作用是,控制帧率。 cvReleaseCapture(&capture) 释放为 CvCapture结构体开辟的内存空间 关闭打开的 AVI文件相关的文件句柄 读取摄像头 只需把 cvCreateFileCapture 改成cvCreateCameraCapture即可。 该函数的输入参数是一个 ID号,只有存在多个摄像头时才起作用。当 ID=-1时,表示 随机选择一个。 HighGUI做了很多工作,使得摄像机图像序列像一个视频文件一样。 cvCreateFileCapture返回空的问题 ( 1)视频文件路径没写对( 2)没有安装解码器( 3)如果使用的是 Opencv2.0或更高版本,那么,能否正确加载 opencv_ffmpeg210.dll( 4)尽管是 AVI文件,但也可能使用了某种 codec,例如 :MJPEG Decompressor。 需要把它转换 OpenCV支持的 AVI文件 . OpenCV支持的AVI。例如使用狸窝全能视频转换器,在《预置方案》处,选择 AVI-Audio_Video Interleaved(*.avi)。或者使用格式工厂也可以。( 5)读摄像头数据,需要安装与摄像头相应的驱动程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪迹天涯@wxy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值