第二章 学习OpenCV——OpenCV入门

第二章 学习OpenCV——OpenCV入门

目录

  • 第二章 学习OpenCVOpenCV入门
    • 目录
      • 简介
      • OpenCV环境配置
      • 例2-1 初试牛刀显示图像
      • 例2-2 播放AVI视频
      • 例2-3 视频播放控制
      • 例2-4 一个简单的变换
      • 例2-5 一个复杂一点的变换
      • 例2-6 Canny边缘检测输出一个单通道灰度级图像
      • 例2-7 进行两次缩放处理和Canny边缘检测
      • 例2-8 摄像头读入数据
      • 例2-9 写入AVI视频文件
      • 例2-10 摄像头输入视频流缩放后显示与存储
      • 例2-11 摄像头输入视频流缩放后显示与存储

简介

笔者刚开始接触计算机视觉这一个领域,在使用《学习OpenCV》这本经典著作的过程中遇到了一系列的问题,很多问题对于一名初学者来说都是共性的,因此在CSDN与大家分享、交流我遇到的问题,希望大家少走弯路,共同进步!笔者小白,若有叙述不当之处,请看客予以纠正,在此谢过。(本文代码基于VS2013+OpenCV2.4.9环境)

OpenCV环境配置

OpenCV 2.4.9 +VS2013 开发环境配置,可参考以下链接:
OpenCV 2.4.9 +VS2013 开发环境配置

例2-1 初试牛刀——显示图像

从磁盘加载并在屏幕上显示一幅图像,程序代码如下:

#include 
#include 

using namespace std;

int main(int argc, char** argv)                     //带输入参数的main函数
{                                   
    IplImage *img = cvLoadImage(argv[1]);           //加载图像至内存返回指针
    cvNamedWindow("Example1", CV_WINDOW_AUTOSIZE);  //创建窗口
    cvShowImage("Example1", img);                   //窗口中显示图像
    cvWaitKey(0);                                   //等待任意按键触发
    cvReleaseImage(&img);                           //释放图像所占内存
    cvDestroyWindow("Example1");                    //销毁窗口
}

带参数main函数程序的运行方法:
①在VS环境下编译生成.exe文件,并将要显示的图像文件放到.exe文件同一文件夹下
②在开始菜单搜索框输入cmd,右键以管理员身份运行
③图像文件所在盘符: 回车
④cd+“空格”+工程Debug文件夹所在路径 回车
⑤应用程序名.exe+“空格”+图像文件名.jpg 回车

具体过程如下:
cmd
第二章 学习OpenCV——OpenCV入门_第1张图片
第二章 学习OpenCV——OpenCV入门_第2张图片
第二章 学习OpenCV——OpenCV入门_第3张图片

为方便调试和实际调用,笔者将以上程序做了一些更改,以便能够直接实现编译调试,代码如下:

#include 
#include 

using namespace std;

int main(int argc, char** argv)                     //带输入参数的main函数
{       
    IplImage *img = cvLoadImage("D:\\Template\\OpenCV\\Template1_ShowImage_cmd\\3.jpg");
//  IplImage *img = cvLoadImage(argv[1]);           //加载图像至内存返回指针
    cvNamedWindow("Example1", CV_WINDOW_AUTOSIZE);  //创建窗口
    cvShowImage("Example1", img);                   //窗口中显示图像
    cvWaitKey(0);                                   //等待任意按键触发
    cvReleaseImage(&img);                           //释放图像所占内存
    cvDestroyWindow("Example1");                    //销毁窗口
}

以上代码的功能,依旧是实现对之前那张图片的调用,只不过省去了cmd调用的环节,.exe文件可直接运行。

例2-2 播放AVI视频

播放视频硬盘中的视频文件的程序与显示图像类似,笔者也将其写成了通过cmd调用和直接调用的两个版本。
通过cmd调用的代码如下(调用方式见例2-1):

#include 
#include 

int main(int argc, char** argv)             //带输入参数的main函数
{
    cvNamedWindow("Example2", CV_WINDOW_AUTOSIZE);       //创建窗口
    CvCapture* capture = cvCreateFileCapture(argv[1]);   //确定读入文件
    IplImage* frame;                                     //当前帧指针
    while (1)
    {
        frame = cvQueryFrame(capture);      //下一帧图像载入内存,填充or更新
        if (!frame) break;
        cvShowImage("Example2", frame);
        char c = cvWaitKey(32);             //等待32ms
        if (c == 27) break;
    }
    cvReleaseCapture(&capture);             //释放资源
    cvDestroyWindow("Example2");            //销毁窗口
}

直接调用的代码如下(调用方式见例2-1):

#include 
#include 

int main(int argc, char** argv)             //带输入参数的main函数
{
    cvNamedWindow("Example2", CV_WINDOW_AUTOSIZE);       //创建窗口
    CvCapture* capture = cvCreateFileCapture("D:\\Template\\OpenCV\\Template2_ShowAVI_cmd\\Debug\\只想告诉你.avi");
    IplImage* frame;                                     //当前帧指针
    while (1)
    {
        frame = cvQueryFrame(capture);      //下一帧图像载入内存,填充or更新
        if (!frame) break;
        cvShowImage("Example2", frame);
        char c = cvWaitKey(32);             //等待32ms
        if (c == 27) break;
    }
    cvReleaseCapture(&capture);             //释放资源
    cvDestroyWindow("Example2");            //销毁窗口
}

运行结果如下图:
第二章 学习OpenCV——OpenCV入门_第4张图片

例2-3 视频播放控制

添加滚动条到基本浏览窗口的程序代码如下:

#include 
#include 

using namespace std;
int g_slider_position = 0;      //滚动条位置全局变量
CvCapture* g_capture = NULL;    //确定要读入AVI文件的指针

void onTrackbarSlide(int pos)
{
    cvSetCaptureProperty(g_capture,CV_CAP_PROP_POS_FRAMES,pos);  //以帧数设置读入位置
}       //回调函数,滚动条被拖动时调用,位置以32位整型返回

int main(int argc, char** argv)
{
    cvNamedWindow("Example3", CV_WINDOW_AUTOSIZE);      //创建窗口
    g_capture = cvCreateFileCapture("D:\\Template\\OpenCV\\Template2_ShowAVI_cmd\\Debug\\只想告诉你.avi");  
//  g_capture = cvCreateFileCapture(argv[1]);           //cmd
    int frames = (int)cvGetCaptureProperty(g_capture, CV_CAP_PROP_FRAME_COUNT);   //获取总帧数以设定滚动条
    if (frames != 0)
    {
        cvCreateTrackbar("Position","Example3",&g_slider_position,frames,onTrackbarSlide);  //创建滚动条
    }           //将变量绑定到滚动条表征最大值和回调函数
    IplImage* frame;
    while (1)
    {
        frame = cvQueryFrame(g_capture);
        if (!frame) break;
        cvShowImage("Example3", frame);
        g_slider_position++;
        cvCreateTrackbar("Position", "Example3", &g_slider_position, frames, onTrackbarSlide);    //滚动条随视频移动
        char c = cvWaitKey(32);
        if (c == 27) break;
    }
    cvReleaseCapture(&g_capture);
    cvDestroyWindow("Example3");
    return(0);
}

运行结果如下:
第二章 学习OpenCV——OpenCV入门_第5张图片

例2-4 一个简单的变换

载入一幅图像并进行平滑处理,程序代码如下:

#include 
#include 

using namespace std;

void example2_4(IplImage* image)
{
    cvNamedWindow("Example4-in", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Example4-out", CV_WINDOW_AUTOSIZE);
    cvShowImage("Example4-in", image);
    IplImage* out = cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,3); //(图像大小,每个像素点数据类型 ,通道数)
    cvSmooth(image,out,CV_GAUSSIAN,3,3);  //对3*3的区域进行高斯平滑处理
    cvShowImage("Example4-out", out);
    cvReleaseImage(&out);
    cvReleaseImage(&image);
    cvWaitKey(0);
    cvDestroyWindow("Example4-in");
    cvDestroyWindow("Example4-out");
}

int main(int argc, char** argv)
{
//  IplImage *img = cvLoadImage(argv[1]);     //cmd
    IplImage *img = cvLoadImage("D:\\Template\\OpenCV\\Template4_ShowImage_Smooth\\Debug\\1.jpg");   //address
    example2_4(img);
}

运行效果如下:
第二章 学习OpenCV——OpenCV入门_第6张图片

例2-5 一个复杂一点的变换

使用cvPyDown()创建一幅宽度和高度为输入图像一般尺寸的图像程序代码如下:

#include 
#include 

using namespace std;

IplImage* doPyrDown(IplImage* in, int filter = IPL_GAUSSIAN_5x5) //高斯滤波器大小,平滑作用
{
    assert(in->width % 2 == 0 && in->height % 2 == 0);  //断言,图像像素必须为偶数
    IplImage* out = cvCreateImage(cvSize(in->width/2, in->height/2), in->depth, in->nChannels);
    cvPyrDown(in, out);     //缩小图像为1/2
//  cvPyrUp(in, out);       //放大2倍函数
    return(out);
}

void example2_5(IplImage* image)
{
    cvNamedWindow("Example5-in", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Example5-out", CV_WINDOW_AUTOSIZE);
    cvShowImage("Example5-in", image);
//  IplImage* out = cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,3);
//  cvSmooth(image,out,CV_GAUSSIAN,3,3);
    IplImage* out = doPyrDown(image);
    out = doPyrDown(out);
    cvShowImage("Example5-out", out);
    cvReleaseImage(&out);
    cvReleaseImage(&image);
    cvWaitKey(0);
    cvDestroyWindow("Example5-in");
    cvDestroyWindow("Example5-out");
}

int main(int argc, char** argv)
{
//  IplImage *img = cvLoadImage(argv[1]);     //cmd
    IplImage *img = cvLoadImage("D:\\Template\\OpenCV\\Template5_Convertion_Half\\Debug\\2.jpg");   //address
    example2_5(img);
}

运行结果如下:
第二章 学习OpenCV——OpenCV入门_第7张图片
特别提醒:
笔者在编写此段代码时忽略了一个小细节,在此处“僵持”了一段时间,出现了“R6010 -abort() has been called” 的错误,错误提示如下:
第二章 学习OpenCV——OpenCV入门_第8张图片
不知道大家是否注意到上面代码中的一句断言,此处很有可能导致你的程序无法编译通过。断言

assert(in->width % 2 == 0 && in->height % 2 == 0);  //断言,图像像素必须为偶数

打开的图像的宽度、高度像素点数均必须为偶数,否则断言为假,程序无法正常运行。

例2-6 Canny边缘检测输出一个单通道(灰度级)图像

程序代码如下:

#include 
#include 

using namespace std;

IplImage* doPyrDown(IplImage* in, int filter = IPL_GAUSSIAN_5x5)
{
    assert(in->width % 2 == 0 && in->height % 2 == 0);  //断言
    IplImage* out = cvCreateImage(cvSize(in->width/2, in->height/2), in->depth, in->nChannels);
    cvPyrDown(in, out);
    return(out);
}

IplImage* doCanny(IplImage *in, double lowThresh, double highThresh, double aperture)
{
    if (in->nChannels != 1)
    {
        cout << in->nChannels << endl;
        cout << "不能输出" << endl;
        return(0);          //Canny只能处理灰度图像
    }   
    IplImage* out = cvCreateImage(cvGetSize(in),IPL_DEPTH_8U, 1);    //书中第一个参数为cvSize(cvGetSize(image)),程序报错
    cvCanny(in, out, lowThresh, highThresh, aperture);               //(输入,输出,控制边缘连接,强边缘的初始分割,Sobel算子大内核大小) 
    return(out);
}

void example2_6(IplImage* image)
{
    cvNamedWindow("Example6-in", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Example6-out", CV_WINDOW_AUTOSIZE);
    cvShowImage("Example6-in", image);

    IplImage* out = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);   
    cvConvertImage(image, out, CV_BGR2GRAY);        //图像强制转化为gray  
    out = doCanny(out, 50, 150, 3);
    cvShowImage("Example6-out",out);
    cvReleaseImage(&out);
    cvReleaseImage(&image);
    cvWaitKey(0);
    cvDestroyWindow("Example6-in");
    cvDestroyWindow("Example6-out");
}

int main(int argc, char** argv)
{
//  IplImage *img = cvLoadImage(argv[1]);     //cmd
    IplImage *img = cvLoadImage("3.jpg",1);   //address:D:\\Template\\OpenCV\\Template6_Convention_Canny\\Debug
    example2_6(img);
}

运行效果如下:
第二章 学习OpenCV——OpenCV入门_第9张图片
错误更正:
笔者在调试这段代码时也遇到了不小问题,原因是Canny只能够处理灰度图像,而笔者所找到的图像基本都不是灰度图像,编译运行会出现如下错误,Canny无法输出变换后的Canny边缘检测图像。
错误如下图:
第二章 学习OpenCV——OpenCV入门_第10张图片
解决方案:
既然Canny需要灰度图像,笔者就为其提供灰度图像,插入以下代码,调用cvConvertImage()函数,将普通图像强制转化为gray。

cvConvertImage(image, out, CV_BGR2GRAY);        //图像强制转化为gray  ****

例2-7 进行两次缩放处理和Canny边缘检测

在一个简单的图像处理流程中进行两次缩放处理与Canny边缘检测,程序代码如下:

#include 
#include 

using namespace std;

IplImage* doPyrDown(IplImage* in, int filter = IPL_GAUSSIAN_5x5)
{
    assert(in->width % 2 == 0 && in->height % 2 == 0);  //断言
    IplImage* out = cvCreateImage(cvSize(in->width/2, in->height/2), in->depth, in->nChannels);
    cvPyrDown(in, out);
    return(out);
}

IplImage* doCanny(IplImage *in, double lowThresh, double highThresh, double aperture)
{
    if (in->nChannels != 1)
    {
        cout << in->nChannels << endl;
        cout << "不能输出" << endl;
        return(0);          //Canny只能处理灰度图像
    }   
    IplImage* out = cvCreateImage(cvGetSize(in),IPL_DEPTH_8U, 1);
    cvCanny(in, out, lowThresh, highThresh, aperture);
    return(out);
}

void Show_Image(IplImage* image)
{
    IplImage* out1 = doPyrDown(image, IPL_GAUSSIAN_5x5);
    IplImage* out2 = doPyrDown(out1, IPL_GAUSSIAN_5x5); //cvCreateImage(cvGetSize(out1), IPL_DEPTH_8U, 1);
    IplImage* out3 = cvCreateImage(cvGetSize(out2), IPL_DEPTH_8U, 1);
    cvConvertImage(out2, out3, CV_BGR2GRAY);     //图像强制转化为gray
    IplImage* out4 = doCanny(out3, 10, 100, 3);
    cvNamedWindow("In", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Half1", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Half2", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Gray", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Out", CV_WINDOW_AUTOSIZE);
    cvShowImage("In", image);
    cvShowImage("Half1", out1);
    cvShowImage("Half2", out2);
    cvShowImage("Gray", out3);
    cvShowImage("Out", out4);
    cvReleaseImage(&out1);
    cvReleaseImage(&out2);
    cvReleaseImage(&out3);
    cvReleaseImage(&out4);
    cvReleaseImage(&image);
    cvWaitKey(0);
    cvDestroyWindow("In");
    cvDestroyWindow("Half1");
    cvDestroyWindow("Half2");
    cvDestroyWindow("Gray");
    cvDestroyWindow("Out");
}

int main(int argc, char** argv)
{
//  IplImage *img = cvLoadImage(argv[1]);     //cmd
    IplImage *img = cvLoadImage("D:\\Template\\OpenCV\\Template6_Convention_Canny\\Debug\\2.jpg");   //address 
    Show_Image(img);
}

运行截图:
第二章 学习OpenCV——OpenCV入门_第11张图片
特别提醒:
书中此部分中,谈到了内存空间的释放问题,这是一个在图像处理中很基础却也很容易被我们所忽略的问题。从单张图像的处理上来说,内存空间资源释放与否对程序本身的运行影响并不大,但如果实际应用中涉及到视频流的处理,将会对整个程序造成毁灭性的破坏,运算速度下降,或是溢出崩溃,因此我们必须手动释放所有我们显示分配的内存空间,关于内存空间的释放详见例2-11,此处不再赘述。

例2-8 摄像头读入数据

从摄像头中实时读入视频流并进行显示在窗口中,程序代码如下:

#include 
#include 
using namespace std;
int main(int argc, char** argv)
{
        cvNamedWindow("Show_Camera", CV_WINDOW_AUTOSIZE);
        CvCapture* capture;
        if (argc == 1)   //此处代码是做一个判断,有摄像头设备则读入摄像头的图像信息,没有则播放本地视频文件
            capture = cvCreateCameraCapture(0);  //参数为摄像头ID
        else
            capture = cvCreateFileCapture("D:\\Template\\OpenCV\\Template2_ShowAVI_cmd\\Debug\\只想告诉你.avi");

        assert(capture!=NULL);     //断言,视频流信息非空

        IplImage* frame;
        while (1)
        {
            frame = cvQueryFrame(capture);
            if (!frame) break;
            cvShowImage("Show_Camera", frame);
            char c = cvWaitKey(32);
            if (c == 27) break;
        }
        cvReleaseCapture(&capture);
        cvDestroyWindow("Show_Camera");
}

运行截图如下:
第二章 学习OpenCV——OpenCV入门_第12张图片
错误更正:
此部分书中提到cvCreateCameraCapture()函数的参数为摄像设备ID,只有存在多个摄像设备时这个参数才起作用。默认值为-1,代表“随机选择一个”,它更适合有且仅有一个设备的情况。 经过笔记本电脑自带摄像头实际测试,笔者该描述有误,参数为-1时运行报错,出现“R6010 -abort() has been called” 的错误:
第二章 学习OpenCV——OpenCV入门_第13张图片
解决方案:
cvCreateCameraCapture()函数声明如下:
函数声明
如果只有一个摄像机时,参数值取0。当参数被设置为-1时,OpenCV会打开一个窗口让用户选择需要使用的摄像机。cvCreateCameraCapture()函数用法

例2-9 写入AVI视频文件

将输入视频流或者捕获的图像序列记录到输出视频流中,程序代码如下:

#include 
#include 
using namespace std;

int main(int argc, char* argv[])
{
    CvCapture*  capture = cvCreateFileCapture("好想告诉你.avi");  
    if (!capture)
            return -1;
        IplImage* bgr_frame = cvQueryFrame(capture);  //下一帧视频载入内存
        double fps = cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);         //获取视频流属性fps
        CvSize size = cvSize((int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH), 
                             (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT));
        CvVideoWriter *writer = cvCreateVideoWriter("好想告诉你(Gray).avi", CV_FOURCC('M', 'J', 'P', 'G'), fps, size);   //视频逐真写入设备创建
        IplImage* logpolar_frame = cvCreateImage(size, IPL_DEPTH_8U, 3);
        while ((bgr_frame = cvQueryFrame(capture)) != NULL)
        {
            cvLogPolar(bgr_frame, logpolar_frame, cvPoint2D32f(bgr_frame->width / 2,bgr_frame->height / 2), 40, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS);  //图像映射到极指数空间
            cvWriteFrame(writer, logpolar_frame);  //逐帧写入
        }
        cvReleaseVideoWriter(&writer);
        cvReleaseImage(&logpolar_frame);
        cvReleaseCapture(&capture);
        return 0;
}

运行截图:
原视频
第二章 学习OpenCV——OpenCV入门_第14张图片
转换后输出视频
第二章 学习OpenCV——OpenCV入门_第15张图片

例2-10 摄像头输入视频流缩放后显示与存储

从摄像机读入视频数据并将缩放变换后的图像存入磁盘,并将变换结果显示在窗口中,程序代码如下:

#include 
#include 
using namespace std;

IplImage* doPyrDown(IplImage* in, int filter = IPL_GAUSSIAN_5x5)
{
    assert(in->width % 2 == 0 && in->height % 2 == 0);  //断言
    IplImage* out = cvCreateImage(cvSize(in->width / 2, in->height / 2), in->depth, in->nChannels);
    cvPyrDown(in, out);
    return(out);
}

int main(int argc, char* argv[])
{
    IplImage* frame;
    IplImage* h_frame;
    CvVideoWriter *writer = NULL;

    cvNamedWindow("Show_Camera", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Show_Half_Camera", CV_WINDOW_AUTOSIZE);
    CvCapture* capture;
    if (argc == 1)
        capture = cvCreateCameraCapture(0);
    else
        capture = cvCreateFileCapture("D:\\Template\\OpenCV\\Template2_ShowAVI_cmd\\Debug\\只想告诉你.avi");

    assert(capture != NULL);

    frame = cvQueryFrame(capture);
    h_frame = doPyrDown(frame);
    writer = cvCreateVideoWriter("camera.avi", CV_FOURCC('X', 'V', 'I', 'D'), 15,cvSize(h_frame->width, h_frame->height));
    while ((frame = cvQueryFrame(capture))!= NULL)
    {
        h_frame = doPyrDown(frame);
        cvShowImage("Show_Camera", frame);
        cvShowImage("Show_Half_Camera", h_frame);
        cvWriteFrame(writer, h_frame);
        cvReleaseImage(&h_frame);
        if (writer)
        {
            cout << "video writer has created!" << endl;
        }
        char c = cvWaitKey(32);
        if (c == 27) break;
    }   
    cvReleaseVideoWriter(&writer);
    cvReleaseImage(&h_frame);
    cvReleaseCapture(&capture);
    cvDestroyWindow("Show_Camera");
    cvDestroyWindow("Show_Half_Camera");
    return 0;
}

运行截图:
第二章 学习OpenCV——OpenCV入门_第16张图片
第二章 学习OpenCV——OpenCV入门_第17张图片
第二章 学习OpenCV——OpenCV入门_第18张图片

例2-11 摄像头输入视频流缩放后显示与存储

从摄像机读入视频数据并将缩放变换后的图像存入磁盘,并将变换结果显示在窗口中,给程序加入滚动条,使得用户可以动态调节缩放比例,缩放比例取值2~8之间,程序代码如下:

#include 
#include 
using namespace std;

int g_slider_position = 0;
int Override = 0;    //缩放倍率
int frames = 3;      //滚动条最大值
int i = 0;

IplImage* doPyrDown(IplImage* in, int filter = IPL_GAUSSIAN_5x5)
{
    assert(in->width % 2 == 0 && in->height % 2 == 0);  //断言
    IplImage* out = cvCreateImage(cvSize(in->width / 2, in->height / 2), in->depth, in->nChannels);
    cvPyrDown(in, out);
    return(out);
}

void onTrackbarSlide(int pos)
{
    Override = pos;
}

int main(int argc, char* argv[])
{
    IplImage* frame;
    IplImage* h_frame0;
    IplImage* h_frame1;
    IplImage* h_frame2;
    CvVideoWriter *writer = NULL;
    CvCapture* capture;

    cvNamedWindow("Show_Camera", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Show_Half_Camera", CV_WINDOW_AUTOSIZE);
    if (argc == 1)
        capture = cvCreateCameraCapture(0);
    else
        capture = cvCreateFileCapture("D:\\Template\\OpenCV\\Template2_ShowAVI_cmd\\Debug\\只想告诉你.avi");

    assert(capture != NULL);

    if (frames != 0)
    {
        cvCreateTrackbar("Position", "Show_Camera", &g_slider_position, frames, onTrackbarSlide);
    }
    frame = cvQueryFrame(capture);
    writer = cvCreateVideoWriter("camera.avi", CV_FOURCC('X', 'V', 'I', 'D'), 15, cvSize(frame->width, frame->height));

    while ((frame = cvQueryFrame(capture))!= NULL)
    {
        if (Override == 0)
        {
            h_frame0 = frame;
            cvShowImage("Show_Half_Camera", h_frame0);
            cvWriteFrame(writer, h_frame0);
        }
        if (Override == 1)
        {
            h_frame0 = doPyrDown(frame);
            cvShowImage("Show_Half_Camera", h_frame0);
            cvWriteFrame(writer, h_frame0);
            cvReleaseImage(&h_frame0);
        }
        if (Override == 2)
        {
            h_frame0 = doPyrDown(frame);
            h_frame1 = doPyrDown(h_frame0);
            cvShowImage("Show_Half_Camera", h_frame1);
            cvWriteFrame(writer, h_frame1);
            cvReleaseImage(&h_frame0);
            cvReleaseImage(&h_frame1);
        }
        if (Override == 3)
        {
            h_frame0 = doPyrDown(frame);
            h_frame1 = doPyrDown(h_frame0);
            h_frame2 = doPyrDown(h_frame1);
            cvShowImage("Show_Half_Camera", h_frame2);
            cvWriteFrame(writer, h_frame2);
            cvReleaseImage(&h_frame0);
            cvReleaseImage(&h_frame1);
            cvReleaseImage(&h_frame2);
        }
        cvShowImage("Show_Camera", frame);
        if (writer)
        {
            cout << "video writer has created!" << endl;
        }
        char c = cvWaitKey(32);
        if (c == 27) break;
    }   
    cvReleaseVideoWriter(&writer);
    cvReleaseCapture(&capture);
    cvDestroyWindow("Show_Camera");
    cvDestroyWindow("Show_Half_Camera");
    return 0;
}

运行截图如下:
第二章 学习OpenCV——OpenCV入门_第19张图片
第二章 学习OpenCV——OpenCV入门_第20张图片
第二章 学习OpenCV——OpenCV入门_第21张图片
第二章 学习OpenCV——OpenCV入门_第22张图片
特别提醒
以上两例的编写,就涉及到了视频流的数据处理,可以看到每个运行截图中都包含了资源管理器的截图,其目的就是为了提醒各位初学者,千万要注意内存空间的分配和释放,切记在使用结束后一定要释放自己为其显示分配的内存空间,如若不然,程序随时可能面临崩溃。
例2-11中内存空间释放采用的是《学习OpenCV》一书中例2-7较为臃肿释放方式:

        if (Override == 0)
        {
            h_frame0 = frame;
            cvShowImage("Show_Half_Camera", h_frame0);
            cvWriteFrame(writer, h_frame0);
        }
        if (Override == 1)
        {
            h_frame0 = doPyrDown(frame);
            cvShowImage("Show_Half_Camera", h_frame0);
            cvWriteFrame(writer, h_frame0);
            cvReleaseImage(&h_frame0);
        }
        if (Override == 2)
        {
            h_frame0 = doPyrDown(frame);
            h_frame1 = doPyrDown(h_frame0);
            cvShowImage("Show_Half_Camera", h_frame1);
            cvWriteFrame(writer, h_frame1);
            cvReleaseImage(&h_frame0);
            cvReleaseImage(&h_frame1);
        }
        if (Override == 3)
        {
            h_frame0 = doPyrDown(frame);
            h_frame1 = doPyrDown(h_frame0);
            h_frame2 = doPyrDown(h_frame1);
            cvShowImage("Show_Half_Camera", h_frame2);
            cvWriteFrame(writer, h_frame2);
            cvReleaseImage(&h_frame0);
            cvReleaseImage(&h_frame1);
            cvReleaseImage(&h_frame2);
        }

尚待解决的问题1——内存无法全部释放
笔者亦曾尝试过采用书中例2-8通过每个独立阶段来释放内存,可能因为笔者对书中代码理解不到位所致,在多次反复调用doPyrDown()函数时,只能够释放一次doPyrDown()分配的内存,在资源管理器中内存资源不断增长。
例2-8如下所示:
第二章 学习OpenCV——OpenCV入门_第23张图片
笔者模仿例2-8形式的释放内存代码

        if (Override == 0)
        {
            h_frame = frame;
            cvShowImage("Show_Half_Camera", h_frame);
            cvWriteFrame(writer, h_frame);
        }
                if (Override == 1)
        {
            h_frame = doPyrDown(frame);
            cvShowImage("Show_Half_Camera", h_frame);
            cvWriteFrame(writer, h_frame);
            cvReleaseImage(&h_frame);
        }

运行截图:
第二章 学习OpenCV——OpenCV入门_第24张图片
第二章 学习OpenCV——OpenCV入门_第25张图片
在不进行缩放或2倍缩放时,程序的内存占用是恒定值。而进行4倍或8倍缩放时,程序所占用的内存就无法得到全部释放,内存空间占用逐步升高,代码及运行截图如下所示。

        if (Override == 2)
        {
            h_frame = doPyrDown(frame);
            h_frame = doPyrDown(h_frame);
            cvShowImage("Show_Half_Camera", h_frame);
            cvWriteFrame(writer, h_frame);
            cvReleaseImage(&h_frame);
        }
        if (Override == 3)
        {
            h_frame = doPyrDown(frame);
            h_frame = doPyrDown(h_frame);
            h_frame = doPyrDown(h_frame);
            cvShowImage("Show_Half_Camera", h_frame);
            cvWriteFrame(writer, h_frame);
            cvReleaseImage(&h_frame);
        }

运行截图:
第二章 学习OpenCV——OpenCV入门_第26张图片
这里写图片描述
这里写图片描述

尚待解决的问题2——在图像缩放过程中无法继续图像视频存储
本程序中摄像头的视频流数据存储,是调用cvCreateVideoWriter()函数实现的,该函数创建了一个写入设备以便逐帧将视频流写入视频文件。

writer = cvCreateVideoWriter("camera.avi", CV_FOURCC('X', 'V', 'I', 'D'), 15, cvSize(frame->width, frame->height));

其最后一个参数规定了写入设备开辟空间的大小,但伴随图像的缩放,其图像大小亦将发生改变,这就导致图像一出现缩放其写入视频文件的动作就不再进行了,只有图像恢复原来尺寸时,视频写入动作才继续开始执行。

望已解决此问题的看客能够解惑,在此谢过~

你可能感兴趣的:(OpenCV)