OpenCV 入门(很详细) 转译

目录(?)[-]

  1. OpenCV 编程入门
    1. 内容
  2. 简介
    1. OpenCV概述
    2. 资料链接
    3. OpenCV 命名约定
    4. 编译命令
    5. C程序实例
  3. GUI命令
    1. 窗口管理
    2. 输入设备 
  4. OpenCV基础数据结构
    1. 图像数据结构
    2. 矩阵与向量
    3. 其他数据结构
  5. 图像处理
    1. 分配与释放图像空间
    2. 读取存储图像
    3. 存取图像元素
    4. 图像转换
    5. 绘图命令
  6. 矩阵操作
    1. 分配释放矩阵空间
    2. 存取矩阵元素
    3. 矩阵/向量操作
  7. 视频序列操作
    1. 从视频序列中抓取一帧
    2. 获取/设定帧信息
    3. 存储视频文件

 

OpenCV 编程入门

美国伊力诺理工学院计算机科学系Gady Adam

翻译:Mensch

2006年11月22日


 

内容

  • 简介
    • OpenCV概述
    • 资料链接
    • OpenCV 命名约定
    • 编译命令
    • C程序实例
  • GUI 命令
    • 窗口管理
    • 输入设备
  •  OpenCV 基础数据结构
    • 图像数据结构
    • 矩阵与向量
    • 其他数据结构
  • 图像处理 
    • 创建与释放图像结构空间
    • 读入与存储图像
    • 读取图像元素
    • 图像转换
    • 绘图命令 
  • 矩阵操作 
    • 创建与释放矩阵结构空间
    • 读取矩阵元素
    • 矩阵/向量操作
  • 视频序列处理
    • 从视频序列中抓取一帧
    • 获取/设定帧信息
    • 存储视频文件

 

 

 

简介

 

OpenCV概述

 

  • 什么是OpenCV 
    • 开源C/C++计算机视觉库.
    • 面向实时应用进行优化.
    • 跨操作系统/硬件/窗口管理器.
    • 通用图像/视频载入、存储和获取.
    • 由中、高层API构成.
    • 为Intel®公司的 Integrated Performance Primitives (IPP) 提供了透明接口.

     

  • 特性:
    • 图像数据操作 (分配,释放, 复制, 设定, 转换).
    • 图像与视频 I/O (基于文件/摄像头输入, 图像/视频文件输出).
    • 矩阵与向量操作与线性代数计算(相乘, 求解, 特征值, 奇异值分解SVD).
    • 各种动态数据结构(列表, 队列, 集, 树, 图).
    • 基本图像处理(滤波, 边缘检测, 角点检测, 采样与插值, 色彩转换, 形态操作, 直方图, 图像金字塔).
    • 结构分析(连接成分, 轮廓处理, 距离转换, 模板匹配, Hough转换, 多边形近似, 线性拟合, 椭圆拟合, Delaunay三角化).
    • 摄像头标定 (寻找并跟踪标定模板, 标定, 基础矩阵估计, homography估计, 立体匹配).
    • 动作分析(光流, 动作分割, 跟踪).
    • 对象辨识 (特征方法, 隐马可夫链模型HMM).
    • 基本GUI(显示图像/视频, 键盘鼠标操作, 滚动条).
    • 图像标识 (直线, 圆锥, 多边形, 文本绘图)

     

  • OpenCV 模块:
    • cv - OpenCV 主要函数.
    • cvaux - 辅助 (实验性) OpenCV 函数.
    • cxcore - 数据结构与线性代数算法.
    • highgui - GUI函数.

     

 

资料链接

 

  • 参考手册:
    • <opencv-root>/docs/index.htm

     

  • 网络资源:
    • 官方网页: http://www.intel.com/technology/computing/opencv/

       

    • 软件下载: http://sourceforge.net/projects/opencvlibrary/

       

     

  • 书籍:
    • Open Source Computer Vision Library by Gary R. Bradski, Vadim Pisarevsky, and Jean-Yves Bouguet, Springer, 1st ed. (June, 2006).

     

  • 视频处理例程 (位于 <opencv-root>/samples/c/目录中):
    • 色彩跟踪: camshiftdemo
    • 点跟踪: lkdemo
    • 动作分割: motempl
    • 边缘检测: laplace

     

  • 图像处理例程(位于<opencv-root>/samples/c/目录中):
    • 边缘检测: edge
    • 分割: pyramid_segmentation
    • 形态: morphology
    • 直方图: demhist
    • 距离转换: distrans
    • 椭圆拟合 fitellipse

     

 

 

OpenCV 命名约定

 

  • 函数命名:
        cvActionTarget[Mod](...)
    
        Action = 核心功能(例如 设定set, 创建create)
        Target = 操作目标 (例如 轮廓contour, 多边形polygon)
        [Mod]  = 可选修饰词 (例如说明参数类型)
    

     

  • 矩阵数据类型:
        CV_<bit_depth>(S|U|F)C<number_of_channels>
    
        S = 带符号整数
        U = 无符号整数
        F = 浮点数 
    
        例:   CV_8UC1 表示一个8位无符号单通道矩阵, 
              CV_32FC2 表示一个32位浮点双通道矩阵.
    

     

  • 图像数据类型:
        IPL_DEPTH_<bit_depth>(S|U|F)
    
        例:   IPL_DEPTH_8U 表示一个8位无符号图像.
              IPL_DEPTH_32F 表示一个32位浮点数图像.
    

     

  • 头文件:
        #include <cv.h>
        #include <cvaux.h>
        #include <highgui.h>  
        #include <cxcore.h>   // 不必要 - 该头文件已在 cv.h 文件中包含
    

     

 

编译命令

 

  • Linux系统:
    g++ hello-world.cpp -o hello-world /
        -I /usr/local/include/opencv -L /usr/local/lib  /
        -lm -lcv -lhighgui -lcvaux
    

     

  • Windows系统:
    注意在项目属性中设好OpenCV头文件以及库文件的路径.
    

     

 

 

C程序实例

 

 

////////////////////////////////////////////////////////////////////////
//
// hello-world.cpp
//
// 一个简单的OpenCV程序
// 它从一个文件中读取图像,将色彩值颠倒,并显示结果. 
//
////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cv.h>
#include <highgui.h>


int main(int argc, char *argv[])
{
  IplImage* img = 0; 
  int height,width,step,channels;
  uchar *data;
  int i,j,k;

  if(argc<2){
    printf("Usage: main <image-file-name>/n/7");
    exit(0);
  }

  // 载入图像  
  img=cvLoadImage(argv[1]);
  if(!img){
    printf("Could not load image file: %s/n",argv[1]);
    exit(0);
  }

  // 获取图像数据
  height    = img->height;
  width     = img->width;
  step      = img->widthStep;
  channels  = img->nChannels;
  data      = (uchar *)img->imageData;
  printf("Processing a %dx%d image with %d channels/n",height,width,channels); 

  // 创建窗口
  cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE); 
  cvMoveWindow("mainWin", 100, 100);

  // 反色图像
  for(i=0;i<height;i++) for(j=0;j<width;j++) for(k=0;k<channels;k++)
    data[i*step+j*channels+k]=255-data[i*step+j*channels+k];

  // 显示图像
  cvShowImage("mainWin", img );

  // wait for a key
  cvWaitKey(0);

  // release the image
  cvReleaseImage(&img );
  return 0;
}

 

 

 

 

GUI命令

 

窗口管理

 

  • 创建并放置一个窗口:
      cvNamedWindow("win1", CV_WINDOW_AUTOSIZE); 
      cvMoveWindow("win1", 100, 100); // 以屏幕左上角为起点的偏移量
    

     

  • 读入图像:
      IplImage* img=0; 
      img=cvLoadImage(fileName);
      if(!img) printf("Could not load image file: %s/n",fileName);
    

     

  • 显示图像:
      cvShowImage("win1",img);
    

    可显示彩色或灰度的字节/浮点图像。 彩色图像数据认定为BGR顺序.

     

  • 关闭窗口:
      cvDestroyWindow("win1");
    

     

  • 改变窗口尺寸:
      cvResizeWindow("win1",100,100); // 新的宽/高值(象素点)

     

 

 

