OpenCV 简单实例参考


CV 参考手册 HUNNISH 注: 本翻译是直接根据 OpenCV Beta 4.0 版本的用户手册翻译的,原文件是: /doc/ref/opencvref_cv.htm, 可以从 SOURCEFORG 上面的 OpenCV 项目下载,也可以直接从 阿须数码 中下载: http://www.assuredigit.com/incoming/sourcecode/opencv/chinese_ docs/ref/opencvref_cv.htm。 翻译中肯定有不少错误,另外也有些术语和原文语义理解不透导致翻译 不准确或者错误,也请有心人赐教。 图像处理、结构分析、运动分析和对象跟踪部分由 R.Z.LIU 翻译,模式 识别、照相机定标与三维重建部分由 H.M.ZHANG 翻译,全文由 Y.C.WEI 统一修改校正。 • 图像处理 o 梯度, 边缘和角点 o 采样 差值和几何变换 o 形态学操作 o 滤波和彩色变换 o 金字塔及其应用 o 连接组件 o 图像和轮廓矩 o 特殊图像变换 o 直方图 o 匹配 • 结构分析 o 轮廓处理 o 计算几何 o 平面划分 • 运动分析和对象跟踪 o 背景统计量的累积 o 运动模板 o 对象跟踪 o 光流 o 预估器 • 模式识别 o 目标检测 • 照相机定标和三维重建 o 照相机定标 o 姿态估计 o 极线几何 • 函数列表 • 参考 图像处理 注意: 本章描述图像处理和分析的一些函数。其中大多数函数都是针对两维象 素数组的,这里,我们称这些数组为“图像”,但是它们不一定非得是 IplImage 结构,也可以是 CvMat 或者 CvMatND 结构。 梯度、边缘和角点 Sobel 使用扩展 Sobel 算子计算一阶、二阶、三阶或混合图像差分 void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size=3 ); src 输入图像. dst 输出图像. xorder x 方向上的差分阶数 yorder y 方向上的差分阶数 aperture_size 扩展 Sobel 核的大小,必须是 1, 3, 5 或 7。 除了尺寸为 1, 其它情况下, aperture_size ×aperture_size 可分离内核将用来计算差分。对 aperture_size=1 的情况, 使用 3x1 或 1x3 内核 (不进行高斯平滑操作)。这里有一个特殊变量 CV_SCHARR (=-1),对应 3x3 Scharr 滤波器,可以给出比 3x3 Sobel 滤波更精确的结果。Scharr 滤波器系数是: | -3 0 3| |-10 0 10| | -3 0 3| 对 x-方向 以及转置矩阵对 y-方向。 函数 cvSobel 通过对图像用相应的内核进行卷积操作来计算图像差分: dst(x,y) = dxorder+yodersrc/dxxorder•dyyorder |(x,y) 由于 Sobel 算子结合了 Gaussian 平滑和微分,所以,其结果或多或少对噪声有一 定的鲁棒性。通常情况,函数调用采用如下参数 (xorder=1, yorder=0, aperture_size=3) 或 (xorder=0, yorder=1, aperture_size=3) 来计算一阶 x- 或 y- 方 向的图像差分。第一种情况对应: |-1 0 1| |-2 0 2| |-1 0 1| 核。第二种对应 |-1 -2 -1| | 0 0 0| | 1 2 1| or | 1 2 1| | 0 0 0| |-1 -2 -1| 核的选则依赖于图像原点的定义 (origin 来自 IplImage 结构的定义)。由于该函 数不进行图像尺度变换,所以和输入图像(数组)相比,输出图像(数组)的元素通常 具有更大的绝对数值(译者注:即象素的深度)。为防止溢出,当输入图像是 8 位 的,要求输出图像是 16 位的。当然可以用函数函数 cvConvertScale 或 cvConvertScaleAbs 转换为 8 位的。除了 8-比特 图像,函数也接受 32-位 浮点 数图像。所有输入和输出图像都必须是单通道的,并且具有相同的图像尺寸或者 ROI尺寸。 Laplace 计算图像的 Laplacian 变换 void cvLaplace( const CvArr* src, CvArr* dst, int aperture_size=3 ); src 输入图像. dst 输出图像. aperture_size 核大小 (与 cvSobel 中定义一样). 函数 cvLaplace 计算输入图像的 Laplacian变换,方法是先用 sobel 算子计算二阶 x- 和 y- 差分,再求和: dst(x,y) = d2src/dx2 + d2src/dy2 对 aperture_size=1 则给出最快计算结果,相当于对图像采用如下内核做 卷积: |0 1 0| |1 -4 1| |0 1 0| 类似于 cvSobel 函数,该函数也不作图像的尺度变换,所支持的输入、 输出图像类型的组合和 cvSobel一致。 Canny 采用 Canny 算法做边缘检测 void cvCanny( const CvArr* image, CvArr* edges, double threshold1, double threshold2, int aperture_size=3 ); image 输入图像. edges 输出的边缘图像 threshold1 第一个阈值 threshold2 第二个阈值 aperture_size Sobel 算子内核大小 (见 cvSobel). 函数 cvCanny 采用 CANNY 算法发现输入图像的边缘而且在输出图像中 标识这些边缘。threshold1 和threshold2 当中的小阈值用来控制边缘连接, 大的阈值用来控制强边缘的初始分割。 PreCornerDetect 计算用于角点检测的特征图, void cvPreCornerDetect( const CvArr* image, CvArr* corners, int aperture_size=3 ); image 输入图像. corners 保存候选角点的特征图 aperture_size Sobel 算子的核大小(见 cvSobel). 函数 cvPreCornerDetect 计算函数 Dx 2Dyy+Dy 2Dxx - 2D xDyDxy 其中 D? 表示 一阶图像差分,D?? 表示二阶图像差分。 角点被认为是函数的局部最大 值: // 假设图像格式为浮点数 IplImage* corners = cvCloneImage(image); IplImage* dilated_corners = cvCloneImage(image); IplImage* corner_mask = cvCreateImage( cvGetSize(image), 8, 1 ); cvPreCornerDetect( image, corners, 3 ); cvDilate( corners, dilated_corners, 0, 1 ); cvSubS( corners, dilated_corners, corners ); cvCmpS( corners, 0, corner_mask, CV_CMP_GE ); cvReleaseImage( &corners ); cvReleaseImage( &dilated_corners ); CornerEigenValsAndVecs 计算图像块的特征值和特征向量,用于角点检测 void cvCornerEigenValsAndVecs( const CvArr* image, CvArr* eigenvv, int block_size, int aperture_size=3 ); image 输入图像. eigenvv 保存结果的数组。必须比输入图像宽 6 倍。 block_size 邻域大小 (见讨论). aperture_size Sobel 算子的核尺寸(见 cvSobel). 对每个象素,函数 cvCornerEigenValsAndVecs 考虑 block_size × block_size 大小的邻域 S(p),然后在邻域上计算图像差分的相关矩阵: | sumS(p)(dI/dx)2 sumS(p)(dI/dx•dI/dy)| M = | | | sumS(p)(dI/dx•dI/dy) sumS(p)(dI/dy)2 | 然后它计算矩阵的特征值和特征向量,并且按如下方式(λ1, λ2, x1, y1, x2, y2)存储这些值到输出图像中,其中 λ1, λ2 - M 的特征值,没有排序 (x1, y1) - 特征向量,对 λ1 (x2, y2) - 特征向量,对 λ2 CornerMinEigenVal 计算梯度矩阵的最小特征值,用于角点检测 void cvCornerMinEigenVal( const CvArr* image, CvArr* eigenval, int block_size, int aperture_size=3 ); image 输入图像. eigenval 保存最小特征值的图像. 与输入图像大小一致 block_size 邻域大小 (见讨论 cvCornerEigenValsAndVecs). aperture_size Sobel 算子的核尺寸(见 cvSobel). 当输入图像是浮点数格式时,该参数表示用来计算差 分固定的浮点滤波器的个数. 函数 cvCornerMinEigenVal 与 cvCornerEigenValsAndVecs 类似,但是 它仅仅计算和存储每个象素点差分相关矩阵的最小特征值,即前一个函 数的 min(λ1, λ2) FindCornerSubPix 精确角点位置 void cvFindCornerSubPix( const CvArr* image, CvPoint2D32f* corners, int count, CvSize win, CvSize zero_zone, CvTermCriteria criteria ); image 输入图像. corners 输入角点的初始坐标,也存储精确的输出坐标 count 角点数目 win 搜索窗口的一半尺寸。如果 win=(5,5) 那么使用 5*2+1 × 5*2+1 = 11 × 11 大小的搜索窗 口 zero_zone 死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。它是用来避免自相 关矩阵出现的某些可能的奇异性。当值为 (-1,-1) 表示没有死区。 criteria 求角点的迭代过程的终止条件。即角点位置的确定,要么迭代数大于某个设定值,或者 是精确度达到某个设定值。 criteria 可以是最大迭代数目,或者是设定的精确度,也 可以是它们的组合。 函数 cvFindCornerSubPix 通过迭代来发现具有子象素精度的角点位 置,或如图所示的放射鞍点(radial saddle points)。 子象素级角点定位的实现是基于对向量正交性的观测而实现的,即从中 央点 q 到其邻域点 p 的向量和 p 点处的图像梯度正交(服从图像和测量 噪声)。考虑以下的表达式: εi=DIpi T•(q-pi) 其中,DIpi 表示在 q 的一个邻域点 pi 处的图像梯度,q 的值通过最小化 εi 得到。通 过将 εi 设为 0,可以建立系统方程如下: sumi(DIpi•DIpi T)•q - sumi(DIpi•DIpi T•pi) = 0 其中 q 的邻域(搜索窗)中的梯度被累加。调用第一个梯度参数 G 和第 二个梯度参数 b,得到: q=G-1•b 该算法将搜索窗的中心设为新的中心 q,然后迭代,直到找到低于某个 阈值点的中心位置。 GoodFeaturesToTrack 确定图像的强角点 void cvGoodFeaturesToTrack( const CvArr* image, CvArr* eig_image, CvArr* temp_image, CvPoint2D32f* corners, int* corner_count, double quality_level, double min_distance, const CvArr* mask=NULL ); image 输入图像,8-位或浮点 32-比特,单通道 eig_image 临时浮点 32-位图像,尺寸与输入图像一致 temp_image 另外一个临时图像,格式与尺寸与 eig_image 一致 corners 输出参数,检测到的角点 corner_count 输出参数,检测到的角点数目 quality_level 最大最小特征值的乘法因子。定义可接受图像角点的最小质量因子。 min_distance 限制因子。得到的角点的最小距离。使用 Euclidian 距离 mask ROI:感兴趣区域。函数在 ROI 中计算角点,如果 mask 为 NULL,则选择整个图像。 函数 cvGoodFeaturesToTrack 在图像中寻找具有大特征值的角点。该函 数,首先用 cvCornerMinEigenVal 计算输入图像的每一个象素点的最小 特征值,并将结果存储到变量 eig_image 中。然后进行非最大值抑制(仅 保留 3x3 邻域中的局部最大值)。下一步将最小特征值小于 quality_level•max(eig_image(x,y)) 排除掉。最后,函数确保所有发现的 角点之间具有足够的距离,(最强的角点第一个保留,然后检查新的角 点与已有角点之间的距离大于 min_distance )。 采样、差值和几何变换 InitLineIterator 初始化线段迭代器 int cvInitLineIterator( const CvArr* image, CvPoint pt1, CvPoint pt2, CvLineIterator* line_iterator, int connectivity=8 ); image 带采线段的输入图像. pt1 线段起始点 pt2 线段结束点 line_iterator 指向线段迭代器状态结构的指针 connectivity 被扫描线段的连通数,4 或 8. 函数 cvInitLineIterator 初始化线段迭代器,并返回两点之间的象素 点数目。两个点必须在图像内。当迭代器初始化后,连接两点的光栅线 上所有点,都可以连续通过调用 CV_NEXT_LINE_POINT 来得到。线段上的点 是使用 4-连通或 8-连通利用 Bresenham 算法逐点计算的。 例子:使用线段迭代器计算彩色线上象素值的和 CvScalar sum_line_pixels( IplImage* image, CvPoint pt1, CvPoint pt2 ) { CvLineIterator iterator; int blue_sum = 0, green_sum = 0, red_sum = 0; int count = cvInitLineIterator( image, pt1, pt2, &iterator, 8 ); for( int i = 0; i < count; i++ ){ blue_sum += iterator.ptr[0]; green_sum += iterator.ptr[1]; red_sum += iterator.ptr[2]; CV_NEXT_LINE_POINT(iterator); /* print the pixel coordinates: demonstrates how to calculate the coordinates */ { int offset, x, y; /* assume that ROI is not set, otherwise need to take it into account. */ offset = iterator.ptr - (uchar*)(image->imageData); y = offset/image->widthStep; x = (offset - y*image->widthStep)/(3*sizeof(uchar) /* size of pixel */); printf("(%d,%d)\n", x, y ); } } return cvScalar( blue_sum, green_sum, red_sum ); } SampleLine 将光栅线读入缓冲区 int cvSampleLine( const CvArr* image, CvPoint pt1, CvPoint pt2, void* buffer, int connectivity=8 ); image 带采线段的输入图像 pt1 起点 pt2 终点 buffer 存储线段点的缓存区,必须有足够大小来存储点 max( |pt2.x-pt1.x|+1, |pt2.y-pt1.y|+1 ) :8-连通情况下,或者 |pt2.x-pt1.x|+|pt2.y-pt1.y|+1 : 4-连通情 况下. connectivity 线段的连通方式, 4 or 8. 函数 cvSampleLine 实现了线段迭代器的一个特殊应用。它读取由两点 pt1 和 pt2 确定的线段上的所有图像点,包括终点,并存储到缓存中。 GetRectSubPix 从图像中提取象素矩形,使用子象素精度 void cvGetRectSubPix( const CvArr* src, CvArr* dst, CvPoint2D32f center ); src 输入图像. dst 提取的矩形. center 提取的象素矩形的中心,浮点数坐标。中心必须位于图像内部. 函数 cvGetRectSubPix 从图像 src 中提取矩形: dst(x, y) = src(x + center.x - (width(dst)-1)*0.5, y + center.y - (height(dst)-1)*0.5) 其中非整数象素点坐标采用双线性差值提取。对多通道图像,每个通道 独立单独完成提取。尽管函数要求矩形的中心一定要在输入图像之中, 但是有可能出现矩形的一部分超出图像边界的情况,这时,该函数复制 边界的模识(hunnish:即用于矩形相交的图像边界线段的象素来代替矩 形超越部分的象素)。 GetQuadrangleSubPix 提取象素四边形,使用子象素精度 void cvGetQuadrangleSubPix( const CvArr* src, CvArr* dst, const CvMat* map_matrix, int fill_outliers=0, CvScalar fill_value=cvScalarAll(0) ); src 输入图像. dst 提取的四边形. map_matrix 3 × 2 变换矩阵 [A|b] (见讨论). fill_outliers 该标志位指定是否对原始图像边界外面的象素点使用复制模式(fill_outliers=0)进行 差值或者将其设置为指定值(fill_outliers=1)。 fill_value 对超出图像边界的矩形象素设定的值(当 fill_outliers=1 时的情况). 函数 cvGetQuadrangleSubPix 以子象素精度从图像 src 中提取四边 形,使用子象素精度,并且将结果存储于 dst ,计算公式是: dst(x+width(dst)/2, y+height(dst)/2)= src( A11x+A12y+b1, A21x+A22y+b2), w 其中 A 和 b 均来自映射矩阵(译者注:A, b 为几何形变参数) map_matrix | A11 A12 b1 | map_matrix = | | | A21 A22 b2 | 其中在非整数坐标 A•(x,y)T+b 的象素点值通过双线性变换得到。多通 道图像的每一个通道都单独计算. 例子:使用 cvGetQuadrangleSubPix 进行图像旋转 #include "cv.h" #include "highgui.h" #include "math.h" int main( int argc, char** argv ) { IplImage* src; /* the first command line parameter must be image file name */ if( argc==2 && (src = cvLoadImage(argv[1], -1))!=0) { IplImage* dst = cvCloneImage( src ); int delta = 1; int angle = 0; cvNamedWindow( "src", 1 ); cvShowImage( "src", src ); for(;;) { float m[6]; double factor = (cos(angle*CV_PI/180.) + 1.1)*3; CvMat M = cvMat( 2, 3, CV_32F, m ); int w = src->width; int h = src->height; m[0] = (float)(factor*cos(-angle*2*CV_PI/180.)); m[1] = (float)(factor*sin(-angle*2*CV_PI/180.)); m[2] = w*0.5f; m[3] = -m[1]; m[4] = m[0]; m[5] = h*0.5f; cvGetQuadrangleSubPix( src, dst, &M, 1, cvScalarAll(0)); cvNamedWindow( "dst", 1 ); cvShowImage( "dst", dst ); if( cvWaitKey(5) == 27 ) break; angle = (angle + delta) % 360; } } return 0; } Resize 图像大小变换 void cvResize( const CvArr* src, CvArr* dst, int interpolation=CV_INTER_LINEAR ); src 输入图像. dst 输出图像. interpolation 差值方法: • CV_INTER_NN - 最近邻差值, • CV_INTER_LINEAR - 双线性差值 (缺省使用) • CV_INTER_AREA - 使用象素关系重采样。当图像缩小时候,该方法可以避免波 纹出现。当图像放大时,类似于 CV_INTER_NN 方法.. • CV_INTER_CUBIC - 立方差值. 函数 cvResize 将图像 src 改变尺寸得到与 dst 同样大小。若设定 ROI, 函数将按常规支持 ROI. WarpAffine 对图像做仿射变换 void cvWarpAffine( const CvArr* src, CvArr* dst, const CvMat* map_matrix, int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, CvScalar fillval=cvScalarAll(0) ); src 输入图像. dst 输出图像. map_matrix 2×3 变换矩阵 flags 插值方法和以下开关选项的组合: • CV_WARP_FILL_OUTLIERS - 填充所有缩小图像的象素。如果部分象素落在输入 图像的边界外,那么它们的值设定为 fillval. • CV_WARP_INVERSE_MAP - 指定 matrix 是输出图像到输入图像的反变换,因 此可以直接用来做象素差值。否则, 函数从 map_matrix 得到反变换。 fillval 用来填充边界外面的值 函数 cvWarpAffine 利用下面指定的矩阵变换输入图像: dst(x',y')<-src(x,y) 如果没有指定 CV_WARP_INVERSE_MAP , (x',y')T=map_matrix•(x,y,1)T+b , 否则, (x, y)T=map_matrix•(x',y&apos,1)T+b 函数与 cvGetQuadrangleSubPix 类似,但是不完全相同。 cvWarpAffine 要求输入和输出图像具有同样的数据类型,有更大的资源开销(因此对 小图像不太合适)而且输出图像的部分可以保留不变。而 cvGetQuadrangleSubPix 可以精确地从 8 位图像中提取四边形到浮点数 缓存区中,具有比较小的系统开销,而且总是全部改变输出图像的内容。 要变换稀疏矩阵,使用 cxcore 中的函数 cvTransform 。 2DRotationMatrix 计算二维旋转的仿射变换矩阵 CvMat* cv2DRotationMatrix( CvPoint2D32f center, double angle, double scale, CvMat* map_matrix ); center 输入图像的旋转中心坐标 angle 旋转角度(度)。正值表示逆时针旋转(坐标原点假设在左上角). scale 各项同性的尺度因子 map_matrix 输出 2×3 矩阵的指针 函数 cv2DRotationMatrix 计算矩阵: [ α β | (1-α)*center.x - β*center.y ] [ -β α | β*center.x + (1-α)*center.y ] where α=scale*cos(angle), β=scale*sin(angle) 该变换并不改变原始旋转中心点的坐标,如果这不是操作目的,则可以 通过调整平移量改变其坐标(译者注:通过简单的推导可知,放射变换的 实现是首先将旋转中心置为坐标原点,再进行旋转和尺度变换,最后重 新将坐标原点设定为输入图像的左上角,这里的平移量是 center.x, center.y). WarpPerspective 对图像进行透视变换 void cvWarpPerspective( const CvArr* src, CvArr* dst, const CvMat* map_matrix, int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, CvScalar fillval=cvScalarAll(0) ); src 输入图像. dst 输出图像. map_matrix 3×3 变换矩阵 flags 插值方法和以下开关选项的组合: • CV_WARP_FILL_OUTLIERS - 填充所有缩小图像的象素。如果部分象素落在输入 图像的边界外,那么它们的值设定为 fillval. • CV_WARP_INVERSE_MAP - 指定 matrix 是输出图像到输入图像的反变换,因 此可以直接用来做象素差值。否则, 函数从 map_matrix 得到反变换。 fillval 用来填充边界外面的值 函数 cvWarpPerspective 利用下面指定矩阵变换输入图像: dst(x',y')<-src(x,y) 若指定 CV_WARP_INVERSE_MAP, (tx',ty',t)T=map_matrix•(x,y,1)T+b 否则, (tx, ty, t)T=map_matrix•(x',y&apos,1)T+b 要变换稀疏矩阵,使用 cxcore 中的函数 cvTransform 。 WarpPerspectiveQMatrix 用 4 个对应点计算透视变换矩阵 CvMat* cvWarpPerspectiveQMatrix( const CvPoint2D32f* src, const CvPoint2D32f* dst, CvMat* map_matrix ); src 输入图像的四边形的 4 个点坐标 dst 输出图像的对应四边形的 4 个点坐标 map_matrix 输出的 3×3 矩阵 函数 cvWarpPerspectiveQMatrix 计算透视变换矩阵,使得: (tix'i,tiy'i,ti)T=matrix•(xi,yi,1)T 其中 dst(i)=(x'i,y'i), src(i)=(xi,yi), i=0..3. 形态学操作 CreateStructuringElementEx 创建结构元素 IplConvKernel* cvCreateStructuringElementEx( int cols, int rows, int anchor_x, int anchor_y, int shape, int* values=NULL ); cols 结构元素的列数目 rows 结构元素的行数目 anchor_x 锚点的相对水平偏移量 anchor_y 锚点的相对垂直便宜量 shape 结构元素的形状,可以是下列值: • CV_SHAPE_RECT, 长方形元素; • CV_SHAPE_CROSS, 交错元素 a cross-shaped element; • CV_SHAPE_ELLIPSE, 椭圆元素; • CV_SHAPE_CUSTOM, 用户自定义元素。这种情况下参数 values 定义了 mask,即象素 的那个邻域必须考虑。 values 指向结构元素的指针,它是一个平面数组,表示对元素矩阵逐行扫描。(非零点表示该点 属于结构元)。如果指针为空,则表示平面数组中的所有元素都是非零的,即结构元是一 个长方形(该参数仅仅当 shape 参数是 CV_SHAPE_CUSTOM 时才予以考虑)。 函数 cv CreateStructuringElementEx 分配和填充结构 IplConvKernel, 它可作为形态操作中的结构元素。 ReleaseStructuringElement 删除结构元素 void cvReleaseStructuringElement( IplConvKernel** element ); element 被删除的结构元素的指针 函数 cvReleaseStructuringElement 释放结构 IplConvKernel 。如果 *element 为 NULL, 则函数不作用。 Erode 使用任意结构元素腐蚀图像 void cvErode( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 ); src 输入图像. dst 输出图像. element 用于腐蚀的结构元素。若为 NULL, 则使用 3×3 长方形的结构元素 iterations 腐蚀的次数 函数 cvErode 对输入图像使用指定的结构元素进行腐蚀,该结构元素决 定每个具有最小值象素点的邻域形状: dst=erode(src,element): dst(x,y)=min((x',y') in element))src(x+x',y+y') 函数可能是本地操作,不需另外开辟存储空间的意思。腐蚀可以重复进 行 (iterations) 次. 对彩色图像,每个彩色通道单独处理。 Dilate 使用任意结构元素膨胀图像 void cvDilate( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 ); src 输入图像. dst 输出图像. element 用于膨胀的结构元素。若为 NULL, 则使用 3×3 长方形的结构元素 iterations 膨胀的次数 函数 cvDilate 对输入图像使用指定的结构元进行膨胀,该结构决定每 个具有最小值象素点的邻域形状: dst=dilate(src,element): dst(x,y)=max((x',y') in element))src(x+x',y+y') 函数支持(in-place)模式。膨胀可以重复进行 (iterations) 次. 对彩 色图像,每个彩色通道单独处理。 MorphologyEx 高级形态学变换 void cvMorphologyEx( const CvArr* src, CvArr* dst, CvArr* temp, IplConvKernel* element, int operation, int iterations=1 ); src 输入图像. dst 输出图像. temp 临时图像,某些情况下需要 element 结构元素 operation 形态操作的类型: CV_MOP_OPEN - 开运算 CV_MOP_CLOSE - 闭运算 CV_MOP_GRADIENT - 形态梯度 CV_MOP_TOPHAT - "顶帽" CV_MOP_BLACKHAT - "黑帽" iterations 膨胀和腐蚀次数. 函数 cvMorphologyEx 在膨胀和腐蚀基本操作的基础上,完成一些高级 的形态变换: 开运算: dst=open(src,element)=dilate(erode(src,element),element) 闭运算: dst=close(src,element)=erode(dilate(src,element),element) 形态梯度 dst=morph_grad(src,element)=dilate(src,element)-erode(src,element) "顶帽": dst=tophat(src,element)=src-open(src,element) "黑帽": dst=blackhat(src,element)=close(src,element)-src 临时图像 temp 在形态梯度以及对“顶帽”和“黑帽”操作时的 in-place 模式下需要。 滤波器与彩色变换 Smooth 各种方法的图像平滑 void cvSmooth( const CvArr* src, CvArr* dst, int smoothtype=CV_GAUSSIAN, int param1=3, int param2=0, double param3=0 ); src 输入图像. dst 输出图像. smoothtype 平滑方法: • CV_BLUR_NO_SCALE ( 简单不带尺度变换的模糊) - 对每个象素的 param1×param2 领域求和。如果邻域大小是变化的,可以事先利用函数 cvIntegral 计算积分图像。 • CV_BLUR (simple blur) - 对每个象素 param1×param2 邻域 求和并做尺度变换 1/(param1•param2). • CV_GAUSSIAN (gaussian blur) - 对图像进行核大小为 param1×param2 的高斯卷 积 • CV_MEDIAN (median blur) - 对图像进行核大小为 param1×param1 的中值滤波 (i.e. 邻域是方的). • CV_BILATERAL (双向滤波) - 应用双向 3x3 滤波,彩色 sigma=param1,空间 sigma=param2. 关于双向滤波,可参考 http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filteri ng.html param1 平滑操作的第一个参数. param2 平滑操作的第二个参数. 对于简单/非尺度变换的高斯模糊的情况,如果 param2 的值 为 零,则表示其被设定为 param1。 param3 对应高斯参数的 Gaussian sigma (标准差). 如果为零,则标准差由下面的核尺寸计算: sigma = (n/2 - 1)*0.3 + 0.8, 其中 n=param1 对应水平核, n=param2 对应垂直核. 对小的卷积核 (3×3 to 7×7) 使用如上公式所示的标准 sigma 速度会快。如果 param3 不 为零,而 param1 和 param2 为零,则核大小有 sigma 计算 (以保证足够精确的操作). 函数 cvSmooth 可使用上面任何一种方法平滑图像。每一种方法都有自 己的特点以及局限。 没有缩放的图像平滑仅支持单通道图像,并且支持8 位到 16 位的转换(与 cvSobel 和 cvaplace 相似)和 32 位浮点数到 32 位浮点数的变换格式。 简单模糊和高斯模糊支持 1- 或 3-通道, 8-比特 和 32-比特 浮点图 像。这两种方法可以(in-place)方式处理图像。 中值和双向滤波工作于 1- 或 3-通道, 8-位图像,但是不能以 in-place 方式处理图像. Filter2D 对图像做卷积 void cvFilter2D( const CvArr* src, CvArr* dst, const CvMat* kernel, CvPoint anchor=cvPoint(-1,-1)); #define cvConvolve2D cvFilter2D src 输入图像. dst 输出图像. kernel 卷积核, 单通道浮点矩阵. 如果想要应用不同的核于不同的通道,先用 cvSplit 函数分解 图像到单个色彩通道上,然后单独处理。 anchor 核的锚点表示一个被滤波的点在核内的位置。 锚点应该处于核内部。缺省值 (-1,-1) 表 示锚点在核中心。 函数 cvFilter2D 对图像进行线性滤波,支持 In-place 操作。当核运 算部分超出输入图像时,函数从最近邻的图像内部象素差值得到边界外 面的象素值。 Integral 计算积分图像 void cvIntegral( const CvArr* image, CvArr* sum, CvArr* sqsum=NULL, CvArr* tilted_sum=NULL ); image 输入图像, W×H, 单通道,8 位或浮点 (32f 或 64f). sum 积分图像, W+1×H+1(译者注:原文的公式应该写成(W+1)×(H+1),避免误会), 单通道,32 位 整数或 double 精度的浮点数(64f). sqsum 对象素值平方的积分图像,W+1×H+1(译者注:原文的公式应该写成(W+1)×(H+1),避免误会), 单通道,32 位整数或 double 精度的浮点数 (64f). tilted_sum 旋转 45 度的积分图像,单通道,32 位整数或 double 精度的浮点数 (64f). 函数 cvIntegral 计算一次或高次积分图像: sum(X,Y)=sumx2 常数 (见下面). 函数 cvCvtColor 将输入图像从一个色彩空间转换为另外一个色彩空 间。函数忽略 IplImage 头中定义的 colorModel 和 channelSeq 域,所以 输入图像的色彩空间应该正确指定 (包括通道的顺序,对RGB空间而言, BGR 意味着布局为 B0 G0 R0 B1 G1 R 1 ... 层叠的 24-位格式,而 RGB 意 味着布局为 R0 G0 B0 R1 G1 B1 ... 层叠的 24-位格式. 函数做如下变换: • RGB 空间内部的变换,如增加/删除 alpha 通道,反相通道顺序,到 16 位 RGB 彩色或者 15 位 RGB 彩色的正逆转换(Rx5:Gx6:Rx5),以及到灰度 图像的正逆转换,使用: • RGB[A]->Gray: Y=0.212671*R + 0.715160*G + 0.072169*B + 0*A • Gray->RGB[A]: R=Y G=Y B=Y A=0 所有可能的图像色彩空间的相互变换公式列举如下: • RGB<=>XYZ (CV_BGR2XYZ, CV_RGB2XYZ, CV_XYZ2BGR, CV_XYZ2RGB): • |X| |0.412411 0.357585 0.180454| |R| • |Y| = |0.212649 0.715169 0.072182|*|G| • |Z| |0.019332 0.119195 0.950390| |B| • • |R| | 3.240479 -1.53715 -0.498535| |X| • |G| = |-0.969256 1.875991 0.041556|*|Y| • |B| | 0.055648 -0.204043 1.057311| |Z| • RGB<=>YCrCb (CV_BGR2YCrCb, CV_RGB2YCrCb, CV_YCrCb2BGR, CV_YCrCb2RGB) • Y=0.299*R + 0.587*G + 0.114*B • Cr=(R-Y)*0.713 + 128 • Cb=(B-Y)*0.564 + 128 • • R=Y + 1.403*(Cr - 128) • G=Y - 0.344*(Cr - 128) - 0.714*(Cb - 128) • B=Y + 1.773*(Cb - 128) • RGB=>HSV (CV_BGR2HSV,CV_RGB2HSV) • V=max(R,G,B) • S=(V-min(R,G,B))*255/V if V!=0, 0 otherwise • • (G - B)*60/S, if V=R • H= 180+(B - R)*60/S, if V=G • 240+(R - G)*60/S, if V=B • • if H<0 then H=H+360 使用上面从 0° 到 360° 变化的公式计算色调(hue)值,确保 它们被 2 除后能适用于 8 位。 • RGB=>Lab (CV_BGR2Lab, CV_RGB2Lab) • |X| |0.433910 0.376220 0.189860| |R/255| • |Y| = |0.212649 0.715169 0.072182|*|G/255| • |Z| |0.017756 0.109478 0.872915| |B/255| • • L = 116*Y1/3 for Y>0.008856 • L = 903.3*Y for Y<=0.008856 • • a = 500*(f(X)-f(Y)) • b = 200*(f(Y)-f(Z)) • where f(t)=t1/3 for t>0.008856 • f(t)=7.787*t+16/116 for t<=0.008856 上面的公式可以参考 http://www.cica.indiana.edu/cica/faq/color_spaces/color.spaces.html • Bayer=>RGB (CV_BayerBG2BGR, CV_BayerGB2BGR, CV_BayerRG2BGR, CV_BayerGR2BGR, CV_BayerBG2RGB, CV_BayerRG2BGR, CV_BayerGB2RGB, CV_BayerGR2BGR, CV_BayerRG2RGB, CV_BayerBG2BGR, CV_BayerGR2RGB, CV_BayerGB2BGR) Bayer 模式被广泛应用于 CCD 和 CMOS 摄像头. 它允许从一个 单独平面中得到彩色图像,该平面中的 R/G/B 象素点被安排如 下: R G R G R G B G B G R G R G R G B G B G R G R G R G B G B G 对象素输出的 RGB 分量由该象素的 1、2或者 4 邻域中具有相同颜 色的点插值得到。以上的模式可以通过向左或者向上平移一个像 素点来作一些修改。转换常量 CV_BayerC1C22{RGB|RGB}中的两个 字母 C1 和 C2 表示特定的模式类型:颜色分量分别来自于第二行, 第二和第三列。比如说,上述的模式具有很流行的"BG"类型。 Threshold 对数组元素进行固定阈值操作 void cvThreshold( const CvArr* src, CvArr* dst, double threshold, double max_value, int threshold_type ); src 原始数组 (单通道, 8-比特 of 32-比特 浮点数). dst 输出数组,必须与 src 的类型一致,或者为 8-比特. threshold 阈值 max_value 使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值. threshold_type 阈值类型 (见讨论) 函数 cvThreshold 对单通道数组应用固定阈值操作。该函数的典型应用 是对灰度图像进行阈值操作得到二值图像。(cvCmpS 也可以达到此目的) 或者是去掉噪声,例如过滤很小或很大象素值的图像点。本函数支持的 对图像取阈值的方法由 threshold_type 确定: threshold_type=CV_THRESH_BINARY: dst(x,y) = max_value, if src(x,y)>threshold 0, otherwise threshold_type=CV_THRESH_BINARY_INV: dst(x,y) = 0, if src(x,y)>threshold max_value, otherwise threshold_type=CV_THRESH_TRUNC: dst(x,y) = threshold, if src(x,y)>threshold src(x,y), otherwise threshold_type=CV_THRESH_TOZERO: dst(x,y) = src(x,y), if (x,y)>threshold 0, otherwise threshold_type=CV_THRESH_TOZERO_INV: dst(x,y) = 0, if src(x,y)>threshold src(x,y), otherwise 下面是图形化的阈值描述: AdaptiveThreshold 自适应阈值方法 void cvAdaptiveThreshold( const CvArr* src, CvArr* dst, double max_value, int adaptive_method=CV_ADAPTIVE_THRESH_MEAN_C, int threshold_type=CV_THRESH_BINARY, int block_size=3, double param1=5 ); src 输入图像. dst 输出图像. max_value 使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值. adaptive_method 自适应阈值算法使用:CV_ADAPTIVE_THRESH_MEAN_C 或 CV_ADAPTIVE_THRESH_GAUSSIAN_C (见讨论). threshold_type 取阈值类型:必须是下者之一 • CV_THRESH_BINARY, • CV_THRESH_BINARY_INV block_size 用来计算阈值的象素邻域大小: 3, 5, 7, ... param1 与方法有关的参数。对方法 CV_ADAPTIVE_THRESH_MEAN_C 和 CV_ADAPTIVE_THRESH_GAUSSIAN_C, 它是一个从均值或加权均值提取的常数(见讨论), 尽 管它可以是负数。 函数 cvAdaptiveThreshold 将灰度图像变换到二值图像,采用下面公 式: threshold_type=CV_THRESH_BINARY: dst(x,y) = max_value, if src(x,y)>T(x,y) 0, otherwise threshold_type=CV_THRESH_BINARY_INV: dst(x,y) = 0, if src(x,y)>T(x,y) max_value, otherwise 其中 TI 是为每一个象素点单独计算的阈值 对方法 CV_ADAPTIVE_THRESH_MEAN_C,先求出块中的均值,再减掉 param1。 对方法 CV_ADAPTIVE_THRESH_GAUSSIAN_C ,先求出块中的加权和 (gaussian), 再减掉 param1。 金字塔及其应用 PyrDown 图像的下采样 void cvPyrDown( const CvArr* src, CvArr* dst, int filter=CV_GAUSSIAN_5x5 ); src 输入图像. dst 输出图像, 宽度和高度应是输入图像的一半 filter 卷积滤波器的类型,目前仅支持 CV_GAUSSIAN_5x5 函数 cvPyrDown 使用 Gaussian 金字塔分解对输入图像向下采样。首先 它对输入图像用指定滤波器进行卷积,然后通过拒绝偶数的行与列来下 采样图像。 PyrUp 图像的上采样 void cvPyrUp( const CvArr* src, CvArr* dst, int filter=CV_GAUSSIAN_5x5 ); src 输入图像. dst 输出图像, 宽度和高度应是输入图像的 2 倍 filter 卷积滤波器的类型,目前仅支持 CV_GAUSSIAN_5x5 函数 cvPyrUp 使用Gaussian 金字塔分解对输入图像向上采样。首先通 过在图像中插入 0 偶数行和偶数列,然后对得到的图像用指定的滤波器 进行高斯卷积,其中滤波器乘以 4 做差值。所以输出图像是输入图像的 4 倍大小。(hunnish: 原理不清楚,尚待探讨) PyrSegmentation 用金字塔实现图像分割 void cvPyrSegmentation( IplImage* src, IplImage* dst, CvMemStorage* storage, CvSeq** comp, int level, double threshold1, double threshold2 ); src 输入图像. dst 输出图像. storage Storage: 存储连通部件的序列结果 comp 分割部件的输出序列指针 components. level 建立金字塔的最大层数 threshold1 建立连接的错误阈值 threshold2 分割簇的错误阈值 函数 cvPyrSegmentation 实现了金字塔方法的图像分割。金字塔建立到 level 指定的最大层数。如果 p(c(a),c(b))=sizeof(CvChain) ,否则 >=sizeof(CvContour) . mode 提取模式. • CV_RETR_EXTERNAL - 只提取最外层的轮廓 • CV_RETR_LIST - 提取所有轮廓,并且放置在 list 中 • CV_RETR_CCOMP - 提取所有轮廓,并且将其组织为两层的 hierarchy: 顶层为连通域 的外围边界,次层为洞的内层边界。 • CV_RETR_TREE - 提取所有轮廓,并且重构嵌套轮廓的全部 hierarchy method 逼近方法 (对所有节点, 不包括使用内部逼近的 CV_RETR_RUNS). • CV_CHAIN_CODE - Freeman 链码的输出轮廓. 其它方法输出多边形(定点序列). • CV_CHAIN_APPROX_NONE - 将所有点由链码形式翻译为点序列形式 • CV_CHAIN_APPROX_SIMPLE - 压缩水平、垂直和对角分割,即函数只保留末端的象素 点; • CV_CHAIN_APPROX_TC89_L1, CV_CHAIN_APPROX_TC89_KCOS - 应用 Teh-Chin 链逼近算法. • CV_LINK_RUNS - 通过连接为 1 的水平碎片使用完全不同的轮廓提取算法。仅有 CV_RETR_LIST 提取模式可以在本方法中应用. offset 每一个轮廓点的偏移量. 当轮廓是从图像 ROI 中提取出来的时候,使用偏移量有用, 因为可以从整个图像上下文来对轮廓做分析. 函数 cvFindContours 从二值图像中提取轮廓,并且返回提取轮廓的数目。 指针 first_contour 的内容由函数填写。它包含第一个最外层轮廓的指针,如 果指针为 NULL,则没有检测到轮廓(比如图像是全黑的)。其它轮廓可以从 first_contour 利用 h_next 和 v_next 链接访问到。 在 cvDrawContours 的样 例显示如何使用轮廓来进行连通域的检测。轮廓也可以用来做形状分析和对 象识别 - 见CVPR2001 教程中的 squares 样例。该教程可以在 SourceForge 网站上找到。 StartFindContours 初始化轮廓的扫描过程 CvContourScanner cvStartFindContours( CvArr* image, CvMemStorage* storage, int header_size=sizeof(CvContour), int mode=CV_RETR_LIST, int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) ); image 输入的 8-比特、单通道二值图像 storage 提取到的轮廓容器 header_size 序列头的尺寸 >=sizeof(CvChain) 若 method=CV_CHAIN_CODE ,否则尺寸 >=sizeof(CvContour) . mode 提取模式,见 cvFindContours. method 逼近方法。它与 cvFindContours 里的定义一样,但是 CV_LINK_RUNS 不能使用。 offset ROI 偏移量,见 cvFindContours. 函数 cvStartFindContours 初始化并且返回轮廓扫描器的指针。扫描器在 cvFindNextContour 使用以提取其余的轮廓。 FindNextContour Finds next contour in the image CvSeq* cvFindNextContour( CvContourScanner scanner ); scanner 被函数 cvStartFindContours 初始化的轮廓扫描器. 函数 cvFindNextContour 确定和提取图像的下一个轮廓,并且返回它的指 针。若没有更多的轮廓,则函数返回 NULL. SubstituteContour 替换提取的轮廓 void cvSubstituteContour( CvContourScanner scanner, CvSeq* new_contour ); scanner 被函数 cvStartFindContours 初始化的轮廓扫描器 .. new_contour 替换的轮廓 函数 cvSubstituteContour 把用户自定义的轮廓替换前一次的函数 cvFindNextContour 调用所提取的轮廓,该轮廓以用户定义的模式存储在边 缘扫描状态之中。轮廓,根据提取状态,被插入到生成的结构,List,二层 hierarchy, 或 tree 中。如果参数 new_contour=NULL, 则提取的轮廓不被包 含入生成结构中,它的所有后代以后也不会被加入到接口中。 EndFindContours 结束扫描过程 CvSeq* cvEndFindContours( CvContourScanner* scanner ); scanner 轮廓扫描的指针. 函数 cvEndFindContours 结束扫描过程,并且返回最高层的第一个轮廓的指 针。 图像与轮廓矩 Moments 计算多边形和光栅形状的最高达三阶的所有矩 void cvMoments( const CvArr* arr, CvMoments* moments, int binary=0 ); arr 图像 (1-通道或 3-通道,有 ROI 设置) 或多边形(点的 CvSeq 或一族点的向量). moments 返回的矩状态接口的指针 binary (仅对图像) 如果标识为非零,则所有零象素点被当成零,其它的被看成 1. 函数 cvMoments 计算最高达三阶的空间和中心矩,并且将结果存在结构 moments 中。矩用来计算形状的重心,面积,主轴和其它的形状特征,如 7 Hu 不变量等。 GetSpatialMoment 从矩状态结构中提取空间矩 double cvGetSpatialMoment( CvMoments* moments, int x_order, int y_order ); moments 矩状态,由 cvMoments 计算 x_order 提取的 x 次矩, x_order >= 0. y_order 提取的 y 次矩, y_order >= 0 并且 x_order + y_order <= 3. 函数 cvGetSpatialMoment 提取空间矩,当图像矩被定义为: Mx_order,y_order=sumx,y(I(x,y)•xx_order•yy_order) 其中 I(x,y) 是象素点 (x, y) 的亮度值. GetCentralMoment 从矩状态结构中提取中心矩 double cvGetCentralMoment( CvMoments* moments, int x_order, int y_order ); moments 矩状态结构指针 x_order 提取的 x 阶矩, x_order >= 0. y_order 提取的 y 阶矩, y_order >= 0 且 x_order + y_order <= 3. 函数 cvGetCentralMoment 提取中心矩,其中图像矩的定义是: μx_order,y_order=sumx,y(I(x,y)•(x-xc)x_order•(y-yc)y_order), 其中 xc=M10/M00, yc=M01/M00 - 重心坐标 GetNormalizedCentralMoment 从矩状态结构中提取归一化的中心矩 double cvGetNormalizedCentralMoment( CvMoments* moments, int x_order, int y_order ); moments 矩状态结构指针 x_order 提取的 x 阶矩, x_order >= 0. y_order 提取的 y 阶矩, y_order >= 0 且 x_order + y_order <= 3. 函数 cvGetNormalizedCentralMoment 提取归一化中心矩: ηx_order,y_order= μx_order,y_order/M00 ((y_order+x_order)/2+1) GetHuMoments 计算 7 Hu 不变量 void cvGetHuMoments( CvMoments* moments, CvHuMoments* hu_moments ); moments 矩状态结构的指针 hu_moments Hu 矩结构的指针. 函数 cvGetHuMoments 计算 7 个 Hu 不变量,它们的定义是: h1=η20+η02 h2=(η20-η02)²+4η11² h3=(η30-3η12)²+ (3η21-η03)² h4=(η30+η12)²+ (η21+η03)² h5=(η30-3η12)(η30+η12)[(η30+η12)²-3(η21+η03)²]+(3η21-η03)(η21+η03)[3(η30+η12)²-(η21+η03)²] h6=(η20-η02)[(η30+η12)²- (η21+η03)²]+4η11(η30+η12)(η21+η03) h7=(3η21-η03)(η21+η03)[3(η30+η12)²-(η21+η03)²]-(η30-3η12)(η21+η03)[3(η30+η12)²-(η21+η03)²] 这些值被证明为对图像缩放、旋转和反射的不变量。对反射,第 7 个除外, 因为它的符合会因为反射而改变。 特殊图像变换 HoughLines 利用 Hough 变换在二值图像中找到直线 CvSeq* cvHoughLines2( CvArr* image, void* line_storage, int method, double rho, double theta, int threshold, double param1=0, double param2=0 ); image 输入 8-比特、单通道 (二值) 图像,其内容可能被函数所改变 line_storage 检测到的线段存储仓. 可以是内存存储仓 (此种情况下,一个线段序列在存储仓中被创 建,并且由函数返回),或者是包含线段参数的特殊类型(见下面)的具有单行/单列的 矩阵(CvMat*)。矩阵头为函数所修改,使得它的 cols/rows 将包含一组检测到的线段。 如果 line_storage 是矩阵,而实际线段的数目超过矩阵尺寸,那么最大可能数目的线 段被返回(线段没有按照长度、可信度或其它指标排序). method Hough 变换变量,是下面变量的其中之一: • CV_HOUGH_STANDARD - 传统或标准 Hough 变换. 每一个线段由两个浮点数 (ρ, θ) 表示,其中 ρ 是点与原点 (0,0) 之间的距离,θ 线段与 x-轴之间的夹角。因此, 矩阵类型必须是 CV_32FC2 type. • CV_HOUGH_PROBABILISTIC - 概率 Hough 变换(如果图像包含一些长的线性分割,则 效率更高). 它返回线段分割而不是整个线段。每个分割用起点和终点来表示, 所以矩阵(或创建的序列)类型是 CV_32SC4. • CV_HOUGH_MULTI_SCALE - 传统 Hough 变换的多尺度变种。线段的编码方式与 CV_HOUGH_STANDARD 的一致。 rho 与象素相关单位的距离精度 theta 弧度测量的角度精度 threshold 阈值参数。如果相应的累计值大于 threshold, 则函数返回的这个线段. param1 第一个方法相关的参数: • 对传统 Hough 变换,不使用(0). • 对概率 Hough 变换,它是最小线段长度. • 对多尺度 Hough 变换,它是距离精度 rho 的分母 (大致的距离精度是 rho 而精 确的应该是 rho / param1 ). param2 第二个方法相关参数: • 对传统 Hough 变换,不使用 (0). • 对概率 Hough 变换,这个参数表示在同一条直线上进行碎线段连接的最大间隔值 (gap), 即当同一条直线上的两条碎线段之间的间隔小于 param2 时,将其合二为 一。 • 对多尺度 Hough 变换,它是角度精度 theta 的分母 (大致的角度精度是 theta 而精确的角度应该是 theta / param2). 函数 cvHoughLines2 实现了用于线段检测的不同 Hough 变换方法. Example. 用 Hough transform 检测线段 /* This is a standalone program. Pass an image name as a first parameter of the program. Switch between standard and probabilistic Hough transform by changing "#if 1" to "#if 0" and back */ #include #include #include int main(int argc, char** argv) { IplImage* src; if( argc == 2 && (src=cvLoadImage(argv[1], 0))!= 0) { IplImage* dst = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* color_dst = cvCreateImage( cvGetSize(src), 8, 3 ); CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* lines = 0; int i; cvCanny( src, dst, 50, 200, 3 ); cvCvtColor( dst, color_dst, CV_GRAY2BGR ); #if 1 lines = cvHoughLines2( dst, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 150, 0, 0 ); for( i = 0; i < lines->total; i++ ) { float* line = (float*)cvGetSeqElem(lines,i); float rho = line[0]; float theta = line[1]; CvPoint pt1, pt2; double a = cos(theta), b = sin(theta); if( fabs(a) < 0.001 ) { pt1.x = pt2.x = cvRound(rho); pt1.y = 0; pt2.y = color_dst->height; } else if( fabs(b) < 0.001 ) { pt1.y = pt2.y = cvRound(rho); pt1.x = 0; pt2.x = color_dst->width; } else { pt1.x = 0; pt1.y = cvRound(rho/b); pt2.x = cvRound(rho/a); pt2.y = 0; } cvLine( color_dst, pt1, pt2, CV_RGB(255,0,0), 3, 8 ); } #else lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 80, 30, 10 ); for( i = 0; i < lines->total; i++ ) { CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i); cvLine( color_dst, line[0], line[1], CV_RGB(255,0,0), 3, 8 ); } #endif cvNamedWindow( "Source", 1 ); cvShowImage( "Source", src ); cvNamedWindow( "Hough", 1 ); cvShowImage( "Hough", color_dst ); cvWaitKey(0); } } 这是函数所用的样本图像: 下面是程序的输出,采用概率 Hough transform ("#if 0" 的部分): DistTransform 计算输入图像的所有非零元素对其最近零元素的距离 void cvDistTransform( const CvArr* src, CvArr* dst, int distance_type=CV_DIST_L2, int mask_size=3, const float* mask=NULL ); src 输入 8-比特、单通道 (二值) 图像. dst 含计算出的距离的输出图像(32-比特、浮点数、单通道). distance_type 距离类型; 可以是 CV_DIST_L1, CV_DIST_L2, CV_DIST_C 或 CV_DIST_USER. mask_size 距离变换掩模的大小,可以是 3 或 5. 对 CV_DIST_L1 或 CV_DIST_C 的情况,参数值被 强制设定为 3, 因为 3×3 mask 给出 5×5 mask 一样的结果,而且速度还更快。 mask 用户自定义距离距离情况下的 mask。 在 3×3 mask 下它由两个数(水平/垂直位量,对 角线位移量)组成, 5×5 mask 下由三个数组成(水平/垂直位移量,对角位移和 国际象 棋里的马步(马走日)) 函数 cvDistTransform 二值图像每一个象素点到它最邻近零象素点的距离。 对零象素,函数设置 0 距离,对其它象素,它寻找由基本位移(水平、垂直、 对角线或knight's move,最后一项对 5×5 mask 有用)构成的最短路径。 全 部的距离被认为是基本距离的和。由于距离函数是对称的,所有水平和垂直 位移具有同样的代价 (表示为 a ), 所有的对角位移具有同样的代价 (表示 为 b), 所有的 knight's 移动具有同样的代价 (表示为 c). 对类型 CV_DIST_C 和 CV_DIST_L1,距离的计算是精确的,而类型 CV_DIST_L2 (欧式距离) 距离的计算有某些相对误差 (5×5 mask 给出更精确的结果), OpenCV 使用 [Borgefors86] 推荐的值: CV_DIST_C (3×3): a=1, b=1 CV_DIST_L1 (3×3): a=1, b=2 CV_DIST_L2 (3×3): a=0.955, b=1.3693 CV_DIST_L2 (5×5): a=1, b=1.4, c=2.1969 下面用户自定义距离的的距离域示例 (黑点 (0) 在白色方块中间): 用户自定义 3×3 mask (a=1, b=1.5) 4.5 4 3.5 3 3.5 4 4.5 4 3 2.5 2 2.5 3 4 3.5 2.5 1.5 1 1.5 2.5 3.5 3 2 1 0 1 2 3 3.5 2.5 1.5 1 1.5 2.5 3.5 4 3 2.5 2 2.5 3 4 4.5 4 3.5 3 3.5 4 4.5 用户自定义 5×5 mask (a=1, b=1.5, c=2) 4.5 3.5 3 3 3 3.5 4.5 3.5 3 2 2 2 3 3.5 3 2 1.5 1 1.5 2 3 3 2 1 0 1 2 3 3 2 1.5 1 1.5 2 3 3.5 3 2 2 2 3 3.5 4 3.5 3 3 3 3.5 4 典型的使用快速粗略距离估计 CV_DIST_L2, 3×3 mask , 如果要更精确的距 离估计,使用 CV_DIST_L2, 5×5 mask。 直方图 CvHistogram 多维直方图 typedef struct CvHistogram { int header_size; /* 头尺寸 */ CvHistType type; /* 直方图类型 */ int flags; /* 直方图标识 */ int c_dims; /* 直方图维数 */ int dims[CV_HIST_MAX_DIM]; /* 每一维的尺寸 */ int mdims[CV_HIST_MAX_DIM]; /* 快速访问元素的系数 */ /* &m[a,b,c] = m + a*mdims[0] + b*mdims[1] + c*mdims[2] */ float* thresh[CV_HIST_MAX_DIM]; /* 每一维的直方块边界数组 */ float* array; /* 所有的直方图数据,扩展为单行 */ struct CvNode* root; /* 存储直方块的平衡树的根结点 */ CvSet* set; /* 内存存储仓的指针 (对平衡树而言) */ int* chdims[CV_HIST_MAX_DIM]; /* 快速计算的缓存 */ } CvHistogram; CreateHist 创建直方图 CvHistogram* cvCreateHist( int dims, int* sizes, int type, float** ranges=NULL, int uniform=1 ); dims 直方图维数的数目 sizes 直方图维数尺寸的数组 type 直方图的表示格式: CV_HIST_ARRAY 意味着直方图数据表示为多维密集数组 CvMatND; CV_HIST_TREE 意味着直方图数据表示为多维稀疏数组 CvSparseMat. ranges 图中方块范围的数组. 它的内容取决于参数 uniform 的值。这个范围的用处是确定何时 计算直方图或决定反向映射(backprojected ),每个方块对应于输入图像的哪个/哪组值。 uniform 归一化标识。 如果不为 0,则ranges[i](0<=ibins, (idx0) ) #define cvQueryHistValue_2D( hist, idx0, idx1 ) \ cvGetReal2D( (hist)->bins, (idx0), (idx1) ) #define cvQueryHistValue_3D( hist, idx0, idx1, idx2 ) \ cvGetReal3D( (hist)->bins, (idx0), (idx1), (idx2) ) #define cvQueryHistValue_nD( hist, idx ) \ cvGetRealND( (hist)->bins, (idx) ) hist 直方图 idx0, idx1, idx2, idx3 直方块的下标索引 idx 下标数组 宏 cvQueryHistValue_*D 返回 1D, 2D, 3D 或 N-D 直方图的指定直方块的 值。对稀疏直方图,如果方块在直方图中不存在,函数返回 0, 而且不创建 新的直方块。 GetHistValue_1D 返回直方块的指针 #define cvGetHistValue_1D( hist, idx0 ) \ ((float*)(cvPtr1D( (hist)->bins, (idx0), 0 )) #define cvGetHistValue_2D( hist, idx0, idx1 ) \ ((float*)(cvPtr2D( (hist)->bins, (idx0), (idx1), 0 )) #define cvGetHistValue_3D( hist, idx0, idx1, idx2 ) \ ((float*)(cvPtr3D( (hist)->bins, (idx0), (idx1), (idx2), 0 )) #define cvGetHistValue_nD( hist, idx ) \ ((float*)(cvPtrND( (hist)->bins, (idx), 0 )) hist 直方图. idx0, idx1, idx2, idx3 直方块的下标索引. idx 下标数组 宏 cvGetHistValue_*D 返回 1D, 2D, 3D 或 N-D 直方图的指定直方块的指 针。对稀疏直方图,函数创建一个新的直方块,且设置其为 0,除非它已经 存在。 GetMinMaxHistValue 发现最大和最小直方块 void cvGetMinMaxHistValue( const CvHistogram* hist, float* min_value, float* max_value, int* min_idx=NULL, int* max_idx=NULL ); hist 直方图 min_value 直方图最小值的指针 max_value 直方图最大值的指针 min_idx 数组中最小坐标的指针 max_idx 数组中最大坐标的指针 函数 cvGetMinMaxHistValue 发现最大和最小直方块以及它们的位置。任何 输出变量都是可选的。在具有同样值几个极值中,返回具有最小下标索引(以 字母排列顺序定)的那一个。 NormalizeHist 归一化直方图 void cvNormalizeHist( CvHistogram* hist, double factor ); hist 直方图的指针. factor 归一化因子 函数 cvNormalizeHist 通过缩放来归一化直方块,使得所有块的和等于 factor. ThreshHist 对直方图取阈值 void cvThreshHist( CvHistogram* hist, double threshold ); hist 直方图的指针. threshold 阈值大小 函数 cvThreshHist 清除那些小于指定阈值得直方块 CompareHist 比较两个稠密直方图 double cvCompareHist( const CvHistogram* hist1, const CvHistogram* hist2, int method ); hist1 第一个稠密直方图 hist2 第二个稠密直方图 method 比较方法,采用其中之一: • CV_COMP_CORREL • CV_COMP_CHISQR • CV_COMP_INTERSECT 函数 cvCompareHist 采用下面指定的方法比较两个稠密直方图(H1 表示第一 个, H2 - 第二个): 相关 (method=CV_COMP_CORREL): d(H1,H2)=sumI(H'1(I)•H'2(I))/sqrt(sumI[H'1(I)2]•sumI[H'2(I)2]) 其中 H'k(I)=Hk(I)-1/N•sumJHk(J) (N=number of histogram bins) Chi-square(method=CV_COMP_CHISQR): d(H1,H2)=sumI[(H1(I)-H2(I))/(H1(I)+H2(I))] 交叉 (method=CV_COMP_INTERSECT): d(H1,H2)=sumImax(H1(I),H2(I)) 函数返回 d(H1,H2) 的值。 为了比较稀疏直方图或更一般的加权稀疏点集(译者注:直方图匹配是图像检 索中的常用方法),考虑使用函数 cvCalcEMD 。 CopyHist 拷贝直方图 void cvCopyHist( const CvHistogram* src, CvHistogram** dst ); src 输入的直方图 dst 输出的直方图指针 函数 cvCopyHist 对直方图作拷贝。如果第二个直方图指针 *dst 是 NULL, 则创建一个与 src 同样大小的直方图。否则,两个直方图必须大小和类型一 致。然后函数将输入的直方块的值复制到输出的直方图中,并且设置取值范 围与 src 的一致。 CalcHist 计算图像 image(s) 的直方图 void cvCalcHist( IplImage** image, CvHistogram* hist, int accumulate=0, const CvArr* mask=NULL ); image 输入图像 s (虽然也可以使用 CvMat** ). hist 直方图指针 accumulate 累计标识。如果设置,则直方图在开始时不被清零。这个特征保证可以为多个图像计算 一个单独的直方图,或者在线更新直方图。 mask 操作 mask, 确定输入图像的哪个象素被计数 函数 cvCalcHist 计算单通道或多通道图像的直方图。 用来增加直方块的数 组元素可从相应输入图像的同样位置提取。 Sample. 计算和显示彩色图像的 2D 色调-饱和度图像 #include #include int main( int argc, char** argv ) { IplImage* src; if( argc == 2 && (src=cvLoadImage(argv[1], 1))!= 0) { IplImage* h_plane = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* s_plane = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* v_plane = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* planes[] = { h_plane, s_plane }; IplImage* hsv = cvCreateImage( cvGetSize(src), 8, 3 ); int h_bins = 30, s_bins = 32; int hist_size[] = {h_bins, s_bins}; float h_ranges[] = { 0, 180 }; /* hue varies from 0 (~0°red) to 180 (~360°red again) */ float s_ranges[] = { 0, 255 }; /* saturation varies from 0 (black-gray-white) to 255 (pure spectrum color) */ float* ranges[] = { h_ranges, s_ranges }; int scale = 10; IplImage* hist_img = cvCreateImage( cvSize(h_bins*scale,s_bins*scale), 8, 3 ); CvHistogram* hist; float max_value = 0; int h, s; cvCvtColor( src, hsv, CV_BGR2HSV ); cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 ); hist = cvCreateHist( 2, hist_size, CV_HIST_ARRAY, ranges, 1 ); cvCalcHist( planes, hist, 0, 0 ); cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 ); cvZero( hist_img ); for( h = 0; h < h_bins; h++ ) { for( s = 0; s < s_bins; s++ ) { float bin_val = cvQueryHistValue_2D( hist, h, s ); int intensity = cvRound(bin_val*255/max_value); cvRectangle( hist_img, cvPoint( h*scale, s*scale ), cvPoint( (h+1)*scale - 1, (s+1)*scale - 1), CV_RGB(intensity,intensity,intensity), /* graw a grayscale histogram. if you have idea how to do it nicer let us know */ CV_FILLED ); } } cvNamedWindow( "Source", 1 ); cvShowImage( "Source", src ); cvNamedWindow( "H-S Histogram", 1 ); cvShowImage( "H-S Histogram", hist_img ); cvWaitKey(0); } } CalcBackProject 计算反向投影 void cvCalcBackProject( IplImage** image, CvArr* back_project, const CvHistogram* hist ); image 输入图像 (也可以传递 CvMat** ). back_project 反向投影图像,与输入图像具有同样类型. hist 直方图 函数 cvCalcBackProject 直方图的反向投影. 对于所有输入的单通道图像 同一位置的象素数组,该函数根据相应的象素数组(RGB),放置其对应的直方 块的值到输出图像中。用统计学术语,输出图像象素点的值是观测数组在某 个分布(直方图)下的的概率。 例如,为了发现图像中的红色目标,可以这 么做: 1. 对红色物体计算色调直方图,假设图像仅仅包含该物体。则直方图有可能有极值, 对应着红颜色。 2. 对将要搜索目标的输入图像,使用直方图计算其色调平面的反向投影,然后对图 像做阈值操作。 3. 在产生的图像中发现连通部分,然后使用某种附加准则选择正确的部分,比如最 大的连同部分。 这是 Camshift 彩色目标跟踪器中的一个逼进算法,除了第三步,CAMSHIFT 算法使用 了上一次目标位置来定位反向投影中的目标。 CalcBackProjectPatch 用直方图比较来定位图像中的模板 void cvCalcBackProjectPatch( IplImage** image, CvArr* dst, CvSize patch_size, CvHistogram* hist, int method, float factor ); image 输入图像 (可以传递 CvMat** ) dst 输出图像. patch_size 扫描输入图像的补丁尺寸 hist 直方图 method 比较方法,传递给 cvCompareHist (见该函数的描述). factor 直方图的归一化因子,将影响输出图像的归一化缩放。如果为 1,则不定。 函数 cvCalcBackProjectPatch 通过输入图像补丁的直方图和给定直方图的 比较,来计算反向投影。提取图像在 ROI 中每一个位置的某种测量结果产生 了数组 image. 这些结果可以是色调, x 差分, y 差分, Laplacian 滤波器, 有方向 Gabor 滤波器等中 的一个或多个。每种测量输出都被划归为它自己 的单独图像。 image 图像数组是这些测量图像的集合。一个多维直方图 hist 从这些图像数组中被采样创建。最后直方图被归一化。直方图 hist 的维数通 常很大等于图像数组 image 的元素个数。 在选择的 ROI 中,每一个新的图像被测量并且转换为一个图像数组。在以锚 点为“补丁”中心的图像 image 区域中计算直方图 (如下图所示)。用参数 norm_factor 来归一化直方图,使得它可以与 hist 互相比较。计算出的直方图 与直方图模型互相比较, (hist 使用函数 cvCompareHist ,比较方法是 method=method). 输出结果被放置到概率图像 dst 补丁锚点的对应位置上。这 个过程随着补丁滑过整个 ROI 而重复进行。迭代直方图的更新可以通过在原 直方图中减除“补丁”已覆盖的尾象素点或者加上新覆盖的象素点来实现, 这种更新方式可以节省大量的操作,尽管目前在函数体中还没有实现。 Back Project Calculation by Patches CalcProbDensity 两个直方图相除 void cvCalcProbDensity( const CvHistogram* hist1, const CvHistogram* hist2, CvHistogram* dst_hist, double scale=255 ); hist1 第一个直方图(分子). hist2 第二个直方图 dst_hist 输出的直方图 scale 输出直方图的尺度因子 函数 cvCalcProbDensity 从两个直方图中计算目标概率密度: dist_hist(I)=0 if hist1(I)==0 scale if hist1(I)!=0 && hist2(I)>hist1(I) hist2(I)*scale/hist1(I) if hist1(I)!=0 && hist2(I)<=hist1(I) 所以输出的直方块小于尺度因子。 匹配 MatchTemplate 比较模板和重叠的图像区域 void cvMatchTemplate( const CvArr* image, const CvArr* templ, CvArr* result, int method ); image 欲搜索的图像。它应该是单通道、8-比特或 32-比特 浮点数图像 templ 搜索模板,不能大于输入图像,且与输入图像具有一样的数据类型 result 比较结果的映射图像。单通道、32-比特浮点数. 如果图像是 W×H 而 templ 是 w×h , 则 result 一定是 (W-w+1)×(H-h+1). method 指定匹配方法: 函数 cvMatchTemplate 与函数 cvCalcBackProjectPatch 类似。它滑动过整 个图像 image, 用指定方法比较 templ 与图像尺寸为 w×h 的重叠区域,并且 将比较结果存到 result 中。 下面是不同的比较方法,可以使用其中的一种 (I 表示图像,T - 模板, R - 结果. 模板与图像重叠区域 x'=0..w-1, y'=0..h-1 之间求和): method=CV_TM_SQDIFF: R(x,y)=sumx',y'[T(x',y')-I(x+x',y+y')]2 method=CV_TM_SQDIFF_NORMED: R(x,y)=sumx',y'[T(x',y')-I(x+x',y+y')]2/sqrt[sumx',y'T(x',y')2•sumx',y'I(x+x',y+y')2] method=CV_TM_CCORR: R(x,y)=sumx',y'[T(x',y')•I(x+x',y+y')] method=CV_TM_CCORR_NORMED: R(x,y)=sumx',y'[T(x',y')•I(x+x',y+y')]/sqrt[sumx',y'T(x',y')2•sumx',y'I(x+x',y+y')2] method=CV_TM_CCOEFF: R(x,y)=sumx',y'[T'(x',y')•I'(x+x',y+y')], where T'(x',y')=T(x',y') - 1/(w•h)•sumx",y"T(x",y") (mean template brightness=>0) I'(x+x',y+y')=I(x+x',y+y') - 1/(w•h)•sumx",y"I(x+x",y+y") (mean patch brightness=>0) method=CV_TM_CCOEFF_NORMED: R(x,y)=sumx',y'[T'(x',y')•I'(x+x',y+y')]/sqrt[sumx',y'T'(x',y')2•sumx',y'I'(x+x',y+y')2 ] 函数完成比较后,通过使用 cvMinMaxLoc找全局最小值CV_TM_SQDIFF*) 或者最大值 (CV_TM_CCORR* and CV_TM_CCOEFF*)。 MatchShapes 比较两个形状 double cvMatchShapes( const void* object1, const void* object2, int method, double parameter=0 ); object1 第一个轮廓或灰度图像 object2 第二个轮廓或灰度图像 method 比较方法,其中之一 CV_CONTOUR_MATCH_I1, CV_CONTOURS_MATCH_I2 or CV_CONTOURS_MATCH_I3. parameter 比较方法的参数 (目前不用). 函数 cvMatchShapes 比较两个形状。 三个实现方法全部使用 Hu 矩 (见 cvGetHuMoments) (A ~ object1, B - object2): method=CV_CONTOUR_MATCH_I1: I1(A,B)=sumi=1..7abs(1/mA i - 1/mB i) method=CV_CONTOUR_MATCH_I2: I2(A,B)=sumi=1..7abs(mA i - mB i) method=CV_CONTOUR_MATCH_I3: I3(A,B)=sumi=1..7abs(mA i - mB i)/abs(mA i) 其中 mA i=sign(hA i)•log(hA i), mB i=sign(hB i)•log(hB i), hA i, hB i - A 和 B 的 Hu 矩. CalcEMD2 两个加权点集之间计算最小工作距离 float cvCalcEMD2( const CvArr* signature1, const CvArr* signature2, int distance_type, CvDistanceFunction distance_func=NULL, const CvArr* cost_matrix=NULL, CvArr* flow=NULL, float* lower_bound=NULL, void* userdata=NULL ); typedef float (*CvDistanceFunction)(const float* f1, const float* f2, void* userdata); signature1 第一个签名,大小为 size1×(dims+1) 的浮点数矩阵,每一行依次存储点的权重和点的 坐标。矩阵允许只有一列(即仅有权重),如果使用用户自定义的代价矩阵。 signature2 第二个签名,与 signature1 的格式一样 size2×(dims+1),尽管行数可以不同(列数要相 同)。当一个额外的虚拟点加入 signature1 或 signature2 中的时候,权重也可不同。 distance_type 使用的准则, CV_DIST_L1, CV_DIST_L2, 和 CV_DIST_C 分别为标准的准则。 CV_DIST_USER 意味着使用用户自定义函数 distance_func 或预先计算好的代价矩阵 cost_matrix 。 distance_func 用户自定义的距离函数。用两个点的坐标计算两点之间的距离。 cost_matrix 自定义大小为 size1×size2 的代价矩阵。 cost_matrix 和 distance_func 两者至少有 一个必须为 NULL. 而且,如果使用代价函数,下边界无法计算,因为它需要准则函数。 flow 产生的大小为 size1×size2 流矩阵(flow matrix): flowij 是从 signature1 的第 i 个 点到 signature2 的第 j 个点的流(flow)。 lower_bound 可选的输入/输出参数:两个签名之间的距离下边界,是两个质心之间的距离。如果使用 自定义代价矩阵,点集的所有权重不等,或者有签名只包含权重(即该签名矩阵只有单 独一列),则下边界也许不会计算。用户必须初始化 *lower_bound. 如果质心之间的距 离大于获等于 *lower_bound (这意味着签名之间足够远), 函数则不计算 EMD. 任何 情况下,函数返回时 *lower_bound 都被设置为计算出来的质心距离。因此如果用户想 同时计算质心距离和 T EMD, *lower_bound 应该被设置为 0. userdata 传输到自定义距离函数的可选数据指针 函数 cvCalcEMD2 计算两个加权点集之间的移动距离或距离下界。在 [RubnerSept98] 中所描述的其中一个应用就是图像提取得多维直方图比较。 EMD 是一个使用某种单纯形算法(simplex algorithm)来解决的交通问题。 其计算复杂度在最坏情况下是指数形式的,但是平均而言它的速度相当快。 对实的准则,下边界的计算可以更快(使用线性时间算法),且它可用来粗 略确定两个点集是否足够远以至无法联系到同一个目标上。 结构分析 轮廓处理函数 ApproxChains 用多边形曲线逼近 Freeman 链 CvSeq* cvApproxChains( CvSeq* src_seq, CvMemStorage* storage, int method=CV_CHAIN_APPROX_SIMPLE, double parameter=0, int minimal_perimeter=0, int recursive=0 ); src_seq 涉及其它链的链指针 storage 存储多边形线段位置的缓存 method 逼近方法 (见函数 cvFindContours 的描述). parameter 方法参数(现在不用). minimal_perimeter 仅逼近周长大于 minimal_perimeter 轮廓。其它的链从结果中除去。 recursive 如果非 0, 函数从 src_seq 中利用 h_next 和 v_next links 连接逼近所有可访问的 链。如果为 0, 则仅逼近单链。 这是一个单独的逼近程序。 对同样的逼近标识,函数 cvApproxChains 与 cvFindContours 的工作方式一模一样。它返回发现的第一个轮廓的指针。其 它的逼近模块,可以用返回结构中的 v_next 和 v_next 域来访问 StartReadChainPoints 初始化链读取 void cvStartReadChainPoints( CvChain* chain, CvChainPtReader* reader ); chain 链的指针 reader 链的读取状态 函数 cvStartReadChainPoints 初始化一个特殊的读取器 (参考 Dynamic Data Structures 以获得关于集合与序列的更多内容). ReadChainPoint 得到下一个链的点 CvPoint cvReadChainPoint( CvChainPtReader* reader ); reader 链的读取状态 函数 cvReadChainPoint 返回当前链的点,并且更新读取位置。 ApproxPoly 用指定精度逼近多边形曲线 CvSeq* cvApproxPoly( const void* src_seq, int header_size, CvMemStorage* storage, int method, double parameter, int parameter2=0 ); src_seq 点集数组序列 header_size 逼近曲线的头尺寸 storage 逼近轮廓的容器。如果为 NULL, 则使用输入的序列 method 逼近方法。目前仅支持 CV_POLY_APPROX_DP , 对应 Douglas-Peucker 算法. parameter 方法相关参数。对 CV_POLY_APPROX_DP 它是指定的逼近精度 parameter2 如果 src_seq 是序列,它表示要么逼近单个序列,要么在 src_seq 的同一个或低级层 次上逼近所有序列 (参考 cvFindContours 中对轮廓继承结构的描述). 如果 src_seq 是 点集的数组 (CvMat*) , 参数指定曲线是闭合 (parameter2!=0) 还是非闭合 (parameter2=0). 函数 cvApproxPoly 逼近一个或多个曲线,并返回逼近结果。对多个曲线的 逼近,生成的树将与输入的具有同样的结构。(1:1 的对应关系). BoundingRect 计算点集的最外面(up-right)矩形边界 CvRect cvBoundingRect( CvArr* points, int update=0 ); points 二维点集,点的序列或向量 (CvMat) update 更新标识。下面是轮廓类型和标识的一些可能组合: • update=0, contour ~ CvContour*: 不计算矩形边界,但直接由轮廓头的 rect 域得 到。 • update=1, contour ~ CvContour*: 计算矩形边界,而且将结果写入到轮廓头的 rect 域中 header. • update=0, contour ~ CvSeq* or CvMat*: 计算并返回边界矩形 • update=1, contour ~ CvSeq* or CvMat*: 产生运行错误 (runtime error is raised) 函数 cvBoundingRect 返回二维点集的最外面 (up-right)矩形边界。 ContourArea 计算整个轮廓或部分轮廓的面积 double cvContourArea( const CvArr* contour, CvSlice slice=CV_WHOLE_SEQ ); contour 轮廓 (定点的序列或数组). slice 感兴趣轮廓部分的起始点,缺省是计算整个轮廓的面积。 函数 cvContourArea 计算整个轮廓或部分轮廓的面积。 对后面的情况,面 积表示轮廓部分和起始点连线构成的封闭部分的面积。如下图所示: NOTE: 轮廓的方向影响面积的符号。因此函数也许会返回负的结果。应用函 数 fabs() 得到面积的绝对值。 ArcLength 计算轮廓周长或曲线长度 double cvArcLength( const void* curve, CvSlice slice=CV_WHOLE_SEQ, int is_closed=-1 ); curve 曲线点集序列或数组 slice 曲线的起始点,缺省是计算整个曲线的长度 is_closed 表示曲线是否闭合,有三种情况: • is_closed=0 - 假设曲线不闭合 • is_closed>0 - 假设曲线闭合 • is_closed<0 - 若曲线是序列,检查 ((CvSeq*)curve)->flags 中的标识 CV_SEQ_FLAG_CLOSED 来确定曲线是否闭合。否则 (曲线由点集的数组 (CvMat*) 表示) 假设曲线不闭合。 函数 cvArcLength 通过依次计算序列点之间的线段长度,并求和来得到曲线 的长度。 CreateContourTree 创建轮廓的继承表示形式 CvContourTree* cvCreateContourTree( const CvSeq* contour, CvMemStorage* storage, double threshold ); contour 输入的轮廓 storage 输出树的容器 threshold 逼近精度 函数 cvCreateContourTree 为输入轮廓 contour 创建一个二叉树,并返回 树根的指针。如果参数 threshold 小于或等于 0 ,则函数创建一个完整的二 叉树。如果 threshold 大于 0 , 函数用 threshold 指定的精度创建二叉树: 如果基线的截断区域顶点小于threshold,该数就停止生长并作为函数的最终 结果返回。 ContourFromContourTree 由树恢复轮廓 CvSeq* cvContourFromContourTree( const CvContourTree* tree, CvMemStorage* storage, CvTermCriteria criteria ); tree 轮廓树 storage 重构的轮廓容器 criteria 停止重构的准则 函数 cvContourFromContourTree 从二叉树恢复轮廓。参数 criteria 决定了 重构的精度和使用树的数目及层次。所以它可建立逼近的轮廓。 函数返回重 构的轮廓。 MatchContourTrees 用树的形式比较两个轮廓 double cvMatchContourTrees( const CvContourTree* tree1, const CvContourTree* tree2, int method, double threshold ); tree1 第一个轮廓树 tree2 第二个轮廓树 method 相似度。仅支持 CV_CONTOUR_TREES_MATCH_I1 。 threshold 相似度阈值 函数 cvMatchContourTrees 计算两个轮廓树的匹配值。从树根开始通过逐层 比较来计算相似度。如果某层的相似度小于 threshold, 则中断比较过程, 且返回当前的差值。 计算几何 MaxRect 对两个给定矩形,寻找矩形边界 CvRect cvMaxRect( const CvRect* rect1, const CvRect* rect2 ); rect1 第一个矩形 rect2 第二个矩形 函数 cvMaxRect 寻找包含两个输入矩形的具有最小面积的矩形边界。 CvBox2D 旋转的二维盒子 typedef struct CvBox2D { CvPoint2D32f center; /* 盒子的中心 */ CvSize2D32f size; /* 盒子的长和宽 */ float angle; /* 水平轴与第一个边的夹角,用弧度表示*/ } CvBox2D; BoxPoints 寻找盒子的顶点 void cvBoxPoints( CvBox2D box, CvPoint2D32f pt[4] ); box 盒子 pt 顶点数组 函数 cvBoxPoints 计算输入的二维盒子的定点。下面是函数代码: void cvBoxPoints( CvBox2D box, CvPoint2D32f pt[4] ) { float a = (float)cos(box.angle)*0.5f; float b = (float)sin(box.angle)*0.5f; pt[0].x = box.center.x - a*box.size.height - b*box.size.width; pt[0].y = box.center.y + b*box.size.height - a*box.size.width; pt[1].x = box.center.x + a*box.size.height - b*box.size.width; pt[1].y = box.center.y - b*box.size.height - a*box.size.width; pt[2].x = 2*box.center.x - pt[0].x; pt[2].y = 2*box.center.y - pt[0].y; pt[3].x = 2*box.center.x - pt[1].x; pt[3].y = 2*box.center.y - pt[1].y; } FitEllipse 二维点集的椭圆拟合 CvBox2D cvFitEllipse2( const CvArr* points ); points 点集的序列或数组 函数 cvFitEllipse 对给定的一组二维点集作椭圆的最佳拟合(最小二乘意 义上的)。返回的结构与 cvEllipse 中的意义类似,除了 size 表示椭圆轴的 整个长度,而不是一半长度。 FitLine 2D 或 3D 点集的直线拟合 void cvFitLine( const CvArr* points, int dist_type, double param, double reps, double aeps, float* line ); points 2D 或 3D 点集,32-比特整数或浮点数坐标 dist_type 拟合的距离类型 (见讨论). param 对某些距离的数字参数,如果是 0, 则选择某些最优值 reps, aeps 半径 (坐标原点到直线的距离) 和角度的精度,一般设为 0.01。 line 输出的直线参数。2D 拟合情况下,它是包含 4 个浮点数的数组 (vx, vy, x0, y0),其 中 (vx, vy) 是线的单位向量而 (x0, y0) 是线上的某个点. 对 3D 拟合,它是包含 6 个浮点数的数组 (vx, vy, vz, x0, y0, z0), 其中 (vx, vy, vz) 是线的单位向量,而 (x0, y0, z0) 是线上某点。 函数 cvFitLine 通过求 sumiρ(ri) 的最小值方法,用 2D 或 3D 点集拟合 直线,其中 ri 是第 i 个点到直线的距离, ρ(r) 是下面的距离函数之一: dist_type=CV_DIST_L2 (L2): ρ(r)=r2/2 (最简单和最快的最小二乘法) dist_type=CV_DIST_L1 (L1): ρ(r)=r dist_type=CV_DIST_L12 (L1-L2): ρ(r)=2•[sqrt(1+r2/2) - 1] dist_type=CV_DIST_FAIR (Fair): ρ(r)=C2•[r/C - log(1 + r/C)], C=1.3998 dist_type=CV_DIST_WELSCH (Welsch): ρ(r)=C2/2•[1 - exp(-(r/C)2)], C=2.9846 dist_type=CV_DIST_HUBER (Huber): ρ(r)= r2/2, if r < C C•(r-C/2), otherwise; C=1.345 ConvexHull2 发现点集的凸外形 CvSeq* cvConvexHull2( const CvArr* input, void* hull_storage=NULL, int orientation=CV_CLOCKWISE, int return_points=0 ); points 2D 点集的序列或数组,32-比特整数或浮点数坐标 hull_storage 输出的数组(CvMat*) 或内存缓存 (CvMemStorage*),用以存储凸外形。 如果是数组, 则它应该是一维的,而且与输入的数组/序列具有同样数目的元素。输出时修改头使得数 组裁减到外形的尺寸。输出时,通过修改头结构将数组裁减到凸外形的尺寸。 orientation 凸外形的旋转方向: 逆时针或顺时针 (CV_CLOCKWISE or CV_COUNTER_CLOCKWISE) return_points 如果非零,点集将以外形 (hull) 存储,而不是 hull_storage 为数组情况下的顶点形式 (indices) 以及 hull_storag 为内存存储模式下的点集形式(points)。 函数 cvConvexHull2 使用 Sklansky 算法计算 2D 点集的凸外形。如果 hull_storage 是内存存储仓, 函数根据 return_points 的值,创建一个包含 外形的点集或指向这些点的指针的序列。 例子. 由点集序列或数组创建凸外形 #include "cv.h" #include "highgui.h" #include #define ARRAY 0 /* switch between array/sequence method by replacing 0<=>1 */ void main( int argc, char** argv ) { IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 ); cvNamedWindow( "hull", 1 ); #if !ARRAY CvMemStorage* storage = cvCreateMemStorage(); #endif for(;;) { int i, count = rand()%100 + 1, hullcount; CvPoint pt0; #if !ARRAY CvSeq* ptseq = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage ); CvSeq* hull; for( i = 0; i < count; i++ ) { pt0.x = rand() % (img->width/2) + img->width/4; pt0.y = rand() % (img->height/2) + img->height/4; cvSeqPush( ptseq, &pt0 ); } hull = cvConvexHull2( ptseq, 0, CV_CLOCKWISE, 0 ); hullcount = hull->total; #else CvPoint* points = (CvPoint*)malloc( count * sizeof(points[0])); int* hull = (int*)malloc( count * sizeof(hull[0])); CvMat point_mat = cvMat( 1, count, CV_32SC2, points ); CvMat hull_mat = cvMat( 1, count, CV_32SC1, hull ); for( i = 0; i < count; i++ ) { pt0.x = rand() % (img->width/2) + img->width/4; pt0.y = rand() % (img->height/2) + img->height/4; points[i] = pt0; } cvConvexHull2( &point_mat, &hull_mat, CV_CLOCKWISE, 0 ); hullcount = hull_mat.cols; #endif cvZero( img ); for( i = 0; i < count; i++ ) { #if !ARRAY pt0 = *CV_GET_SEQ_ELEM( CvPoint, ptseq, i ); #else pt0 = points[i]; #endif cvCircle( img, pt0, 2, CV_RGB( 255, 0, 0 ), CV_FILLED ); } #if !ARRAY pt0 = **CV_GET_SEQ_ELEM( CvPoint*, hull, hullcount - 1 ); #else pt0 = points[hull[hullcount-1]]; #endif for( i = 0; i < hullcount; i++ ) { #if !ARRAY CvPoint pt = **CV_GET_SEQ_ELEM( CvPoint*, hull, i ); #else CvPoint pt = points[hull[i]]; #endif cvLine( img, pt0, pt, CV_RGB( 0, 255, 0 )); pt0 = pt; } cvShowImage( "hull", img ); int key = cvWaitKey(0); if( key == 27 ) // 'ESC' break; #if !ARRAY cvClearMemStorage( storage ); #else free( points ); free( hull ); #endif } } CheckContourConvexity 测试轮廓的凸性 int cvCheckContourConvexity( const CvArr* contour ); contour 被测试轮廓 (点序列或数组). 函数 cvCheckContourConvexity 输入的轮廓是否为凸的。必须是简单轮廓, 比如没有自交叉。 CvConvexityDefect 用来描述一个简单轮廓凸性缺陷的结构体 typedef struct CvConvexityDefect { CvPoint* start; /* 缺陷开始的轮廓点 */ CvPoint* end; /* 缺陷结束的轮廓点 */ CvPoint* depth_point; /* 缺陷中距离凸形最远的轮廓点(谷底) */ float depth; /* 谷底距离凸形的深度*/ } CvConvexityDefect; Picture. Convexity defects of hand contour. ConvexityDefects 发现轮廓凸形缺陷 CvSeq* cvConvexityDefects( const CvArr* contour, const CvArr* convexhull, CvMemStorage* storage=NULL ); contour 输入轮廓 convexhull 用 cvConvexHull2 得到的凸外形,它应该包含轮廓的定点或下标,而不是外形点的本身, 即 cvConvexHull2 中的参数 return_points 应该设置为 0. storage 凸性缺陷的输出序列容器。如果为 NULL, 使用轮廓或外形的存储仓。 函数 cvConvexityDefects 发现输入轮廓的所有凸性缺陷,并且返回 CvConvexityDefect 结构序列。 MinAreaRect2 对给定的 2D 点集,寻找最小面积的包围矩形 CvBox2D cvMinAreaRect2( const CvArr* points, CvMemStorage* storage=NULL ); points 点序列或点集数组 storage 可选的临时存储仓 函数 cvMinAreaRect2 通过建立凸外形并且旋转外形以寻找给定 2D 点集的 最小面积的包围矩形. Picture. Minimal-area bounding rectangle for contour MinEnclosingCircle 对给定的 2D 点集,寻找最小面积的包围圆形 int cvMinEnclosingCircle( const CvArr* points, CvPoint2D32f* center, float* radius ); points 点序列或点集数组 center 输出参数:圆心 radius 输出参数:半径 函数 cvMinEnclosingCircle 对给定的 2D 点集迭代寻找最小面积的包围圆 形。如果产生的圆包含所有点,返回非零。否则返回零(算法失败)。 CalcPGH 计算轮廓的 pair-wise 几何直方图 void cvCalcPGH( const CvSeq* contour, CvHistogram* hist ); contour 输入轮廓,当前仅仅支持具有整数坐标的点集 hist 计算出的直方图,必须是两维的。 函数 cvCalcPGH 计算轮廓的 2D pair-wise(Hunnish: 不知如何翻译,只好 保留) 几何直方图 (pair-wise geometrical histogram :PGH), 算法描 述见 [Iivarinen97]. 算法考虑的每一对轮廓边缘。计算每一对边缘之间的 夹角以及最大最小距离。具体做法是,轮流考虑每一个边缘做为基准,函数 循环遍历所有边缘。在考虑基准边缘和其它边缘的时候, 选择非基准线上的 点到基准线上的最大和最小距离。边缘之间的角度定义了直方图的行,而在 其中增加对应计算出来的最大和最小距离的所有直方块, (即直方图是 [Iivarninen97] 定义中的转置). 该直方图用来做轮廓匹配。 平面划分 CvSubdiv2D 平面划分 #define CV_SUBDIV2D_FIELDS() \ CV_GRAPH_FIELDS() \ int quad_edges; \ int is_geometry_valid; \ CvSubdiv2DEdge recent_edge; \ CvPoint2D32f topleft; \ CvPoint2D32f bottomright; typedef struct CvSubdiv2D { CV_SUBDIV2D_FIELDS() } CvSubdiv2D; 平面划分是将一个平面分割为一组互不重叠的能够覆盖整个平面的区域 P(facets)。上面结构描述了建立在 2D 点集上的划分结构,其中点集互相连 接并且构成平面图形,该图形通过结合一些无限连接外部划分点(称为凸形点) 的边缘,将一个平面用边按照其边缘划分成很多小区域(facets)。 对于每一个划分操作,都有一个对偶划分与之对应,对偶的意思是小区域和 点(划分的顶点)变换角色,即在对偶划分中,小区域被当做一个顶点(以下称 之为虚拟点),而原始的划分顶点被当做小区域。在如下所示的图例中,原始 的划分用实线来表示,而对偶划分用点线来表示。 OpenCV 使用 Delaunay's 算法将平面分割成小的三角形区域。分割的实现通 过从一个假定的三角形(该三角形确保包括所有的分割点)开始不断迭代来完 成。在这种情况下,对偶划分就是输入的 2d 点集的 Voronoi 图表。这种划 分可以用于对一个平面的 3d 分段变换、形态变换、平面点的快速定位以及建 立特定的图结构 (比如 NNG,RNG 等等)。 CvQuadEdge2D 平面划分中的 Quad-edge(四方边缘结构) /* quad-edge 中的一条边缘,低两位表示该边缘的索引号,其它高位表示边缘指针。 */ typedef long CvSubdiv2DEdge; /* 四方边缘的结构场 */ #define CV_QUADEDGE2D_FIELDS() \ int flags; \ struct CvSubdiv2DPoint* pt[4]; \ CvSubdiv2DEdge next[4]; typedef struct CvQuadEdge2D { CV_QUADEDGE2D_FIELDS() } CvQuadEdge2D; Quad-edge(译者注:以下称之为四方边缘结构)是平面划分的基元,其中包括 四个边缘 (e, eRot 以及它们的逆)。 CvSubdiv2DPoint 原始和对偶划分点 #define CV_SUBDIV2D_POINT_FIELDS()\ int flags; \ CvSubdiv2DEdge first; \ CvPoint2D32f pt; #define CV_SUBDIV2D_VIRTUAL_POINT_FLAG (1 << 30) typedef struct CvSubdiv2DPoint { CV_SUBDIV2D_POINT_FIELDS() } CvSubdiv2DPoint; Subdiv2DGetEdge 返回给定的边缘之一 CvSubdiv2DEdge cvSubdiv2DGetEdge( CvSubdiv2DEdge edge, CvNextEdgeType type ); #define cvSubdiv2DNextEdge( edge ) cvSubdiv2DGetEdge( edge, CV_NEXT_AROUND_ORG ) edge 划分的边缘 (并不是四方边缘结构) type 确定函数返回哪条相关边缘,是下面几种之一: • CV_NEXT_AROUND_ORG - 边缘原点的下一条 (eOnext on the picture above if e is the input edge) • CV_NEXT_AROUND_DST - 边缘顶点的下一条 (eDnext) • CV_PREV_AROUND_ORG - 边缘原点的前一条 (reversed eRnext) • CV_PREV_AROUND_DST - 边缘终点的前一条 (reversed eLnext) • CV_NEXT_AROUND_LEFT - 左区域的下一条 (eLnext) • CV_NEXT_AROUND_RIGHT - 右区域的下一条(eRnext) • CV_PREV_AROUND_LEFT - 左区域的前一条 (reversed eOnext) • CV_PREV_AROUND_RIGHT - 右区域的前一条 (reversed eDnext) 函数 cvSubdiv2DGetEdge 返回与输入边缘相关的边缘 Subdiv2DRotateEdge 返回同一个四方边缘结构中的另一条边缘 CvSubdiv2DEdge cvSubdiv2DRotateEdge( CvSubdiv2DEdge edge, int rotate ); edge 划分的边缘 (并不是四方边缘结构) type 确定函数根据输入的边缘返回同一四方边缘结构中的哪条边缘,是下面几种之一: • 0 - 输入边缘 (上图中的 e,如果 e 是输入边缘) • 1 - 旋转边缘 (eRot) • 2 -逆边缘 ( e 的反向边缘) • 3 - 旋转边缘的反向边缘(eRot 的反向边缘, 图中绿色) 函数 cvSubdiv2DRotateEdge 根据输入的边缘返回四方边缘结构中的一条边 缘 Subdiv2DEdgeOrg 返回边缘的原点 CvSubdiv2DPoint* cvSubdiv2DEdgeOrg( CvSubdiv2DEdge edge ); edge 划分的边缘 (并不是四方边缘结构) 函数 cvSubdiv2DEdgeOrg 返回边缘的原点。如果该边缘是从对偶划分得到并 且虚点坐标还没有计算出来,可能返回空指针。虚点可以用函数来 cvCalcSubdivVoronoi2D计算。 Subdiv2DEdgeDst Returns edge destination CvSubdiv2DPoint* cvSubdiv2DEdgeDst( CvSubdiv2DEdge edge ); edge 划分的边缘 (并不是四方边缘结构) 函数 cvSubdiv2DEdgeDst 返回边缘的终点。如果该边缘是从对偶划分得到并 且虚点坐标还没有计算出来,可能返回空指针。虚点可以用函数来 cvCalcSubdivVoronoi2D计算。 CreateSubdivDelaunay2D 生成的空 Delaunay 三角测量 CvSubdiv2D* cvCreateSubdivDelaunay2D( CvRect rect, CvMemStorage* storage ); rect Rectangle 包括所有待加入划分操作的 2d 点的四方形。 storage 划分操作的存储器 函数 cvCreateSubdivDelaunay2D 生成一个空的Delaunay 划分, 其中 2d points可以进一步使用函数 cvSubdivDelaunay2DInsert来添加。所有的点 一定要在指定的四方形中添加,否则就会报运行错误。 SubdivDelaunay2DInsert 向 Delaunay 三角测量中插入一个点 CvSubdiv2DPoint* cvSubdivDelaunay2DInsert( CvSubdiv2D* subdiv, CvPoint2D32f pt); subdiv 通过函数 cvCreateSubdivDelaunay2D.生成的Delaunay划分 pt 待插入的点 函数 cvSubdivDelaunay2DInsert 向划分的结构中插入一个点并且正确地改 变划分的拓朴结构。如果划分结构中已经存在一个相同的坐标点,则不会有 新点插入。该函数返回指向已插入点的指针。在这个截断,不计算任何虚点 坐标。 Subdiv2DLocate 在 Delaunay 三角测量中定位输入点 CvSubdiv2DPointLocation cvSubdiv2DLocate( CvSubdiv2D* subdiv, CvPoint2D32f pt, CvSubdiv2DEdge* edge, CvSubdiv2DPoint** vertex=NULL ); subdiv Delaunay 或者是其它分割结构. pt 待定位的输入点 edge 与输入点对应的输入边缘(点在其上或者其右) vertex 与输入点对应的输出顶点坐标(指向 double 类型),可选。 函数 cvSubdiv2DLocate 在划分中定位输入点,共有 5 种类型: • 输入点落入某小区域内。 函数返回参数 CV_PTLOC_INSIDE 且*edge 中包含小 区域的边缘之一。 • 输入点落 p 在边缘之上。 函数返回参数 CV_PTLOC_ON_EDGE 且 *edge 包含 此边缘。 • 输入点与划分的顶点之一相对应。 函数返回参数 CV_PTLOC_VERTEX 且 *vertex 中包括指向该顶点的指针; • 输入点落在划分的参考区域之外。 函数返回参数 CV_PTLOC_OUTSIDE_RECT 且不填写任何指针。 • 输入参数之一有误。函数报运行错误(如果已经选则了沉默或者父母出错模式,则 函数返回 CV_PTLOC_ERROR) 。 FindNearestPoint2D 根据输入点,找到其最近的划分顶点 CvSubdiv2DPoint* cvFindNearestPoint2D( CvSubdiv2D* subdiv, CvPoint2D32f pt ); subdiv Delaunay 或者其它划分方式 pt 输入点 函数 cvFindNearestPoint2D 是另一个定位输入点的函数。该函数找到输入 点的最近划分顶点。尽管划分出的小区域(facet)被用来作为起始点,但是输 入点不一定非得在最终找到的顶点所在的小区域之内。该函数返回指向找到 的划分顶点的指针。 CalcSubdivVoronoi2D 计算 Voronoi 图表的细胞结构 void cvCalcSubdivVoronoi2D( CvSubdiv2D* subdiv ); subdiv Delaunay 划分,其中所有的点已经添加 。 函数 cvCalcSubdivVoronoi2D 计算虚点的坐标,所有与原划分中的某顶点相 对应的虚点形成了(当他们相互连接时)该顶点的Voronoi 细胞的边界。 ClearSubdivVoronoi2D 移除所有的虚点 void cvClearSubdivVoronoi2D( CvSubdiv2D* subdiv ); subdiv Delaunay 划分 函数 cvClearSubdivVoronoi2D 移除所有的虚点。当划分的结果被函数 cvCalcSubdivVoronoi2D的前一次调用更改时,该函数被 cvCalcSubdivVoronoi2D内部调用 。 还有一些其它的底层处理函数与平面划分操作协同工作,参见 cv.h 及源码。 生成 delaunay.c 三角测量以及 2d 随机点集的 Voronoi 图表的演示代码可 以在 opencv/samples/c 目录下的 delaunay.c 文件中找到。 运动分析与对象跟踪 背景统计量的累积 Acc 将帧叠加到累积器(accumulator)中 void cvAcc( const CvArr* image, CvArr* sum, const CvArr* mask=NULL ); image 输入图像, 1- 或 3-通道, 8-比特或 32-比特浮点数. (多通道的每一个通道都单独处理). sum 同一个输入图像通道的累积,32-比特或 64-比特浮点数数组. mask 可选的运算 mask. 函数 cvAcc 将整个图像 image 或某个选择区域叠加到 sum 中: sum(x,y)=sum(x,y)+image(x,y) if mask(x,y)!=0 SquareAcc 叠加输入图像的平方到累积器中 void cvSquareAcc( const CvArr* image, CvArr* sqsum, const CvArr* mask=NULL ); image 输入图像, 1- 或 3-通道, 8-比特或 32-比特浮点数 (多通道的每一个通道都单独处理) sqsum 同一个输入图像通道的累积,32-比特或 64-比特浮点数数组. mask 可选的运算 mask. 函数 cvSquareAcc 叠加输入图像 image 或某个选择区域的二次方,到累积器 sqsum 中 sqsum(x,y)=sqsum(x,y)+image(x,y)2 if mask(x,y)!=0 MultiplyAcc 将两幅输入图像的乘积叠加到累积器中 void cvMultiplyAcc( const CvArr* image1, const CvArr* image2, CvArr* acc, const CvArr* mask=NULL ); image1 第一个输入图像, 1- or 3-通道, 8-比特 or 32-比特 浮点数 (多通道的每一个通道都单独 处理) image2 第二个输入图像, 与第一个图像的格式一样 acc 同一个输入图像通道的累积,32-比特或 64-比特浮点数数组. mask 可选的运算 mask. 函数 cvMultiplyAcc 叠加两个输入图像的乘积到累积器 acc: acc(x,y)=acc(x,y) + image1(x,y)•image2(x,y) if mask(x,y)!=0 RunningAvg 更新 running average ( Hunnish: 不知道 running average 如何翻译才恰当) void cvRunningAvg( const CvArr* image, CvArr* acc, double alpha, const CvArr* mask=NULL ); image 输入图像, 1- or 3-通道, 8-比特 or 32-比特 浮点数 (each channel of multi-channel image is processed independently). acc 同一个输入图像通道的累积,32-比特或 64-比特浮点数数组. alpha 输入图像权重 mask 可选的运算 mask 函数 cvRunningAvg 计算输入图像 image 的加权和,以及累积器 acc 使得 acc 成为帧序列的一个 running average: acc(x,y)=(1-α)•acc(x,y) + α•image(x,y) if mask(x,y)!=0 其中 α (alpha) 调节更新速率 (累积器以多快的速率忘掉前面的帧). 运动模板 UpdateMotionHistory 去掉影像(silhouette) 以更新运动历史图像 void cvUpdateMotionHistory( const CvArr* silhouette, CvArr* mhi, double timestamp, double duration ); silhouette 影像 mask,运动发生地方具有非零象素 mhi 运动历史图像(单通道, 32-比特 浮点数),为本函数所更新 timestamp 当前时间,毫秒或其它单位 duration 运动跟踪的最大持续时间,用 timestamp 一样的时间单位 函数 cvUpdateMotionHistory 用下面方式更新运动历史图像: mhi(x,y)=timestamp if silhouette(x,y)!=0 0 if silhouette(x,y)=0 and mhi(x,y)rect 字段) 与窗口内部所有象素点的和 (comp->area 字段). 函数 cvMeanShift 在给定反向投影和初始搜索窗口位置的情况下,用迭代方 法寻找目标中心。当搜索窗口中心的移动小于某个给定值时或者函数已经达 到最大迭代次数时停止迭代。 函数返回迭代次数。 CamShift 发现目标中心,尺寸和方向 int cvCamShift( const CvArr* prob_image, CvRect window, CvTermCriteria criteria, CvConnectedComp* comp, CvBox2D* box=NULL ); prob_image 目标直方图的反向投影 (见 cvCalcBackProject). window 初始搜索窗口 criteria 确定窗口搜索停止的准则 comp 生成的结构,包含收敛的搜索窗口坐标 (comp->rect 字段) 与窗口内部所有象素点的和 (comp->area 字段). box 目标的带边界盒子。如果非 NULL, 则包含目标的尺寸和方向。 函数 cvCamShift 实现了 CAMSHIFT 目标跟踪算法([Bradski98]). 首先它 调用函数 cvMeanShift 寻找目标中心,然后计算目标尺寸和方向。最后返回 函数 cvMeanShift 中的迭代次数。 CvCamShiftTracker 类在 cv.hpp 中被声明,函数实现了彩色目标的跟踪。 SnakeImage 改变轮廓位置使得它的能量最小 void cvSnakeImage( const IplImage* image, CvPoint* points, int length, float* alpha, float* beta, float* gamma, int coeff_usage, CvSize win, CvTermCriteria criteria, int calc_gradient=1 ); image 输入图像或外部能量域 points 轮廓点 (snake). length 轮廓点的数目 alpha 连续性能量的权 Weight[s],单个浮点数或长度为 length 的浮点数数组,每个轮廓点有 一个权 beta 曲率能量的权 Weight[s],与 alpha 类似 gamma 图像能量的权 Weight[s],与 alpha 类似 coeff_usage 前面三个参数的不同使用方法: • CV_VALUE 表示每个 alpha, beta, gamma 都是指向为所有点所用的一个单独数值; • CV_ARRAY 表示每个 alpha, beta, gamma 是一个指向系数数组的指针,snake 上面 各点的系数都不相同。因此,各个系数数组必须与轮廓具有同样的大小。所有 数组必须与轮廓具有同样大小 win 每个点用于搜索最小值的邻域尺寸,两个 win.width 和 win.height 都必须是奇数 criteria 终止条件 calc_gradient 梯度符号。如果非零,函数为每一个图像象素计算梯度幅值,且把它当成能量场,否则 考虑输入图像本身。 函数 cvSnakeImage 更新 snake 是为了最小化 snake 的整个能量,其中能 量是依赖于轮廓形状的内部能量(轮廓越光滑,内部能量越小)以及依赖于能 量场的外部能量之和,外部能量通常在哪些局部能量极值点中达到最小值(这 些局部能量极值点与图像梯度表示的图像边缘相对应)。 参数 criteria.epsilon 用来定义必须从迭代中除掉以保证迭代正常运行的点 的最少数目。 如果在迭代中去掉的点数目小于 criteria.epsilon 或者函数达到了最大的迭 代次数 criteria.max_iter ,则终止函数。 光流 CalcOpticalFlowHS 计算两幅图像的光流 void cvCalcOpticalFlowHS( const CvArr* prev, const CvArr* curr, int use_previous, CvArr* velx, CvArr* vely, double lambda, CvTermCriteria criteria ); prev 第一幅图像, 8-比特, 单通道. curr 第二幅图像, 8-比特, 单通道. use_previous 使用以前的 (输入) 速度域 velx 光流的水平部分,与输入图像大小一样, 32-比特,浮点数, 单通道. vely 光流的垂直部分,与输入图像大小一样, 32-比特, 浮点数, 单通道. lambda Lagrangian 乘子 criteria 速度计算的终止条件 函数 cvCalcOpticalFlowHS 为输入图像的每一个象素计算光流,使用 Horn & Schunck 算法 [Horn81]. CalcOpticalFlowLK 计算两幅图像的光流 void cvCalcOpticalFlowLK( const CvArr* prev, const CvArr* curr, CvSize win_size, CvArr* velx, CvArr* vely ); prev 第一幅图像, 8-比特, 单通道. curr 第二幅图像, 8-比特, 单通道. win_size 用来归类象素的平均窗口尺寸 (Size of the averaging window used for grouping pixels) velx 光流的水平部分,与输入图像大小一样, 32-比特, 浮点数, 单通道. vely 光流的垂直部分,与 输入图像大小一样, 32-比特, 浮点数, 单通道. 函数 cvCalcOpticalFlowLK 为输入图像的每一个象素计算光流,使用 Lucas & Kanade 算法 [Lucas81]. CalcOpticalFlowBM 用块匹配方法计算两幅图像的光流 void cvCalcOpticalFlowBM( const CvArr* prev, const CvArr* curr, CvSize block_size, CvSize shift_size, CvSize max_range, int use_previous, CvArr* velx, CvArr* vely ); prev 第一幅图像, 8-比特, 单通道. curr 第二幅图像, 8-比特, 单通道. block_size 比较的基本块尺寸 shift_size 块坐标的增量 max_range 块周围象素的扫描邻域的尺寸 use_previous 使用以前的 (输入) 速度域 velx 光流的水平部分,尺寸为 floor((prev->width - block_size.width)/shiftSize.width) × floor((prev->height - block_size.height)/shiftSize.height) , 32-比特,浮点数, 单通道. vely 光流的垂直部分,与 velx 大小一样,32-比特,浮点数, 单通道. 函数 cvCalcOpticalFlowBM 为重叠块 block_size.width×block_size.height 中 的每一个象素计算光流,因此其速度域小于整个图像的速度域。对每一个在 图像 prev 中的块,函数试图在 curr 中某些原始块或其偏移 (velx(x0,y0),vely(x0,y0)) 块的邻域里寻找类似的块,如同在前一个函数 调用中所计算的类似(如果 use_previous=1) CalcOpticalFlowPyrLK 计算一个稀疏特征集的光流,使用金字塔中的迭代 Lucas-Kanade 方法 void cvCalcOpticalFlowPyrLK( const CvArr* prev, const CvArr* curr, CvArr* prev_pyr, CvArr* curr_pyr, const CvPoint2D32f* prev_features, CvPoint2D32f* curr_features, int count, CvSize win_size, int level, char* status, float* track_error, CvTermCriteria criteria, int flags ); prev 在时间 t 的第一帧 curr 在时间 t + dt 的第二帧 prev_pyr 第一帧的金字塔缓存. 如果指针非 NULL , 则缓存必须有足够的空间来存储金字塔从层 1 到层 #level 的内容。尺寸 (image_width+8)*image_height/3 比特足够了 curr_pyr 与 prev_pyr 类似, 用于第二帧 prev_features 需要发现光流的点集 curr_features 包含新计算出来的位置的 点集 features 第二幅图像中 count 特征点的数目 win_size 每个金字塔层的搜索窗口尺寸 level 最大的金字塔层数。如果为 0 , 不使用金字塔 (即金字塔为单层), 如果为 1 , 使用两层, 下面依次类推。 status 数组。如果对应特征的光流被发现,数组中的每一个元素都被设置为 1, 否则设置为 0。 error 双精度数组,包含原始图像碎片与移动点之间的差。为可选参数,可以是 NULL . criteria 准则,指定在每个金字塔层,为某点寻找光流的迭代过程的终止条件。 flags 其它选项: • CV_LKFLOW_PYR_A_READY , 在调用之前,先计算第一帧的金字塔 • CV_LKFLOW_PYR_B_READY , 在调用之前,先计算第二帧的金字塔 • CV_LKFLOW_INITIAL_GUESSES , 在调用之前,数组 B 包含特征的初始坐标 (Hunnish: 在本节中没有出现数组 B,不知是指的哪一个) 函数 cvCalcOpticalFlowPyrLK 实现了金字塔中 Lucas-Kanade 光流计算的 稀疏迭代版本 ([Bouguet00])。 它根据给出的前一帧特征点坐标计算当前视 频帧上的特征点坐标。 函数寻找具有子象素精度的坐标值。 两个参数 prev_pyr 和 curr_pyr 都遵循下列规则: 如果图像指针为 0, 函数 在内部为其分配缓存空间,计算金字塔,然后再处理过后释放缓存。 否则, 函数计算金字塔且存储它到缓存中,除非设置标识 CV_LKFLOW_PYR_A[B]_READY 。 图像应该足够大以便能够容纳 Gaussian 金字塔数据。 调用函数以后,金字 塔被计算而且相应图像的标识也被设置,为下一次调用准备就绪 (比如:对 除了第一个图像的所有图像序列,标识 CV_LKFLOW_PYR_A_READY 被设置). 预估器 CvKalman Kalman 滤波器状态 typedef struct CvKalman { int MP; /* 测量向量维数 */ int DP; /* 状态向量维数 */ int CP; /* 控制向量维数 */ /* 向后兼容字段 */ #if 1 float* PosterState; /* =state_pre->data.fl */ float* PriorState; /* =state_post->data.fl */ float* DynamMatr; /* =transition_matrix->data.fl */ float* MeasurementMatr; /* =measurement_matrix->data.fl */ float* MNCovariance; /* =measurement_noise_cov->data.fl */ float* PNCovariance; /* =process_noise_cov->data.fl */ float* KalmGainMatr; /* =gain->data.fl */ float* PriorErrorCovariance;/* =error_cov_pre->data.fl */ float* PosterErrorCovariance;/* =error_cov_post->data.fl */ float* Temp1; /* temp1->data.fl */ float* Temp2; /* temp2->data.fl */ #endif CvMat* state_pre; /* 预测状态 (x'(k)): x(k)=A*x(k-1)+B*u(k) */ CvMat* state_post; /* 矫正状态 (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) */ CvMat* transition_matrix; /* 状态传递矩阵 state transition matrix (A) */ CvMat* control_matrix; /* 控制矩阵 control matrix (B) (如果没有控制,则不使用它)*/ CvMat* measurement_matrix; /* 测量矩阵 measurement matrix (H) */ CvMat* process_noise_cov; /* 处理噪声相关矩阵 process noise covariance matrix (Q) */ CvMat* measurement_noise_cov; /* 测量噪声相关矩阵 measurement noise covariance matrix (R) */ CvMat* error_cov_pre; /* 先验错误估计相关矩阵 priori error estimate covariance matrix (P'(k)): P'(k)=A*P(k-1)*At + Q)*/ CvMat* gain; /* Kalman 增益矩阵 gain matrix (K(k)): K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R)*/ CvMat* error_cov_post; /* 后验错误估计相关矩阵 posteriori error estimate covariance matrix (P(k)): P(k)=(I-K(k)*H)*P'(k) */ CvMat* temp1; /* 临时矩阵 temporary matrices */ CvMat* temp2; CvMat* temp3; CvMat* temp4; CvMat* temp5; } CvKalman; (Hunnish: 害怕有翻译上的不准确,因此在翻译注释时,保留了原来的英文 术语,以便大家对照) 结构 CvKalman 用来保存 Kalman 滤波器状态。它由函数 cvCreateKalman 创建,由函数f cvKalmanPredict 和 cvKalmanCorrect 更新,由 cvReleaseKalman 释放. 通常该结构是为标准 Kalman 所使用的 (符号和公 式都借自非常优秀的 Kalman 教程 [Welch95]): xk=A•xk-1+B•uk+wk 译者注:系统运动方程 zk=H•xk+vk, 译者注:系统观测方程 其中: xk ( x k-1) - 系统在时刻 k (k-1) 的状态向量 (state of the system at the moment k (k-1)) zk - 在时刻 k 的系统状态测量向量 (measurement of the system state at the moment k) uk - 应用于时刻 k 的外部控制 (external control applied at the moment k) wk 和 vk 分别为正态分布的运动和测量噪声 p(w) ~ N(0,Q) p(v) ~ N(0,R), 即, Q - 运动噪声的相关矩阵,常量或变量 R - 测量噪声的相关矩阵,常量或变量 对标准 Kalman 滤波器,所有矩阵: A, B, H, Q 和 R 都是通过 cvCreateKalman 在分配结构 CvKalman 时初始化一次。但是,同样的结构和 函数,通过在当前系统状态邻域中线性化扩展 Kalman 滤波器方程,可以用 来模拟扩展 Kalman 滤波器,在这种情况下, A, B, H (也许还有 Q 和 R) 在 每一步中都被更新。 CreateKalman 分配 Kalman 滤波器结构 CvKalman* cvCreateKalman( int dynam_params, int measure_params, int control_params=0 ); dynam_params 状态向量维数 measure_params 测量向量维数 control_params 控制向量维数 函数 cvCreateKalman 分配 CvKalman 以及它的所有矩阵和初始参数 ReleaseKalman 释放 Kalman 滤波器结构 void cvReleaseKalman( CvKalman** kalman ); kalman 指向 Kalman 滤波器结构的双指针 函数 cvReleaseKalman 释放结构 CvKalman 和里面所有矩阵 KalmanPredict 估计后来的模型状态 const CvMat* cvKalmanPredict( CvKalman* kalman, const CvMat* control=NULL ); #define cvKalmanUpdateByTime cvKalmanPredict kalman Kalman 滤波器状态 control 控制向量 (uk), 如果没有外部控制 (control_params=0) 应该为 NULL 函数 cvKalmanPredict 根据当前状态估计后来的随机模型状态,并存储于 kalman->state_pre: x'k=A•xk+B•uk P'k=A•Pk-1*AT + Q, 其中 x'k 是预测状态 (kalman->state_pre), xk-1 是前一步的矫正状态 (kalman->state_post) (应该在开始的某个地方初始化,即缺省为零向量), uk 是外部控制(control 参数), P'k 是先验误差相关矩阵 (kalman->error_cov_pre) Pk-1 是前一步的后验误差相关矩阵(kalman->error_cov_post) (应该在开始的某个地方初始化,即缺省为单位矩阵), 函数返回估计得到的状态值 KalmanCorrect 调节模型状态 const CvMat* cvKalmanCorrect( CvKalman* kalman, const CvMat* measurement ); #define cvKalmanUpdateByMeasurement cvKalmanCorrect kalman 被更新的 Kalman 结构的指针 measurement 指向测量向量的指针,向量形式为 CvMat 函数 cvKalmanCorrect 在给定的模型状态的测量基础上,调节随机模型状 态: Kk=P'k•HT•(H•P'k•HT+R)-1 xk=x'k+Kk•(zk-H•x'k) Pk=(I-Kk•H)•P'k 其中 zk - 给定测量(mesurement parameter) Kk - Kalman "增益" 矩阵 函数存储调节状态到 kalman->state_post 中并且输出时返回它 例子. 使用 Kalman 滤波器跟踪一个旋转的点 #include "cv.h" #include "highgui.h" #include int main(int argc, char** argv) { /* A matrix data */ const float A[] = { 1, 1, 0, 1 }; IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 ); CvKalman* kalman = cvCreateKalman( 2, 1, 0 ); /* state is (phi, delta_phi) - angle and angle increment */ CvMat* state = cvCreateMat( 2, 1, CV_32FC1 ); CvMat* process_noise = cvCreateMat( 2, 1, CV_32FC1 ); /* only phi (angle) is measured */ CvMat* measurement = cvCreateMat( 1, 1, CV_32FC1 ); CvRandState rng; int code = -1; cvRandInit( &rng, 0, 1, -1, CV_RAND_UNI ); cvZero( measurement ); cvNamedWindow( "Kalman", 1 ); for(;;) { cvRandSetRange( &rng, 0, 0.1, 0 ); rng.disttype = CV_RAND_NORMAL; cvRand( &rng, state ); memcpy( kalman->transition_matrix->data.fl, A, sizeof(A)); cvSetIdentity( kalman->measurement_matrix, cvRealScalar(1) ); cvSetIdentity( kalman->process_noise_cov, cvRealScalar(1e-5) ); cvSetIdentity( kalman->measurement_noise_cov, cvRealScalar(1e-1) ); cvSetIdentity( kalman->error_cov_post, cvRealScalar(1)); /* choose random initial state */ cvRand( &rng, kalman->state_post ); rng.disttype = CV_RAND_NORMAL; for(;;) { #define calc_point(angle) \ cvPoint( cvRound(img->width/2 + img->width/3*cos(angle)), \ cvRound(img->height/2 - img->width/3*sin(angle))) float state_angle = state->data.fl[0]; CvPoint state_pt = calc_point(state_angle); /* predict point position */ const CvMat* prediction = cvKalmanPredict( kalman, 0 ); float predict_angle = prediction->data.fl[0]; CvPoint predict_pt = calc_point(predict_angle); float measurement_angle; CvPoint measurement_pt; cvRandSetRange( &rng, 0, sqrt(kalman->measurement_noise_cov->data.fl[0]), 0 ); cvRand( &rng, measurement ); /* generate measurement */ cvMatMulAdd( kalman->measurement_matrix, state, measurement, measurement ); measurement_angle = measurement->data.fl[0]; measurement_pt = calc_point(measurement_angle); /* plot points */ #define draw_cross( center, color, d ) \ cvLine( img, cvPoint( center.x - d, center.y - d ), \ cvPoint( center.x + d, center.y + d ), color, 1, 0 ); \ cvLine( img, cvPoint( center.x + d, center.y - d ), \ cvPoint( center.x - d, center.y + d ), color, 1, 0 ) cvZero( img ); draw_cross( state_pt, CV_RGB(255,255,255), 3 ); draw_cross( measurement_pt, CV_RGB(255,0,0), 3 ); draw_cross( predict_pt, CV_RGB(0,255,0), 3 ); cvLine( img, state_pt, predict_pt, CV_RGB(255,255,0), 3, 0 ); /* adjust Kalman filter state */ cvKalmanCorrect( kalman, measurement ); cvRandSetRange( &rng, 0, sqrt(kalman->process_noise_cov->data.fl[0]), 0 ); cvRand( &rng, process_noise ); cvMatMulAdd( kalman->transition_matrix, state, process_noise, state ); cvShowImage( "Kalman", img ); code = cvWaitKey( 100 ); if( code > 0 ) /* break current simulation by pressing a key */ break; } if( code == 27 ) /* exit by ESCAPE */ break; } return 0; } CvConDensation ConDensaation 状态 typedef struct CvConDensation { int MP; // 测量向量的维数: Dimension of measurement vector int DP; // 状态向量的维数: Dimension of state vector float* DynamMatr; // 线性动态系统矩阵:Matrix of the linear Dynamics system float* State; // 状态向量: Vector of State int SamplesNum; // 粒子数: Number of the Samples float** flSamples; // 粒子向量数组: array of the Sample Vectors float** flNewSamples; // 粒子向量临时数组: temporary array of the Sample Vectors float* flConfidence; // 每个粒子的置信度(译者注:也就是粒子的权值): Confidence for each Sample float* flCumulative; // 权值的累计: Cumulative confidence float* Temp; // 临时向量:Temporary vector float* RandomSample; // 用来更新粒子集的随机向量: RandomVector to update sample set CvRandState* RandS; // 产生随机向量的结构数组: Array of structures to generate random vectors } CvConDensation; 结构 CvConDensation中条件概率密度传播(译者注:粒子滤波的一种特例) (Con-Dens-Aation: 单词 CONditional DENSity propagATION 的缩写)跟 踪器的状态。该算法描述可参考 http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/ISARD1/condensation. html CreateConDensation 分配 ConDensation 滤波器结构 CvConDensation* cvCreateConDensation( int dynam_params, int measure_params, int sample_count ); dynam_params 状态向量的维数 measure_params 测量向量的维数 sample_count 粒子数 函数 cvCreateConDensation 创建结构 CvConDensation 并且返回结构指 针。 ReleaseConDensation 释放 ConDensation 滤波器结构 void cvReleaseConDensation( CvConDensation** condens ); condens 要释放结构的双指针 函数 cvReleaseConDensation 释放结构 CvConDensation (见 cvConDensation) 并且清空所有事先被开辟的内存空间。 ConDensInitSampleSet 初始化 ConDensation 算法中的粒子集 void cvConDensInitSampleSet( CvConDensation* condens, CvMat* lower_bound, CvMat* upper_bound ); condens 需要初始化的结构指针 lower_bound 每一维的下界向量 upper_bound 每一维的上界向量 函数 cvConDensInitSampleSet 在指定区间内填充结构 CvConDensation 中 的样例数组。 ConDensUpdateByTime 估计下个模型状态 void cvConDensUpdateByTime( CvConDensation* condens ); condens 要更新的机构指针 函数 cvConDensUpdateByTime 从当前状态估计下一个随机模型状态。 模式识别 目标检测 目标检测方法最初由Paul Viola [Viola01]提出,并由Rainer Lienhart [Lienhart02]对这一方法进行了改善. 首先,利用样本(大约几百幅样本图 片)的 harr 特征进行分类器训练,得到一个级联的boosted分类器。训练样 本分为正例样本和反例样本,其中正例样本是指待检目标样本(例如人脸或汽 车等),反例样本指其它任意图片,所有的样本图片都被归一化为同样的尺寸 大小(例如,20x20)。 分类器训练完以后,就可以应用于输入图像中的感兴趣区域(与训练样本相同 的尺寸)的检测。检测到目标区域(汽车或人脸)分类器输出为 1,否则输出为 0。为了检测整副图像,可以在图像中移动搜索窗口,检测每一个位置来确定 可能的目标。 为了搜索不同大小的目标物体,分类器被设计为可以进行尺寸 改变,这样比改变待检图像的尺寸大小更为有效。所以,为了在图像中检测 未知大小的目标物体,扫描程序通常需要用不同比例大小的搜索窗口对图片 进行几次扫描。 分类器中的“级联”是指最终的分类器是由几个简单分类器级联组成。在图 像检测中,被检窗口依次通过每一级分类器, 这样在前面几层的检测中大部 分的候选区域就被排除了,全部通过每一级分类器检测的区域即为目标区域。 目前支持这种分类器的 boosting 技术有四种: Discrete Adaboost, Real Adaboost, Gentle Adaboost and Logitboost。"boosted" 即指级联分类器 的每一层都可以从中选取一个 boosting 算法(权重投票),并利用基础分类器 的自我训练得到。基础分类器是至少有两个叶结点的决策树分类器。 海尔特 征是基础分类器的输入,主要描述如下。目前的算法主要利用下面的海尔特 征。 每个特定分类器所使用的特征用形状、感兴趣区域中的位置以及比例系数(这 里的比例系数跟检测时候采用的比例系数是不一样的,尽管最后会取两个系 数的乘积值)来定义。例如在第三行特征(2c)的情况下,响应计算为覆盖全 部特征整个矩形框(包括两个白色矩形框和一个黑色矩形框)象素的和减去黑 色矩形框内象素和的三倍 。每个矩形框内的象素和都可以通过积分图象很快 的计算出来。(察看下面和对 cvIntegral的描述). 通过 HaarFaceDetect 的演示版可以察看目标检测的工作情况。 下面只是检测部分的参考手册。 haartraining 是它的一个单独的应用,可以 用来对系列样本训练级联的 boosted 分类器。详细察看 opencv/apps/haartraining。 CvHaarFeature, CvHaarClassifier, CvHaarStageClassifier, CvHaarClassifierCascade Boosted Haar 分类器结构 #define CV_HAAR_FEATURE_MAX 3 /* 一个 harr 特征由 2-3 个具有相应权重的矩形组成 a haar feature consists of 2-3 rectangles with appropriate weights */ typedef struct CvHaarFeature { int tilted; /* 0 means up-right feature, 1 means 45--rotated feature */ /* 2-3 rectangles with weights of opposite signs and with absolute values inversely proportional to the areas of the rectangles. if rect[2].weight !=0, then the feature consists of 3 rectangles, otherwise it consists of 2 */ struct { CvRect r; float weight; } rect[CV_HAAR_FEATURE_MAX]; } CvHaarFeature; /* a single tree classifier (stump in the simplest case) that returns the response for the feature at the particular image location (i.e. pixel sum over subrectangles of the window) and gives out a value depending on the responce */ typedef struct CvHaarClassifier { int count; /* number of nodes in the decision tree */ /* these are "parallel" arrays. Every index i corresponds to a node of the decision tree (root has 0-th index). left[i] - index of the left child (or negated index if the left child is a leaf) right[i] - index of the right child (or negated index if the right child is a leaf) threshold[i] - branch threshold. if feature responce is <= threshold, left branch is chosen, otherwise right branch is chosed. alpha[i] - output value correponding to the leaf. */ CvHaarFeature* haar_feature; float* threshold; int* left; int* right; float* alpha; } CvHaarClassifier; /* a boosted battery of classifiers(=stage classifier): the stage classifier returns 1 if the sum of the classifiers' responces is greater than threshold and 0 otherwise */ typedef struct CvHaarStageClassifier { int count; /* number of classifiers in the battery */ float threshold; /* threshold for the boosted classifier */ CvHaarClassifier* classifier; /* array of classifiers */ /* these fields are used for organizing trees of stage classifiers, rather than just stright cascades */ int next; int child; int parent; } CvHaarStageClassifier; typedef struct CvHidHaarClassifierCascade CvHidHaarClassifierCascade; /* cascade or tree of stage classifiers */ typedef struct CvHaarClassifierCascade { int flags; /* signature */ int count; /* number of stages */ CvSize orig_window_size; /* original object size (the cascade is trained for) */ /* these two parameters are set by cvSetImagesForHaarClassifierCascade */ CvSize real_window_size; /* current object size */ double scale; /* current scale */ CvHaarStageClassifier* stage_classifier; /* array of stage classifiers */ CvHidHaarClassifierCascade* hid_cascade; /* hidden optimized representation of the cascade, created by cvSetImagesForHaarClassifierCascade */ } CvHaarClassifierCascade; 所有的结构都代表一个级联 boosted Haar 分类器。级联有下面的等级结构: Cascade: Stage1: Classifier11: Feature11 Classifier12: Feature12 ... Stage2: Classifier21: Feature21 ... ... 整个等级可以手工构建,也可以利用函数 cvLoadHaarClassifierCascade从 已有的磁盘文件或嵌入式基中导入。 cvLoadHaarClassifierCascade 从文件中装载训练好的级联分类器或者从 OpenCV 中嵌入的分类器数据库中导入 CvHaarClassifierCascade* cvLoadHaarClassifierCascade( const char* directory, CvSize orig_window_size ); directory 训练好的级联分类器的路径 orig_window_size 级联分类器训练中采用的检测目标的尺寸。因为这个信息没有在级联分类器中存储,所 有要单独指出。 函数 cvLoadHaarClassifierCascade 用于从文件中装载训练好的利用海尔 特征的级联分类器,或者从OpenCV中嵌入的分类器数据库中导入。分类器的 训练可以应用函数haartraining(详细察看opencv/apps/haartraining) 函数 已经过时了。现在的目标检测分类器通常存储在 XML 或 YAML 文件中, 而不是通过路径导入。从文件中导入分类器,可以使用函数 cvLoad 。 cvReleaseHaarClassifierCascade 释放 haar classifier cascade。 void cvReleaseHaarClassifierCascade( CvHaarClassifierCascade** cascade ); cascade 双指针类型指针指向要释放的 cascade. 指针由函数声明。 函数 cvReleaseHaarClassifierCascade 释放cascade的动态内存,其中 cascade的动态内存或者是手工创建,或者通过函数 cvLoadHaarClassifierCascade 或 cvLoad分配。 cvHaarDetectObjects 检测图像中的目标 typedef struct CvAvgComp { CvRect rect; /* bounding rectangle for the object (average rectangle of a group) */ int neighbors; /* number of neighbor rectangles in the group */ } CvAvgComp; CvSeq* cvHaarDetectObjects( const CvArr* image, CvHaarClassifierCascade* cascade, CvMemStorage* storage, double scale_factor=1.1, int min_neighbors=3, int flags=0, CvSize min_size=cvSize(0,0) ); image 被检图像 cascade harr 分类器级联的内部标识形式 storage 用来存储检测到的一序列候选目标矩形框的内存区域。 scale_factor 在前后两次相继的扫描中,搜索窗口的比例系数。例如 1.1 指将搜索窗口依次扩大 10%。 min_neighbors 构成检测目标的相邻矩形的最小个数(缺省-1)。如果组成检测目标的小矩形的个数和小 于 min_neighbors-1 都会被排除。如果 min_neighbors 为 0, 则函数不做任何操作就返 回所有的被检候选矩形框,这种设定值一般用在用户自定义对检测结果的组合程序上。 flags 操作方式。当前唯一可以定义的操作方式是 CV_HAAR_DO_CANNY_PRUNING。如果被设定, 函数利用 Canny 边缘检测器来排除一些边缘很少或者很多的图像区域,因为这样的区域 一般不含被检目标。人脸检测中通过设定阈值使用了这种方法,并因此提高了检测速度。 min_size 检测窗口的最小尺寸。缺省的情况下被设为分类器训练时采用的样本尺寸(人脸检测中缺 省大小是~20×20)。 函数 cvHaarDetectObjects 使用针对某目标物体训练的级联分类器在图像 中找到包含目标物体的矩形区域,并且将这些区域作为一序列的矩形框返回。 函数以不同比例大小的扫描窗口对图像进行几次搜索(察看 cvSetImagesForHaarClassifierCascade)。 每次都要对图像中的这些重叠区 域利用 cvRunHaarClassifierCascade进行检测。 有时候也会利用某些继承 (heuristics)技术以减少分析的候选区域,例如利用 Canny 裁减 (prunning)方法。 函数在处理和收集到候选的方框(全部通过级联分类器 各层的区域)之后,接着对这些区域进行组合并且返回一系列各个足够大的组 合中的平均矩形。调节程序中的缺省参数(scale_factor=1.1, min_neighbors=3, flags=0)用于对目标进行更精确同时也是耗时较长的进一步检测。为了能对视 频图像进行更快的实时检测,参数设置通常是:scale_factor=1.2, min_neighbors=2, flags=CV_HAAR_DO_CANNY_PRUNING, min_size= (例如, 对于视频会议的图像区域). 例子:利用级联的Haar classifiers寻找检测目标(e.g. faces). #include "cv.h" #include "highgui.h" CvHaarClassifierCascade* load_object_detector( const char* cascade_path ) { return (CvHaarClassifierCascade*)cvLoad( cascade_path ); } void detect_and_draw_objects( IplImage* image, CvHaarClassifierCascade* cascade, int do_pyramids ) { IplImage* small_image = image; CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* faces; int i, scale = 1; /* if the flag is specified, down-scale the 输入图像 to get a performance boost w/o loosing quality (perhaps) */ if( do_pyramids ) { small_image = cvCreateImage( cvSize(image->width/2,image->height/2), IPL_DEPTH_8U, 3 ); cvPyrDown( image, small_image, CV_GAUSSIAN_5x5 ); scale = 2; } /* use the fastest variant */ faces = cvHaarDetectObjects( small_image, cascade, storage, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING ); /* draw all the rectangles */ for( i = 0; i < faces->total; i++ ) { /* extract the rectanlges only */ CvRect face_rect = *(CvRect*)cvGetSeqElem( faces, i, 0 ); cvRectangle( image, cvPoint(face_rect.x*scale,face_rect.y*scale), cvPoint((face_rect.x+face_rect.width)*scale, (face_rect.y+face_rect.height)*scale), CV_RGB(255,0,0), 3 ); } if( small_image != image ) cvReleaseImage( &small_image ); cvReleaseMemStorage( &storage ); } /* takes image filename and cascade path from the command line */ int main( int argc, char** argv ) { IplImage* image; if( argc==3 && (image = cvLoadImage( argv[1], 1 )) != 0 ) { CvHaarClassifierCascade* cascade = load_object_detector(argv[2]); detect_and_draw_objects( image, cascade, 1 ); cvNamedWindow( "test", 0 ); cvShowImage( "test", image ); cvWaitKey(0); cvReleaseHaarClassifierCascade( &cascade ); cvReleaseImage( &image ); } return 0; } cvSetImagesForHaarClassifierCascade 为隐藏的 cascade(hidden cascade)指定图像 void cvSetImagesForHaarClassifierCascade( CvHaarClassifierCascade* cascade, const CvArr* sum, const CvArr* sqsum, const CvArr* tilted_sum, double scale ); cascade 隐藏 Harr 分类器级联 ( Hidden Haar classifier cascade ) , 由函数 cvCreateHidHaarClassifierCascade生成 sum 32-比特,单通道图像的积分图像(Integral (sum) 单通道 image of 32-比特 integer format). 这幅图像以及随后的两幅用于对快速特征的评价和亮度/对比度的归一化。 它 们都可以利用函数 cvIntegral从 8-比特或浮点数 单通道的输入图像中得到。 sqsum 单通道 64 比特图像的平方和图像 tilted_sum 单通道 32 比特整数格式的图像的倾斜和(Tilted sum) scale cascade的窗口比例. 如果 scale=1, 就只用原始窗口尺寸检测 (只检测同样尺寸大小的 目标物体) - 原始窗口尺寸在函数 cvLoadHaarClassifierCascade 中定义 (在 ""中缺省为 24x24), 如果scale=2, 使用的窗口是上面的两倍 (在 face cascade中缺省值是 48x48 )。 这样尽管可以将检测速度提高四倍,但同时尺寸小于 48x48 的人脸将不能被检测到。 函数 cvSetImagesForHaarClassifierCascade 为hidden classifier cascade 指定图像 and/or 窗口比例系数。 如果图像指针为空,会继续使用 原来的图像(i.e. NULLs 意味这"不改变图像")。比例系数没有 "protection" 值,但是原来的值可以通过函数 cvGetHaarClassifierCascadeScale 重新得到并使用。这个函数用于对特定 图像中检测特定目标尺寸的cascade分类器的设定。函数通过 cvHaarDetectObjects进行内部调用,但当需要在更低一层的函数 cvRunHaarClassifierCascade中使用的时候,用户也可以自行调用。 cvRunHaarClassifierCascade 在给定位置的图像中运行 cascade of boosted classifier int cvRunHaarClassifierCascade( CvHaarClassifierCascade* cascade, CvPoint pt, int start_stage=0 ); cascade Haar 级联分类器 pt 待检测区域的左上角坐标。待检测区域大小为原始窗口尺寸乘以当前设定的比例系数。 当前窗口尺寸可以通过 cvGetHaarClassifierCascadeWindowSize重新得到。 start_stage 级联层的初始下标值(从 0 开始计数)。函数假定前面所有每层的分类器都已通过。这 个特征通过函数 cvHaarDetectObjects内部调用,用于更好的处理器高速缓冲存储器。 函数 cvRunHaarHaarClassifierCascade 用于对单幅图片的检测。在函数调 用前首先利用 cvSetImagesForHaarClassifierCascade设定积分图和合适的 比例系数 (=> 窗口尺寸)。当分析的矩形框全部通过级联分类器每一层的时 返回正值(这是一个候选目标),否则返回 0 或负值。 照相机定标和三维重建 照相机定标 CalibrateCamera 对相机单精度定标 void cvCalibrateCamera( int image_count, int* point_counts, CvSize image_size, CvPoint2D32f* image_points, CvPoint3D32f* object_points, CvVect32f distortion_coeffs, CvMatr32f camera_matrix, CvVect32f translation_vectors, CvMatr32f rotation_matrixes, int use_intrinsic_guess ); image_count 图像个数。 point_counts 每副图像定标点个数的数组 image_size 图像大小 image_points 指向图像的指针 object_points 指向检测目标的指针 distortion_coeffs 寻找到四个变形系数的数组。 camera_matrix 相机参数矩阵。Camera matrix found. translation_vectors 每个图像中模式位置的平移矢量矩阵。 rotation_matrixes 图像中模式位置的旋转矩阵。 use_intrinsic_guess 是否使用内部猜测(Intrinsic guess). 设为 1,则使用 函数 cvCalibrateCamera 利用目标图像模式和目标模式的象素点信息计算 相机参数。 CalibrateCamera_64d 相机双精度定标 void cvCalibrateCamera_64d( int image_count, int* point_counts, CvSize image_size, CvPoint2D64d* image_points, CvPoint3D64d* object_points, CvVect64d distortion_coeffs, CvMatr64d camera_matrix, CvVect64d translation_vectors, CvMatr64d rotation_matrixes, int use_intrinsic_guess ); image_count 图像个数 point_counts 每副图像定标点数目的数组 image_size 图像大小 image_points 图像指针 object_points 模式对象指针 Pointer to the pattern. distortion_coeffs 寻找变形系数 Distortion coefficients found. camera_matrix 相机矩阵 Camera matrix found. translation_vectors 图像中模式位置的平移矢量矩阵 rotation_matrixes 图像中每个模式位置的旋转矩阵 use_intrinsic_guess Intrinsic guess. If equal to 1, intrinsic guess is needed. 函数 cvCalibrateCamera_64d 跟 函数 cvCalibrateCamera用法基本相同, 不过 cvCalibrateCamera_64d使用的是双精度类型。 Rodrigues 进行旋转矩阵和旋转向量间的转换,采用单精度。 void cvRodrigues( CvMat* rotation_matrix, CvMat* rotation_vector, CvMat* jacobian, int conv_type); rotation_matrix 旋转矩阵(3x3), 32-比特 or 64-比特 浮点数 rotation_vector 跟旋转矩阵相同类型的旋转向量(3x1 or 1x3) jacobian 雅可比矩阵 3 × 9 conv_type 转换方式;设定为 CV_RODRIGUES_M2V,矩阵转化为向量。CV_RODRIGUES_V2M 向量转化为 矩阵 函数 cvRodrigues 进行旋转矩阵和旋转向量之间的相互转换。 UnDistortOnce 校正相机镜头变形 Corrects camera lens distortion void cvUnDistortOnce( const CvArr* src, CvArr* dst, const float* intrinsic_matrix, const float* distortion_coeffs, int interpolate=1 ); src 原图像(变形图像) dst 目标图像 (校正)图像. intrinsic_matrix 相机的内参数矩阵(3x3). distortion_coeffs 向量的四个变形系数 k1, k2, p1 和 p2. interpolate 双线性卷积标志。 函数 cvUnDistortOnce 校正单幅图像的镜头变形。相机的内置参数矩阵和变 形系数k1, k2 , p1 ,和 p2 事先由函数 cvCalibrateCamera计算得到。 UnDistortInit 计算畸变点数组和插值系数 Calculates arrays of distorted points indices and interpolation coefficients void cvUnDistortInit( const CvArr* src, CvArr* undistortion_map, const float* intrinsic_matrix, const float* distortion_coeffs, int interpolate=1 ); src 任意的原图像(变形图形),图像的大小要和通道数匹配。 undistortion_map 32-比特整数的图像,如果 interpolate=0,与输入图像尺寸相同,如果 interpolate=1, 是输入图像的 3 倍 intrinsic_matrix 摄像机的内参数矩阵 distortion_coeffs 向量的 4 个变形系数 k1, k2, p1 and p2 interpolate 双线性卷积标志 函数 cvUnDistortInit 利用摄像机内参数和变形系数计算畸变点指数数组 和插值系数。为函数 cvUnDistort计算非畸变图。 摄像机的内参数矩阵和变形系数可以由函数 cvCalibrateCamera计算。 UnDistort 校正相机镜头变形 Corrects camera lens distortion void cvUnDistort( const CvArr* src, CvArr* dst, const CvArr* undistortion_map, int interpolate=1 ); src 原图像(变形图像) dst 目标图像(校正后图像) undistortion_map 未变形图像,由函数 cvUnDistortInit提前计算 interpolate 双线性卷积标志,与函数 cvUnDistortInit中相同。 函数 cvUnDistort 提前计算出的未变形图校正相机镜头变形,速度比函数 cvUnDistortOnce 快。(利用先前计算出的非变形图来校正摄像机的镜头变 形。速度比函数 cvUnDistortOnce快) FindChessBoardCornerGuesses 发现棋盘内部角点的大概位置 int cvFindChessBoardCornerGuesses( const CvArr* image, CvArr* thresh, CvMemStorage* storage, CvSize board_size, CvPoint2D32f* corners, int* corner_count=NULL ); image 输入的棋盘图像,象素位数为 IPL_DEPTH_8U. thresh 临时图像,与输入图像的大小、格式一样 storage 中间数据的存储区,如果为空,函数生成暂时的内存区域。 board_size 棋盘每一行和每一列的内部角点数目。宽(列数目)必须小于等于高(行数目) corners 角点数组的指针 corner_count 带符号的已发现角点数目. 正值表示整个棋盘的角点都以找到,负值表示不是所有的角 点都被找到。 函数 cvFindChessBoardCornerGuesses 试图确定输入图像是否是棋盘视图, 并且确定棋盘的内部角点。如果所有角点都被发现,函数返回非零值,并且 将角点按一定顺序排列(逐行由左到右),否则,函数返回零。例如一个简 单棋盘有 8 x 8 方块和 7 x 7 内部方块相切的角点。单词“大概 approximate" 表示发现的角点坐标与实际坐标会有几个象素的偏差。为得到 更精确的坐标,可使用函数 cvFindCornerSubPix. 姿态估计 FindExtrinsicCameraParams 为模式寻找摄像机外参数矩阵 void cvFindExtrinsicCameraParams( int point_count, CvSize image_size, CvPoint2D32f* image_points, CvPoint3D32f* object_points, CvVect32f focal_length, CvPoint2D32f principal_point, CvVect32f distortion_coeffs, CvVect32f rotation_vector, CvVect32f translation_vector ); point_count 点的个数 ImageSize 图像大小 image_points 图像指针 object_points 模式指针 Pointer to the pattern. focal_length 焦距 principal_point 基点 distortion_coeffs 变形系数。 rotation_vector 旋转向量 translation_vector 平移向量 函数 cvFindExtrinsicCameraParams 寻找模式的摄像机外参数矩阵 FindExtrinsicCameraParams_64d 以双精度形式寻找照相机外参数 Finds extrinsic camera parameters for pattern with double precision void cvFindExtrinsicCameraParams_64d( int point_count, CvSize image_size, CvPoint2D64d* image_points, CvPoint3D64d* object_points, CvVect64d focal_length, CvPoint2D64d principal_point, CvVect64d distortion_coeffs, CvVect64d rotation_vector, CvVect64d translation_vector ); point_count 点的个数 ImageSize 图像尺寸 image_points 图像指针 object_points 模式指针 Pointer to the pattern. focal_length 焦距 principal_point 基点 distortion_coeffs 变形系数 rotation_vector 旋转向量 translation_vector 平移向量 函数 cvFindExtrinsicCameraParams_64d 建立对象模式的外参数,双精度 。 CreatePOSITObject 初始化目标信息 Initializes structure containing object information CvPOSITObject* cvCreatePOSITObject( CvPoint3D32f* points, int point_count ); points 指向 3D 对象模型的指针 point_count 目标对象的点数 函数 cvCreatePOSITObject 为对象结构分配内存并计算对象的逆矩阵。 预处理的对象数据存储在结构 CvPOSITObject中,通过OpenCV内部调用,即 用户不能直接得到得到数据结构。用户只可以创建这个结构并将指针传递给 函数。 对象是一系列给定坐标的像素点,函数 cvPOSIT计算从照相机坐标系到目标 点points[0] 之间的向量。 当完成上述对象的工作以后,必须使用函数 cvReleasePOSITObject释放内存。 POSIT 执行 POSIT 算法。Implements POSIT algorithm void cvPOSIT( CvPOSITObject* posit_object, CvPoint2D32f* image_points, double focal_length, CvTermCriteria criteria, CvMatr32f rotation_matrix, CvVect32f translation_vector ); posit_object 指向对象结构的指针 image_points 指针,指向目标像素点在二维平面图上的投影。 focal_length 使用的摄像机的焦距 criteria POSIT 迭代算法程序终止的条件 rotation_matrix 旋转矩阵 translation_vector 平移矩阵 Translation vector. 函数 cvPOSIT 执行POSIT算法。图像坐标在摄像机坐标系统中给出。焦距可 以通过摄像机标定得到。算法每一次迭代都会重新计算在估计位置的透视投 影。 两次投影之间的范式差值是对应点间的最大距离。如果差值过小,参数 criteria.epsilon 就会终止程序。 ReleasePOSITObject 释放 3D 对象结构 void cvReleasePOSITObject( CvPOSITObject** posit_object ); posit_object 指向 CvPOSIT structure 双指针 函数 cvReleasePOSITObject 释放函数 cvCreatePOSITObject分配的内存。 CalcImageHomography 计算长方形或椭圆形平面对象的单应矩阵(例如,胳膊) Calculates homography matrix for oblong planar object (e.g. arm) void cvCalcImageHomography( float* line, CvPoint3D32f* center, float* intrinsic, float* homography ); line 主要对象的轴方向 (vector (dx,dy,dz)). center 对象坐标中心 ((cx,cy,cz)). intrinsic 摄像机内参数 (3x3 matrix). homography 输出的单应矩阵(3x3). 函数 cvCalcImageHomography 计算初始图像由图像平面到 3D oblong object line界定的平面转换的单应矩阵。 (察看OpenCV指南中的 3D重建一章Figure 6-10 ) 外极线几何 FindFundamentalMat 计算图像中对应点的基本矩阵 int cvFindFundamentalMat( CvMat* points1, CvMat* points2, CvMat* fundamental_matrix, int method, double param1, double param2, CvMat* status=0); points1 第一幅图像点的数组 2xN/Nx2 或 3xN/Nx3 (N 点的个数). 点坐标应该是浮点数(双精 度或单精度)。 points2 第二副图像的点的数组,格式、大小与第一幅图像相同。 fundamental_matrix 输出的基本矩阵。大小是 3x3 or 9x3 (7-point method can returns up to 3 matrices). method 计算基本矩阵的方法 CV_FM_7POINT - for 7-point algorithm. Number of points == 7 CV_FM_8POINT - for 8-point algorithm. Number of points >= 8 CV_FM_RANSAC - for RANSAC algorithm. Number of points >= 8 CV_FM_LMEDS - for LMedS algorithm. Number of points >= 8 param1 这个参数只用于方法 RANSAC 或 LMedS 。它是点到外极线的最大距离,超过这个值 的点将被舍弃,不用于后面的计算。通常这个值的设定是 0.5 or 1.0 。 param2 这个参数只用于方法 RANSAC 或 LMedS 。 它表示矩阵在某种精度上的正确的理想 值。例如可以被设为 0.99 。 status N 个元素的数组, 在计算过程中没有被舍弃的点,元素被被置为 1。否则置为 0。数组由 方法 RANSAC and LMedS 计算。其它方法的时候 status 被置为 1's。这个参数是可选的。 Th 外极线几何可以用下面的等式描述: p2 T*F*p1=0, 其中 F 是基本矩阵,ip1 和 p2 分别是两幅图上的对应点。 函数 FindFundamentalMat 利用上面列出的四种方法之一计算基本矩阵,并返回 基本矩阵的值:没有找到矩阵,返回 0,找到一个矩阵返回 1,多个矩阵返回 3。 基本矩阵可以用来进一步计算两幅图像的对应外极点的坐标。 7 点法使用确定的 7 个点。这种方法能找到 1 个或者 3 个基本矩阵,返回矩 阵的个数;如果目标数组中有足够的空间存储所有检测到的矩阵,该函数将 所有矩阵存储,否则,只存贮其中之一。其它方法使用 8 点或者更多点并且 返回一个基本矩阵。 Example. Fundamental matrix calculation int point_count = 100; CvMat* points1; CvMat* points2; CvMat* status; CvMat* fundamental_matrix; points1 = cvCreateMat(2,point_count,CV_32F); points2 = cvCreateMat(2,point_count,CV_32F); status = cvCreateMat(1,point_count,CV_32F); /* Fill the points here ... */ fundamental_matrix = cvCreateMat(3,3,CV_32F); int num = cvFindFundamentalMat(points1,points2,fundamental_matrix,CV_FM_RANSAC,1.0,0.99,s tatus); if( num == 1 ) { printf("Fundamental matrix was found\n"); } else { printf("Fundamental matrix was not found\n"); } /*====== Example of code for three matrixes ======*/ CvMat* points1; CvMat* points2; CvMat* fundamental_matrix; points1 = cvCreateMat(2,7,CV_32F); points2 = cvCreateMat(2,7,CV_32F); /* Fill the points here... */ fundamental_matrix = cvCreateMat(9,3,CV_32F); int num = cvFindFundamentalMat(points1,points2,fundamental_matrix,CV_FM_7POINT,0,0,0); printf("Found %d matrixes\n",num); ComputeCorrespondEpilines 根据一幅图像中的点计算其在另一幅图像中对应的外极线。 void cvComputeCorrespondEpilines( const CvMat* points, int which_image, const CvMat* fundamental_matrix, CvMat* correspondent_lines); points 输入点: 2xN or 3xN array (N 点的个数) which_image 包含点的图像指数(1 or 2) fundamental_matrix 基本矩阵 correspondent_lines 计算外极点, 3xN array 函数 ComputeCorrespondEpilines 根据外级线几何的基本方程计算每个输入点 的对应外级线。如果点位于第一幅图像(which_image=1),对应的外极线可以如 下计算 : l2=F*p1 其中 F 是基本矩阵,p1 是第一幅图像中的点, l2 - 是与第二幅对应的外极线。如果点 位于第二副图像中 which_image=2),计算如下: l1=FT*p2 其中 p2 是第二幅图像中的点,l1 是对应于第一幅图像的外极线 每条外极线都可以用三个系数表示 a, b, c: a*x + b*y + c = 0 归一化后的外极线系数存储在 correspondent_lines. 按字母顺序的函数列表 2 2DRotationMatrix A Acc ApproxChains ArcLength AdaptiveThreshold ApproxPoly B BoundingRect BoxPoints C CalcBackProject CalibrateCamera ConvexityDefects CalcBackProjectPatch CalibrateCamera_64d CopyHist CalcEMD2 CamShift CornerEigenValsAndVecs CalcGlobalOrientation Canny CornerMinEigenVal CalcHist CheckContourConvexity CreateConDensation CalcImageHomography ClearHist CreateContourTree CalcMotionGradient ClearSubdivVoronoi2D CreateHist CalcOpticalFlowBM CompareHist CreateKalman CalcOpticalFlowHS ComputeCorrespondEpilines CreatePOSITObject CalcOpticalFlowLK ConDensInitSampleSet CreateStructuringElementEx CalcOpticalFlowPyrLK ConDensUpdateByTime CreateSubdivDelaunay2D CalcPGH ContourArea CvtColor CalcProbDensity ContourFromContourTree CalcSubdivVoronoi2D ConvexHull2 D Dilate DistTransform E EndFindContours Erode F Filter2D FindExtrinsicCameraParams FindNextContour FindChessBoardCornerGuesses FindExtrinsicCameraParams_64d FitEllipse FindContours FindFundamentalMat FitLine2D FindCornerSubPix FindNearestPoint2D FloodFill G GetCentralMoment GetMinMaxHistValue GetRectSubPix GetHistValue_1D GetNormalizedCentralMoment GetSpatialMoment GetHuMoments GetQuadrangleSubPix GoodFeaturesToTrack H HaarDetectObjects HoughLines I InitLineIterator Integral K KalmanCorrect KalmanPredict L Laplace LoadHaarClassifierCascade M MakeHistHeaderForArray MaxRect Moments MatchContourTrees MeanShift MorphologyEx MatchShapes MinAreaRect2 MultiplyAcc MatchTemplate MinEnclosingCircle N NormalizeHist P POSIT PyrDown PyrUp PreCornerDetect PyrSegmentation Q QueryHistValue_1D R ReadChainPoint ReleaseKalman Rodrigues ReleaseConDensation ReleasePOSITObject RunHaarClassifierCascade ReleaseHaarClassifierCascade ReleaseStructuringElement RunningAvg ReleaseHist Resize S SampleLine Sobel Subdiv2DGetEdge SegmentMotion SquareAcc Subdiv2DLocate SetHistBinRanges StartFindContours Subdiv2DRotateEdge SetImagesForHaarClassifierCascade StartReadChainPoints SubdivDelaunay2DInsert Smooth Subdiv2DEdgeDst SubstituteContour SnakeImage Subdiv2DEdgeOrg T ThreshHist Threshold U UnDistort UnDistortOnce UnDistortInit UpdateMotionHistory W WarpAffine WarpPerspective WarpPerspectiveQMatrix 参考书目 下面的参考书目列出了对于 Intel Computer Vision Library 用户的一些有 用的出版物。目录不是很全,只作为一个学习的起点。 1. [Borgefors86] Gunilla Borgefors, "Distance Transformations in Digital Images". Computer Vision, Graphics and Image Processing 34, 344-371 (1986). 2. [Bouguet00] Jean-Yves Bouguet. Pyramidal Implementation of the Lucas Kanade Feature Tracker. The paper is included into OpenCV distribution (algo_tracking.pdf) 3. [Bradski98] G.R. Bradski. Computer vision face tracking as a component of a perceptual user interface. In Workshop on Applications of Computer Vision, pages 214?19, Princeton, NJ, Oct. 1998. Updated version can be found at http://www.intel.com/technology/itj/q21998/articles/art_2.htm. Also, it is included into OpenCV distribution (camshift.pdf) 4. [Bradski00] G. Bradski and J. Davis. Motion Segmentation and Pose Recognition with Motion History Gradients. IEEE WACV'00, 2000. 5. [Burt81] P. J. Burt, T. H. Hong, A. Rosenfeld. Segmentation and Estimation of Image Region Properties Through Cooperative Hierarchical Computation. IEEE Tran. On SMC, Vol. 11, N.12, 1981, pp. 802-809. 6. [Canny86] J. Canny. A Computational Approach to Edge Detection, IEEE Trans. on Pattern Analysis and Machine Intelligence, 8(6), pp. 679-698 (1986). 7. [Davis97] J. Davis and Bobick. The Representation and Recognition of Action Using Temporal Templates. MIT Media Lab Technical Report 402, 1997. 8. [DeMenthon92] Daniel F. DeMenthon and Larry S. Davis. Model-Based Object Pose in 25 Lines of Code. In Proceedings of ECCV '92, pp. 335-343, 1992. 9. [Fitzgibbon95] Andrew W. Fitzgibbon, R.B.Fisher. A Buyer's Guide to Conic Fitting. Proc.5th British Machine Vision Conference, Birmingham, pp. 513-522, 1995. 10. [Horn81] Berthold K.P. Horn and Brian G. Schunck. Determining Optical Flow. Artificial Intelligence, 17, pp. 185-203, 1981. 11. [Hu62] M. Hu. Visual Pattern Recognition by Moment Invariants, IRE Transactions on Information Theory, 8:2, pp. 179-187, 1962. 12. [Iivarinen97] Jukka Iivarinen, Markus Peura, Jaakko Srel, and Ari Visa. Comparison of Combined Shape Descriptors for Irregular Objects, 8th British Machine Vision Conference, BMVC'97. http://www.cis.hut.fi/research/IA/paper/publications/bmvc97/bmvc97.html 13. [Jahne97] B. Jahne. Digital Image Processing. Springer, New York, 1997. 14. [Lucas81] Lucas, B., and Kanade, T. An Iterative Image Registration Technique with an Application to Stereo Vision, Proc. of 7th International Joint Conference on Artificial Intelligence (IJCAI), pp. 674-679. 15. [Kass88] M. Kass, A. Witkin, and D. Terzopoulos. Snakes: Active Contour Models, International Journal of Computer Vision, pp. 321-331, 1988. 16. [Lienhart02] Rainer Lienhart and Jochen Maydt. An Extended Set of Haar-like Features for Rapid Object Detection. IEEE ICIP 2002, Vol. 1, pp. 900-903, Sep. 2002. This paper, as well as the extended technical report, can be retrieved at http://www.lienhart.de/Publications/publications.html 17. [Matas98] J.Matas, C.Galambos, J.Kittler. Progressive Probabilistic Hough Transform. British Machine Vision Conference, 1998. 18. [Rosenfeld73] A. Rosenfeld and E. Johnston. Angle Detection on Digital Curves. IEEE Trans. Computers, 22:875-878, 1973. 19. [RubnerJan98] Y. Rubner. C. Tomasi, L.J. Guibas. Metrics for Distributions with Applications to Image Databases. Proceedings of the 1998 IEEE International Conference on Computer Vision, Bombay, India, January 1998, pp. 59-66. 20. [RubnerSept98] Y. Rubner. C. Tomasi, L.J. Guibas. The Earth Mover's Distance as a Metric for Image Retrieval. Technical Report STAN-CS-TN-98-86, Department of Computer Science, Stanford University, September 1998. 21. [RubnerOct98] Y. Rubner. C. Tomasi. Texture Metrics. Proceeding of the IEEE International Conference on Systems, Man, and Cybernetics, San-Diego, CA, October 1998, pp. 4601-4607. http://robotics.stanford.edu/~rubner/publications.html 22. [Serra82] J. Serra. Image Analysis and Mathematical Morphology. Academic Press, 1982. 23. [Schiele00] Bernt Schiele and James L. Crowley. Recognition without Correspondence Using Multidimensional Receptive Field Histograms. In International Journal of Computer Vision 36 (1), pp. 31-50, January 2000. 24. [Suzuki85] S. Suzuki, K. Abe. Topological Structural Analysis of Digital Binary Images by Border Following. CVGIP, v.30, n.1. 1985, pp. 32-46. 25. [Teh89] C.H. Teh, R.T. Chin. On the Detection of Dominant Points on Digital Curves. - IEEE Tr. PAMI, 1989, v.11, No.8, p. 859-872. 26. [Trucco98] Emanuele Trucco, Alessandro Verri. Introductory Techniques for 3-D Computer Vision. Prentice Hall, Inc., 1998. 27. [Viola01] Paul Viola and Michael J. Jones. Rapid Object Detection using a Boosted Cascade of Simple Features. IEEE CVPR, 2001. The paper is available online at http://www.ai.mit.edu/people/viola/ 28. [Welch95] Greg Welch, Gary Bishop. An Introduction To the Kalman Filter. Technical Report TR95-041, University of North Carolina at Chapel Hill, 1995. Online version is available at http://www.cs.unc.edu/~welch/kalman/kalman_filter/kalman.html 29. [Williams92] D. J. Williams and M. Shah. A Fast Algorithm for Active Contours and Curvature Estimation. CVGIP: Image Understanding, Vol. 55, No. 1, pp. 14-26, Jan., 1992. http://www.cs.ucf.edu/~vision/papers/shah/92/WIS92A.pdf. 30. [Yuille89] A.Y.Yuille, D.S.Cohen, and P.W.Hallinan. Feature Extraction from Faces Using Deformable Templates in CVPR, pp. 104-109, 1989. 31. [Zhang96] Z. Zhang. Parameter Estimation Techniques: A Tutorial with Application to Conic Fitting, Image and Vision Computing Journal, 1996. 32. [Zhang99] Z. Zhang. Flexible Camera Calibration By Viewing a Plane From Unknown Orientations. International Conference on Computer Vision (ICCV'99), Corfu, Greece, pages 666-673, September 1999. 33. [Zhang00] Z. Zhang. A Flexible New Technique for Camera Calibration. IEEE Transactions on Pattern Analysis and Machine Intelligence, 22(11):1330-1334, 2000.
还剩115页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

tsq820612

贡献于2015-01-13

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf