如题:使用opencv打开摄像头或视频文件,实时显示原始视频,将视频每一帧依次做灰度转换、高斯滤波、canny边缘检测处理(原始视频和这3个中间步骤处理结果分别在一个窗口显示),最后将边缘检测结果保存为一个视频avi文件。
这里问题综合性比较大,这里进行分治。
该类问题可分为四个方面的处理:
(1)打开 视频或者是摄像头,并播放视频
(2)对视频的每一帧做处理
(3)同窗体显示四个结果
(4)保存视频文件
以下分为这三个方面进行处理:
(1)打开 视频或者摄像头,并播放视频
这个利用opencv来说算是非常简单的操作了:
步骤分为四步:(简单操作,播放效果不是最佳的,适用于入门)
a.创建摄像头/播放器
b.将视频文件赋值给播放器
c.逐帧播放
d.释放播放器和文件
这里直接给上源码(面向对象的方法):
/******************************************************
文件名 :main.cpp
描 述 :灰度转换、高斯滤波、canny边缘检测处理视频,并存储canny边缘处理的视频
语 言 :c++
作 者 :重交亲爸爸
修 改 :
日 期 :2018-05-10
说 明 :opencv的支持
******************************************************/
#include <cv.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<opencv2\core\core.hpp>
using namespace cv;
using namespace std;
/******************************************************
类 名 :Myclass
描 述 :灰度转换、高斯滤波、canny边缘检测处理视频,并存储canny边缘处理的视频
语 言 :
作 者 :
修 改 :
日 期 :2018-05-10
说 明 :
******************************************************/
class MyClass
{
public:
MyClass(char* argv);
~MyClass();
void play();//播放
private:
CvCapture* capture;//视频播放器
};
/******************************************************
函数名称: MyClass
函数功能: 初始化
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
MyClass::MyClass(char* argv)
{
//capture = cvCreateFileCapture(argv[1]);
capture = cvCreateFileCapture(argv);
if (!cvGetCaptureProperty(capture, CAP_PROP_FRAME_COUNT))
{
exit();
}
}
/******************************************************
函数名称: ~MyClass
函数功能: 释放空间
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明:very important
******************************************************/
MyClass::~MyClass()
{
cvReleaseCapture(&capture);
capture = NULL;
}
/******************************************************
函数名称: play
函数功能: 播放
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
void MyClass::play(){
cvNamedWindow("播放页面", );
IplImage* frame;
while (true)
{
frame = cvQueryFrame(capture);
if (!frame)break;
cvShowImage("播放页面",(frame));
char c = cvWaitKey();
if (c == )break;//Esc的编码为27
}
}
int main(int argc,char** argv){
//char* a = "F:\\Pictures\\fcq.avi";
//char* b = "F:\\Pictures\\fcq2.avi";
if (argc > ){
MyClass *myclass = new MyClass(argv[]);
myclass->play();
delete myclass;
}
else{
printf("无视频或者录像存储空间");
exit();
}
return ;
}
使用vs2013生成解决方案后,打开window power shell进行测试。寻找到文件夹,打开相应的可执行文件
测试结果:
ok.你已经成功完成第一步了。^.^
(2)对视频的每一帧做处理
a.高斯滤波
百度百科的解释:高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
这里有opencv封装好的方法可以直接操作,这里采用的是cvSmooth的方法,然后填入核函数CV_GAUSSIAN,设置图像源和转变后的接收源。
/******************************************************
函数名称: R_Gussian
函数功能: 返回高斯滤波处理
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
IplImage* MyClass::R_Gussian(IplImage* frame){
//IplImage* edges = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, image->nChannels);
IplImage* edges = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
cvSmooth(frame, edges, CV_GAUSSIAN, , frame->nChannels); // Gauss平滑滤波,核大小为7x7
return edges;
}
按照(1)中进行测试,结果:
看上去有点模糊了。
b.灰度变换
百度百科的解释:灰度变换是指根据某种目标条件按一定变换关系逐点改变源图像中每一个像素灰度值的方法。目的是为了改善画质,使图像的显示效果更加清晰。 图像的灰度变换处理是图像增强处理技术中的一种非常基础、直接的空间域图像处理方法,也是图像数字化软件和图像显示软件的一个重要组成部分。
同理,opencv也提供的灰度变化的函数,这里要注意的是转变的时候都是通道变成1
/******************************************************
函数名称: R_Gray
函数功能: 返回灰度处理
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明:由于需要在同一个窗体显示,需要将通道数目统一,不能使用单通道
******************************************************/
IplImage* MyClass::R_Gray(IplImage* frame){
//IplImage* edges = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);
//IplImage *frame_gray = cvCreateImage(cvGetSize(image), frame->depth, 1);
/*IplImage* edges = cvCreateImage(cvGetSize(imagesrc), imagesrc->depth, imagesrc->nChannels);
cvCvtColor(frame, edges, CV_BGR2GRAY);
return edges;*/ IplImage *frame_gray = cvCreateImage(cvGetSize(frame), frame->depth, );
IplImage *frame1 = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
cvCvtColor(frame, frame_gray, CV_RGB2GRAY);
cvCvtColor(frame_gray, frame1, CV_GRAY2BGR);//见说明
return frame1;
}
这里多一步的目的是为了后面多个图像显示在一块窗口上(如果不考虑同窗体显示的话就不需要统一通道数)
结果测试:
灰度效果显示出来了。
c.Canny边缘检测
百度百科的解释:Canny 算法使用 4 个 mask 检测水平、垂直以及对角线方向的边缘。原始图像与每个 mask 所作的卷积都存储起来。对于每个点我们都标识在这个点上的最大值以及生成的边缘的方向。
这里使用cvCanny的方法进行转变,但是这个cvCanny边缘检测还需要弄成灰度图。
/******************************************************
函数名称: R_Canny
函数功能: 返回Canny边缘处理
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明: 由于需要在同一个窗体显示,需要将通道数目统一,不能使用单通道
******************************************************/
IplImage* MyClass::R_Canny(IplImage* frame){
//IplImage* edges = cvCreateImage(cvGetSize(grayimage), IPL_DEPTH_8U, 1);
IplImage *frame_gray = cvCreateImage(cvGetSize(frame), frame->depth, );
IplImage *frame1 = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
cvCanny(frame, frame_gray, , , );
cvCvtColor(frame_gray, frame1, CV_GRAY2BGR);
return frame1;
}
同样也要统一通道数。
测试结果:
这样我们三个图像处理就完成了。^.^
(3)同窗体显示四个结果
这里参考了CSDN上的一个方法https://blog.csdn.net/augusdi/article/details/9019473
作者稍作整理了下,站在巨人的肩膀上。
/******************************************************
函数名称: cvShowManyImages
函数功能: 展示多个图片
传入参数: const char* title, int nArgs
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人: CSDN Augusdi
修 改 人:
其它说明:
******************************************************/
void MyClass::cvShowManyImages(const char* title, int nArgs, ...){
// img - Used for getting the arguments
IplImage *img; // DispImage - the image in which input images are to be copied
IplImage *DispImage; int size;
int i;
int m, n;
int x, y; // w - Maximum number of images in a row
// h - Maximum number of images in a column
int w, h; // scale - How much we have to resize the image
float scale;
int max; // If the number of arguments is lesser than 0 or greater than 12
// return without displaying
if (nArgs <= ) {
printf("Number of arguments too small....n");
return;
}
else if (nArgs > ) {
printf("Number of arguments too large....n");
return;
}
// Determine the size of the image,
// and the number of rows/cols
// from number of arguments
else if (nArgs == ) {
w = h = ;
size = ;
}
else if (nArgs == ) {
w = ; h = ;
size = ;
}
else if (nArgs == || nArgs == ) {
w = ; h = ;
size = ;
}
else if (nArgs == || nArgs == ) {
w = ; h = ;
size = ;
}
else if (nArgs == || nArgs == ) {
w = ; h = ;
size = ;
}
else {
w = ; h = ;
size = ;
} // Create a new 3 channel image
DispImage = cvCreateImage(cvSize( + size*w, + size*h), , ); // Used to get the arguments passed
va_list args;
va_start(args, nArgs); // Loop for nArgs number of arguments
for (i = , m = , n = ; i < nArgs; i++, m += ( + size)) { // Get the Pointer to the IplImage
img = va_arg(args, IplImage*); // Check whether it is NULL or not
// If it is NULL, release the image, and return
if (img == ) {
printf("Invalid arguments");
cvReleaseImage(&DispImage);
return;
} // Find the width and height of the image
x = img->width;
y = img->height; // Find whether height or width is greater in order to resize the image
max = (x > y) ? x : y; // Find the scaling factor to resize the image
scale = (float)((float)max / size); // Used to Align the images
if (i % w == && m != ) {
m = ;
n += + size;
} // Set the image ROI to display the current image
cvSetImageROI(DispImage, cvRect(m, n, (int)(x / scale), (int)(y / scale))); // Resize the input image and copy the it to the Single Big Image
cvResize(img, DispImage); // Reset the ROI in order to display the next image
cvResetImageROI(DispImage);
} // Create a new window, and show the Single Big Image
//cvNamedWindow( title, 1 );
cvShowImage(title, DispImage); //cvDestroyWindow(title); // End the number of arguments
va_end(args); // Release the Image Memory
cvReleaseImage(&DispImage);
}
这里加入一个多视频播放处理:
/******************************************************
函数名称: Multi_play
函数功能: 播放
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
void MyClass::Multi_play(){
IplImage *frame;
cvNamedWindow("播放页面按Esc退出", );
cvResizeWindow("播放页面按Esc退出", , );
while (true)
{
frame = cvQueryFrame(capture);
if (!frame)break;
IplImage* grayimage = R_Gray(frame);
IplImage* gsimage = R_Gussian(frame);
IplImage* cnimage = R_Canny(frame);
//IplImage *frame_not = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);//负片
//cvNot(frame, frame_not);
//cvShowImage("播放页面按Esc退出", R_Canny(R_Gray(frame)));
//cvWriteFrame(writer, cnimage);
cvShowManyImages("播放页面按Esc退出", , frame, grayimage, gsimage, cnimage);
char c = cvWaitKey();
if (c == )break; cvReleaseImage(&grayimage);
cvReleaseImage(&gsimage);
cvReleaseImage(&cnimage);
//cvReleaseImage(&frame_not);
}
}
结果测试:
成功生成,这里注意一点就是如果放入的图片不是同一通道的话,会报错。
(4)保存视频文件
保存视频就比较简单了。
a.创建视频记录器 CvVideoWriter* writer;
b.初始化视频记录器 如初始化代码所示
c.视频记录器记录每一帧 如播放功能代码所示
d.释放视频记录器 如释放代码所示
/******************************************************
函数名称: MyClass
函数功能: 初始化
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
MyClass::MyClass(char* argv,char* filepath)
{
//capture = cvCreateFileCapture(argv[1]);
capture = cvCreateFileCapture(argv);
if (!cvGetCaptureProperty(capture, CAP_PROP_FRAME_COUNT))
{
exit();
}
double fps = cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
CvSize size = cvSize(
(int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH),
(int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT)
);
writer = cvCreateVideoWriter(
filepath,
CV_FOURCC('M', 'J', 'P', 'G'),
fps,
size
);
}
/******************************************************
函数名称: ~MyClass
函数功能: 释放空间
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
MyClass::~MyClass()
{
cvReleaseVideoWriter(&writer);
cvReleaseCapture(&capture);
writer = NULL;
capture = NULL;
}
/******************************************************
函数名称: Multi_play
函数功能: 播放
传入参数:
返 回 值:
建立时间: 2018-05-10
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
void MyClass::Multi_play(){
IplImage *frame;
cvNamedWindow("播放页面按Esc退出", );
cvResizeWindow("播放页面按Esc退出", , );
while (true)
{
frame = cvQueryFrame(capture);
if (!frame)break;
IplImage* grayimage = R_Gray(frame);
IplImage* gsimage = R_Gussian(frame);
IplImage* cnimage = R_Canny(frame);
IplImage *frame_not = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
cvNot(frame, frame_not);
//cvShowImage("播放页面按Esc退出", R_Canny(R_Gray(frame)));
cvWriteFrame(writer, cnimage);
cvShowManyImages("播放页面按Esc退出", , frame, grayimage, gsimage, cnimage);
char c = cvWaitKey();
if (c == )break; cvReleaseImage(&grayimage);
cvReleaseImage(&gsimage);
cvReleaseImage(&cnimage);
cvReleaseImage(&frame_not);
}
}
ok,在window power shell输入的时候要加上保存地址了
视频停止或者终止后,在相应文件下就生成了新文件.avi了
如需要源码请转移至码云:https://gitee.com/cjqbaba/MediaTest/tree/Capture_show进行源码克隆下载
如有问题请留言评论。转载请注明出处,谢谢。