输入设备 

 

  • 响应鼠标事件:
    • 定义鼠标handler:
        void mouseHandler(int event, int x, int y, int flags, void* param)
        {
          switch(event){
            case CV_EVENT_LBUTTONDOWN:
              if(flags & CV_EVENT_FLAG_CTRLKEY) 
                printf("Left button down with CTRL pressed/n");
              break;
      
            case CV_EVENT_LBUTTONUP:
              printf("Left button up/n");
              break;
          }
        }
      
        // x,y:   针对左上角的像点坐标
      
        // event: CV_EVENT_LBUTTONDOWN,   CV_EVENT_RBUTTONDOWN,   CV_EVENT_MBUTTONDOWN,
        //        CV_EVENT_LBUTTONUP,     CV_EVENT_RBUTTONUP,     CV_EVENT_MBUTTONUP,
        //        CV_EVENT_LBUTTONDBLCLK, CV_EVENT_RBUTTONDBLCLK, CV_EVENT_MBUTTONDBLCLK,
        //        CV_EVENT_MOUSEMOVE:
      
        // flags: CV_EVENT_FLAG_CTRLKEY, CV_EVENT_FLAG_SHIFTKEY, CV_EVENT_FLAG_ALTKEY,
        //        CV_EVENT_FLAG_LBUTTON, CV_EVENT_FLAG_RBUTTON,  CV_EVENT_FLAG_MBUTTON
      

       

    • 注册handler:
        mouseParam=5;
        cvSetMouseCallback("win1",mouseHandler,&mouseParam);
      

       

     

     

  • 响应键盘事件:
    • 键盘没有事件handler.

       

    • 直接获取键盘操作:
        int key;
        key=cvWaitKey(10); // 输入等待10ms
      

       

    • 等待按键并获取键盘操作:
        int key;
        key=cvWaitKey(0); // 无限等待键盘输入
      

       

    • 键盘输入循环:
        while(1){
          key=cvWaitKey(10);
          if(key==27) break;
      
          switch(key){
            case 'h':
              ...
              break;
            case 'i':
              ...
              break;
          }
        }
      

       

     

  • 处理滚动条事件:
    • 定义滚动条handler:
        void trackbarHandler(int pos)
        {
          printf("Trackbar position: %d/n",pos);
        }
      

       

    • 注册handler:
        int trackbarVal=25;
        int maxVal=100;
        cvCreateTrackbar("bar1", "win1", &trackbarVal ,maxVal , trackbarHandler);
      

       

    • 获取滚动条当前位置:
        int pos = cvGetTrackbarPos("bar1","win1");
      

       

    • 设定滚动条位置:
        cvSetTrackbarPos("bar1", "win1", 25);
      

       

     

 

OpenCV基础数据结构

 

图像数据结构

 

  • IPL 图像:
    IplImage
      |-- int  nChannels;     // 色彩通道数(1,2,3,4)
      |-- int  depth;         // 象素色深: 
      |                       //   IPL_DEPTH_8U, IPL_DEPTH_8S, 
      |                       //   IPL_DEPTH_16U,IPL_DEPTH_16S, 
      |                       //   IPL_DEPTH_32S,IPL_DEPTH_32F, 
      |                       //   IPL_DEPTH_64F
      |-- int  width;         // 图像宽度(象素点数)
      |-- int  height;        // 图像高度(象素点数)
    
      |-- char* imageData;    // 指针指向成一列排列的图像数据
      |                       // 注意色彩顺序为BGR
      |-- int  dataOrder;     // 0 - 彩色通道交叉存取 BGRBGRBGR, 
      |                       // 1 - 彩色通道分隔存取 BBBGGGRRR
      |                       // 函数cvCreateImage只能创建交叉存取的图像
      |-- int  origin;        // 0 - 起点为左上角,
      |                       // 1 - 起点为右下角(Windows位图bitmap格式)
      |-- int  widthStep;     // 每行图像数据所占字节大小
      |-- int  imageSize;     // 图像数据所占字节大小 = 高度*每行图像数据字节大小
      |-- struct _IplROI *roi;// 图像ROI. 若不为NULL则表示需要处理的图像
      |                       // 区域.
      |-- char *imageDataOrigin; // 指针指向图像数据原点
      |                          // (用来校准图像存储单元的重新分配)
      |
      |-- int  align;         // 图像行校准: 4或8字节校准
      |                       // OpenCV不采用它而使用widthStep
      |-- char colorModel[4]; // 图像色彩模型 - 被OpenCV忽略
    

     

 

 

