一、OpenCV概述与功能介绍
OpenCV是Intel®开源计算机视觉库。它由一系列 C 函数和少量 C++ 类构成,实现了图像处理和计算机视觉方面的很多通用算法。OpenCV 拥有包括 300 多个C函数的跨平台的中、高层 API。它不依赖于其它的外部库——尽管也可以使用某些外部库。
OpenCV 对非商业应用和商业应用都是免费(FREE)的。(细节参考 license)。代码下载地址:http://www.sourceforge.net/projects/opencvlibrary
OpenCV 为Intel® Integrated Performance Primitives (IPP) 提供了透明接口。 这意味着如果有为特定处理器优化的的 IPP 库, OpenCV 将在运行时自动加载这些库。 更多关于 IPP 的信息请参考: http://www.intel.com/software/products/ipp/index.htm
它有以下特点:
1) 开放的C/C++源码
2) 基于Intel处理器指令集开发的优化代码
3) 统一的结构和功能定义
4) 强大的图像和矩阵运算能力
5) 方便灵活的用户接口
6)同时支持MS-WINDOWS、LINUX平台
作为一个基本的计算机视觉、图像处理和模式识别的开源项目,OPENCV可以直接应用于很多领域,作为第二次开发的理想工具。
OpenCV功能介绍:
OpenCV包含如下几个部分:
Cxcore:一些基本函数(各种数据类型的基本运算等)。
Cv:图像处理和计算机视觉功能(图像处理,结构分析,运动分析,物体跟踪,模式识别,摄像机定标)
Ml:机器学习模块,目前内容主要为分类器。
Cvaux:一些实验性的函数(ViewMorphing,三维跟踪,PCA,HMM)
Highgui:用户交互部分,(GUI,图象视频I/O,系统调用函数)
二、OpenCV安装
OpenCV2.0刚刚发布,VC 2008 Express下安装OpenCV2.0请参考:
http://www.opencv.org.cn/index.php/VC_2008_Express%E4%B8%8B%E5%AE%89%E8%A3%85OpenCV2.0
三、基础知识:
1、opencv 数据类型转换操作小结
(1)图像中或矩阵数组中数据格式转换:
cvConvert( image, image_temp );
cvConvertScale( const CvArr* src, CvArr* dst, double scale CV_DEFAULT(1), double shift CV_DEFAULT(0) );
cvScale(src, dst);
// Converts CvArr (IplImage or CvMat,...) to CvMat.
cvGetMat( const CvArr* arr, CvMat* header, int* coi CV_DEFAULT(NULL), int allowND CV_DEFAULT(0));
cvCopy( const CvArr* src, CvArr* dst, const CvArr* mask ); //可以实现对不规制图形的提取
(2)多通道图像转成数组中数据
cvGetMat( const CvArr* array, CvMat* mat, int* pCOI, int allowND )
cvCopy(img,mat);
// Converts CvArr (IplImage or CvMat,...) to CvMat.
cvGetMat( const CvArr* arr, CvMat* header, int* coi CV_DEFAULT(NULL), int allowND CV_DEFAULT(0));
(3) 数组中数据转成多通道图像
cvCopy( const CvArr* src, CvArr* dst, const CvArr* mask=NULL );
cvGetMat( const CvArr* arr, CvMat* header, int* coi CV_DEFAULT(NULL), int allowND CV_DEFAULT(0));
2、二值化函数cvAdaptiveThreshold和cvThreshold的一些发现
自适应二值化计算像素的邻域的平均灰度,来决定二值化的值。如果整个区域几乎是一样灰度的,则无法给出合适的结果了。之所以看起来像边缘检测,是因为窗尺寸设置的小,可以改大一点试一试。
cvAdaptiveThreshold( src, dst, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 21); //窗设置为21
没有万能的二值化方法,具体问题具体分析,自适应二值化对于光照不均的文字,条码等,效果很好。窗口大小选择,考虑被检测物体尺寸。自适应阈值化中的阈值完全是由你所选择的邻域所确定的,如果你所选择的邻域非常小(比如3×3),那么很显然阈值的“自适应程度”就非常高,这在结果图像中就表现为边缘检测的效果。如果邻域选择的比较大(比如31×31),那么阈值的“自适应程度”就比较低,这在结果图像中就表现为二值化的效果。
3、用 gabor 和 AdaBoost (MultiBoost )做目标检测图像识别
http://www.opencv.org.cn/forum/viewtopic.php?f=10&t=7790
4、视频跟踪方法
跟踪的方法我知道的有KLMAN滤波.粒子滤波.camshift.meanshift。
5、怎么访问图像像素
(坐标是从0开始的,并且是相对图像原点的位置。图像原点或者是左上角 (img->origin=IPL_ORIGIN_TL) 或者是左下角 (img->origin=IPL_ORIGIN_BL) )
假设有 8-bit 1-通道的图像 I (IplImage* img):
---------------------------------------------------------------------
I(x,y) ~ ((uchar*)(img->imageData + img->widthStep*y))[x]
---------------------------------------------------------------------
假设有 8-bit 3-通道的图像 I (IplImage* img):
---------------------------------------------------------------------
I(x,y)blue ~ ((uchar*)(img->imageData + img->widthStep*y))[x*3]
I(x,y)green ~ ((uchar*)(img->imageData + img->widthStep*y))[x*3+1]
I(x,y)red ~ ((uchar*)(img->imageData + img->widthStep*y))[x*3+2]
------------------------------------------------------------------------------
例如,给点 (100,100) 的亮度增加 30 ,那么可以这样做:
------------------------------------------------------------------------------
CvPoint pt = {100,100};
((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3] += 30;
((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3+1] += 30;
((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3+2] += 30;
-----------------------------------------------------------------------------
或者更高效地:
-----------------------------------------------------------------------------
CvPoint pt = {100,100};
uchar* temp_ptr = &((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3];
temp_ptr[0] += 30;
temp_ptr[1] += 30;
temp_ptr[2] += 30;
-----------------------------------------------------------------------------
假设有 32-bit 浮点数, 1-通道 图像 I (IplImage* img):
-----------------------------------------------------------------------------
I(x,y) ~ ((float*)(img->imageData + img->widthStep*y))[x]
-----------------------------------------------------------------------------
现在,一般的情况下,假设有 N-通道,类型为 T 的图像:
-----------------------------------------------------------------------------
I(x,y)c ~ ((T*)(img->imageData + img->widthStep*y))[x*N + c]
-----------------------------------------------------------------------------
你可以使用宏 CV_IMAGE_ELEM( image_header, elemtype, y, x_Nc )
-----------------------------------------------------------------------------
I(x,y)c ~ CV_IMAGE_ELEM( img, T, y, x*N + c )
-----------------------------------------------------------------------------
也有针对各种图像(包括 4 通道图像)和矩阵的函数(cvGet2D, cvSet2D), 但是它们非常慢。
6、如何访问矩阵元素?
方法是类似的(下面的例子都是针对 0 起点的列和行)
设有 32-bit 浮点数的实数矩阵 M (CvMat* mat):
----------------------------------------------------------------------------
M(i,j) ~ ((float*)(mat->data.ptr + mat->step*i))[j]
----------------------------------------------------------------------------
设有 64-bit 浮点数的复数矩阵 M (CvMat* mat):
----------------------------------------------------------------------------
Re M(i,j) ~ ((double*)(mat->data.ptr + mat->step*i))[j*2]
Im M(i,j) ~ ((double*)(mat->data.ptr + mat->step*i))[j*2+1]
----------------------------------------------------------------------------
对单通道矩阵,有宏 CV_MAT_ELEM( matrix, elemtype, row, col ), 例如对 32-bit
浮点数的实数矩阵:
M(i,j) ~ CV_MAT_ELEM( mat, float, i, j ),
例如,这儿是一个 3x3 单位矩阵的初始化:
CV_MAT_ELEM( mat, float, 0, 0 ) = 1.f;
CV_MAT_ELEM( mat, float, 0, 1 ) = 0.f;
CV_MAT_ELEM( mat, float, 0, 2 ) = 0.f;
CV_MAT_ELEM( mat, float, 1, 0 ) = 0.f;
CV_MAT_ELEM( mat, float, 1, 1 ) = 1.f;
CV_MAT_ELEM( mat, float, 1, 2 ) = 0.f;
CV_MAT_ELEM( mat, float, 2, 0 ) = 0.f;
CV_MAT_ELEM( mat, float, 2, 1 ) = 0.f;
CV_MAT_ELEM( mat, float, 2, 2 ) = 1.f;
7、如何在 OpenCV 中处理我自己的数据
设你有 300x200 32-bit 浮点数 image/array, 也就是对一个有 60000 个元素的数组。
----------------------------------------------------------------------------
int cols = 300, rows = 200;
float* myarr = new float[rows*cols];
// 第一步,初始化 CvMat 头
CvMat mat = cvMat( rows, cols,
CV_32FC1, // 32 位浮点单通道类型
myarr // 用户数据指针(数据没有被复制)
);
// 第二步,使用 cv 函数, 例如计算 l2 (Frobenius) 模
double norm = cvNorm( &mat, 0, CV_L2 );
...
delete myarr;
其它情况在参考手册中有描述。 见 cvCreateMatHeader,cvInitMatHeader,cvCreateImageHeader, cvSetData 等
8、如何读入和显示图像
----------------------------------------------------------------------------
/* usage: prog <image_name> */
#include "cv.h"
#include "highgui.h"
int main( int argc, char** argv )
{
IplImage* img;
if( argc == 2 && (img = cvLoadImage( argv[1], 1)) != 0 )
{
cvNamedWindow( "Image view", 1 );
cvShowImage( "Image view", img );
cvWaitKey(0); // 非常重要,内部包含事件处理循环
cvDestroyWindow( "Image view" );
cvReleaseImage( &img );
return 0;
}
return -1;
}
9、图像的通道
描述一个像素点,如果是灰度,那么只需要一个数值来描述它,就是单通道。 如果一个像素点,有RGB三种颜色来描述它,就是三通道。4通道通常为RGBA,在某些处理中可能会用到。 2通道图像不常见,通常在程序处理中会用到,如傅里叶变换,可能会用到,一个通道为实数,一个通道为虚数,主要是编程方便。
10、HBITMAP 转换IplImage、IplImage转换为DIB
// HBITMAP 转换IplImage
IplImage* hBitmap2Ipl(HBITMAP hBmp)
{
BITMAP bmp;
::GetObject(hBmp,sizeof(BITMAP),&bmp);
int nChannels = bmp.bmBitsPixel == 1 ? 1 : bmp.bmBitsPixel/8 ;
int depth = bmp.bmBitsPixel == 1 ? IPL_DEPTH_1U : IPL_DEPTH_8U;
IplImage* img = cvCreateImageHeader( cvSize(bmp.bmWidth, bmp.bmHeight)
, depth, nChannels );
img->imageData =
(char*)malloc(bmp.bmHeight*bmp.bmWidth*nChannels*sizeof(char));
memcpy(img->imageData,(char*)(bmp.bmBits),bmp.bmHeight*bmp.bmWidth*nChannels);
return img;
}
void createDIB(IplImage* &pict){
IplImage * Red=cvCreateImage( cvSize(IMAGE_WIDTH,IMAGE_HEIGHT),
IPL_DEPTH_8U, 1 );
IplImage * Green=cvCreateImage( cvSize(IMAGE_WIDTH,IMAGE_HEIGHT),
IPL_DEPTH_8U, 1 );
IplImage * Blue=cvCreateImage( cvSize(IMAGE_WIDTH,IMAGE_HEIGHT),
IPL_DEPTH_8U, 1 );
cvSetImageCOI( pict, 3);
cvCopy(pict,Red);
cvSetImageCOI( pict, 2);
cvCopy(pict,Green);
cvSetImageCOI(pict, 1);
cvCopy(pict,Blue);
//Initialize the BMP display buffer
bmi = (BITMAPINFO*)buffer;
bmih = &(bmi->bmiHeader);
memset( bmih, 0, sizeof(*bmih));
bmih->biSize = sizeof(BITMAPINFOHEADER);
bmih->biWidth = IMAGE_WIDTH;
bmih->biHeight = IMAGE_HEIGHT; // -IMAGE_HEIGHT;
bmih->biPlanes = 1;
bmih->biCompression = BI_RGB;
bmih->biBitCount = 24;
palette = bmi->bmiColors;
for( int i = 0; i < 256; i++ ){
palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed =
(BYTE)i;
palette[i].rgbReserved = 0;
}
cvReleaseImage(&Red);
cvReleaseImage(&Green);
cvReleaseImage(&Blue);
}
// HBITMAP转换DIB
HBITMAP plIamgeToDIB(IplImage *pImg,int Size)
{
HDC hDC = ::CreateCompatibleDC(0);
BYTE tmp[sizeof(BITMAPINFO)+255*4];
BITMAPINFO *bmi = (BITMAPINFO*)tmp;
HBITMAP hBmp;
int i;
memset(bmi,0,sizeof(BITMAPINFO));
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi->bmiHeader.biWidth = pImg->width;
bmi->bmiHeader.biHeight = -pImg->height;
bmi->bmiHeader.biPlanes = Size;
bmi->bmiHeader.biBitCount = pImg->nChannels * pImg->depth;
bmi->bmiHeader.biCompression = BI_RGB;
bmi->bmiHeader.biSizeImage = pImg->width*pImg->height*1;
bmi->bmiHeader.biClrImportant =0 ;
<s