Opencv系列1.2--实例介绍

更多更详细的文章请关注微信公众号:SLAM之路

Opencv系列1.2--实例介绍_第1张图片

本部分通过三个简单实例,介绍Opencv程序的整体架构,并介绍简易的读取可写入方法等基本操作,认识一些基本的库;

0、头文件include

1、读取显示图片

2、读取显示视频

3、视频快进快退

4、简易变换

5、稍难变换

6、从相机输入

7、写视频

0、头文件include

C语言前段包含了头文件,而Opencv的库文件通过include构造了模块化的函数库,可根据实际应用情况进行选择,以提高运编译速度;或者使用总的文件库,包含了OpenCV2每个函数库,include/opencv2/opencv.hpp;

#include "opencv2/core/core_c.h" :C语言数据结构和算法函数库
#include "opencv2/core/core.hpp" :C++数据结构和算法函数库

#include "opencv2/highgui/highgui_c.h"C图像显示/滑动/鼠标交互

#include "opencv2/highgui/highgui.hpp" C++显示/滑动/鼠标交互

#include "opencv2/imgproc/imgproc_c.h" C语言图像处理函数库

#include "opencv2/imgproc/imgproc.hpp"C++图像处理函数库

#include "opencv2/flann/miniflann.hpp" 最近邻匹配函数库

#include "opencv2/video/photo.hpp" 面向处理和恢复图像的函数库

#include "opencv2/video/video.hpp" 视频跟踪及背景分割函数库

#include "opencv2/features2d/features2d.hpp" 二维特征跟踪支持库

#include "opencv2/objdetect/objdetect.hpp" 面部探测

#include "opencv2/calib3d/calib3d.hpp" 3D标定

#include "opencv2/ml/ml.hpp" 机器学习

 

1、读取显示图片

#include #inlude every supported opencv function
//也可以使用具体的函数库#include "opencv2/highgui/highgui.hpp"//Opencv的函数都存在于一个命名空间:cv,为了调用函数必须使用cv::格式//为避免这种繁琐写法,可以在程序开始使用using namespace cv;即可省略cv::
int main(int argc, char** arvv{cv::Mat img = cv::imread(argv[1],-1);  //cv::Mat 默认照片格式,-1表示灰度图像,使用这种数据结构处理各种图像:单通道、多通道、整型浮点型数据; //cv::imread()可读取BMP/DIB/JPEG/JPE/PNG等等图片,并根据图像数据结构分配内存。 if(img.empty())return -1;              //判断图像是否为空
cv::namedWindow("Example1",cv::WINDOW_AUTOSIZE);//打开窗口,定义窗口性质:窗口标题名及大小(0:表示固定窗口)
cv::imshow("Example1",img);             //图像名称,必须与namedWindow一致;cv::waitKey(0);                         //图像框显示时间,0表示始终直到其他操作;正数表示停留时间(毫秒);
cv::destroyWindow("Example1");          //关掉图相框,重新分配内存;当程序复杂时,应当合理运用
return 0;)

2、读取显示视频

本质上是按顺序读取图片,即增加一个循环并决定何时循环结束;

#include #include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"
int main(int argc, char** argv) {
    cv::namedWindow("Example3", 0);    //创建显示窗口,名字叫Example3,可自由变换大小            cv::VideoCapture cap;    //cv::VideoCapture视频捕捉对象,该对象可打开关闭多种ffmpeg支持的多种视频文件       cap.open("me.mp4");    //视频捕捉对象打开后,即包含了所读视频文件的所有信息,包括状态信息;    //这种方式创造视频对象,cap初始化指向视频开始,       cv::Mat frame;    //图片frame实例化视频中的各帧;         for(;;){        cap>>frame;        //视频捕捉对象进入循环后,即可逐帧被读取                if(frame.empty())break;        //判断视频文件是否读入,如果未读入退出循环;                cv::imshow("Example3",frame);        //成功读入后,则逐帧通过imshow显示;        
        if(cv::waitKey(33)>=0)break;        //如果显示各帧,则等待33ms,如果用户这时候敲下按键则退出循环;否则33ms后将再次执行循环;        //所有分配的数据会自动释放;    }
    return 0;}

cv::VideoCapture中提供的属性标识如下图所示:

3、视频快进快退

例2中的视频无法快进,所以本例讲增加滑动进度条,以实现这一功能;同时,我们也将增加S键单步快进功能,R键恢复播放;无论使用者拖拉进度条到新位置,我们都将以单步模式停止在那;

实现策略:增加一个全局变量表示跟踪条的位置,增加一个调用用于更新全局变量并重新指定读取视频位置;因此,创建跟踪条并与调用联系起来,即可实现快进快退功能;

#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"#include #include 
using namespace std;
int g_slider_position = 0; //全局变量,保存进度条位置状态;
int g_run = 1, g_dontset = 0; //只要g_run不是0,表示新的一帧图像,正数表示停止前还有多少帧,负数系统停止//start out in single step mode//当用户拖拉进度条到新位置时,我们默认设定g_run=1视频暂停;//为使进度条能跟随视频播放前进,每次得到新一帧时都调用函数更新进度条位置//为避免在单步模式下调用进度条函数,使用g_dontset,在没有触发单步模式下进行更新;
cv::VideoCapture g_cap;//调用需要有权限访问视频捕捉对象,所以将其设定为全局变量;
//为使进度条能跟随视频播放前进,每次得到新一帧时都调用函数更新进度条位置void onTrackbarSlide( int pos, void *) {    g_cap.set( cv::CAP_PROP_POS_FRAMES, pos );    if( !g_dontset )    g_run = 1;  g_dontset = 0;}//当用户拖拉进度条,则调用该函数;pos确定进度条新的位置;//g_cap.set()设定视频到新的位置;//if语句设定,仅在调用被用户点击触发或者没有被主函数调用时,在新的下一帧进入后进入单步模式,//调用g_cap.set()和对应的g_cap.get()会经常使用,这些算法使我们可以配置视频捕捉对象的结构;//cv::CAP_PROP_POS_FRAMES,指定我们希望读取的帧;

int main( int argc, char** argv ) {    cv::namedWindow( "Example2_4", cv::WINDOW_AUTOSIZE );    g_cap.open( "me.mp4" );        int frames = (int) g_cap.get(cv::CAP_PROP_FRAME_COUNT);    int tmpw   = (int) g_cap.get(cv::CAP_PROP_FRAME_WIDTH);    int tmph   = (int) g_cap.get(cv::CAP_PROP_FRAME_HEIGHT);    cout << "Video has " << frames << " frames of dimensions("       << tmpw << ", " << tmph << ")." << endl;    //通过g_cap.get()得到视频帧数,视频图像的宽高;并输出基本信息;        cv::createTrackbar("Position", "Example2_4", &g_slider_position, frames, onTrackbarSlide);   //创建进度条,函数cv::createTraceBar()创建进度条标签,在Position指定的位置;   //并且指定显示的窗口Example2_4;   //设定变量约束进度条边界   //进度条的最大值(即视频帧数)   //滑动条移动时调用函数关联       cv::Mat frame;          for(;;) {        if( g_run != 0 ) {            g_cap >> frame;             if(frame.empty()) break;                       int current_pos = (int)g_cap.get(cv::CAP_PROP_POS_FRAMES);           //读取当前视频帧位置;                       g_dontset = 1;            //以保证下次进度条调用不会进入单步模式                                 cv::setTrackbarPos("Position", "Example2_4", current_pos);            //触发进度条调用更新位置                        cv::imshow( "Example2_4", frame );                      g_run-=1;            //要么进入单步模式,要么使视频根据用户设定的状态播放;        }                char c = (char) cv::waitKey(10);        if( c == 's' ) // single step            {g_run = 1; cout << "Single step, run = " << g_run << endl;}        if( c == 'r' ) // run mode             {g_run = -1; cout << "Run mode, run = " << g_run <        if( c == 27 )      break;    }  return(0);}

4、简易变换

典型的简易操作之一是:图像的滤波(计算机视觉主要涉及视频流的滤波应用);用高斯或其他核函数卷积图像,有效减少图像信息内容;Opencv是卷积计算变得简单;

在显示图像前,加载图像然后滤波图像;

#include 
void example2_5(const cvt::Mat & image){ //创建窗口显示输入和处理后的图像 cv::namedWindow("Example2-5-in",cv::WINDOW_AUTOSIZE); cv::namedWindow("Example2-5-out",cv::WINDOW_AUTOSIZE);
 //输出读入的图像 cv::imshow("Example2-5-in", image);  //创建图像保存滤波后的结果 cv::Mat out;  //滤波处理,使用高斯滤波,GaussianBlur(),blur(),bilateralFilter(); cv::GaussianBlur(image, out, cv::Size(5,5), 3, 3); cv::GaussianBlur(out, out, cv::Size(5,5), 3, 3);  //在输出窗口显示滤波图像 cv::imshow("Example2-5-out",out);  cv::waitKey(0);}

 

5、稍难变换

上例的高斯滤波函数并未体现任何目的;本例使用高斯滤波函数系数2降低图像采样率,如果多次降低采样率,会得到一个尺度空间(或图像金字塔),这被广泛用于计算机视觉,处理观测场景和物体时变化的比例;

降低采样率等同于用一些函数进行卷积,这种采样会将高频引入信号中,为避免这样,我们需要运行高通滤波函数在整个信号上以保证整体都在采样频率下;在Opencv中,高斯滤波和降低采样通过函数cv::pyrDown()完成。

使用cv::pyrDown()创造一个新图像,具有输入图像一半宽和高;

#include
int main(int argc, char ** argv){cv::Mat img1, img2;
cv::namedWindow("Example1",cv::WINDOW_AUTOSIZE);cv::namedWindow("Example2",cv::WINDOW_AUTOSIZE);
img1 = cv::imread(argv[1]);cv::imshow("Example1",img1);
cv::pyrDown(img1, img2);
cv::imshow("Example2", img2);
cv::waitKey(0);
return 0;}

更加复杂的例子:使用Canny边缘探测器,cv::Canny(),通过它生成一副图像,与输入图像大小一样,但是仅需要一个通道;所以我们需要使用cv::cvtColor()变化为灰度单通道图像(BGR->灰度图:cv::COLOR_BGR2GRAY);

Canny边缘探测器将其结果写入单通道灰度图像

#includeint main(int argc, char** argv){
cv::Mat img_rgb, img_gry, img_cny;
cv::namedWindow("Example Gray", cv::WINDOW_AUTOSIZE);cv::namedWindow("Example Canny", cv::WINDOW_AUTOSIZE);
img_rgb = cv::imread(argv[1]);
cv::cvtColor(img_rgb, img_gry, cv::COLOR_BGR2GRAY);
cv::imshow("Example Gray", img_gry);
cv::Canny(img_gry, img_cny, 10,100,3,true);
cv::imshow("Example Canny", img_cny);
cv::waitKey(0);
}

缩小图像两倍,寻找那些仍然存在的行;

cv::cvtColor(img_rgb, img_gry, cv::COLOR_BGR2GRAY);cv::pyrDown(img_gry, img_pyr);cv::pyrDown(img_pyr, img_pyr2);cv::Canny(img_pyr2, img_cny, 10, 100, 3, true);

一种简单的读取和写像素值的方法

int x = 16 , y = 32;cv::Vec3b intensity = img_rgb.at(y,x);
uchar blue = intensity[0];uchar green = intensity[1];uchar red   = intensity[2];
std::cout<<"at(x,y)=("<<<(unsigned int)blue<<","<<(unsigned int)green<<","<<(unsigned int)red<<<<")"<
std::cout<<"Gray pixel there is :"<<(unsigned int)img_gry.at(y,x)<
x /= 4;y /= 4;std::cout<<"Pyramid2 pixel there is:"<<(unsigned int)img_pyr2.at(y,x)<
img_cny.at(y,x)=128;//设定Canny像素128;

6、从相机输入

类似于视频捕捉对象,后者是指定路径或文件名,而前者是给与相机ID编号(0表示仅有一个相机连接在系统);

#include #include using namespace std;
int main( int argc, char** argv ) {
    cv::namedWindow("hello",cv::WINDOW_AUTOSIZE);     cv::VideoCapture cap;          if(argc==1){   //若调用参数只有一个,则启动相机         cap.open(0);     }else{         //若调用参数有两个,则第二个为视频路径         cap.open(argv[1]);     }         if(!cap.isOpened()){  //判断是否成功打开视频        std::cerr<<"Couldn't open capture"<        return -1;    }         cv::Mat frame;     cv::Mat img_gry,img_cny;        for(;;)     {         cap>>frame;         cv::cvtColor(frame,img_gry,cv::COLOR_BGR2GRAY);//参考前序例子         cv::Canny(img_gry,img_cny,10,100,3, true);//参考前序例子         cv::imshow("hello",img_cny);         cv::waitKey(20);     }     return(0);}

7、写视频

cv::VideoCapture:创建读取视频的对象

cv::VideoWriter:创建写视频的对象,通过该对象可以把每一帧写入视频,最后使用cv::VideoWriter.release()释放。

读取视频,并将视频转化成log-polar格式然后写入新视频

#include "opencv2/opencv.hpp"#include #include using namespace std;
int main( int argc, char** argv ) {     cv::namedWindow("Example",cv::WINDOW_AUTOSIZE);     cv::namedWindow("Log-Polar",cv::WINDOW_AUTOSIZE);
     cv::VideoCapture cap;     cap.open('me.mp4');     double fps =cap.get(cv::CAP_PROP_FPS);     //frames per second     cv::Size size((int)cap.get(cv::CAP_PROP_FRAME_WIDTH),                   (int)cap.get(cv::CAP_PROP_FRAME_HEIGHT));
     cv::VideoWriter writer;     writer.open("hello",CV_FOURCC('M','P','E','G'),fps,size);     //1:filename;2:视频流压缩格式;3:replay frame rate;4:image size;本例参数3和4和原视频一致;     cv::Mat logpolar_frame, bgr_frame;     for(;;){         cap>>bgr_frame;         if (bgr_frame.empty())break;
         cv::imshow("Example",bgr_frame);         cv::logPolar(                 bgr_frame,                 logpolar_frame,                 cv::Point2f (bgr_frame.cols/2,bgr_frame.rows/2),                 40,                 cv::WARP_FILL_OUTLIERS                 );         cv::imshow("Log-Polar",logpolar_frame);         writer<         char c= cv::waitKey(10);         //key esc;         if (c==27)break;     }     cap.release();     return(0);}

cv::VideoWriter对象的创建有两种方式,第一种是使用构造函数的形式,第二种使用open()的方式,具体如下:

cv::VideoWriter out(
    const string& filename, // 输入文件名
    int fourcc, // 编码形式,使用 CV_FOURCC()宏
    double fps, // 输出视频帧率
    cv::Size frame_size, // 单帧图片的大小
    bool is_color = true // 如果是false,可传入灰度图像 
  );
  
  cv::VideoWriter out;
  out.open(
    "my_video.mpg", //输出文件名
    CV_FOURCC('D','I','V','X'), // MPEG-4 编码
    30.0, // 帧率 (FPS)
    cv::Size( 640, 480 ), // 单帧图片分辨率为 640x480
    true // 只输入彩色图
  );

你可能感兴趣的:(opencv)