矩阵与向量

 

  • 矩阵:
    CvMat                      // 2维数组
      |-- int   type;          // 元素类型(uchar,short,int,float,double)
      |-- int   step;          // 一行所占字节长度
      |-- int   rows, cols;    // 尺寸大小
      |-- int   height, width; // 备用尺寸参照
      |-- union data;
         |-- uchar*  ptr;     // 针对unsigned char矩阵的数据指针
          |-- short*  s;       // 针对short矩阵的数据指针
          |-- int*    i;       // 针对integer矩阵的数据指针
          |-- float*  fl;      // 针对float矩阵的数据指针
          |-- double* db;      // 针对double矩阵的数据指针
    
    
    CvMatND                    // N-维数组
      |-- int   type;          // 元素类型(uchar,short,int,float,double)
      |-- int   dims;          // 数组维数
      |-- union data;
      |   |-- uchar*  ptr;     // 针对unsigned char矩阵的数据指针
      |   |-- short*  s;       // 针对short矩阵的数据指针
      |   |-- int*    i;       // 针对integer矩阵的数据指针
      |   |-- float*  fl;      // 针对float矩阵的数据指针
      |   |-- double* db;      // 针对double矩阵的数据指针
      |
      |-- struct dim[];        // 每个维的信息
          |-- size;            // 该维内元素个数
          |-- step;            // 该维内元素之间偏移量
    
    
    CvSparseMat // 稀疏N维数组
    

     

  • 通用数组:
    CvArr*     // 仅作为函数参数,说明函数接受多种类型的数组,例如:
               //    IplImage*, CvMat* 或者 CvSeq*. 
               // 只需通过分析数组头部的前4字节便可确定数组类型

     

     

  • 标量:
    CvScalar
      |-- double val[4]; //4D向量
    

    初始化函数:

    CvScalar s = cvScalar(double val0, double val1=0, double val2=0, double val3=0);
    

    举例:

    CvScalar s = cvScalar(20.0);
    s.val[0]=10.0;
    

    注意:初始化函数与数据结构同名,只是首字母小写. 它不是C++的构造函数.

     

 

其他数据结构

 

  • 点:
    CvPoint      p = cvPoint(int x, int y);
    CvPoint2D32f p = cvPoint2D32f(float x, float y);
    CvPoint3D32f p = cvPoint3D32f(float x, float y, float z);
    例如:
    p.x=5.0;
    p.y=5.0;
    

     

  • 长方形尺寸:
    CvSize       r = cvSize(int width, int height);
    CvSize2D32f  r = cvSize2D32f(float width, float height);
    

     

  • 带偏移量的长方形尺寸:
    CvRect       r = cvRect(int x, int y, int width, int height);
    

     

 

 

图像处理

 

分配与释放图像空间

 

  • 分配图像空间:
    IplImage* cvCreateImage(CvSize size, int depth, int channels);
    
      size:  cvSize(width,height);
    
      depth: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,
             IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F, IPL_DEPTH_64F
    
      channels: 1, 2, 3 or 4. 
        注意数据为交叉存取.彩色图像的数据编排为b0 g0 r0 b1 g1 r1 ...
    

    举例:

    // 分配一个单通道字节图像
    IplImage* img1=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); 
    
    // 分配一个三通道浮点图像
    IplImage* img2=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
    

     

  • 释放图像空间:
    IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); 
    cvReleaseImage(&img);
    

     

  • 复制图像:
    IplImage* img1=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); 
    IplImage* img2;
    img2=cvCloneImage(img1);
    

     

  • 设定/获取兴趣区域:
    void  cvSetImageROI(IplImage* image, CvRect rect);
    void  cvResetImageROI(IplImage* image);
    vRect cvGetImageROI(const IplImage* image);
    
    大部分OpenCV函数都支持ROI.
    

     

  • 设定/获取兴趣通道:
    void cvSetImageCOI(IplImage* image, int coi); // 0=all
    int cvGetImageCOI(const IplImage* image);
    
    大部分OpenCV函数暂不支持COI.
    

     

 

读取存储图像

 

  • 从文件中载入图像:
      IplImage* img=0; 
      img=cvLoadImage(fileName);
      if(!img) printf("Could not load image file: %s/n",fileName);
    
      Supported image formats: BMP, DIB, JPEG, JPG, JPE, PNG, PBM, PGM, PPM,
                               SR, RAS, TIFF, TIF
    

    载入图像默认转为3通道彩色图像. 如果不是,则需加flag:

      img=cvLoadImage(fileName,flag);
    
      flag: >0 载入图像转为三通道彩色图像
            =0 载入图像转为单通道灰度图像
            <0 不转换载入图像(通道数与图像文件相同).
    

     

  • 图像存储为图像文件:
      if(!cvSaveImage(outFileName,img)) printf("Could not save: %s/n",outFileName);
    

    输入文件格式由文件扩展名决定.

     

 

