More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  北极星的天空PhotosProfileFriendsMore Tools Explore the Spaces community
There are no music lists on this space.
No list items have been added yet.
View space
ahwei
View space
ibear
View space
Xuejin.Lee
View space
小波
View space
(没有名字)
View space
黄金杰克森
View space
YULE~~~
View space
xingjia

北极星的天空

January 24

解决问题的思路:cvConvertScale

      在论坛上看到,关于数据格式的问题也困扰着其他很多人,我这里简单的做一个小小的总结:
      1、在结构IplImage中char*型imagedata指向排列的图像数据,排列格式常用的有RGB, BGR
      2、像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,IPL_DEPTH_16S, IPL_DEPTH_32S, PL_DEPTH_32F and IPL_DEPTH_64F 可支持。
      图像元素的位深度,可以是下面的其中之一:
      IPL_DEPTH_8U - 无符号8位整型
      IPL_DEPTH_8S - 有符号8位整型
      IPL_DEPTH_16U - 无符号16位整型
      IPL_DEPTH_16S - 有符号16位整型
      IPL_DEPTH_32S - 有符号32位整型
      IPL_DEPTH_32F - 单精度浮点数
      IPL_DEPTH_64F - 双精度浮点数
      3、线性转换函数 cvConvertScale 
      void cvConvertScale( const CvArr* src, CvArr* dst, double scale=1, double shift=0 );
      #define cvCvtScale cvConvertScale
      #define cvScale cvConvertScale
      #define cvConvert( src, dst ) cvConvertScale( (src), (dst), 1, 0 )
      src
      输入数组.
      dst
      输出数组
      scale
      比例因子.
      shift
      该加数被加到输入数组元素按比例缩放后得到的元素上 

      函数 cvConvertScale 有多个不同的目的因此就有多个同义函数(如上面的#define所示)。该函数首先对输入数组的元素进行比例缩放,然后将shift加到比例缩放后得到的各元素上,即: dst(I)=src(I)*scale + (shift,shift,...),最后可选的类型转换将结果拷贝到输出数组。

      多通道的数组对各个通道是独立处理的。

      类型转换主要用舍入和溢出截断来完成。也就是如果缩放+转换后的结果值不能用输出数组元素类型值精确表达,就设置成在输出数组数据轴上最接近该数的值。  

     如果 scale=1, shift=0 就不会进行比例缩放. 这是一个特殊的优化,相当于该函数的同义函数名:cvConvert 。如果原来数组和输出数组的类型相同,这是另一种特殊情形,可以被用于比例缩放和平移矩阵或图像,此时相当于该函数的同义函数名:cvScale。
     因此可以用这个函数实现不同位深之间的转换,也可以用于不同数据类型之间的转换!
 
January 19

OpenCV的学习资料和问题

     最近一直用OpenCV开发东西,但是遇到一个小问题:如何进行图象间的精度转换,如从8位无符号整形到32位单精度浮点型?(在求角点和梯度时会用到32位的single float point,还有的图像不是8位的,需要精度更高的图像)
 
这里也贴一点关于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);


January 09

久违了,我的天空

     进入了2008年,再次光顾我的天空,他还好吗?
     冷清代替再次见到他的新鲜感,虽然很多心绪没有大多转化成了文字,总是没有机会充实到天空中!
     转硕成功,现在的生活得主题就是努力充实自己,尽量找一个好点的工作。新的一年一定会带来新的气象!
     有空再来。
 
June 04

三个多月体重降了十斤,哈哈,爽!

     基本上实现了事先定的目标。看来减肥也是一件难事,只要有信心,一切都可以实现。
     以前打球,打一会就累,而且跳也跳不高,现在突然感觉不到累。而且跳起来也不觉得累了,盖帽的次数也多了起来。好现象!继续努力使体重再降5斤左右吧。
     努力,加油!
June 03

开往春天的地铁

      一个小女孩叫M,有一天她遇到了天使并且天使告诉她会满足她的一个愿望,于是小女孩非常高兴得向天使许下了愿望。她希望在她18岁生日的那天能够遇到一个他喜欢的男孩,他有飘逸的金色头发,会谈吉他唱歌,他们会很相爱,并且生下四个可爱的孩子并且他们都是爱唱歌的女孩....
      M真的在18岁生日的时候遇到了J,他确实有着金色飘逸的头发,但是他不会唱歌,而且是个厨师,他们也生下了四个孩子,但是都是男孩,而且他们都很喜欢踢足球....
      又一天,天使过来找M。M大哭着问天使:你为什么没有满足的我的愿望?你没有遵守你的诺言。天使笑了:为什么要遵守诺言,你没有付给我任何东西啊。M又问,难道天使满足被人的愿望也要代价吗? 天使说:需要,我需要你们付出快乐。
 
      觉得电影中男主角演得很好,一眼就认出他就是《阳光灿烂的日子》中的大帅哥。据说他不是专业演员,但是把做男人的味道都表现了出来,不甘,辛苦,但却很少抗争。觉得现实中的男人何尝不是这样的呢。
View more entries
 
There are no categories in use.
No list items have been added yet.