存取图像元素

 

  • 假设需要读取在i行j列像点的第k通道. 其中, 行数i的范围为[0, height-1], 列数j的范围为[0, width-1], 通道k的范围为[0, nchannels-1].

     

  • 间接存取: (比较通用, 但效率低, 可读取任一类型图像数据)

     

    • 对单通道字节图像:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
      CvScalar s;
      s=cvGet2D(img,i,j); // get the (i,j) pixel value
      printf("intensity=%f/n",s.val[0]);
      s.val[0]=111;
      cvSet2D(img,i,j,s); // set the (i,j) pixel value
      

       

    • 对多通道浮点或字节图像:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
      CvScalar s;
      s=cvGet2D(img,i,j); // get the (i,j) pixel value
      printf("B=%f, G=%f, R=%f/n",s.val[0],s.val[1],s.val[2]);
      s.val[0]=111;
      s.val[1]=111;
      s.val[2]=111;
      cvSet2D(img,i,j,s); // set the (i,j) pixel value
      

       

     

  • 直接存取: (效率高, 但容易出错)

     

    • 对单通道字节图像:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
      ((uchar *)(img->imageData + i*img->widthStep))[j]=111;
      

       

    • 对多通道字节图像:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
      ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 0]=111; // B
      ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 1]=112; // G
      ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 2]=113; // R
      

       

    • 对多通道浮点图像:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
      ((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 0]=111; // B
      ((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 1]=112; // G
      ((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 2]=113; // R
      

       

     

  • 用指针直接存取 : (在某些情况下简单高效)

     

    • 对单通道字节图像:
      IplImage* img  = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
      int height     = img->height;
      int width      = img->width;
      int step       = img->widthStep/sizeof(uchar);
      uchar* data    = (uchar *)img->imageData;
      data[i*step+j] = 111;
      

       

    • 对多通道字节图像:
      IplImage* img  = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
      int height     = img->height;
      int width      = img->width;
      int step       = img->widthStep/sizeof(uchar);
      int channels   = img->nChannels;
      uchar* data    = (uchar *)img->imageData;
      data[i*step+j*channels+k] = 111;
      

       

    • 对单通道浮点图像(假设用4字节调整):
      IplImage* img  = cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
      int height     = img->height;
      int width      = img->width;
      int step       = img->widthStep/sizeof(float);
      int channels   = img->nChannels;
      float * data    = (float *)img->imageData;
      data[i*step+j*channels+k] = 111;
      

       

     

     

  • 使用 c++ wrapper 进行直接存取: (简单高效)

     

    • 对单/多通道字节图像,多通道浮点图像定义一个 c++ wrapper:
      template<class T> class Image
      {
        private:
        IplImage* imgp;
        public:
        Image(IplImage* img=0) {imgp=img;}
        ~Image(){imgp=0;}
        void operator=(IplImage* img) {imgp=img;}
        inline T* operator[](const int rowIndx) {
          return ((T *)(imgp->imageData + rowIndx*imgp->widthStep));}
      };
      
      typedef struct{
        unsigned char b,g,r;
      } RgbPixel;
      
      typedef struct{
        float b,g,r;
      } RgbPixelFloat;
      
      typedef Image<RgbPixel>       RgbImage;
      typedef Image<RgbPixelFloat>  RgbImageFloat;
      typedef Image<unsigned char>  BwImage;
      typedef Image<float>          BwImageFloat;
      

       

    • 单通道字节图像:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
      BwImage imgA(img);
      imgA[i][j] = 111;
      

       

    • 多通道字节图像:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
      RgbImage  imgA(img);
      imgA[i][j].b = 111;
      imgA[i][j].g = 111;
      imgA[i][j].r = 111;
      

       

    • 多通道浮点图像:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
      RgbImageFloat imgA(img);
      imgA[i][j].b = 111;
      imgA[i][j].g = 111;
      imgA[i][j].r = 111;
      

       

     

 

图像转换

 

  • 转为灰度或彩色字节图像:
    cvConvertImage(src, dst, flags=0);
    
      src = float/byte grayscale/color image
      dst = byte grayscale/color image
      flags = CV_CVTIMG_FLIP     (flip vertically)
              CV_CVTIMG_SWAP_RB  (swap the R and B channels)
    

     

  • 转换彩色图像为灰度图像:

     


    使用OpenCV转换函数:

    cvCvtColor(cimg,gimg,CV_BGR2GRAY); // cimg -> gimg
    

     


    直接转换:

    for(i=0;i<cimg->height;i++) for(j=0;j<cimg->width;j++) 
      gimgA[i][j]= (uchar)(cimgA[i][j].b*0.114 + 
                           cimgA[i][j].g*0.587 + 
                           cimgA[i][j].r*0.299);
    

     

  • 颜色空间转换:

     

    cvCvtColor(src,dst,code); // src -> dst
    
      code    = CV_<X>2<Y>
      <X>/<Y> = RGB, BGR, GRAY, HSV, YCrCb, XYZ, Lab, Luv, HLS
    
    e.g.: CV_BGR2GRAY, CV_BGR2HSV, CV_BGR2Lab
    

     

 

绘图命令

 

  • 画长方体:
    // 用宽度为1的红线在(100,100)与(200,200)之间画一长方体
    cvRectangle(img, cvPoint(100,100), cvPoint(200,200), cvScalar(255,0,0), 1);
    

     

  • 画圆:
    // 在(100,100)处画一半径为20的圆,使用宽度为1的绿线
    cvCircle(img, cvPoint(100,100), 20, cvScalar(0,255,0), 1);
    

     

  • 画线段:
    // 在(100,100)与(200,200)之间画绿色线段,宽度为1
    cvLine(img, cvPoint(100,100), cvPoint(200,200), cvScalar(0,255,0), 1);
    

     

  • 画一组线段:
    CvPoint  curve1[]={10,10,  10,100,  100,100,  100,10};
    CvPoint  curve2[]={30,30,  30,130,  130,130,  130,30,  150,10};
    CvPoint* curveArr[2]={curve1, curve2};
    int      nCurvePts[2]={4,5};
    int      nCurves=2;
    int      isCurveClosed=1;
    int      lineWidth=1;
    
    cvPolyLine(img,curveArr,nCurvePts,nCurves,isCurveClosed,cvScalar(0,255,255),lineWidth);
    

     

  • 画内填充色的多边形:
    cvFillPoly(img,curveArr,nCurvePts,nCurves,cvScalar(0,255,255));
    

     

  • 添加文本:
    CvFont font;
    double hScale=1.0;
    double vScale=1.0;
    int    lineWidth=1;
    cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX|CV_FONT_ITALIC, hScale,vScale,0,lineWidth);
    
    cvPutText (img,"My comment",cvPoint(200,400), &font, cvScalar(255,255,0));
    

    Other possible fonts:

    CV_FONT_HERSHEY_SIMPLEX, CV_FONT_HERSHEY_PLAIN,
    CV_FONT_HERSHEY_DUPLEX, CV_FONT_HERSHEY_COMPLEX,
    CV_FONT_HERSHEY_TRIPLEX, CV_FONT_HERSHEY_COMPLEX_SMALL,
    CV_FONT_HERSHEY_SCRIPT_SIMPLEX, CV_FONT_HERSHEY_SCRIPT_COMPLEX,
    

     

 

 

矩阵操作

 

分配释放矩阵空间

 

  • 综述:
    • OpenCV有针对矩阵操作的C语言函数. 许多其他方法提供了更加方便的C++接口,其效率与OpenCV一样.
    • OpenCV将向量作为1维矩阵处理.
    • 矩阵按行存储,每行有4字节的校整.

     

  • 分配矩阵空间:
    CvMat* cvCreateMat(int rows, int cols, int type);
    
      type: 矩阵元素类型. 格式为CV_<bit_depth>(S|U|F)C<number_of_channels>.  
      例如: CV_8UC1 表示8位无符号单通道矩阵, CV_32SC2表示32位有符号双通道矩阵.
    
      例程:
      CvMat* M = cvCreateMat(4,4,CV_32FC1);
    

     

  • 释放矩阵空间:
    CvMat* M = cvCreateMat(4,4,CV_32FC1);
    cvReleaseMat(&M);
    

     

  • 复制矩阵:
    CvMat* M1 = cvCreateMat(4,4,CV_32FC1);
    CvMat* M2;
    M2=cvCloneMat(M1);
    

     

  • 初始化矩阵:
    double a[] = { 1,  2,  3,  4,
                   5,  6,  7,  8,
                   9, 10, 11, 12 };
    
    CvMat Ma=cvMat(3, 4, CV_64FC1, a);
    

    另一种方法:

    CvMat Ma;
    cvInitMatHeader(&Ma, 3, 4, CV_64FC1, a);
    

     

  • 初始化矩阵为单位阵:
    CvMat* M = cvCreateMat(4,4,CV_32FC1);
    cvSetIdentity(M); // 这里似乎有问题,不成功
    

     

 

存取矩阵元素

 

  • 假设需要存取一个2维浮点矩阵的第(i,j)个元素.

     

  • 间接存取矩阵元素:
    cvmSet(M,i,j,2.0); // Set M(i,j)
    t = cvmGet(M,i,j); // Get M(i,j)
    

     

  • 直接存取,假设使用4-字节校正:
    CvMat* M    = cvCreateMat(4,4,CV_32FC1);
    int n       = M->cols;
    float *data = M->data.fl;
    
    data[i*n+j] = 3.0;
    

     

  • 直接存取,校正字节任意:
    CvMat* M    = cvCreateMat(4,4,CV_32FC1);
    int   step  = M->step/sizeof(float);
    float *data = M->data.fl;
    
    (data+i*step)[j] = 3.0;
    

     

  • 直接存取一个初始化的矩阵元素:
    double a[16];
    CvMat Ma = cvMat(3, 4, CV_64FC1, a);
    a[i*4+j] = 2.0; // Ma(i,j)=2.0;
    

     

 

矩阵/向量操作

 

  • 矩阵-矩阵操作:
    CvMat *Ma, *Mb, *Mc;
    cvAdd(Ma, Mb, Mc);      // Ma+Mb   -> Mc
    cvSub(Ma, Mb, Mc);      // Ma-Mb   -> Mc
    cvMatMul(Ma, Mb, Mc);   // Ma*Mb   -> Mc
    

     

  • 按元素的矩阵操作:
    CvMat *Ma, *Mb, *Mc;
    cvMul(Ma, Mb, Mc);      // Ma.*Mb  -> Mc
    cvDiv(Ma, Mb, Mc);      // Ma./Mb  -> Mc
    cvAddS(Ma, cvScalar(-10.0), Mc); // Ma.-10 -> Mc
    

     

     

  • 向量乘积:
    double va[] = {1, 2, 3};
    double vb[] = {0, 0, 1};
    double vc[3];
    
    CvMat Va=cvMat(3, 1, CV_64FC1, va);
    CvMat Vb=cvMat(3, 1, CV_64FC1, vb);
    CvMat Vc=cvMat(3, 1, CV_64FC1, vc);
    
    double res=cvDotProduct(&Va,&Vb); // 点乘:   Va . Vb -> res
    cvCrossProduct(&Va, &Vb, &Vc);    // 向量积: Va x Vb -> Vc
    end{verbatim}
    

    注意 Va, Vb, Vc 在向量积中向量元素个数须相同.

     

     

  • 单矩阵操作:
    CvMat *Ma, *Mb;
    cvTranspose(Ma, Mb);      // transpose(Ma) -> Mb (不能对自身进行转置)
    CvScalar t = cvTrace(Ma); // trace(Ma) -> t.val[0] 
    double d = cvDet(Ma);     // det(Ma) -> d
    cvInvert(Ma, Mb);         // inv(Ma) -> Mb
    

     

  • 非齐次线性系统求解:
    CvMat* A  = cvCreateMat(3,3,CV_32FC1);
    CvMat* x  = cvCreateMat(3,1,CV_32FC1);
    CvMat* b  = cvCreateMat(3,1,CV_32FC1);
    cvSolve(&A, &b, &x);    // solve (Ax=b) for x
    

     

  • 特征值分析(针对对称矩阵):
    CvMat* A  = cvCreateMat(3,3,CV_32FC1);
    CvMat* E  = cvCreateMat(3,3,CV_32FC1);
    CvMat* l  = cvCreateMat(3,1,CV_32FC1);
    cvEigenVV(&A, &E, &l);  // l = A的特征值 (降序排列)
                            // E = 对应的特征向量 (每行)
    

     

  • 奇异值分解SVD:
    CvMat* A  = cvCreateMat(3,3,CV_32FC1);
    CvMat* U  = cvCreateMat(3,3,CV_32FC1);
    CvMat* D  = cvCreateMat(3,3,CV_32FC1);
    CvMat* V  = cvCreateMat(3,3,CV_32FC1);
    cvSVD(A, D, U, V, CV_SVD_U_T|CV_SVD_V_T); // A = U D V^T
    

    标号使得 U 和 V 返回时被转置(若没有转置标号,则有问题不成功!!!).

     

 

视频序列操作

 

从视频序列中抓取一帧

 

  • OpenCV支持从摄像头或视频文件(AVI)中抓取图像.

     

  • 从摄像头获取初始化:
    CvCapture* capture = cvCaptureFromCAM(0); // capture from video device #0
    

     

  • 从视频文件获取初始化:
    CvCapture* capture = cvCaptureFromAVI("infile.avi");
    

     

  • 抓取帧:
    IplImage* img = 0; 
    if(!cvGrabFrame(capture)){              // 抓取一帧 
      printf("Could not grab a frame/n/7");
      exit(0);
    }
    img=cvRetrieveFrame(capture);           // 恢复获取的帧图像
    

    要从多个摄像头同时获取图像, 首先从每个摄像头抓取一帧. 在抓取动作都结束后再恢复帧图像. 

     

  • 释放抓取源:
    cvReleaseCapture(&capture);
    

    注意由设备抓取的图像是由capture函数自动分配和释放的. 不要试图自己释放它.

     

 

获取/设定帧信息

 

  • 获取设备特性:
    cvQueryFrame(capture); // this call is necessary to get correct 
                           // capture properties
    int frameH    = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
    int frameW    = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
    int fps       = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
    int numFrames = (int) cvGetCaptureProperty(capture,  CV_CAP_PROP_FRAME_COUNT);
    

    所有帧数似乎只与视频文件有关. 用摄像头时不对,奇怪!!!.

     

     

  • 获取帧信息:
    float posMsec   =       cvGetCaptureProperty(capture, CV_CAP_PROP_POS_MSEC);
    int posFrames   = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES);
    float posRatio  =       cvGetCaptureProperty(capture, CV_CAP_PROP_POS_AVI_RATIO);
    

    获取所抓取帧在视频序列中的位置, 从首帧开始按[毫秒]算. 或者从首帧开始从0标号, 获取所抓取帧的标号. 或者取相对位置,首帧为0,末帧为1, 只对视频文件有效.

     

  • 设定所抓取的第一帧标号:
    // 从视频文件相对位置0.9处开始抓取
    cvSetCaptureProperty(capture, CV_CAP_PROP_POS_AVI_RATIO, (double)0.9);
    

    只对从视频文件抓取有效. 不过似乎也不成功!!!

     

 

存储视频文件

 

  • 初始化视频存储器:
    CvVideoWriter *writer = 0;
    int isColor = 1;
    int fps     = 25;  // or 30
    int frameW  = 640; // 744 for firewire cameras
    int frameH  = 480; // 480 for firewire cameras
    writer=cvCreateVideoWriter("out.avi",CV_FOURCC('P','I','M','1'),
                               fps,cvSize(frameW,frameH),isColor);
    

    其他有效编码:

    CV_FOURCC('P','I','M','1')    = MPEG-1 codec
    CV_FOURCC('M','J','P','G')    = motion-jpeg codec (does not work well)
    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
    

    若把视频编码设为-1则将打开一个编码选择窗口(windows系统下).

     

     

  • 存储视频文件:
    IplImage* img = 0; 
    int nFrames = 50;
    for(i=0;i<nFrames;i++){
      cvGrabFrame(capture);          // 抓取帧
      img=cvRetrieveFrame(capture);  // 恢复图像
      cvWriteFrame(writer,img);      // 将帧添加入视频文件
    }
    

    若想在抓取中查看抓取图像, 可在循环中加入下列代码:

    cvShowImage("mainWin", img); 
    key=cvWaitKey(20);           // wait 20 ms
    

    若没有20[毫秒]延迟,将无法正确显示视频序列.

     

  • 释放视频存储器:
    cvReleaseVideoWriter(&writer);

你可能感兴趣的:(数据结构,image,存储,vb,float,图像处理)